Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 24 additions & 6 deletions mathics/builtin/evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -406,13 +406,31 @@ class OutputSizeLimit(Predefined):
To set no limit on output size, use $OutputSizeLimit = Infinity.
</dl>

>> $OutputSizeLimit = 100;
>> Table[i, {i, 1, 100}]
= {1, 2, 3, 4, 5, <<90>>, 96, 97, 98, 99, 100}
>> $OutputSizeLimit = 10;
>> $OutputSizeLimit = 50;

>> Table[i, {i, 1, 100}]
= {1, <<98>>, 100}
>> $OutputSizeLimit = Infinity;
: Parts of this output were omitted (see <<71>>). To generate the whole output, please set $OutputSizeLimit = Infinity.
= {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, <<71>>, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100}

#> Take[Range[1000], 1001]
: Cannot take positions 1 through 1001 in {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, <<976>>, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, 1000}.
: Parts of this output were omitted (see <<976>>). To generate the whole output, please set $OutputSizeLimit = Infinity.
= Take[{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, <<976>>, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, 1000}, 1001]

#> {}
= {}

#> $OutputSizeLimit = 100;

#> Table[Graphics[Table[Circle[],{10}]], {5}]
= {-Graphics-, -Graphics-, -Graphics-, -Graphics-, -Graphics-}

#> Quiet[ImageAvailable = SameQ[Head[Image[{{0, 1}, {1, 0}}] // ToBoxes], ImageBox]];
#> If[ImageAvailable, Table[Image[{{1, 0}, {0, 1}}], {5}], {"-Image-", "-Image-", "-Image-", "-Image-", "-Image-"}]
= {-Image-, -Image-, -Image-, -Image-, -Image-}

#> $OutputSizeLimit = Infinity;

"""

name = '$OutputSizeLimit'
Expand Down
122 changes: 67 additions & 55 deletions mathics/builtin/inout.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import re
import sympy
import mpmath
from itertools import chain

from mathics.builtin.base import (
Builtin, BinaryOperator, BoxConstruct, BoxConstructError, Operator)
Expand All @@ -24,7 +25,6 @@
from_python, MachineReal, PrecisionReal, Omitted)
from mathics.core.numbers import (
dps, prec, convert_base, machine_precision, reconstruct_digits)
from mathics.builtin.lists import riffle

MULTI_NEWLINE_RE = re.compile(r"\n{2,}")

Expand Down Expand Up @@ -82,20 +82,12 @@ def parenthesize(precedence, leaf, leaf_boxes, when_equal):


def make_boxes_infix(leaves, ops, precedence, grouping, form, evaluation):
segment = []
boxes = evaluation.make_boxes(leaves, form, segment)
def materialize(prefix, inner, suffix):
return Expression('RowBox', Expression('List', *list(chain(prefix, inner, suffix))))

seg_shortened, seg_l, seg_r = segment
if seg_shortened:
leaves = leaves[:seg_l] + [Symbol('Null')] + leaves[seg_r:]
ops = ops[:seg_l] + ops[seg_r - 1:] # ellipsis item gets rightmost operator from ellipsed chunk

result = []
for index, leaf_box in enumerate(zip(leaves, boxes)):
leaf, box = leaf_box

if index > 0:
result.append(ops[index - 1])
def make_leaf(index):
leaf = leaves[index]
box = Expression('MakeBoxes', leaf, form)

parenthesized = False
if grouping == 'System`NonAssociative':
Expand All @@ -105,13 +97,12 @@ def make_boxes_infix(leaves, ops, precedence, grouping, form, evaluation):
elif grouping == 'System`Right' and index == 0:
parenthesized = True

if seg_shortened and index == seg_l:
leaf = box # ellipsis item, do not parenthesize
else:
leaf = parenthesize(precedence, leaf, box, parenthesized)
return parenthesize(precedence, leaf, box, parenthesized)

result.append(leaf)
return Expression('RowBox', Expression('List', *result))
return evaluation.make_boxes(
None, make_leaf, len(leaves),
None, None, ops,
materialize, form)


def real_to_s_exp(expr, n):
Expand Down Expand Up @@ -480,24 +471,24 @@ def apply_general(self, expr, f, evaluation):

# Parenthesize infix operators at the head of expressions,
# like (a + b)[x], but not f[a] in f[a][b].
#
head_boxes = parenthesize(670,
head, MakeBoxes(head, f), False)
result = [head_boxes, String(left)]

if len(leaves) > 1:
if f_name in ('System`InputForm', 'System`OutputForm',
'System`FullForm'):
sep = ', '
else:
sep = ','
boxes = evaluation.make_boxes(leaves, f)
row = riffle(boxes, String(sep))
result.append(RowBox(Expression('List', *row)))
elif len(leaves) == 1:
result.append(MakeBoxes(leaves[0], f))
result.append(String(right))
return RowBox(Expression('List', *result))
prefix = parenthesize(670, head, Expression('MakeBoxes', head, f), False)

def make_leaf(i):
return Expression('MakeBoxes', leaves[i], f)

if f_name in ('System`InputForm', 'System`OutputForm',
'System`FullForm'):
sep = ', '
else:
sep = ','

def materialize(prefix, inner, suffix):
if len(inner) > 1:
inner = [Expression('RowBox', Expression('List', *inner))]
return Expression('RowBox', Expression('List', *list(chain(prefix, inner, suffix))))

return evaluation.make_boxes(
prefix, make_leaf, len(leaves), String(left), String(right), String(sep), materialize, f)

def _apply_atom(self, x, f, evaluation):
'''MakeBoxes[x_?AtomQ,
Expand Down Expand Up @@ -797,20 +788,31 @@ def apply_makeboxes(self, array, f, evaluation, options):
f:StandardForm|TraditionalForm|OutputForm]'''

lengths = [len(row.leaves) for row in array.leaves]
segment = []
boxes = evaluation.make_boxes([item for row in array.leaves for item in row.leaves], f, segment)
if segment[0]: # too long?
return Omitted('<<%d>>' % sum(lengths))
else:
rows = []
i = 0
for l in lengths:
rows.append(Expression('List', *boxes[i:i + l]))
i += l
return Expression(
'GridBox',
Expression('List', *rows),
*options_to_rules(options))
n_leaves = sum(lengths)

def materialize(boxes):
if len(boxes) == n_leaves:
rows = []
i = 0
for l in lengths:
rows.append(Expression('List', *boxes[i:i + l]))
i += l
return Expression(
'GridBox',
Expression('List', *rows),
*options_to_rules(options))
else: # too long
return Omitted('<<%d>>' % n_leaves)

flat = [item for row in array.leaves for item in row.leaves]

def make_leaf(i):
return Expression('MakeBoxes', flat[i], f)

return evaluation.make_boxes(
None, make_leaf, n_leaves,
None, None, None,
materialize, f)


class TableForm(Builtin):
Expand Down Expand Up @@ -961,9 +963,11 @@ class Subscript(Builtin):
def apply_makeboxes(self, x, y, f, evaluation):
'MakeBoxes[Subscript[x_, y__], f:StandardForm|TraditionalForm]'

y = y.get_sequence()
return Expression(
'SubscriptBox', Expression('MakeBoxes', x, f), *list_boxes(y, f, evaluation))
def materialize(prefix, inner, suffix):
return Expression('SubscriptBox', *list(chain(prefix, inner, suffix)))

return list_boxes(
Expression('MakeBoxes', x, f), y.get_sequence(), materialize, f, evaluation)


class SubscriptBox(Builtin):
Expand Down Expand Up @@ -1140,6 +1144,12 @@ def apply_makeboxes(self, s, args, f, evaluation):
'''MakeBoxes[StringForm[s_String, args___],
f:StandardForm|TraditionalForm|OutputForm]'''

# StringForm does not call evaluation.make_boxes
# since we use it for messages and we never want
# to omit parts of the message. args are subject
# to MakeBoxes (see below) and thus can get parts
# omitted.

s = s.value
args = args.get_sequence()
result = []
Expand Down Expand Up @@ -1640,6 +1650,8 @@ class General(Builtin):
'invalidargs': "Invalid arguments.",

'notboxes': "`1` is not a valid box structure.",
'omit': "Parts of this output were omitted (see `1`). " +
"To generate the whole output, please set $OutputSizeLimit = Infinity.",

'pyimport': "`1`[] is not available. Your Python installation misses the \"`2`\" module.",
}
Expand Down
44 changes: 24 additions & 20 deletions mathics/builtin/lists.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,11 @@ def apply_makeboxes(self, items, f, evaluation):
f:StandardForm|TraditionalForm|OutputForm|InputForm]'''

items = items.get_sequence()
return Expression(
'RowBox', Expression('List', *list_boxes(items, f, evaluation, "{", "}")))

def materialize(prefix, inner, suffix):
return Expression('RowBox', Expression('List', *list(chain(prefix, inner, suffix))))

return list_boxes(None, items, materialize, f, evaluation, "{", "}")


class ListQ(Test):
Expand Down Expand Up @@ -93,25 +96,22 @@ def test(self, expr):
return expr.get_head_name() != 'System`List'


def list_boxes(items, f, evaluation, open=None, close=None):
result = evaluation.make_boxes(items, f)
def list_boxes(prefix, items, materialize, f, evaluation, open=None, close=None):
if open is not None:
open = String(open)
if close is not None:
close = String(close)

if f.get_name() in ('System`OutputForm', 'System`InputForm'):
sep = ", "
else:
sep = ","
result = riffle(result, String(sep))
if len(items) > 1:
result = Expression('RowBox', Expression('List', *result))
elif items:
result = result[0]
if result:
result = [result]
else:
result = []
if open is not None and close is not None:
return [String(open)] + result + [String(close)]
else:
return result

def make_leaf(i):
return Expression('MakeBoxes', items[i], f)

return evaluation.make_boxes(
prefix, make_leaf, len(items), open, close, sep, materialize, f)


class Length(Builtin):
Expand Down Expand Up @@ -822,14 +822,18 @@ def apply_makeboxes(self, list, i, f, evaluation):
f:StandardForm|TraditionalForm|OutputForm|InputForm]'''

i = i.get_sequence()

list = Expression('MakeBoxes', list, f)

if f.get_name() in ('System`OutputForm', 'System`InputForm'):
open, close = "[[", "]]"
else:
open, close = "\u301a", "\u301b"
indices = list_boxes(i, f, evaluation, open, close)
result = Expression('RowBox', Expression('List', list, *indices))
return result

def materialize(prefix, inner, suffix):
return Expression('RowBox', Expression('List', *list(chain(prefix, inner, suffix))))

return list_boxes(list, i, materialize, f, evaluation, open, close)

def apply(self, list, i, evaluation):
'Part[list_, i___]'
Expand Down
36 changes: 23 additions & 13 deletions mathics/core/evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import itertools

from mathics import settings
from mathics.core.expression import ensure_context, KeyComparable, make_boxes_strategy
from mathics.core.expression import ensure_context, KeyComparable, make_boxes_strategy, Omissions

FORMATS = ['StandardForm', 'FullForm', 'TraditionalForm',
'OutputForm', 'InputForm',
Expand Down Expand Up @@ -178,7 +178,7 @@ def __init__(self, definitions=None,

self.quiet_all = False
self.format = format
self.boxes_strategy = make_boxes_strategy(None, self)
self.boxes_strategy = make_boxes_strategy(None, None, self)
self.catch_interrupt = catch_interrupt

def parse(self, query):
Expand Down Expand Up @@ -317,7 +317,7 @@ def get_stored_result(self, result):
def stop(self):
self.stopped = True

def format_output(self, expr, format=None):
def format_output(self, expr, format=None, warn_about_omitted=True):
if format is None:
format = self.format

Expand All @@ -329,18 +329,24 @@ def format_output(self, expr, format=None):
old_boxes_strategy = self.boxes_strategy
try:
capacity = self.definitions.get_config_value('System`$OutputSizeLimit')
self.boxes_strategy = make_boxes_strategy(capacity, self)
omissions = Omissions()
self.boxes_strategy = make_boxes_strategy(capacity, omissions, self)

options = {}

# for xml/MathMLForm and tex/TexForm, output size limits are applied in the output
# form's apply methods (e.g. see MathMLForm.apply) and then passed through
# result.boxes_to_text which, in these two cases, must not apply any additional
# clipping (it would clip already clipped string material).

# for text/OutputForm, on the other hand, the call to result.boxes_to_text is the
# only place there is to apply output size limits, which has us resort to setting
# options['output_size_limit']. NOTE: disabled right now, causes problems with
# long message outputs (see test case Table[i, {i, 1, 100}] under OutputSizeLimit).

if format == 'text':
result = expr.format(self, 'System`OutputForm')
# for MathMLForm and TexForm, output size limits are applied in the form's apply
# methods (e.g. see MathMLForm.apply) and then passed through result.boxes_to_text
# which must, in these cases, not apply additional clipping, as this would clip
# already clipped string material. for OutputForm, on the other hand, the call to
# result.boxes_to_text is the only place we have to apply output size limits.
options['output_size_limit'] = capacity
# options['output_size_limit'] = capacity
elif format == 'xml':
result = Expression(
'StandardForm', expr).format(self, 'System`MathMLForm')
Expand All @@ -356,6 +362,9 @@ def format_output(self, expr, format=None):
self.message('General', 'notboxes',
Expression('FullForm', result).evaluate(self))
boxes = None

if warn_about_omitted:
omissions.warn(self)
finally:
self.boxes_strategy = old_boxes_strategy

Expand All @@ -378,8 +387,9 @@ def get_quiet_messages(self):
return []
return value.leaves

def make_boxes(self, items, form, segment=None):
return self.boxes_strategy.make(items, form, segment)
def make_boxes(self, prefix, make_leaf, n_leaves, left, right, sep, materialize, form):
return self.boxes_strategy.make(
prefix, make_leaf, n_leaves, left, right, sep, materialize, form)

def message(self, symbol, tag, *args):
from mathics.core.expression import (String, Symbol, Expression,
Expand Down Expand Up @@ -415,7 +425,7 @@ def message(self, symbol, tag, *args):
text = String("Message %s::%s not found." % (symbol_shortname, tag))

text = self.format_output(Expression(
'StringForm', text, *(from_python(arg) for arg in args)), 'text')
'StringForm', text, *(from_python(arg) for arg in args)), 'text', warn_about_omitted=False)

self.out.append(Message(symbol_shortname, tag, text))
self.output.out(self.out[-1])
Expand Down
Loading