Skip to content

Commit faf14d7

Browse files
authored
Merge pull request #480 from poke1024/imathics-new
Changes for imathics kernel
2 parents 76e1fc5 + 73fc501 commit faf14d7

File tree

7 files changed

+104
-33
lines changed

7 files changed

+104
-33
lines changed

mathics/builtin/graphics.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
from math import floor, ceil, log10
1313
import json
14+
import base64
1415
from six.moves import map
1516
from six.moves import range
1617
from six.moves import zip
@@ -1453,14 +1454,19 @@ def boxes_to_xml(self, leaves, **options):
14531454
w += 2
14541455
h += 2
14551456

1456-
xml = (
1457-
'<svg xmlns:svg="http://www.w3.org/2000/svg" '
1458-
'xmlns="http://www.w3.org/2000/svg"\nversion="1.0" width="%f" '
1459-
'height="%f" viewBox="%f %f %f %f">%s</svg>') % (
1460-
width, height, xmin, ymin, w, h, svg)
1461-
1462-
xml = """<mtable><mtr><mtd>%s</mtd></mtr></mtable>""" % xml
1463-
return xml
1457+
svg_xml = '''
1458+
<svg xmlns:svg="http://www.w3.org/2000/svg"
1459+
xmlns="http://www.w3.org/2000/svg"
1460+
version="1.1"
1461+
viewBox="%s">
1462+
%s
1463+
</svg>
1464+
''' % (' '.join('%f' % t for t in (xmin, ymin, w, h)), svg)
1465+
1466+
return '<mglyph width="%dpx" height="%dpx" src="data:image/svg+xml;base64,%s"/>' % (
1467+
int(width),
1468+
int(height),
1469+
base64.b64encode(svg_xml.encode('utf8')).decode('utf8'))
14641470

14651471
def axis_ticks(self, xmin, xmax):
14661472
def round_to_zero(value):

mathics/builtin/inout.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1732,8 +1732,7 @@ class MathMLForm(Builtin):
17321732
= <math><mi>\u03bc</mi></math>
17331733
17341734
#> MathMLForm[Graphics[Text["\u03bc"]]]
1735-
= <math><mtable><mtr><mtd><svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
1736-
. version="1.0" width="..." height="..." viewBox="..."><foreignObject x="..." y="..." ox="0.000000" oy="0.000000" style="stroke: none; fill: none; color: rgb(0.000000%, 0.000000%, 0.000000%)"><math><mtext>\u03bc</mtext></math></foreignObject></svg></mtd></mtr></mtable></math>
1735+
= <math><mglyph width="..." height="..." src="data:image/svg+xml;base64,..."/></math>
17371736
17381737
## The <mo> should contain U+2062 INVISIBLE TIMES
17391738
#> MathMLForm[MatrixForm[{{2*a, 0},{0,0}}]]

mathics/core/evaluation.py

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,23 @@ def get_data(self):
144144
}
145145

146146

147+
class Output(object):
148+
def max_stored_size(self, settings):
149+
return settings.MAX_STORED_SIZE
150+
151+
def out(self, out):
152+
pass
153+
154+
def clear(self, wait):
155+
raise NotImplementedError
156+
157+
def display(self, data, metadata):
158+
raise NotImplementedError
159+
160+
147161
class Evaluation(object):
148162
def __init__(self, definitions=None,
149-
out_callback=None, format='text', catch_interrupt=True):
163+
output=None, format='text', catch_interrupt=True):
150164
from mathics.core.definitions import Definitions
151165

152166
if definitions is None:
@@ -156,7 +170,7 @@ def __init__(self, definitions=None,
156170
self.timeout = False
157171
self.stopped = False
158172
self.out = []
159-
self.out_callback = out_callback
173+
self.output = output if output else Output()
160174
self.listeners = {}
161175
self.options = None
162176
self.predetermined_out = None
@@ -221,7 +235,7 @@ def evaluate():
221235
self.definitions.add_rule('Out', Rule(
222236
Expression('Out', line_no), stored_result))
223237
if result != Symbol('Null'):
224-
return self.format_output(result)
238+
return self.format_output(result, self.format)
225239
else:
226240
return None
227241
try:
@@ -261,7 +275,7 @@ def evaluate():
261275
if exc_result is not None:
262276
self.recursion_depth = 0
263277
if exc_result != Symbol('Null'):
264-
result = self.format_output(exc_result)
278+
result = self.format_output(exc_result, self.format)
265279

266280
result = Result(self.out, result, line_no)
267281
self.out = []
@@ -288,23 +302,31 @@ def get_stored_result(self, result):
288302

289303
# Prevent too large results from being stored, as this can exceed the
290304
# DB's max_allowed_packet size
291-
data = pickle.dumps(result)
292-
if len(data) > 10000:
293-
return Symbol('Null')
305+
max_stored_size = self.output.max_stored_size(settings)
306+
if max_stored_size is not None:
307+
data = pickle.dumps(result)
308+
if len(data) > max_stored_size:
309+
return Symbol('Null')
294310
return result
295311

296312
def stop(self):
297313
self.stopped = True
298314

299-
def format_output(self, expr):
315+
def format_output(self, expr, format=None):
316+
if format is None:
317+
format = self.format
318+
319+
if isinstance(format, dict):
320+
return dict((k, self.format_output(expr, f)) for k, f in format.items())
321+
300322
from mathics.core.expression import Expression, BoxError
301323

302-
if self.format == 'text':
324+
if format == 'text':
303325
result = expr.format(self, 'System`OutputForm')
304-
elif self.format == 'xml':
326+
elif format == 'xml':
305327
result = Expression(
306328
'StandardForm', expr).format(self, 'System`MathMLForm')
307-
elif self.format == 'tex':
329+
elif format == 'tex':
308330
result = Expression('StandardForm', expr).format(
309331
self, 'System`TeXForm')
310332
else:
@@ -368,20 +390,18 @@ def message(self, symbol, tag, *args):
368390
text = String("Message %s::%s not found." % (symbol_shortname, tag))
369391

370392
text = self.format_output(Expression(
371-
'StringForm', text, *(from_python(arg) for arg in args)))
393+
'StringForm', text, *(from_python(arg) for arg in args)), 'text')
372394

373395
self.out.append(Message(symbol_shortname, tag, text))
374-
if self.out_callback:
375-
self.out_callback(self.out[-1])
396+
self.output.out(self.out[-1])
376397

377398
def print_out(self, text):
378399
from mathics.core.expression import from_python
379400

380-
text = self.format_output(from_python(text))
401+
text = self.format_output(from_python(text), 'text')
381402

382403
self.out.append(Print(text))
383-
if self.out_callback:
384-
self.out_callback(self.out[-1])
404+
self.output.out(self.out[-1])
385405
if settings.DEBUG_PRINT:
386406
print('OUT: ' + text)
387407

mathics/main.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
from mathics.core.definitions import Definitions
1515
from mathics.core.expression import strip_context
16-
from mathics.core.evaluation import Evaluation
16+
from mathics.core.evaluation import Evaluation, Output
1717
from mathics.core.parser import LineFeeder, FileLineFeeder
1818
from mathics import version_string, license_string, __version__
1919
from mathics import settings
@@ -176,6 +176,14 @@ def empty(self):
176176
return False
177177

178178

179+
class TerminalOutput(Output):
180+
def __init__(self, shell):
181+
self.shell = shell
182+
183+
def out(self, out):
184+
return self.shell.out_callback(out)
185+
186+
179187
def main():
180188
argparser = argparse.ArgumentParser(
181189
prog='mathics',
@@ -238,7 +246,7 @@ def main():
238246
if args.execute:
239247
for expr in args.execute:
240248
print(shell.get_in_prompt() + expr)
241-
evaluation = Evaluation(shell.definitions, out_callback=shell.out_callback)
249+
evaluation = Evaluation(shell.definitions, output=TerminalOutput(shell))
242250
result = evaluation.parse_evaluate(expr, timeout=settings.TIMEOUT)
243251
shell.print_result(result)
244252

@@ -249,7 +257,8 @@ def main():
249257
feeder = FileLineFeeder(args.FILE)
250258
try:
251259
while not feeder.empty():
252-
evaluation = Evaluation(shell.definitions, out_callback=shell.out_callback, catch_interrupt=False)
260+
evaluation = Evaluation(
261+
shell.definitions, output=TerminalOutput(shell), catch_interrupt=False)
253262
query = evaluation.parse_feeder(feeder)
254263
if query is None:
255264
continue
@@ -270,7 +279,7 @@ def main():
270279

271280
while True:
272281
try:
273-
evaluation = Evaluation(shell.definitions, out_callback=shell.out_callback)
282+
evaluation = Evaluation(shell.definitions, output=TerminalOutput(shell))
274283
query = evaluation.parse_feeder(shell)
275284
if query is None:
276285
continue

mathics/settings.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727

2828
MAX_RECURSION_DEPTH = 512
2929

30+
# max pickle.dumps() size for storing results in DB
31+
# historically 10000 was used on public mathics servers
32+
MAX_STORED_SIZE = 10000
33+
3034
ADMINS = (
3135
('Admin', '[email protected]'),
3236
)

mathics/web/media/js/mathics.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,10 +274,38 @@ function translateDOMElement(element, svg) {
274274
return dom;
275275
}
276276

277+
function convertMathGlyphs(dom) {
278+
// convert mglyphs to their classic representation (<svg> or <img>), so the new mglyph logic does not make
279+
// anything worse in the classic Mathics frontend for now. In the long run, this code should vanish.
280+
281+
var MML = "http://www.w3.org/1998/Math/MathML";
282+
var glyphs = dom.getElementsByTagName("mglyph");
283+
for (var i = 0; i < glyphs.length; i++) {
284+
var glyph = glyphs[i];
285+
var src = glyph.getAttribute('src');
286+
if (src.startsWith('data:image/svg+xml;base64,')) {
287+
var svgText = atob(src.substring(src.indexOf(",") + 1));
288+
var mtable =document.createElementNS(MML, "mtable");
289+
mtable.innerHTML = '<mtr><mtd>' + svgText + '</mtd></mtr>';
290+
var svg = mtable.getElementsByTagNameNS("*", "svg")[0];
291+
svg.setAttribute('width', glyph.getAttribute('width'));
292+
svg.setAttribute('height', glyph.getAttribute('height'));
293+
glyph.parentNode.replaceChild(mtable, glyph);
294+
} else if (src.startsWith('data:image/')) {
295+
var img = document.createElement('img');
296+
img.setAttribute('src', src)
297+
img.setAttribute('width', glyph.getAttribute('width'));
298+
img.setAttribute('height', glyph.getAttribute('height'));
299+
glyph.parentNode.replaceChild(img, glyph);
300+
}
301+
}
302+
}
303+
277304
function createLine(value) {
278305
if (value.startsWith('<math')) {
279306
var dom = document.createElement('div');
280307
dom.updateDOM(value);
308+
convertMathGlyphs(dom);
281309
return translateDOMElement(dom.childNodes[0]);
282310
} else {
283311
var lines = value.split('\n');

mathics/web/views.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,15 @@
1818
from django.contrib.auth.models import User
1919

2020
from mathics.core.definitions import Definitions
21-
from mathics.core.evaluation import Evaluation, Message, Result
21+
from mathics.core.evaluation import Evaluation, Message, Result, Output
2222

2323
from mathics.web.models import Query, Worksheet
2424
from mathics.web.forms import LoginForm, SaveForm
2525
from mathics.doc import documentation
2626
from mathics.doc.doc import DocPart, DocChapter, DocSection
2727
import six
2828
from six.moves import range
29+
from string import Template
2930

3031
if settings.DEBUG:
3132
JSON_CONTENT_TYPE = 'text/html'
@@ -44,6 +45,10 @@ def __init__(self, result={}):
4445
super(JsonResponse, self).__init__(response, content_type=JSON_CONTENT_TYPE)
4546

4647

48+
class WebOutput(Output):
49+
pass
50+
51+
4752
def require_ajax_login(func):
4853
def new_func(request, *args, **kwargs):
4954
if not request.user.is_authenticated():
@@ -102,7 +107,7 @@ def query(request):
102107

103108
user_definitions = request.session.get('definitions')
104109
definitions.set_user_definitions(user_definitions)
105-
evaluation = Evaluation(definitions, format='xml')
110+
evaluation = Evaluation(definitions, format='xml', output=WebOutput())
106111
feeder = MultiLineFeeder(input, '<notebook>')
107112
results = []
108113
try:

0 commit comments

Comments
 (0)