From eb6a66b689158a087979f59fa0224ac3c93d8bad Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Thu, 18 Sep 2014 00:50:32 +0100 Subject: [PATCH] Refactor our displayhook IPython integration --- src/sage/doctest/forker.py | 4 +- src/sage/groups/matrix_gps/matrix_group.py | 2 +- src/sage/misc/displayhook.py | 592 +-------------------- src/sage/misc/latex.py | 2 +- src/sage/misc/sageinspect.py | 18 +- src/sage/repl/display/__init__.py | 0 src/sage/repl/display/fancy_repr.py | 372 +++++++++++++ src/sage/repl/display/formatter.py | 281 ++++++++++ src/sage/repl/display/pretty_print.py | 176 ++++++ src/sage/repl/display/python_hook.py | 62 +++ src/sage/repl/display/util.py | 163 ++++++ src/sage/repl/interpreter.py | 5 + src/sage/repl/ipython_extension.py | 4 +- src/sage/structure/parent.pyx | 2 +- 14 files changed, 1083 insertions(+), 600 deletions(-) create mode 100644 src/sage/repl/display/__init__.py create mode 100644 src/sage/repl/display/fancy_repr.py create mode 100644 src/sage/repl/display/formatter.py create mode 100644 src/sage/repl/display/pretty_print.py create mode 100644 src/sage/repl/display/python_hook.py create mode 100644 src/sage/repl/display/util.py diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index 47c8cdc6c8f..78a5206ad89 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -100,8 +100,8 @@ def init_sage(): sage.doctest.DOCTEST_MODE=True import sage.all_cmdline sage.interfaces.quit.invalidate_all() - import sage.misc.displayhook - sys.displayhook = sage.misc.displayhook.DisplayHook() + import sage.repl.display.python_hook + sys.displayhook = sage.repl.display.python_hook.DoctestDisplayHook() # Switch on extra debugging from sage.structure.debug_options import debug diff --git a/src/sage/groups/matrix_gps/matrix_group.py b/src/sage/groups/matrix_gps/matrix_group.py index e920b5d9cef..b0185ea01ce 100644 --- a/src/sage/groups/matrix_gps/matrix_group.py +++ b/src/sage/groups/matrix_gps/matrix_group.py @@ -230,7 +230,7 @@ def _repr_(self): return 'Matrix group over {0} with {1} generators'.format( self.base_ring(), self.ngens()) else: - from sage.misc.displayhook import format_list + from sage.repl.display.util import format_list return 'Matrix group over {0} with {1} generators {2}'.format( self.base_ring(), self.ngens(), format_list(self.gens())) diff --git a/src/sage/misc/displayhook.py b/src/sage/misc/displayhook.py index 80d1cdff0fc..3878231620f 100644 --- a/src/sage/misc/displayhook.py +++ b/src/sage/misc/displayhook.py @@ -1,600 +1,28 @@ -# -*- coding: utf-8 -*- -r""" -Implements a displayhook for Sage. - -This displayhook has two new facilities, by default the displayhook contains a -new facility for displaying lists of matrices in an easier to read format:: - - sage: [identity_matrix(i) for i in range(2,5)] - [ - [1 0 0 0] - [1 0 0] [0 1 0 0] - [1 0] [0 1 0] [0 0 1 0] - [0 1], [0 0 1], [0 0 0 1] - ] - -This facility uses :meth:`_repr_` (and a simple string) to try do a nice read -format (see :meth:`sage.structure.parent._repr_option` for details). - -With this displayhook there exists an other way for displaying object and more -generally, all sage expression as an ASCII art object:: - - sage: from sage.repl.interpreter import get_test_shell - sage: shell = get_test_shell() - sage: shell.run_cell('%display ascii_art') - sage: shell.run_cell('integral(x^2/pi^x, x)') - / 2 2 \ -x*log(pi) - -\x *log (pi) + 2*x*log(pi) + 2/*e - --------------------------------------------- - 3 - log (pi) - sage: shell.run_cell("i = var('i')") - sage: shell.run_cell('sum(i*x^i, i, 0, 10)') - 10 9 8 7 6 5 4 3 2 - 10*x + 9*x + 8*x + 7*x + 6*x + 5*x + 4*x + 3*x + 2*x + x - sage: shell.run_cell('StandardTableaux(4).list()') - [ - [ 1 4 1 3 - [ 1 3 4 1 2 4 1 2 3 1 3 1 2 2 2 - [ 1 2 3 4, 2 , 3 , 4 , 2 4, 3 4, 3 , 4 - - 1 ] - 1 2 2 ] - 3 3 ] - , 4 , 4 ] - sage: shell.run_cell('%display simple') - -This other facility uses a simple `AsciiArt` object -(see :class:`sage.misc.ascii_art.AsciiArt` and -:meth:`sage.structure.parent._ascii_art_`). - -AUTHORS: - -- Bill Cauchois (2009): initial version -- Jean-Baptiste Priez (2013): ASCII art -- Volker Braun (2013): refactored into DisplayHookBase """ +Old Displayhook -import __builtin__ -import sys -import types - -from sage.misc.lazy_import import lazy_import -lazy_import('sage.matrix.matrix', 'is_Matrix') - - -class ListFormatter(object): - - # This is used to wrap lines when printing "tall" lists. - MAX_COLUMN = 70 - - def _check_tall_list_and_format(self, the_list): - """ - First check whether a list is "tall" -- whether the reprs of the - elements of the list will span multiple lines and cause the list - to be printed awkwardly. If not, this function returns ``None`` and - does nothing; you should revert back to the normal method for - printing an object (its repr). If so, return the string in the - special format. Note that the special format isn't just for - matrices. Any object with a multiline repr will be formatted. - - INPUT: - - - ``the_list`` - The list (or a tuple). - - TESTS:: - - sage: from sage.misc.displayhook import DisplayHookBase - sage: dhb = DisplayHookBase() - - We test :meth:`_check_tall_list_and_format` indirectly by - calling :meth:`simple_format_obj` on a list of matrices:: - - sage: print dhb.simple_format_obj( - ....: [matrix([[1, 2, 3, 4], [5, 6, 7, 8]]) for i in xrange(7)]) - [ - [1 2 3 4] [1 2 3 4] [1 2 3 4] [1 2 3 4] [1 2 3 4] [1 2 3 4] - [5 6 7 8], [5 6 7 8], [5 6 7 8], [5 6 7 8], [5 6 7 8], [5 6 7 8], - - [1 2 3 4] - [5 6 7 8] - ] - - We return ``None`` if we don't have anything special to do:: - - sage: dhb.simple_format_obj('one-line string') - sage: dhb.simple_format_obj(matrix([[1,2,3]])) - """ - # For every object to be printed, split its repr on newlines and store the - # result in this list. - split_reprs = [] - tall = False - for elem in the_list: - split_reprs.append(repr(elem).split('\n')) - if len(split_reprs[-1]) > 1: - # Meanwhile, check to make sure the list is actually "tall". - tall = True - if not tall: - return None - # Figure out which type of parenthesis to use, based on the type of the_list. - if isinstance(the_list, tuple): - parens = '()' - elif isinstance(the_list, list): - parens = '[]' - else: - raise TypeError('expected list or tuple') - - # running_lines is a list of lines, which are stored as lists of strings - # to be joined later. For each split repr, we add its lines to the - # running_lines array. When current_column exceeds MAX_COLUMN, process - # and output running_lines using _print_tall_list_row. - running_lines = [[]] - current_column = 0 - s = [parens[0]] - for split_repr in split_reprs: - width = max(len(x) for x in split_repr) - if current_column + width > self.MAX_COLUMN and not (width > self.MAX_COLUMN): - s.extend(self._tall_list_row(running_lines)) - running_lines = [[]] - current_column = 0 - current_column += width + 2 - # Add the lines from split_repr to the running_lines array. It may - # be necessary to add or remove lines from either one so that the - # number of lines matches up. - for i in xrange(len(running_lines), len(split_repr)): - running_lines.insert(0, [' ' * len(x) for x in running_lines[-1]]) - line_diff = len(running_lines) - len(split_repr) - for i, x in enumerate(split_repr): - running_lines[i + line_diff].append(x.ljust(width)) - for i in xrange(line_diff): - running_lines[i].append(' ' * width) - # Output any remaining entries. - if len(running_lines[0]) > 0: - s.extend(self._tall_list_row(running_lines, True)) - s.append(parens[1]) - return "\n".join(s) - - def _tall_list_row(self, running_lines, last_row=False): - """ - Helper for :meth:`_check_tall_list_and_format` - - This helper function processes and outputs the contents of the - running_lines array. - - TESTS:: - - sage: from sage.misc.displayhook import format_list - sage: format_list._tall_list_row(['a b', 'b c', 'c']) - ['a b', 'b c', 'c,', ''] - """ - s=[] - for i, line in enumerate(running_lines): - if i + 1 != len(running_lines): - sep, tail = ' ', '' - else: - # The commas go on the bottom line of this row. - sep, tail = ', ', '' if last_row else ',' - s.append(sep.join(line) + tail) - # Separate rows with a newline to make them stand out. - if not last_row: - s.append("") - return s - - def try_format_list(self, obj): - """ - Format list/tuple. - - OUTPUT: - - A string representation or ``None``. The latter means that no - Sage-specific formatting is defined and the default should be - used. - - EXAMPLES:: - - sage: from sage.misc.displayhook import format_list - sage: format_list.try_format_list('Hello, world!') - sage: format_list('Hello, world!') - "'Hello, world!'" - """ - ascii_art_repr = False - for o in obj: - try: - ascii_art_repr = ascii_art_repr or o.parent()._repr_option('element_ascii_art') - except (AttributeError, TypeError): - pass - if ascii_art_repr: - return self._check_tall_list_and_format(obj) - else: - return None - - def __call__(self, obj): - """ - Return a string formatting. - - This method is like :meth:`try_format_list` except that it - will always return a string. - - OUTPUT: - - String. - - EXAMPLES:: - - sage: from sage.misc.displayhook import format_list - sage: format_list.try_format_list('Hello, world!') - sage: format_list('Hello, world!') - "'Hello, world!'" - """ - s = self.try_format_list(obj) - if s is None: - return repr(obj) - else: - return s - -format_list = ListFormatter() - -class DisplayHookBase(object): - - def simple_format_obj(self, obj): - """This function is used internally by the displayhook. - - We attempt to keep ascii art of list/tuple members intact as we - print them. See :meth:`sage.structure.parent._repr_option` for - details. - - OUTPUT: - - Return a string if we want to print it in a special way; - otherwise, return ``None``. - - EXAMPLES:: - - sage: from sage.misc.displayhook import DisplayHookBase - sage: dhb = DisplayHookBase() - - For most objects, nothing is done (``None`` is returned): - - sage: dhb.simple_format_obj('Hello, world!') - sage: dhb.simple_format_obj((1, 2, 3, 4)) - - We demonstrate the special format for lists of matrices:: - - sage: dhb.simple_format_obj( - ....: [matrix([[1], [2]]), matrix([[3], [4]])]) - '[\n[1] [3]\n[2], [4]\n]' - """ - # since #15036, we check in displayhook if a matrix is has - # large enough dimensions to be printed in abbreviated form. - # If so, we append a helpful message to indicate how to print - # the entries of the matrix. - # we need to do a late import of the is_Matrix method here to - # avoid startup problems. - if is_Matrix(obj): - from sage.matrix.matrix0 import max_rows,max_cols - if obj.nrows() >= max_rows or obj.ncols() >= max_cols: - return repr(obj) + " (use the '.str()' method to see the entries)" - else: - return None - if isinstance(obj, (tuple, list)) and len(obj) > 0: - return format_list.try_format_list(obj) - else: - return None - - def set_display(self, mode): - r""" - Select the text formatting method. - - INPUT: - - - ``mode`` -- string. One of ``simple``, ``ascii_art``, or ``typeset``. - - See :func:`simple_format_obj` or :func:`sage.misc.ascii_art.ascii_art`. - - TESTS:: - - sage: [identity_matrix(i) for i in range(3,7)] - [ - [1 0 0 0 0 0] - [1 0 0 0 0] [0 1 0 0 0 0] - [1 0 0 0] [0 1 0 0 0] [0 0 1 0 0 0] - [1 0 0] [0 1 0 0] [0 0 1 0 0] [0 0 0 1 0 0] - [0 1 0] [0 0 1 0] [0 0 0 1 0] [0 0 0 0 1 0] - [0 0 1], [0 0 0 1], [0 0 0 0 1], [0 0 0 0 0 1] - ] - sage: from sage.repl.interpreter import get_test_shell - sage: shell = get_test_shell() - sage: shell.run_cell('%display ascii_art') # indirect doctest - sage: shell.run_cell("i = var('i')") - sage: shell.run_cell('sum(i*x^i, i, 0, 10)') - 10 9 8 7 6 5 4 3 2 - 10*x + 9*x + 8*x + 7*x + 6*x + 5*x + 4*x + 3*x + 2*x + x - sage: shell.run_cell('%display simple') - """ - if mode not in ['simple', 'ascii_art', 'typeset']: - raise ValueError('invalid mode set') - self.mode = mode - - mode = 'simple' - - @property - def simple(self): - """ - Whether the mode is the "simple" (default) display. - - EXAMPLES:: - - sage: sys.displayhook.simple - True - sage: sys.displayhook.ascii_art - False - """ - return self.mode == 'simple' - - @property - def ascii_art(self): - """ - Whether the mode is the ascii art display. - - EXAMPLES:: - - sage: sys.displayhook.simple - True - sage: sys.displayhook.ascii_art - False - """ - return self.mode == 'ascii_art' - - @property - def typeset(self): - """ - Whether the mode is the notebook "Typeset" display. - - EXAMPLES:: - - sage: sys.displayhook.simple - True - sage: sys.displayhook.typeset - False - """ - return self.mode == 'typeset' - - def try_format_obj(self, obj): - """ - Format non-graphics object. - - OUTPUT: - - A string representation or ``None``. The latter means that no - Sage-specific formatting is defined and the default should be - used. - - TESTS:: - - sage: from sage.misc.displayhook import DisplayHookBase - sage: dhb = DisplayHookBase() - sage: dhb.try_format_obj('Hello, world!') - """ - if self.simple: - return self.simple_format_obj(obj) - if self.ascii_art: - from sage.misc.ascii_art import ascii_art - return ascii_art(obj) - if self.typeset: - from sage.misc.latex import pretty_print - pretty_print(obj) - return '' - assert(False) - - def try_format_graphics(self, obj): - """ - Format graphics object. - - OUTPUT: - - Boolean. Whether the object is graphics and was successfully - displayed. - - TESTS:: - - sage: from sage.misc.displayhook import DisplayHookBase - sage: dhb = DisplayHookBase() - sage: dhb.try_format_graphics('Hello, world!') - False - """ - from sage.structure.sage_object import SageObject - if isinstance(obj, SageObject) and hasattr(obj, '_graphics_'): - return obj._graphics_() - return False - - -from IPython.core.formatters import PlainTextFormatter -class SagePlainTextFormatter(DisplayHookBase, PlainTextFormatter): - r""" - A replacement for the plain text formatter which can use two facilities: - - - correctly print lists of matrices or other objects (see - :meth:`sage.structure.parent._repr_option`), - - print ASCII art objects (like expressions) (see - :meth:`sage.structure.parent._ascii_art_`). - - EXAMPLES:: - - sage: from sage.repl.interpreter import get_test_shell - sage: shell = get_test_shell() - sage: shell.display_formatter.formatters['text/plain'] - <...displayhook.SagePlainTextFormatter object at 0x...> - sage: shell.run_cell('a = identity_matrix(ZZ, 2); [a,a]') - [ - [1 0] [1 0] - [0 1], [0 1] - ] - """ - def __init__(self, *args, **kwds): - import IPython.lib.pretty - IPython.lib.pretty._default_pprint = self._python_default_pprint - super(SagePlainTextFormatter, self).__init__(*args, **kwds) - - def _python_default_pprint(self, obj, p, cycle): - """ - The default print function. - - IPython 2.2.0 has a non-configurable default pretty printer - that doesn't do what we want. So we are forced to monkey-patch - it with this method. - """ - from IPython.lib.pretty import _safe_repr - p.text(_safe_repr(obj)) - - def _type_printers_default(self): - """ - The default pretty printers - - OUTPUT: - - Dictionary with key the type to handle and value a - pretty-print function for said type. - - EXAMPLES:: - - sage: shell = sage.repl.interpreter.get_test_shell() - sage: sptf = shell.display_formatter.formatters[u'text/plain'] - sage: sptf._type_printers_default() - {: , - ... - sage: shell.displayhook(type) - - sage: shell.displayhook([type, type]) - [, ] - - TESTS: - - We don't like the following IPython defaults, see :trac:`14466`: - - sage: import types - sage: def ugly(obj): # show IPython defaults - ....: from StringIO import StringIO - ....: stream = StringIO() - ....: from IPython.lib import pretty - ....: printer = pretty.RepresentationPrinter(stream, False, - ....: 78, u'\n', - ....: singleton_pprinters=pretty._singleton_pprinters, - ....: type_pprinters=pretty._type_pprinters, - ....: deferred_pprinters=pretty._deferred_type_pprinters) - ....: printer.pretty(obj) - ....: printer.flush() - ....: return stream.getvalue() - - sage: cls_type = types.ClassType('name', (), {}); cls_type - - sage: ugly(cls_type) - '__main__.name' - - sage: types.TypeType - - sage: ugly(types.TypeType) - 'type' - - sage: types.BuiltinFunctionType - - sage: ugly(types.BuiltinFunctionType) - 'builtin_function_or_method' - """ - from IPython.lib.pretty import _safe_repr - pprinters = super(SagePlainTextFormatter, self)._type_printers_default() - # We don't like how IPython abbreviates types :trac:`14466` - del pprinters[types.TypeType]# = python_default - del pprinters[types.ClassType]# = python_default - del pprinters[types.BuiltinFunctionType]# = python_default - return pprinters - - - def _deferred_printers_default(self): - pprinters = super(SagePlainTextFormatter, self)._deferred_printers_default() - return pretty._deferred_type_pprinters.copy() - - - def __call__(self, obj): - r""" - Computes the format data of ``result``. If the - :func:`sage.misc.displayhook.simple_format_obj` writes a string, then - we override IPython's :class:`DisplayHook` formatting. - - EXAMPLES:: - - sage: from sage.repl.interpreter import get_test_shell - sage: shell = get_test_shell() - sage: fmt = shell.display_formatter.formatters['text/plain'] - sage: fmt - <...displayhook.SagePlainTextFormatter object at 0x...> - sage: shell.displayhook.compute_format_data(2) - ({u'text/plain': '2'}, {}) - sage: a = identity_matrix(ZZ, 2) - sage: shell.displayhook.compute_format_data([a,a]) - ({u'text/plain': '[\n[1 0] [1 0]\n[0 1], [0 1]\n]'}, {}) - sage: fmt.set_display('ascii_art') - sage: shell.displayhook.compute_format_data([a,a]) - ({u'text/plain': [ [1 0] [1 0] ] - [ [0 1], [0 1] ]}, {}) - - sage: i = var('i') - sage: shell.displayhook.compute_format_data(sum(i*x^i, i, 0, 10)) - ({u'text/plain': 10 9 8 7 6 5 4 3 2 - 10*x + 9*x + 8*x + 7*x + 6*x + 5*x + 4*x + 3*x + 2*x + x}, {}) - sage: fmt.set_display('simple') - """ - if self.try_format_graphics(obj): - return '' - s = self.try_format_obj(obj) - if s is None: - s = super(SagePlainTextFormatter, self).__call__(obj) - return s - +Just for compatibility with the notebook, you should not use this any +more. Look into ``sage/repl/display`` instead. +""" -SPTextFormatter = None +from sage.repl.display.python_hook import DoctestDisplayHook +from sage.repl.display.formatter import SageConsoleTextFormatter -class DisplayHook(DisplayHookBase): - """ - Sage Display Hook +class DisplayHook(DoctestDisplayHook): - This is not used directly in interactive Sage (where we use the - IPython system for display hooks). This class provides a way to - use the Sage "plain text" display formatting when not using - interactive Sage, for example when running doctests. - """ def __init__(self): """ Python constructor EXAMPLES:: - sage: from sage.misc.displayhook import DisplayHook + sage: from sage.repl.display.python_hook import DisplayHook sage: d = DisplayHook() sage: d(set([1, 2, 3])) # Sage commandline output {1, 2, 3} sage: print(set([1, 2, 3])) # Plain Python output set([1, 2, 3]) """ - self.formatter = SagePlainTextFormatter() - - def __call__(self, obj): - """ - Format the object using Sage's formatting, or format it using the old - display hook if Sage does not want to handle the object. - - EXAMPLES:: - - sage: from sage.misc.displayhook import DisplayHook - sage: d = DisplayHook() - sage: d((identity_matrix(3), identity_matrix(3))) - ( - [1 0 0] [1 0 0] - [0 1 0] [0 1 0] - [0 0 1], [0 0 1] - ) - """ - if obj is None: - return - print(self.formatter(obj)) - __builtin__._ = obj + # do not call super, we set our own formatter + self.formatter = SageConsoleTextFormatter() diff --git a/src/sage/misc/latex.py b/src/sage/misc/latex.py index 41a1d12613d..b4f8de327a0 100644 --- a/src/sage/misc/latex.py +++ b/src/sage/misc/latex.py @@ -2519,7 +2519,7 @@ def pretty_print_default(enable=True): 'foo' """ import sys - sys.displayhook.set_display('typeset' if enable else 'simple') + sys.displayhook.formatter.set_display('typeset' if enable else 'simple') common_varnames = ['alpha', diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index dff4baa4fd8..be9af6f0996 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -1797,19 +1797,17 @@ def sage_getsourcelines(obj, is_binary=False): (['cdef class MPolynomialRing_libsingular(MPolynomialRing_generic):\n', '\n', ' def __cinit__(self):\n', - ... - ' M.append(new_MP(self, p_Copy(tempvector, _ring)))\n', - ' return M\n'], ...) + ...) sage: sage_getsourcelines(I) (['class MPolynomialIdeal( MPolynomialIdeal_singular_repr, \\\n', - ... - ' return result_ring.ideal(result)\n'], ...) + ...) sage: x = var('x') sage: sage_getsourcelines(x) (['cdef class Expression(CommutativeRingElement):\n', ' cpdef object pyobject(self):\n', - ... - ' return self / x\n'], ...) + ...) + sage: sage_getsourcelines(x)[0][-1] # last line + ' return self / x\n' We show some enhancements provided by :trac:`11768`. First, we use a dummy parent class that has defined an element class by a @@ -1839,8 +1837,7 @@ class Element: sage: C = Rings() sage: HC = C.hom_category() sage: sage_getsourcelines(HC) - ([' class HomCategory(HomCategory):\n', ...], ...) - + ([' class HomCategory(HomCategory):\n', ...) Testing against a bug that has occured during work on #11768:: @@ -1852,8 +1849,7 @@ class Element: ' MPolynomialIdeal_magma_repr, \\\n', ' Ideal_generic ):\n', ' def __init__(self, ring, gens, coerce=True):\n', - ... - ' return result_ring.ideal(result)\n'], ...) + ...) AUTHORS: diff --git a/src/sage/repl/display/__init__.py b/src/sage/repl/display/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/repl/display/fancy_repr.py b/src/sage/repl/display/fancy_repr.py new file mode 100644 index 00000000000..ad3d6189393 --- /dev/null +++ b/src/sage/repl/display/fancy_repr.py @@ -0,0 +1,372 @@ +# -*- coding: utf-8 -*- +""" +Representations of objects. +""" + +#***************************************************************************** +# Copyright (C) 2014 Volker Braun +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + + +import types + +from IPython.lib.pretty import ( + _safe_getattr, _baseclass_reprs, _safe_repr, + _type_pprinters, +) + +from IPython.lib import pretty + +from sage.repl.display.util import format_list + + +class ObjectReprABC(object): + """ + The abstract base class of an object representer. + """ + def __repr__(self): + """ + Return string representation. + + OUTPUT: + + String. + + EXAMPLES:: + + sage: from sage.repl.display.fancy_repr import ObjectReprABC + sage: ObjectReprABC() + ObjectReprABC pretty printer + """ + return('{0} pretty printer'.format(self.__class__.__name__)) + + def __call__(self, obj, p, cycle): + """ + Format object. + + INPUT: + + - ``obj`` -- anything. Object to format. + + - ``p`` -- PrettyPrinter instance. + + - ``cycle`` -- boolean. Whether there is a cycle. + + OUTPUT: + + Boolean. Whether the representer is applicable to ``obj``. If + ``True``, the string representation is appended to ``p``. + + EXAMPLES:: + + sage: from sage.repl.display.fancy_repr import ObjectReprABC + sage: ObjectReprABC().format_string(123) # indirect doctest + 'Error: ObjectReprABC.__call__ is abstract' + """ + p.text('Error: ObjectReprABC.__call__ is abstract') + return True + + def format_string(self, obj): + """ + For doctesting only: Directly return string. + + INPUT: + + - ``obj`` -- anything. Object to format. + + OUTPUT: + + String. + + EXAMPLES:: + + sage: from sage.repl.display.fancy_repr import ObjectReprABC + sage: ObjectReprABC().format_string(123) + 'Error: ObjectReprABC.__call__ is abstract' + """ + from sage.repl.display.pretty_print import SagePrettyPrinter + import StringIO + stream = StringIO.StringIO() + p = SagePrettyPrinter(stream, 79, '\n') + ok = self(obj, p, False) + if ok: + p.flush() + return stream.getvalue() + else: + return '--- object not handled by representer ---' + + +class SomeIPythonRepr(ObjectReprABC): + + def __init__(self): + """ + Some selected representers from IPython + + EXAMPLES:: + + sage: from sage.repl.display.fancy_repr import SomeIPythonRepr + sage: SomeIPythonRepr() + SomeIPythonRepr pretty printer + """ + type_repr = _type_pprinters.copy() + del type_repr[types.TypeType] + del type_repr[types.ClassType] + del type_repr[types.BuiltinFunctionType] + del type_repr[types.FunctionType] + del type_repr[str] + self._type_repr = type_repr + + def __call__(self, obj, p, cycle): + """ + Format object. + + INPUT: + + - ``obj`` -- anything. Object to format. + + - ``p`` -- PrettyPrinter instance. + + - ``cycle`` -- boolean. Whether there is a cycle. + + OUTPUT: + + Boolean. Whether the representer is applicable to ``obj``. If + ``True``, the string representation is appended to ``p``. + + EXAMPLES:: + + sage: from sage.repl.display.fancy_repr import SomeIPythonRepr + sage: pp = SomeIPythonRepr() + sage: pp.format_string(set([1, 2, 3])) + '{1, 2, 3}' + """ + try: + pretty_repr = self._type_repr[type(obj)] + except KeyError: + return False + pretty_repr(obj, p, cycle) + return True + + +class LargeMatrixHelpRepr(ObjectReprABC): + """ + Representation including help for large Sage matrices + """ + + def __call__(self, obj, p, cycle): + """ + Format matrix. + + INPUT: + + - ``obj`` -- anything. Object to format. + + - ``p`` -- PrettyPrinter instance. + + - ``cycle`` -- boolean. Whether there is a cycle. + + OUTPUT: + + Boolean. Whether the representer is applicable to ``obj``. If + ``True``, the string representation is appended to ``p``. + + EXAMPLES:: + + sage: from sage.repl.display.fancy_repr import LargeMatrixHelpRepr + sage: M = identity_matrix(40) + sage: pp = LargeMatrixHelpRepr() + sage: pp.format_string(M) + "40 x 40 dense matrix over Integer Ring (use the '.str()' method to see the entries)" + sage: pp.format_string([M, M]) + '--- object not handled by representer ---' + + Leads to:: + + sage: M + 40 x 40 dense matrix over Integer Ring (use the '.str()' method to see the entries) + sage: [M, M] + [40 x 40 dense matrix over Integer Ring, + 40 x 40 dense matrix over Integer Ring] + """ + if not p.toplevel(): + # Do not print the help for matrices inside containers + return False + from sage.matrix.matrix1 import Matrix + if not isinstance(obj, Matrix): + return False + from sage.matrix.matrix0 import max_rows, max_cols + if obj.nrows() < max_rows and obj.ncols() < max_cols: + return False + p.text( + str(obj) + " (use the '.str()' method to see the entries)" + ) + return True + + + +class PlainPythonRepr(ObjectReprABC): + """ + The ordinary Python representation + """ + + def __call__(self, obj, p, cycle): + """ + Format matrix. + + INPUT: + + - ``obj`` -- anything. Object to format. + + - ``p`` -- PrettyPrinter instance. + + - ``cycle`` -- boolean. Whether there is a cycle. + + OUTPUT: + + Boolean. Whether the representer is applicable to ``obj``. If + ``True``, the string representation is appended to ``p``. + + EXAMPLES:: + + sage: from sage.repl.display.fancy_repr import PlainPythonRepr + sage: pp = PlainPythonRepr() + sage: pp.format_string(type(1)) + "" + """ + klass = _safe_getattr(obj, '__class__', None) or type(obj) + klass_repr = _safe_getattr(klass, '__repr__', None) + if klass_repr in _baseclass_reprs: + p.text(klass_repr(obj)) + else: + # A user-provided repr. Find newlines and replace them with p.break_() + output = _safe_repr(obj) + for idx, output_line in enumerate(output.splitlines()): + if idx: + p.break_() + p.text(output_line) + return True + + +class AsciiArtRepr(ObjectReprABC): + """ + Ascii Art representation + """ + + def __call__(self, obj, p, cycle): + """ + Return ascii art format. + + INPUT: + + - ``obj`` -- anything. Object to format. + + - ``p`` -- PrettyPrinter instance. + + - ``cycle`` -- boolean. Whether there is a cycle. + + OUTPUT: + + Boolean. Whether the representer is applicable to ``obj``. If + ``True``, the string representation is appended to ``p``. + + EXAMPLES:: + + sage: from sage.repl.display.fancy_repr import AsciiArtRepr + sage: pp = AsciiArtRepr() + sage: pp.format_string(x/2) + 'x\n-\n2' + """ + from sage.misc.ascii_art import ascii_art + output = ascii_art(obj) + p.text(output) + return True + + +class TypesetRepr(ObjectReprABC): + """ + Typeset representation + """ + + def __call__(self, obj, p, cycle): + """ + Return typeset format. + + INPUT: + + - ``obj`` -- anything. Object to format. + + - ``p`` -- PrettyPrinter instance. + + - ``cycle`` -- boolean. Whether there is a cycle. + + OUTPUT: + + Boolean. Whether the representer is applicable to ``obj``. If + ``True``, the string representation is appended to ``p``. + + EXAMPLES:: + + sage: from sage.repl.display.fancy_repr import TypesetRepr + sage: pp = TypesetRepr() + sage: pp.format_string(x/2) + + '' + """ + # We should probably return that as string, but + # latex.pretty_print doesn't give us a useful interface + from sage.misc.latex import pretty_print + pretty_print(obj) + return True + + +class TallListRepr(ObjectReprABC): + """ + Special representation for lists with tall entries (e.g. matrices) + """ + + def __call__(self, obj, p, cycle): + """ + Format list/tuple. + + INPUT: + + - ``obj`` -- anything. Object to format. + + - ``p`` -- PrettyPrinter instance. + + - ``cycle`` -- boolean. Whether there is a cycle. + + OUTPUT: + + Boolean. Whether the representer is applicable to ``obj``. If + ``True``, the string representation is appended to ``p``. + + EXAMPLES:: + + sage: from sage.repl.display.fancy_repr import TallListRepr + sage: format_list = TallListRepr().format_string + sage: format_list([1, 2, identity_matrix(2)]) + '[\n [1 0]\n1, 2, [0 1]\n]' + """ + if not (isinstance(obj, (tuple, list)) and len(obj) > 0): + return False + ascii_art_repr = False + for o in obj: + try: + ascii_art_repr = ascii_art_repr or o.parent()._repr_option('element_ascii_art') + except (AttributeError, TypeError): + pass + if not ascii_art_repr: + return False + output = format_list.try_format(obj) + if output is None: + return False + p.text(output) + return True + + diff --git a/src/sage/repl/display/formatter.py b/src/sage/repl/display/formatter.py new file mode 100644 index 00000000000..8d7e4f41339 --- /dev/null +++ b/src/sage/repl/display/formatter.py @@ -0,0 +1,281 @@ +# -*- coding: utf-8 -*- +r""" +IPython Displayhook Formatters + +The classes in this module can be used as IPython displayhook +formatters. It has two main features, by default the displayhook +contains a new facility for displaying lists of matrices in an easier +to read format:: + + sage: [identity_matrix(i) for i in range(2,5)] + [ + [1 0 0 0] + [1 0 0] [0 1 0 0] + [1 0] [0 1 0] [0 0 1 0] + [0 1], [0 0 1], [0 0 0 1] + ] + +This facility uses :meth:`_repr_` (and a simple string) to try do a nice read +format (see :meth:`sage.structure.parent._repr_option` for details). + +With this displayhook there exists an other way for displaying object and more +generally, all sage expression as an ASCII art object:: + + sage: from sage.repl.interpreter import get_test_shell + sage: shell = get_test_shell() + sage: shell.run_cell('%display ascii_art') + sage: shell.run_cell('integral(x^2/pi^x, x)') + / 2 2 \ -x*log(pi) + -\x *log (pi) + 2*x*log(pi) + 2/*e + --------------------------------------------- + 3 + log (pi) + sage: shell.run_cell("i = var('i')") + sage: shell.run_cell('sum(i*x^i, i, 0, 10)') + 10 9 8 7 6 5 4 3 2 + 10*x + 9*x + 8*x + 7*x + 6*x + 5*x + 4*x + 3*x + 2*x + x + sage: shell.run_cell('StandardTableaux(4).list()') + [ + [ 1 4 1 3 + [ 1 3 4 1 2 4 1 2 3 1 3 1 2 2 2 + [ 1 2 3 4, 2 , 3 , 4 , 2 4, 3 4, 3 , 4 + + 1 ] + 1 2 2 ] + 3 3 ] + , 4 , 4 ] + sage: shell.run_cell('%display simple') + +This other facility uses a simple `AsciiArt` object (see +:class:`sage.misc.ascii_art.AsciiArt` and +:meth:`sage.structure.parent._ascii_art_`). +""" + +#***************************************************************************** +# Copyright (C) 2014 Volker Braun +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + + +from IPython.core.formatters import PlainTextFormatter, warn_format_error +from IPython.utils.py3compat import str_to_unicode, unicode_to_str + +from sage.repl.display.pretty_print import ( + SagePrettyPrinter, AsciiArtPrettyPrinter, TypesetPrettyPrinter +) + + +class SagePlainTextFormatter(PlainTextFormatter): + + def __init__(self, *args, **kwds): + r""" + Improved plain text formatter. + + In particular, it has the following two features: + + - correctly print lists of matrices or other objects (see + :meth:`sage.structure.parent._repr_option`), + + - print ASCII art objects (like expressions) (see + :meth:`sage.structure.parent._ascii_art_`). + + EXAMPLES:: + + sage: from sage.repl.interpreter import get_test_shell + sage: shell = get_test_shell() + sage: shell.display_formatter.formatters['text/plain'] + + sage: shell.run_cell('a = identity_matrix(ZZ, 2); [a,a]') + [ + [1 0] [1 0] + [0 1], [0 1] + ] + """ + super(SagePlainTextFormatter, self).__init__(*args, **kwds) + self.set_display('simple') + + def set_display(self, mode): + r""" + Select the text formatting method. + + INPUT: + + - ``mode`` -- string. One of ``simple``, ``ascii_art``, or ``typeset``. + + EXAMPLES:: + + sage: [identity_matrix(i) for i in range(3,7)] + [ + [1 0 0 0 0 0] + [1 0 0 0 0] [0 1 0 0 0 0] + [1 0 0 0] [0 1 0 0 0] [0 0 1 0 0 0] + [1 0 0] [0 1 0 0] [0 0 1 0 0] [0 0 0 1 0 0] + [0 1 0] [0 0 1 0] [0 0 0 1 0] [0 0 0 0 1 0] + [0 0 1], [0 0 0 1], [0 0 0 0 1], [0 0 0 0 0 1] + ] + sage: from sage.repl.interpreter import get_test_shell + sage: shell = get_test_shell() + sage: shell.run_cell('%display ascii_art') # indirect doctest + sage: shell.run_cell("i = var('i')") + sage: shell.run_cell('sum(i*x^i, i, 0, 10)') + 10 9 8 7 6 5 4 3 2 + 10*x + 9*x + 8*x + 7*x + 6*x + 5*x + 4*x + 3*x + 2*x + x + sage: shell.run_cell('%display simple') + """ + if mode not in ['simple', 'ascii_art', 'typeset']: + raise ValueError('invalid mode set') + self._mode = mode + self._pretty_printer_class = dict( + simple=SagePrettyPrinter, + ascii_art=AsciiArtPrettyPrinter, + typeset=TypesetPrettyPrinter, + )[mode] + + _mode = 'simple' + + @property + def simple(self): + """ + Whether the mode is the "simple" (default) display. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: sys.displayhook.formatter.simple + True + sage: sys.displayhook.formatter.ascii_art + False + """ + return self._mode == 'simple' + + @property + def ascii_art(self): + """ + Whether the mode is the ascii art display. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: sys.displayhook.formatter.simple + True + sage: sys.displayhook.formatter.ascii_art + False + """ + return self._mode == 'ascii_art' + + @property + def typeset(self): + """ + Whether the mode is the notebook "Typeset" display. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: sys.displayhook.formatter.simple + True + sage: sys.displayhook.formatter.typeset + False + """ + return self._mode == 'typeset' + + @warn_format_error + def __call__(self, obj): + """ + Compute the pretty representation of the object. + + Adapted from ``IPython.core.formatters.PlainTextPrettyPrint``. + + INPUT: + + - ``obj`` -- anything. + + OUTPUT: + + String. The plain text representation. + + EXAMPLES:: + + sage: from sage.repl.interpreter import get_test_shell + sage: shell = get_test_shell() + sage: fmt = shell.display_formatter.formatters['text/plain'] + sage: fmt + + sage: shell.displayhook.compute_format_data(2) + ({u'text/plain': '2'}, {}) + sage: a = identity_matrix(ZZ, 2) + sage: shell.displayhook.compute_format_data([a,a]) + ({u'text/plain': '[\n[1 0] [1 0]\n[0 1], [0 1]\n]'}, {}) + sage: fmt.set_display('ascii_art') + sage: shell.displayhook.compute_format_data([a,a]) + ({u'text/plain': '[ [1 0] [1 0] ]\n[ [0 1], [0 1] ]'}, {}) + sage: i = var('i') + sage: shell.displayhook.compute_format_data(sum(i*x^i, i, 0, 10)) + ({u'text/plain': ' 10 9 8 7 6 5 4 3 + 2 \n10*x + 9*x + 8*x + 7*x + 6*x + 5*x + 4*x + 3*x + 2*x + x'}, + {}) + sage: fmt.set_display('simple') + """ + import StringIO + stream = StringIO.StringIO() + printer = self._pretty_printer_class( + stream, self.max_width, unicode_to_str(self.newline)) + printer.pretty(obj) + printer.flush() + return stream.getvalue() + + +class SageConsoleTextFormatter(SagePlainTextFormatter): + + @warn_format_error + def __call__(self, obj): + """ + Display ``obj``. + + This is the default formatter for the Sage command line + interface. + + If the object is graphics, the empty string is written to the + output. An external viewer is started in a separate process as + a side effect. + + Otherwise, the usual textual representation is generated. + + INPUT: + + - ``obj`` -- anything. + + OUTPUT: + + String. The plain text representation. + + EXAMPLES:: + + sage: class FooGraphics(SageObject): + ....: def _graphics_(self): + ....: print('showing graphics') + ....: return True + sage: from sage.repl.display.formatter import SageConsoleTextFormatter + sage: fmt = SageConsoleTextFormatter() + sage: fmt(FooGraphics()) + showing graphics + '' + """ + from sage.structure.sage_object import SageObject + if isinstance(obj, SageObject) and hasattr(obj, '_graphics_'): + success = obj._graphics_() + if success: + return '' + return super(SageConsoleTextFormatter, self).__call__(obj) diff --git a/src/sage/repl/display/pretty_print.py b/src/sage/repl/display/pretty_print.py new file mode 100644 index 00000000000..7c5651c7760 --- /dev/null +++ b/src/sage/repl/display/pretty_print.py @@ -0,0 +1,176 @@ +# -*- coding: utf-8 -*- +""" +The Sage pretty printer + +Any transformation to a string goes through here. In other words, the +:class:`~sage.repl.displayhook.formatter.SagePlainTextFormatter` is +entirely implemented via :class:`SagePrettyPrinter`. Other formatters +may or may not use :class:`SagePrettyPrinter` to generate text output. + +AUTHORS: + +- Bill Cauchois (2009): initial version +- Jean-Baptiste Priez (2013): ASCII art +- Volker Braun (2013): refactored into DisplayHookBase +""" + +#***************************************************************************** +# Copyright (C) 2014 Volker Braun +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + + +from IPython.lib.pretty import PrettyPrinter + +from sage.repl.display.fancy_repr import * + + + + +class SagePrettyPrinter(PrettyPrinter): + + DEBUG = False + + # These object representers will be tried, in this order, until + # one is found that is able to deal with the object. + pretty_repr = ( + TallListRepr(), + LargeMatrixHelpRepr(), + SomeIPythonRepr(), + PlainPythonRepr(), + ) + + def toplevel(self): + """ + Return whether we are currently at the top level. + + OUTPUT: + + Boolean. Whether we are currently pretty-printing an object at + the outermost level (``True``), or whether the object is + inside a container (``False``). + + EXAMPLES:: + + sage: from sage.repl.display.pretty_print import SagePrettyPrinter + sage: import StringIO + sage: stream = StringIO.StringIO() + sage: spp = SagePrettyPrinter(stream, 78, '\n') + sage: spp.toplevel() + True + """ + return len(self.stack) <= 1 # only the object currently being represented + + def __init__(self, output, max_width, newline, max_seq_length=None): + """ + Pretty print Sage objects for the commandline + + INPUT: + + See IPython documentation. + + EXAMPLES:: + + sage: 123 + 123 + + IPython pretty printers:: + + sage: set({1, 2, 3}) + {1, 2, 3} + sage: dict(zzz=123, aaa=99, xab=10) # sorted by keys + {'aaa': 99, 'xab': 10, 'zzz': 123} + + These are overridden in IPython in a way that we feel is somewhat + confusing, and we prefer to print them like plain Python which is + more informative. See :trac:`14466` :: + + sage: 'this is a string' + 'this is a string' + sage: type(123) + + sage: type + + sage: [type, type] + [, ] + sage: import types + sage: types.ClassType('name', (), {}) + + sage: types.TypeType + + sage: types.BuiltinFunctionType + + + sage: def foo(): pass + sage: foo + + """ + super(SagePrettyPrinter, self).__init__( + output, max_width, newline, max_seq_length=max_seq_length) + self.stack = [] + + def pretty(self, obj): + r""" + Pretty print ``obj`` + + This is the only method that outside code should invoke. + + INPUT: + + - ``obj`` -- anything. + + OUTPUT: + + String representation for object. + + EXAMPLES:: + + sage: from sage.repl.display.pretty_print import SagePrettyPrinter + sage: import StringIO + sage: stream = StringIO.StringIO() + sage: SagePrettyPrinter(stream, 78, '\n').pretty([type, 123, 'foo']) + sage: stream.getvalue() + "[," + """ + obj_id = id(obj) + cycle = obj_id in self.stack + self.stack.append(obj_id) + self.begin_group() + try: + ok = False + for representation in self.pretty_repr: + if self.DEBUG: print('Trying {0}'.format(representation)) + ok = representation(obj, self, cycle) + if self.DEBUG: print('ok = {0}'.format(ok)) + if ok not in [True, False]: + raise RuntimeError('printer failed to return boolean') + if ok: + break + if not ok: + raise RuntimeError('no printer registered for object') + finally: + self.end_group() + self.stack.pop() + + + +class AsciiArtPrettyPrinter(SagePrettyPrinter): + + pretty_repr = ( + AsciiArtRepr(), + ) + SagePrettyPrinter.pretty_repr + + +class TypesetPrettyPrinter(SagePrettyPrinter): + + pretty_repr = ( + TypesetRepr(), + ) + SagePrettyPrinter.pretty_repr + + + + diff --git a/src/sage/repl/display/python_hook.py b/src/sage/repl/display/python_hook.py new file mode 100644 index 00000000000..e695b4f7f42 --- /dev/null +++ b/src/sage/repl/display/python_hook.py @@ -0,0 +1,62 @@ +""" +The display hook for plain Python + +This is not used directly in interactive Sage (where we use the +IPython system for display hooks). This class provides a way to use +the Sage "plain text" display formatting when not using interactive +Sage, for example when running doctests. +""" + +#***************************************************************************** +# Copyright (C) 2014 Volker Braun +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + + +import __builtin__ + + +from sage.repl.display.formatter import SagePlainTextFormatter + + +class DoctestDisplayHook(object): + + def __init__(self): + """ + Python constructor + + EXAMPLES:: + + sage: from sage.repl.display.python_hook import DoctestDisplayHook + sage: d = DoctestDisplayHook() + sage: d(set([1, 2, 3])) # Sage commandline output + {1, 2, 3} + sage: print(set([1, 2, 3])) # Plain Python output + set([1, 2, 3]) + """ + self.formatter = SagePlainTextFormatter() + + def __call__(self, obj): + """ + Format the object using Sage's formatting, or format it using the old + display hook if Sage does not want to handle the object. + + EXAMPLES:: + + sage: from sage.repl.display.python_hook import DoctestDisplayHook + sage: d = DoctestDisplayHook() + sage: d((identity_matrix(3), identity_matrix(3))) + ( + [1 0 0] [1 0 0] + [0 1 0] [0 1 0] + [0 0 1], [0 0 1] + ) + """ + if obj is None: + return + print(self.formatter(obj)) + __builtin__._ = obj diff --git a/src/sage/repl/display/util.py b/src/sage/repl/display/util.py new file mode 100644 index 00000000000..6de8c031eb1 --- /dev/null +++ b/src/sage/repl/display/util.py @@ -0,0 +1,163 @@ +# -*- coding: utf-8 -*- +""" +Utility functions for pretty-printing + +These utility functions are used in the implementations of ``_repr_`` +methods elsewhere. +""" + +#***************************************************************************** +# Copyright (C) 2014 Volker Braun +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + + +class _TallListFormatter(object): + """ + Special representation for lists with tall entries (e.g. matrices) + """ + + # This is used to wrap lines when printing "tall" lists. + MAX_COLUMN = 70 + + def _tall_list_row(self, running_lines, last_row=False): + """ + Helper for :meth:`_check_tall_list_and_format` + + This helper function processes and outputs the contents of the + running_lines array. + + TESTS:: + + sage: from sage.repl.display.util import format_list + sage: format_list._tall_list_row(['a b', 'b c', 'c']) + ['a b', 'b c', 'c,', ''] + """ + s=[] + for i, line in enumerate(running_lines): + if i + 1 != len(running_lines): + sep, tail = ' ', '' + else: + # The commas go on the bottom line of this row. + sep, tail = ', ', '' if last_row else ',' + s.append(sep.join(line) + tail) + # Separate rows with a newline to make them stand out. + if not last_row: + s.append("") + return s + + def try_format(self, the_list): + """ + First check whether a list is "tall" -- whether the reprs of the + elements of the list will span multiple lines and cause the list + to be printed awkwardly. If not, this function returns ``None`` and + does nothing; you should revert back to the normal method for + printing an object (its repr). If so, return the string in the + special format. Note that the special format isn't just for + matrices. Any object with a multiline repr will be formatted. + + INPUT: + + - ``the_list`` - The list (or a tuple). + + OUTPUT: + + String or ``None``. The latter is returned if the list is not + deemed to be tall enough and another formatter should be used. + + TESTS:: + + sage: from sage.repl.display.util import format_list + sage: print format_list.try_format( + ....: [matrix([[1, 2, 3, 4], [5, 6, 7, 8]]) for i in xrange(7)]) + [ + [1 2 3 4] [1 2 3 4] [1 2 3 4] [1 2 3 4] [1 2 3 4] [1 2 3 4] + [5 6 7 8], [5 6 7 8], [5 6 7 8], [5 6 7 8], [5 6 7 8], [5 6 7 8], + + [1 2 3 4] + [5 6 7 8] + ] + + sage: format_list.try_format(['not', 'tall']) is None + True + """ + # For every object to be printed, split its repr on newlines and store the + # result in this list. + split_reprs = [] + tall = False + for elem in the_list: + split_reprs.append(repr(elem).split('\n')) + if len(split_reprs[-1]) > 1: + # Meanwhile, check to make sure the list is actually "tall". + tall = True + if not tall: + return None + # Figure out which type of parenthesis to use, based on the type of the_list. + if isinstance(the_list, tuple): + parens = '()' + elif isinstance(the_list, list): + parens = '[]' + else: + raise TypeError('expected list or tuple') + + # running_lines is a list of lines, which are stored as lists of strings + # to be joined later. For each split repr, we add its lines to the + # running_lines array. When current_column exceeds MAX_COLUMN, process + # and output running_lines using _print_tall_list_row. + running_lines = [[]] + current_column = 0 + s = [parens[0]] + for split_repr in split_reprs: + width = max(len(x) for x in split_repr) + if current_column + width > self.MAX_COLUMN and not (width > self.MAX_COLUMN): + s.extend(self._tall_list_row(running_lines)) + running_lines = [[]] + current_column = 0 + current_column += width + 2 + # Add the lines from split_repr to the running_lines array. It may + # be necessary to add or remove lines from either one so that the + # number of lines matches up. + for i in xrange(len(running_lines), len(split_repr)): + running_lines.insert(0, [' ' * len(x) for x in running_lines[-1]]) + line_diff = len(running_lines) - len(split_repr) + for i, x in enumerate(split_repr): + running_lines[i + line_diff].append(x.ljust(width)) + for i in xrange(line_diff): + running_lines[i].append(' ' * width) + # Output any remaining entries. + if len(running_lines[0]) > 0: + s.extend(self._tall_list_row(running_lines, True)) + s.append(parens[1]) + return "\n".join(s) + + def __call__(self, the_list): + """ + Return "tall list" string representation. + + See also :meth:`try_format`. + + INPUT: + + - ``the_list`` -- list or tuple. + + OUTPUT: + + String. + + EXAMPLES:: + + sage: from sage.repl.display.util import format_list + sage: format_list(['not', 'tall']) + "['not', 'tall']" + """ + output = self.try_format(the_list) + if output is None: + output = repr(the_list) + return output + + +format_list = _TallListFormatter() diff --git a/src/sage/repl/interpreter.py b/src/sage/repl/interpreter.py index 51fef688094..a28a22de408 100644 --- a/src/sage/repl/interpreter.py +++ b/src/sage/repl/interpreter.py @@ -440,6 +440,11 @@ def get_test_shell(): app = SageTerminalApp.instance(config=copy.deepcopy(DEFAULT_SAGE_CONFIG)) if app.shell is None: app.initialize(argv=[]) + # overwrite the default (console + graphics) formatter with the plain text one + import sage.repl.display.formatter as formatter + app.shell.display_formatter.formatters['text/plain'] = ( + formatter.SagePlainTextFormatter(config=app.shell.config)) + # No quit noise app.shell.verbose_quit = False return app.shell diff --git a/src/sage/repl/ipython_extension.py b/src/sage/repl/ipython_extension.py index 5f7f9940472..8b5fc61c55d 100644 --- a/src/sage/repl/ipython_extension.py +++ b/src/sage/repl/ipython_extension.py @@ -298,9 +298,9 @@ def __init__(self, shell=None): self.auto_magics = SageMagics(shell) self.shell.register_magics(self.auto_magics) - import sage.misc.displayhook as displayhook + import sage.repl.display.formatter as formatter self.shell.display_formatter.formatters['text/plain'] = ( - displayhook.SagePlainTextFormatter(config=shell.config)) + formatter.SageConsoleTextFormatter(config=shell.config)) import sage.misc.edit_module as edit_module self.shell.set_hook('editor', edit_module.edit_devel) diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index 60a85a64e7b..ee3ba9bdbf2 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -982,7 +982,7 @@ cdef class Parent(category_object.CategoryObject): column, or the meaning is lost. - ``'element_ascii_art'``: same but for the output of the - elements. Used in :mod:`sage.misc.displayhook`. + elements. Used in :mod:`sage.repl.display.formatter`. - ``'element_is_atomic'``: the elements print atomically, that is, parenthesis are not required when *printing* out any of