diff --git a/common/lib/chem/chem/chemcalc.py b/common/lib/chem/chem/chemcalc.py
index dd4abefed2..196c99ae5c 100644
--- a/common/lib/chem/chem/chemcalc.py
+++ b/common/lib/chem/chem/chemcalc.py
@@ -2,10 +2,12 @@ from __future__ import division
from fractions import Fraction
+import markupsafe
import nltk
from nltk.tree import Tree
from pyparsing import Literal, OneOrMore, ParseException, StringEnd
+
ARROWS = ('<->', '->')
# Defines a simple pyparsing tokenizer for chemical equations
@@ -26,6 +28,16 @@ phases = ["(s)", "(l)", "(g)", "(aq)"]
tokens = reduce(lambda a, b: a ^ b, map(Literal, elements + digits + symbols + phases))
tokenizer = OneOrMore(tokens) + StringEnd()
+# HTML, Text are temporarily copied from openedx.core.djangolib.markup
+# These libraries need to be moved out of edx-platform to be used by
+# other applications.
+# See LEARNER-5853 for more details.
+Text = markupsafe.escape # pylint: disable=invalid-name
+
+
+def HTML(html): # pylint: disable=invalid-name
+ return markupsafe.Markup(html)
+
def _orjoin(l):
return "'" + "' | '".join(l) + "'"
@@ -166,20 +178,20 @@ def _render_to_html(tree):
return tree[0][0]
# If a fraction, return the fraction
if len(tree) == 3:
- return " {num}⁄{den} ".format(num=tree[0][0], den=tree[2][0])
+ return HTML(" {num}⁄{den} ").format(num=tree[0][0], den=tree[2][0])
return "Error"
def subscript(tree, children):
- return "{sub}".format(sub=children)
+ return HTML("{sub}").format(sub=children)
def superscript(tree, children):
- return "{sup}".format(sup=children)
+ return HTML("{sup}").format(sup=children)
def round_brackets(tree, children):
- return "({insider})".format(insider=children)
+ return HTML("({insider})").format(insider=children)
def square_brackets(tree, children):
- return "[{insider}]".format(insider=children)
+ return HTML("[{insider}]").format(insider=children)
dispatch = {'count': molecule_count,
'number_suffix': subscript,
@@ -190,7 +202,7 @@ def _render_to_html(tree):
if isinstance(tree, str):
return tree
else:
- children = "".join(map(_render_to_html, tree))
+ children = HTML("").join(map(_render_to_html, tree))
if tree.label() in dispatch:
return dispatch[tree.label()](tree, children)
else:
@@ -207,20 +219,20 @@ def render_to_html(eq):
"""
Render as an error span
"""
- return '{0}'.format(s)
+ return HTML('{0}').format(s)
def render_arrow(arrow):
"""
Turn text arrows into pretty ones
"""
if arrow == '->':
- return u'\u2192'
+ return HTML(u'\u2192')
if arrow == '<->':
- return u'\u2194'
+ return HTML(u'\u2194')
# this won't be reached unless we add more arrow types, but keep it to avoid explosions when
- # that happens.
- return arrow
+ # that happens. HTML-escape this unknown arrow just in case.
+ return Text(arrow)
def render_expression(ex):
"""
@@ -232,7 +244,7 @@ def render_to_html(eq):
return err(ex)
def spanify(s):
- return u'{0}'.format(s)
+ return HTML(u'{0}').format(s)
left, arrow, right = split_on_arrow(eq)
if arrow == '':
diff --git a/common/lib/chem/chem/tests.py b/common/lib/chem/chem/tests.py
index 93f6132798..7625095aa9 100644
--- a/common/lib/chem/chem/tests.py
+++ b/common/lib/chem/chem/tests.py
@@ -296,13 +296,20 @@ class Test_Render_Equations(unittest.TestCase):
log(out + ' ------- ' + correct, 'html')
self.assertEqual(out, correct)
- def test_render_simple_brackets(self):
+ def test_render_simple_round_brackets(self):
test_string = "(Ar)"
out = render_to_html(test_string)
correct = u'(Ar)'
log(out + ' ------- ' + correct, 'html')
self.assertEqual(out, correct)
+ def test_render_simple_square_brackets(self):
+ test_string = "[Ar]"
+ out = render_to_html(test_string)
+ correct = u'[Ar]'
+ log(out + ' ------- ' + correct, 'html')
+ self.assertEqual(out, correct)
+
def test_render_eq1(self):
test_string = "H^+ + OH^- -> H2O"
out = render_to_html(test_string)
@@ -320,7 +327,24 @@ class Test_Render_Equations(unittest.TestCase):
def test_render_eq3(self):
test_string = "H^+ + OH^- <= H2O" # unsupported arrow
out = render_to_html(test_string)
- correct = u'H^+ + OH^- <= H2O'
+ correct = u'H^+ + OH^- <= H2O'
+ log(out + ' ------- ' + correct, 'html')
+ self.assertEqual(out, correct)
+
+ def test_render_eq4(self):
+ test_string = "[H^+] + OH^- <-> (H2O)" # with brackets
+ out = render_to_html(test_string)
+ correct = u'[H+]+OH-\u2194(H2O)'
+ log(out + ' ------- ' + correct, 'html')
+ self.assertEqual(out, correct)
+
+ def test_escaping(self):
+ """
+ Tests that invalid input is escaped.
+ """
+ test_string = ""
+ out = render_to_html(test_string)
+ correct = u'<script>f()</script>'
log(out + ' ------- ' + correct, 'html')
self.assertEqual(out, correct)
diff --git a/common/lib/chem/setup.py b/common/lib/chem/setup.py
index 005836c1f0..12f8bddefd 100644
--- a/common/lib/chem/setup.py
+++ b/common/lib/chem/setup.py
@@ -2,12 +2,13 @@ from setuptools import setup
setup(
name="chem",
- version="0.1.2",
+ version="0.2.0",
packages=["chem"],
install_requires=[
"pyparsing==2.2.0",
"numpy==1.6.2",
"scipy==0.14.0",
"nltk",
+ "markupsafe", # Should be replaced by other utilities. See LEARNER-5853 for more details.
],
)