From 18e4c1cec4bc52fdbc12d84fb425ef40820141ec Mon Sep 17 00:00:00 2001 From: Carlos Muniz Date: Fri, 4 Feb 2022 11:45:02 -0500 Subject: [PATCH 1/7] refactor: Remove symmath from common/lib --- common/lib/symmath/setup.py | 11 - common/lib/symmath/symmath/README.md | 30 - common/lib/symmath/symmath/__init__.py | 3 - common/lib/symmath/symmath/formula.py | 588 ------------------ common/lib/symmath/symmath/symmath_check.py | 337 ---------- common/lib/symmath/symmath/test_formula.py | 117 ---- .../lib/symmath/symmath/test_symmath_check.py | 89 --- 7 files changed, 1175 deletions(-) delete mode 100644 common/lib/symmath/setup.py delete mode 100644 common/lib/symmath/symmath/README.md delete mode 100644 common/lib/symmath/symmath/__init__.py delete mode 100644 common/lib/symmath/symmath/formula.py delete mode 100644 common/lib/symmath/symmath/symmath_check.py delete mode 100644 common/lib/symmath/symmath/test_formula.py delete mode 100644 common/lib/symmath/symmath/test_symmath_check.py diff --git a/common/lib/symmath/setup.py b/common/lib/symmath/setup.py deleted file mode 100644 index 01e91bd133a5..000000000000 --- a/common/lib/symmath/setup.py +++ /dev/null @@ -1,11 +0,0 @@ -# lint-amnesty, pylint: disable=missing-module-docstring -from setuptools import setup - -setup( - name="symmath", - version="0.3", - packages=["symmath"], - install_requires=[ - "sympy", - ], -) diff --git a/common/lib/symmath/symmath/README.md b/common/lib/symmath/symmath/README.md deleted file mode 100644 index 8da9aa87eebd..000000000000 --- a/common/lib/symmath/symmath/README.md +++ /dev/null @@ -1,30 +0,0 @@ -(Originally written by Ike.) - -At a high level, the main challenges of checking symbolic math expressions are -(1) making sure the expression is mathematically legal, and (2) simplifying the -expression for comparison with what is expected. - -(1) Generation (and testing) of legal input is done by using MathJax to provide -input math in an XML format known as Presentation MathML (PMathML). Such -expressions typeset correctly, but may not be mathematically legal, like "5 / -(1 = 2)". The PMathML is converted into "Content MathML" (CMathML), which is -by definition mathematically legal, using an XSLT 2.0 stylesheet, via a module -in SnuggleTeX. CMathML is then converted into a sympy expression. This work is -all done in `symmath/formula.py`. - -(2) Simplifying the expression and checking against what is expected is done by -using sympy, and a set of heuristics based on options flags provided by the -problem author. For example, the problem author may specify that the expected -expression is a matrix, in which case the dimensionality of the input -expression is checked. Other options include specifying that the comparison be -checked numerically in addition to symbolically. The checking is done in -stages, first with no simplification, then with increasing levels of testing; -if a match is found at any stage, then an "ok" is returned. Helpful messages -are also returned, eg if the input expression is of a different type than the -expected. This work is all done in `symmath/symmath_check.py`. - -Links: - -SnuggleTex: http://www2.ph.ed.ac.uk/snuggletex/documentation/overview-and-features.html -MathML: http://www.w3.org/TR/MathML2/overview.html -SymPy: http://sympy.org/en/index.html diff --git a/common/lib/symmath/symmath/__init__.py b/common/lib/symmath/symmath/__init__.py deleted file mode 100644 index 8d00aadd229e..000000000000 --- a/common/lib/symmath/symmath/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# lint-amnesty, pylint: disable=missing-module-docstring -from .formula import * -from .symmath_check import * diff --git a/common/lib/symmath/symmath/formula.py b/common/lib/symmath/symmath/formula.py deleted file mode 100644 index 199b5aea1d56..000000000000 --- a/common/lib/symmath/symmath/formula.py +++ /dev/null @@ -1,588 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -""" -Flexible python representation of a symbolic mathematical formula. -Acceptes Presentation MathML, Content MathML (and could also do OpenMath). -Provides sympy representation. -""" -# -# File: formula.py -# Date: 04-May-12 (creation) -# Author: I. Chuang -# - - -import logging -import operator -import os -import re -import string -import unicodedata -#import subprocess -from copy import deepcopy -from functools import reduce - -import six # lint-amnesty, pylint: disable=unused-import -import sympy -from lxml import etree -from sympy import latex, sympify -from sympy.physics.quantum.qubit import Qubit -from sympy.physics.quantum.state import Ket -from sympy.printing.latex import LatexPrinter -from sympy.printing.str import StrPrinter - -from openedx.core.djangolib.markup import HTML - -log = logging.getLogger(__name__) - -log.warning("Dark code. Needs review before enabling in prod.") - -os.environ['PYTHONIOENCODING'] = 'utf-8' - -#----------------------------------------------------------------------------- - - -class dot(sympy.operations.LatticeOp): # pylint: disable=invalid-name, no-member - """my dot product""" - zero = sympy.Symbol('dotzero') - identity = sympy.Symbol('dotidentity') - - -def _print_dot(_self, expr): - """Print statement used for LatexPrinter""" - return r'{((%s) \cdot (%s))}' % (expr.args[0], expr.args[1]) - -LatexPrinter._print_dot = _print_dot # pylint: disable=protected-access - -#----------------------------------------------------------------------------- -# unit vectors (for 8.02) - - -def _print_hat(_self, expr): - """Print statement used for LatexPrinter""" - return '\\hat{%s}' % str(expr.args[0]).lower() - -LatexPrinter._print_hat = _print_hat # pylint: disable=protected-access -StrPrinter._print_hat = _print_hat # pylint: disable=protected-access - -#----------------------------------------------------------------------------- -# helper routines - - -def to_latex(expr): - """ - Convert expression to latex mathjax format - """ - if expr is None: - return '' - expr_s = latex(expr) - expr_s = expr_s.replace(r'\XI', 'XI') # workaround for strange greek - - # substitute back into latex form for scripts - # literally something of the form - # 'scriptN' becomes '\\mathcal{N}' - # note: can't use something akin to the _print_hat method above because we - # sometimes get 'script(N)__B' or more complicated terms - expr_s = re.sub( - r'script([a-zA-Z0-9]+)', - r'\\mathcal{\\1}', - expr_s - ) - - #return '%s{}{}' % (xs[1:-1]) - if expr_s[0] == '$': - return HTML('[mathjax]{expression}[/mathjax]
').format(expression=expr_s[1:-1]) # for sympy v6 - return HTML('[mathjax]{expression}[/mathjax]
').format(expression=expr_s) # for sympy v7 - - -def my_evalf(expr, chop=False): - """ - Enhanced sympy evalf to handle lists of expressions - and catch eval failures without dropping out. - """ - if isinstance(expr, list): - try: - return [x.evalf(chop=chop) for x in expr] - except Exception: # pylint: disable=broad-except - return expr - try: - return expr.evalf(chop=chop) - except Exception: # pylint: disable=broad-except - return expr - - -def my_sympify(expr, normphase=False, matrix=False, abcsym=False, do_qubit=False, symtab=None): - """ - Version of sympify to import expression into sympy - """ - # make all lowercase real? - if symtab: - varset = symtab - else: - varset = { - 'p': sympy.Symbol('p'), - 'g': sympy.Symbol('g'), - 'e': sympy.E, # for exp - 'i': sympy.I, # lowercase i is also sqrt(-1) - 'Q': sympy.Symbol('Q'), # otherwise it is a sympy "ask key" - 'I': sympy.Symbol('I'), # otherwise it is sqrt(-1) - 'N': sympy.Symbol('N'), # or it is some kind of sympy function - 'ZZ': sympy.Symbol('ZZ'), # otherwise it is the PythonIntegerRing - 'XI': sympy.Symbol('XI'), # otherwise it is the capital \XI - 'hat': sympy.Function('hat'), # for unit vectors (8.02) - } - if do_qubit: # turn qubit(...) into Qubit instance - varset.update({ - 'qubit': Qubit, - 'Ket': Ket, - 'dot': dot, - 'bit': sympy.Function('bit'), - }) - if abcsym: # consider all lowercase letters as real symbols, in the parsing - for letter in string.ascii_lowercase: - if letter in varset: # exclude those already done - continue - varset.update({letter: sympy.Symbol(letter, real=True)}) - - sexpr = sympify(expr, locals=varset) - if normphase: # remove overall phase if sexpr is a list - if isinstance(sexpr, list): - if sexpr[0].is_number: - ophase = sympy.sympify('exp(-I*arg(%s))' % sexpr[0]) - sexpr = [sympy.Mul(x, ophase) for x in sexpr] - - def to_matrix(expr): - """ - Convert a list, or list of lists to a matrix. - """ - # if expr is a list of lists, and is rectangular, then return Matrix(expr) - if not isinstance(expr, list): - return expr - for row in expr: - if not isinstance(row, list): - return expr - rdim = len(expr[0]) - for row in expr: - if not len(row) == rdim: - return expr - return sympy.Matrix(expr) - - if matrix: - sexpr = to_matrix(sexpr) - return sexpr - -#----------------------------------------------------------------------------- -# class for symbolic mathematical formulas - - -class formula(object): - """ - Representation of a mathematical formula object. Accepts mathml math expression - for constructing, and can produce sympy translation. The formula may or may not - include an assignment (=). - """ - def __init__(self, expr, asciimath='', options=None): - self.expr = expr.strip() - self.asciimath = asciimath - self.the_cmathml = None - self.the_sympy = None - self.options = options - - def is_presentation_mathml(self): - """ - Check if formula is in mathml presentation format. - """ - return 'f-2" this is - really terrible for turning into cmathml. undo this here. - """ - for k in xml: - tag = gettag(k) - if tag == 'mrow': - if len(k) == 2: - if gettag(k[0]) == 'mi' and k[0].text in ['f', 'g'] and gettag(k[1]) == 'mo': - idx = xml.index(k) - xml.insert(idx, deepcopy(k[0])) # drop the container - xml.insert(idx + 1, deepcopy(k[1])) - xml.remove(k) - fix_pmathml(k) - - fix_pmathml(xml) - - def fix_hat(xml): - """ - hat i is turned into i^ ; mangle - this into hat(f) hat i also somtimes turned into - j ^ - """ - for k in xml: - tag = gettag(k) - if tag == 'mover': - if len(k) == 2: - if gettag(k[0]) == 'mi' and gettag(k[1]) == 'mo' and str(k[1].text) == '^': - newk = etree.Element('mi') - newk.text = 'hat(%s)' % k[0].text - xml.replace(k, newk) - if gettag(k[0]) == 'mrow' and gettag(k[0][0]) == 'mi' and \ - gettag(k[1]) == 'mo' and str(k[1].text) == '^': - newk = etree.Element('mi') - newk.text = 'hat(%s)' % k[0][0].text - xml.replace(k, newk) - fix_hat(k) - fix_hat(xml) - - def flatten_pmathml(xml): - """ - Give the text version of certain PMathML elements - - Sometimes MathML will be given with each letter separated (it - doesn't know if its implicit multiplication or what). From an xml - node, find the (text only) variable name it represents. So it takes - - m - a - x - - and returns 'max', for easier use later on. - """ - tag = gettag(xml) - if tag == 'mn': - return xml.text - elif tag == 'mi': - return xml.text - elif tag == 'mrow': - return ''.join([flatten_pmathml(y) for y in xml]) - raise Exception('[flatten_pmathml] unknown tag %s' % tag) - - def fix_mathvariant(parent): - """ - Fix certain kinds of math variants - - Literally replace N - with 'scriptN'. There have been problems using script_N or script(N) - """ - for child in parent: - if gettag(child) == 'mstyle' and child.get('mathvariant') == 'script': - newchild = etree.Element('mi') - newchild.text = 'script%s' % flatten_pmathml(child[0]) - parent.replace(child, newchild) - fix_mathvariant(child) - fix_mathvariant(xml) - - # find "tagged" superscripts - # they have the character \u200b in the superscript - # replace them with a__b so snuggle doesn't get confused - def fix_superscripts(xml): - """ Look for and replace sup elements with 'X__Y' or 'X_Y__Z' - - In the javascript, variables with '__X' in them had an invisible - character inserted into the sup (to distinguish from powers) - E.g. normal: - - a - b - c - - to be interpreted '(a_b)^c' (nothing done by this method) - - And modified: - - b - x - - - d - - - to be interpreted 'a_b__c' - - also: - - x - - - B - - - to be 'x__B' - """ - for k in xml: - tag = gettag(k) - - # match things like the last example-- - # the second item in msub is an mrow with the first - # character equal to \u200b - if ( - tag == 'msup' and - len(k) == 2 and gettag(k[1]) == 'mrow' and - gettag(k[1][0]) == 'mo' and k[1][0].text == '\u200b' # whew - ): - - # replace the msup with 'X__Y' - k[1].remove(k[1][0]) - newk = etree.Element('mi') - newk.text = '%s__%s' % (flatten_pmathml(k[0]), flatten_pmathml(k[1])) - xml.replace(k, newk) - - # match things like the middle example- - # the third item in msubsup is an mrow with the first - # character equal to \u200b - if ( - tag == 'msubsup' and - len(k) == 3 and gettag(k[2]) == 'mrow' and - gettag(k[2][0]) == 'mo' and k[2][0].text == '\u200b' # whew - ): - - # replace the msubsup with 'X_Y__Z' - k[2].remove(k[2][0]) - newk = etree.Element('mi') - newk.text = '%s_%s__%s' % (flatten_pmathml(k[0]), flatten_pmathml(k[1]), flatten_pmathml(k[2])) - xml.replace(k, newk) - - fix_superscripts(k) - fix_superscripts(xml) - - def fix_msubsup(parent): - """ - Snuggle returns an error when it sees an replace such - elements with an , except the first element is of - the form a_b. I.e. map a_b^c => (a_b)^c - """ - for child in parent: - # fix msubsup - if gettag(child) == 'msubsup' and len(child) == 3: - newchild = etree.Element('msup') - newbase = etree.Element('mi') - newbase.text = '%s_%s' % (flatten_pmathml(child[0]), flatten_pmathml(child[1])) - newexp = child[2] - newchild.append(newbase) - newchild.append(newexp) - parent.replace(child, newchild) - - fix_msubsup(child) - fix_msubsup(xml) - - self.xml = xml # pylint: disable=attribute-defined-outside-init - return self.xml - - def get_content_mathml(self): # lint-amnesty, pylint: disable=missing-function-docstring - if self.the_cmathml: - return self.the_cmathml - - # pre-process the presentation mathml before sending it to snuggletex to convert to content mathml - try: - xml = self.preprocess_pmathml(self.expr).decode('utf-8') - except Exception as err: # pylint: disable=broad-except - log.warning('Err %s while preprocessing; expr=%s', err, self.expr) - return "Error! Cannot process pmathml" - pmathml = etree.tostring(xml, pretty_print=True) - self.the_pmathml = pmathml # pylint: disable=attribute-defined-outside-init - return self.the_pmathml - - cmathml = property(get_content_mathml, None, None, 'content MathML representation') - - def make_sympy(self, xml=None): # lint-amnesty, pylint: disable=too-many-statements - """ - Return sympy expression for the math formula. - The math formula is converted to Content MathML then that is parsed. - - This is a recursive function, called on every CMML node. Support for - more functions can be added by modifying opdict, abould halfway down - """ - - if self.the_sympy: - return self.the_sympy - - if xml is None: # root - if not self.is_mathml(): - return my_sympify(self.expr) - if self.is_presentation_mathml(): - cmml = None - try: - cmml = self.cmathml - xml = etree.fromstring(str(cmml)) - except Exception as err: - if 'conversion from Presentation MathML to Content MathML was not successful' in cmml: # lint-amnesty, pylint: disable=unsupported-membership-test - msg = "Illegal math expression" - else: - msg = 'Err %s while converting cmathml to xml; cmml=%s' % (err, cmml) - raise Exception(msg) # lint-amnesty, pylint: disable=raise-missing-from - xml = self.fix_greek_in_mathml(xml) - self.the_sympy = self.make_sympy(xml[0]) - else: - xml = etree.fromstring(self.expr) - xml = self.fix_greek_in_mathml(xml) - self.the_sympy = self.make_sympy(xml[0]) - return self.the_sympy - - def gettag(expr): - return re.sub('{http://[^}]+}', '', expr.tag) - - def op_plus(*args): - return args[0] if len(args) == 1 else op_plus(*args[:-1]) + args[-1] - - def op_times(*args): - return reduce(operator.mul, args) - - def op_minus(*args): - if len(args) == 1: - return -args[0] - if not len(args) == 2: # lint-amnesty, pylint: disable=unneeded-not - raise Exception('minus given wrong number of arguments!') - #return sympy.Add(args[0],-args[1]) - return args[0] - args[1] - - opdict = { - 'plus': op_plus, - 'divide': operator.div, # lint-amnesty, pylint: disable=no-member - 'times': op_times, - 'minus': op_minus, - 'root': sympy.sqrt, - 'power': sympy.Pow, - 'sin': sympy.sin, - 'cos': sympy.cos, - 'tan': sympy.tan, - 'cot': sympy.cot, - 'sinh': sympy.sinh, - 'cosh': sympy.cosh, - 'coth': sympy.coth, - 'tanh': sympy.tanh, - 'asin': sympy.asin, - 'acos': sympy.acos, - 'atan': sympy.atan, - 'atan2': sympy.atan2, - 'acot': sympy.acot, - 'asinh': sympy.asinh, - 'acosh': sympy.acosh, - 'atanh': sympy.atanh, - 'acoth': sympy.acoth, - 'exp': sympy.exp, - 'log': sympy.log, - 'ln': sympy.ln, - } - - def parse_presentation_symbol(xml): - """ - Parse , , , and - """ - tag = gettag(xml) - if tag == 'mn': - return xml.text - elif tag == 'mi': - return xml.text - elif tag == 'msub': - return '_'.join([parse_presentation_symbol(y) for y in xml]) - elif tag == 'msup': - return '^'.join([parse_presentation_symbol(y) for y in xml]) - raise Exception('[parse_presentation_symbol] unknown tag %s' % tag) - - # parser tree for Content MathML - tag = gettag(xml) - - # first do compound objects - - if tag == 'apply': # apply operator - opstr = gettag(xml[0]) - if opstr in opdict: - op = opdict[opstr] # pylint: disable=invalid-name - args = [self.make_sympy(expr) for expr in xml[1:]] - try: - res = op(*args) - except Exception as err: - self.args = args # pylint: disable=attribute-defined-outside-init - self.op = op # pylint: disable=attribute-defined-outside-init, invalid-name - raise Exception('[formula] error=%s failed to apply %s to args=%s' % (err, opstr, args)) # lint-amnesty, pylint: disable=raise-missing-from - return res - else: - raise Exception('[formula]: unknown operator tag %s' % (opstr)) - - elif tag == 'list': # square bracket list - if gettag(xml[0]) == 'matrix': - return self.make_sympy(xml[0]) - else: - return [self.make_sympy(expr) for expr in xml] - - elif tag == 'matrix': - return sympy.Matrix([self.make_sympy(expr) for expr in xml]) - - elif tag == 'vector': - return [self.make_sympy(expr) for expr in xml] - - # atoms are below - - elif tag == 'cn': # number - return sympy.sympify(xml.text) - - elif tag == 'ci': # variable (symbol) - if len(xml) > 0 and (gettag(xml[0]) == 'msub' or gettag(xml[0]) == 'msup'): # subscript or superscript - usym = parse_presentation_symbol(xml[0]) - sym = sympy.Symbol(str(usym)) - else: - usym = six.text_type(xml.text) - if 'hat' in usym: - sym = my_sympify(usym) - else: - if usym == 'i' and self.options is not None and 'imaginary' in self.options: # i = sqrt(-1) - sym = sympy.I - else: - sym = sympy.Symbol(str(usym)) - return sym - - else: # unknown tag - raise Exception('[formula] unknown tag %s' % tag) - - sympy = property(make_sympy, None, None, 'sympy representation') diff --git a/common/lib/symmath/symmath/symmath_check.py b/common/lib/symmath/symmath/symmath_check.py deleted file mode 100644 index ffd56c8922d5..000000000000 --- a/common/lib/symmath/symmath/symmath_check.py +++ /dev/null @@ -1,337 +0,0 @@ -# lint-amnesty, pylint: disable=missing-module-docstring -# !/usr/bin/python -# -*- coding: utf-8 -*- -# -# File: symmath_check.py -# Date: 02-May-12 (creation) -# -# Symbolic mathematical expression checker for edX. Uses sympy to check for expression equality. -# -# Takes in math expressions given as Presentation MathML (from ASCIIMathML), converts to Content MathML using SnuggleTeX - - -import logging -import traceback - -from markupsafe import escape - -from openedx.core.djangolib.markup import HTML - -from .formula import * # lint-amnesty, pylint: disable=wildcard-import - -log = logging.getLogger(__name__) - -#----------------------------------------------------------------------------- -# check function interface -# -# This is one of the main entry points to call. - - -def symmath_check_simple(expect, ans, adict={}, symtab=None, extra_options=None): # lint-amnesty, pylint: disable=dangerous-default-value, unused-argument - """ - Check a symbolic mathematical expression using sympy. - The input is an ascii string (not MathML) converted to math using sympy.sympify. - """ - - options = {'__MATRIX__': False, '__ABC__': False, '__LOWER__': False} - if extra_options: - options.update(extra_options) - for op in options: # find options in expect string - if op in expect: - expect = expect.replace(op, '') - options[op] = True - expect = expect.replace('__OR__', '__or__') # backwards compatibility - - if options['__LOWER__']: - expect = expect.lower() - ans = ans.lower() - - try: - ret = check(expect, ans, - matrix=options['__MATRIX__'], - abcsym=options['__ABC__'], - symtab=symtab, - ) - except Exception as err: # lint-amnesty, pylint: disable=broad-except - return {'ok': False, - 'msg': HTML('Error {err}
Failed in evaluating check({expect},{ans})').format( - err=err, expect=expect, ans=ans - )} - return ret - -#----------------------------------------------------------------------------- -# pretty generic checking function - - -def check(expect, given, numerical=False, matrix=False, normphase=False, abcsym=False, do_qubit=True, symtab=None, dosimplify=False): # lint-amnesty, pylint: disable=line-too-long - """ - Returns dict with - - 'ok': True if check is good, False otherwise - 'msg': response message (in HTML) - - "expect" may have multiple possible acceptable answers, separated by "__OR__" - - """ - - if "__or__" in expect: # if multiple acceptable answers - eset = expect.split('__or__') # then see if any match - for eone in eset: - ret = check(eone, given, numerical, matrix, normphase, abcsym, do_qubit, symtab, dosimplify) - if ret['ok']: - return ret - return ret - - flags = {} - if "__autonorm__" in expect: - flags['autonorm'] = True - expect = expect.replace('__autonorm__', '') - matrix = True - - threshold = 1.0e-3 - if "__threshold__" in expect: - (expect, st) = expect.split('__threshold__') - threshold = float(st) - numerical = True - - if str(given) == '' and not str(expect) == '': # lint-amnesty, pylint: disable=unneeded-not - return {'ok': False, 'msg': ''} - - try: - xgiven = my_sympify(given, normphase, matrix, do_qubit=do_qubit, abcsym=abcsym, symtab=symtab) - except Exception as err: # lint-amnesty, pylint: disable=broad-except - return {'ok': False, 'msg': HTML('Error {err}
in evaluating your expression "{given}"').format( - err=err, given=given - )} - - try: - xexpect = my_sympify(expect, normphase, matrix, do_qubit=do_qubit, abcsym=abcsym, symtab=symtab) - except Exception as err: # lint-amnesty, pylint: disable=broad-except - return {'ok': False, 'msg': HTML('Error {err}
in evaluating OUR expression "{expect}"').format( - err=err, expect=expect - )} - - if 'autonorm' in flags: # normalize trace of matrices - try: - xgiven /= xgiven.trace() - except Exception as err: # lint-amnesty, pylint: disable=broad-except - return {'ok': False, 'msg': HTML('Error {err}
in normalizing trace of your expression {xgiven}'). - format(err=err, xgiven=to_latex(xgiven))} - try: - xexpect /= xexpect.trace() - except Exception as err: # lint-amnesty, pylint: disable=broad-except - return {'ok': False, 'msg': HTML('Error {err}
in normalizing trace of OUR expression {xexpect}'). - format(err=err, xexpect=to_latex(xexpect))} - - msg = 'Your expression was evaluated as ' + to_latex(xgiven) - # msg += '
Expected ' + to_latex(xexpect) - - # msg += "
flags=%s" % flags - - if matrix and numerical: - xgiven = my_evalf(xgiven, chop=True) - dm = my_evalf(sympy.Matrix(xexpect) - sympy.Matrix(xgiven), chop=True) - msg += " = " + to_latex(xgiven) - if abs(dm.vec().norm().evalf()) < threshold: - return {'ok': True, 'msg': msg} - else: - pass - #msg += "dm = " + to_latex(dm) + " diff = " + str(abs(dm.vec().norm().evalf())) - #msg += "expect = " + to_latex(xexpect) - elif dosimplify: - if sympy.simplify(xexpect) == sympy.simplify(xgiven): - return {'ok': True, 'msg': msg} - elif numerical: - if abs((xexpect - xgiven).evalf(chop=True)) < threshold: - return {'ok': True, 'msg': msg} - elif xexpect == xgiven: - return {'ok': True, 'msg': msg} - - #msg += "

expect='%s', given='%s'" % (expect,given) # debugging - # msg += "

dot test " + to_latex(dot(sympy.Symbol('x'),sympy.Symbol('y'))) - return {'ok': False, 'msg': msg} - -#----------------------------------------------------------------------------- -# helper function to convert all

to - - -def make_error_message(msg): - # msg = msg.replace('

','

').replace('

','

') - msg = HTML('
{msg}
').format(msg=msg) - return msg - - -def is_within_tolerance(expected, actual, tolerance): - if expected == 0: - return abs(actual) < tolerance - else: - return abs(abs(actual - expected) / expected) < tolerance - -#----------------------------------------------------------------------------- -# Check function interface, which takes pmathml input -# -# This is one of the main entry points to call. - - -def symmath_check(expect, ans, dynamath=None, options=None, debug=None, xml=None): # lint-amnesty, pylint: disable=too-many-statements - """ - Check a symbolic mathematical expression using sympy. - The input may be presentation MathML. Uses formula. - - This is the default Symbolic Response checking function - - Desc of args: - expect is a sympy string representing the correct answer. It is interpreted - using my_sympify (from formula.py), which reads strings as sympy input - (e.g. 'integrate(x^2, (x,1,2))' would be valid, and evaluate to give 1.5) - - ans is student-typed answer. It is expected to be ascii math, but the code - below would support a sympy string. - - dynamath is the PMathML string converted by MathJax. It is used if - evaluation with ans is not sufficient. - - options is a string with these possible substrings, set as an xml property - of the problem: - -matrix - make a sympy matrix, rather than a list of lists, if possible - -qubit - passed to my_sympify - -imaginary - used in formla, presumably to signal to use i as sqrt(-1)? - -numerical - force numerical comparison. - """ - - msg = '' - # msg += '

abname=%s' % abname - # msg += '

adict=%s' % (repr(adict).replace('<','<')) - - threshold = 1.0e-3 # for numerical comparison (also with matrices) - DEBUG = debug - - if xml is not None: - DEBUG = xml.get('debug', False) # override debug flag using attribute in symbolicmath xml - if DEBUG in ['0', 'False']: - DEBUG = False - - # options - if options is None: - options = '' - do_matrix = 'matrix' in options - do_qubit = 'qubit' in options - do_numerical = 'numerical' in options - - # parse expected answer - try: - fexpect = my_sympify(str(expect), matrix=do_matrix, do_qubit=do_qubit) - except Exception as err: # lint-amnesty, pylint: disable=broad-except - msg += HTML('

Error {err} in parsing OUR expected answer "{expect}"

').format(err=err, expect=expect) - return {'ok': False, 'msg': make_error_message(msg)} - - ###### Sympy input ####### - # if expected answer is a number, try parsing provided answer as a number also - try: - fans = my_sympify(str(ans), matrix=do_matrix, do_qubit=do_qubit) - except Exception as err: # lint-amnesty, pylint: disable=broad-except, unused-variable - fans = None - - # do a numerical comparison if both expected and answer are numbers - if hasattr(fexpect, 'is_number') and fexpect.is_number \ - and hasattr(fans, 'is_number') and fans.is_number: - if is_within_tolerance(fexpect, fans, threshold): - return {'ok': True, 'msg': msg} - else: - msg += HTML('

You entered: {fans}

').format(fans=to_latex(fans)) - return {'ok': False, 'msg': msg} - - if do_numerical: # numerical answer expected - force numerical comparison - if is_within_tolerance(fexpect, fans, threshold): - return {'ok': True, 'msg': msg} - else: - msg += HTML('

You entered: {fans} (note that a numerical answer is expected)

').\ - format(fans=to_latex(fans)) - return {'ok': False, 'msg': msg} - - if fexpect == fans: - msg += HTML('

You entered: {fans}

').format(fans=to_latex(fans)) - return {'ok': True, 'msg': msg} - - ###### PMathML input ###### - # convert mathml answer to formula - try: - mmlans = dynamath[0] if dynamath else None - except Exception as err: # lint-amnesty, pylint: disable=broad-except - mmlans = None - if not mmlans: - return {'ok': False, 'msg': '[symmath_check] failed to get MathML for input; dynamath=%s' % dynamath} - - f = formula(mmlans, options=options) - - # get sympy representation of the formula - # if DEBUG: msg += '

mmlans=%s' % repr(mmlans).replace('<','<') - try: - fsym = f.sympy - msg += HTML('

You entered: {sympy}

').format(sympy=to_latex(f.sympy)) - except Exception as err: # lint-amnesty, pylint: disable=broad-except - log.exception("Error evaluating expression '%s' as a valid equation", ans) - msg += HTML("

Error in evaluating your expression '{ans}' as a valid equation

").format(ans=ans) - if "Illegal math" in str(err): - msg += HTML("

Illegal math expression

") - if DEBUG: - msg += HTML('Error: {err}

DEBUG messages:

{format_exc}

' - '

cmathml=

{cmathml}

pmathml=

{pmathml}


').format( - err=escape(str(err)), format_exc=traceback.format_exc(), cmathml=escape(f.cmathml), - pmathml=escape(mmlans) - ) - return {'ok': False, 'msg': make_error_message(msg)} - - # do numerical comparison with expected - if hasattr(fexpect, 'is_number') and fexpect.is_number: - if hasattr(fsym, 'is_number') and fsym.is_number: - if abs(abs(fsym - fexpect) / fexpect) < threshold: - return {'ok': True, 'msg': msg} - return {'ok': False, 'msg': msg} - msg += HTML("

Expecting a numerical answer!

given = {ans}

fsym = {fsym}

").format( - ans=repr(ans), fsym=repr(fsym) - ) - # msg += "

cmathml =

%s

" % str(f.cmathml).replace('<','<') - return {'ok': False, 'msg': make_error_message(msg)} - - # Here is a good spot for adding calls to X.simplify() or X.expand(), - # allowing equivalence over binomial expansion or trig identities - - # exactly the same? - if fexpect == fsym: - return {'ok': True, 'msg': msg} - - if isinstance(fexpect, list): - try: - xgiven = my_evalf(fsym, chop=True) - dm = my_evalf(sympy.Matrix(fexpect) - sympy.Matrix(xgiven), chop=True) - if abs(dm.vec().norm().evalf()) < threshold: - return {'ok': True, 'msg': msg} - except sympy.ShapeError: - msg += HTML("

Error - your input vector or matrix has the wrong dimensions") - return {'ok': False, 'msg': make_error_message(msg)} - except Exception as err: # lint-amnesty, pylint: disable=broad-except - msg += HTML("

Error %s in comparing expected (a list) and your answer

").format(escape(str(err))) - if DEBUG: - msg += HTML("

{format_exc}
").format(format_exc=traceback.format_exc()) - return {'ok': False, 'msg': make_error_message(msg)} - - #diff = (fexpect-fsym).simplify() - #fsym = fsym.simplify() - #fexpect = fexpect.simplify() - try: - diff = (fexpect - fsym) - except Exception as err: # lint-amnesty, pylint: disable=broad-except - diff = None - - if DEBUG: - msg += HTML('

DEBUG messages:

Got: {fsym}

Expecting: {fexpect}

')\ - .format(fsym=repr(fsym), fexpect=repr(fexpect).replace('**', '^').replace('hat(I)', 'hat(i)')) - # msg += "

Got: %s" % str([type(x) for x in fsym.atoms()]).replace('<','<') - # msg += "

Expecting: %s" % str([type(x) for x in fexpect.atoms()]).replace('<','<') - if diff: - msg += HTML("

Difference: {diff}

").format(diff=to_latex(diff)) - msg += HTML('
') - - # Used to return more keys: 'ex': fexpect, 'got': fsym - return {'ok': False, 'msg': msg} diff --git a/common/lib/symmath/symmath/test_formula.py b/common/lib/symmath/symmath/test_formula.py deleted file mode 100644 index 1235f7b771c1..000000000000 --- a/common/lib/symmath/symmath/test_formula.py +++ /dev/null @@ -1,117 +0,0 @@ -""" -Tests of symbolic math -""" - -import re -import unittest - -from lxml import etree - -from . import formula - - -def stripXML(xml): - xml = xml.replace('\n', '') - xml = re.sub(r'\> +\<', '><', xml) - return xml - - -class FormulaTest(unittest.TestCase): # lint-amnesty, pylint: disable=missing-class-docstring - # for readability later - mathml_start = '' - mathml_end = '' - - def setUp(self): - super(FormulaTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments - self.formulaInstance = formula('') - - def test_replace_mathvariants(self): - expr = ''' - - N -''' - - expected = 'scriptN' - - # wrap - expr = stripXML(self.mathml_start + expr + self.mathml_end) - expected = stripXML(self.mathml_start + expected + self.mathml_end) - - # process the expression - xml = etree.fromstring(expr) - xml = self.formulaInstance.preprocess_pmathml(xml) - test = etree.tostring(xml) - - # success? - assert test.decode('utf-8') == expected - - def test_fix_simple_superscripts(self): - expr = ''' - - a - - - b - -''' - - expected = 'a__b' - - # wrap - expr = stripXML(self.mathml_start + expr + self.mathml_end) - expected = stripXML(self.mathml_start + expected + self.mathml_end) - - # process the expression - xml = etree.fromstring(expr) - xml = self.formulaInstance.preprocess_pmathml(xml) - test = etree.tostring(xml) - - # success? - assert test.decode('utf-8') == expected - - def test_fix_complex_superscripts(self): - expr = ''' - - a - b - - - c - -''' - - expected = 'a_b__c' - - # wrap - expr = stripXML(self.mathml_start + expr + self.mathml_end) - expected = stripXML(self.mathml_start + expected + self.mathml_end) - - # process the expression - xml = etree.fromstring(expr) - xml = self.formulaInstance.preprocess_pmathml(xml) - test = etree.tostring(xml) - - # success? - assert test.decode('utf-8') == expected - - def test_fix_msubsup(self): - expr = ''' - - a - b - c -''' - - expected = 'a_bc' # which is (a_b)^c - - # wrap - expr = stripXML(self.mathml_start + expr + self.mathml_end) - expected = stripXML(self.mathml_start + expected + self.mathml_end) - - # process the expression - xml = etree.fromstring(expr) - xml = self.formulaInstance.preprocess_pmathml(xml) - test = etree.tostring(xml) - - # success? - assert test.decode('utf-8') == expected diff --git a/common/lib/symmath/symmath/test_symmath_check.py b/common/lib/symmath/symmath/test_symmath_check.py deleted file mode 100644 index 5a3cfe85f494..000000000000 --- a/common/lib/symmath/symmath/test_symmath_check.py +++ /dev/null @@ -1,89 +0,0 @@ -# lint-amnesty, pylint: disable=missing-module-docstring -from unittest import TestCase - -from six.moves import range - -from .symmath_check import symmath_check - - -class SymmathCheckTest(TestCase): # lint-amnesty, pylint: disable=missing-class-docstring - def test_symmath_check_integers(self): - number_list = range(-100, 100) - self._symmath_check_numbers(number_list) - - def test_symmath_check_floats(self): - number_list = [i + 0.01 for i in range(-100, 100)] - self._symmath_check_numbers(number_list) - - def test_symmath_check_same_symbols(self): - expected_str = "x+2*y" - dynamath = ''' - - - - x - + - 2 - * - y - - -'''.strip() - - # Expect that the exact same symbolic string is marked correct - result = symmath_check(expected_str, expected_str, dynamath=[dynamath]) - assert (('ok' in result) and result['ok']) - - def test_symmath_check_equivalent_symbols(self): - expected_str = "x+2*y" - input_str = "x+y+y" - dynamath = ''' - - - - x - + - y - + - y - - -'''.strip() - - # Expect that equivalent symbolic strings are marked correct - result = symmath_check(expected_str, input_str, dynamath=[dynamath]) - assert (('ok' in result) and result['ok']) - - def test_symmath_check_different_symbols(self): - expected_str = "0" - input_str = "x+y" - dynamath = ''' - - - - x - + - y - - -'''.strip() - - # Expect that an incorrect response is marked incorrect - result = symmath_check(expected_str, input_str, dynamath=[dynamath]) - assert (('ok' in result) and (not result['ok'])) - assert 'fail' not in result['msg'] - - def _symmath_check_numbers(self, number_list): # lint-amnesty, pylint: disable=missing-function-docstring - - for n in number_list: - - # expect = ans, so should say the answer is correct - expect = n - ans = n - result = symmath_check(str(expect), str(ans)) - assert (('ok' in result) and result['ok']), ('%f should == %f' % (expect, ans)) - - # Change expect so that it != ans - expect += 0.1 - result = symmath_check(str(expect), str(ans)) - assert (('ok' in result) and (not result['ok'])), ('%f should != %f' % (expect, ans)) From b80d8d60306ebd9f78dbac5306887d60e4e624e4 Mon Sep 17 00:00:00 2001 From: Carlos Muniz Date: Fri, 4 Feb 2022 11:45:44 -0500 Subject: [PATCH 2/7] refactor: Remove all mentions of symmath --- docs/guides/conf.py | 2 -- docs/guides/docstrings/common_lib.rst | 1 - scripts/verify-dunder-init.sh | 2 +- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/guides/conf.py b/docs/guides/conf.py index 58441b5c1bb1..f3038eb46271 100644 --- a/docs/guides/conf.py +++ b/docs/guides/conf.py @@ -22,7 +22,6 @@ sys.path.append(root / "docs/guides") sys.path.append(root / "common/lib/capa") sys.path.append(root / "common/lib/safe_lxml") -sys.path.append(root / "common/lib/symmath") sys.path.append(root / "common/lib/xmodule") # Use a settings module that allows all LMS and Studio code to be imported @@ -225,7 +224,6 @@ 'cms': 'cms', 'common/lib/capa/capa': 'common/lib/capa', 'common/lib/safe_lxml/safe_lxml': 'common/lib/safe_lxml', - 'common/lib/symmath/symmath': 'common/lib/symmath', 'common/lib/xmodule/xmodule': 'common/lib/xmodule', 'lms': 'lms', 'openedx': 'openedx', diff --git a/docs/guides/docstrings/common_lib.rst b/docs/guides/docstrings/common_lib.rst index 419063bd5b9b..4a4a1d40d638 100644 --- a/docs/guides/docstrings/common_lib.rst +++ b/docs/guides/docstrings/common_lib.rst @@ -10,5 +10,4 @@ out from edx-platform into separate packages at some point. common/lib/capa/modules common/lib/safe_lxml/modules - common/lib/symmath/modules common/lib/xmodule/modules diff --git a/scripts/verify-dunder-init.sh b/scripts/verify-dunder-init.sh index 5055138e3dd2..bc5da5860ffe 100755 --- a/scripts/verify-dunder-init.sh +++ b/scripts/verify-dunder-init.sh @@ -38,7 +38,7 @@ exclude+='|^common/test/data/?.*$' # * common/lib/xmodule -> EXCLUDE from check. # * common/lib/xmodule/xmodule/modulestore -> INCLUDE in check. exclude+='|^common/lib$' -exclude+='|^common/lib/(capa|safe_lxml|sandbox-packages|symmath|xmodule)$' +exclude+='|^common/lib/(capa|safe_lxml|sandbox-packages|xmodule)$' # Docs, scripts. exclude+='|^docs/.*$' From 6a227dfd8611e7ab244f4bac787ebb397c8b22fa Mon Sep 17 00:00:00 2001 From: Carlos Muniz Date: Fri, 4 Feb 2022 11:46:51 -0500 Subject: [PATCH 3/7] chore: Update requirements to remove symmath --- requirements/constraints.txt | 9 ++++++--- requirements/edx-sandbox/py38.in | 1 - requirements/edx-sandbox/py38.txt | 17 ++++++++--------- requirements/edx/base.txt | 12 +++++------- requirements/edx/development.txt | 18 +++++++++--------- requirements/edx/local.in | 1 - requirements/edx/testing.txt | 12 +++++------- 7 files changed, 33 insertions(+), 37 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 9c25b4a341fe..705c61e059ee 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -97,8 +97,11 @@ py2neo<2022 # Sphinx requires docutils<0.18. This pin can be removed once https://github.com/sphinx-doc/sphinx/issues/9777 is closed. docutils<0.18 -# Temporary constraint on openedx-calc. Latest openedx-calc also contains symmath, which may upend symmath package version -openedx-calc==2.0.1 - # scipy version 1.8 requires numpy>=1.17.3, we've pinned numpy to <1.17.0 in requirements/edx-sandbox/py38.in scipy<1.8.0 + +# mistune is a dependency of m2r (which is a dependency of sphinxcontrib-openapi) +# m2r fails to specify the version of mistune that it needs leading to the error message: +# AttributeError: module 'mistune' has no attribute 'BlockGrammar' +# https://github.com/miyakogi/m2r/issues/66 +mistune<2.0.0 diff --git a/requirements/edx-sandbox/py38.in b/requirements/edx-sandbox/py38.in index ac1af4fcf966..9d90dcba97a9 100644 --- a/requirements/edx-sandbox/py38.in +++ b/requirements/edx-sandbox/py38.in @@ -20,4 +20,3 @@ numpy>=1.16.0,<1.17.0 # NOTE: if you change code in these packages, you MUST change the version # number in its setup.py or the code WILL NOT be installed during deploy. -e common/lib/sandbox-packages --e common/lib/symmath diff --git a/requirements/edx-sandbox/py38.txt b/requirements/edx-sandbox/py38.txt index 8c9db0848104..5e20e359c2a5 100644 --- a/requirements/edx-sandbox/py38.txt +++ b/requirements/edx-sandbox/py38.txt @@ -4,9 +4,7 @@ # # make upgrade # -common/lib/sandbox-packages - # via -r requirements/edx-sandbox/py38.in -common/lib/symmath +-e common/lib/sandbox-packages # via -r requirements/edx-sandbox/py38.in cffi==1.15.0 # via cryptography @@ -30,8 +28,11 @@ lxml==4.5.0 # via # -c requirements/edx-sandbox/../constraints.txt # -r requirements/edx-sandbox/py38.in + # openedx-calc markupsafe==2.0.1 - # via chem + # via + # chem + # openedx-calc matplotlib==3.3.4 # via # -c requirements/edx-sandbox/../constraints.txt @@ -53,10 +54,8 @@ numpy==1.16.6 # matplotlib # openedx-calc # scipy -openedx-calc==2.0.1 - # via - # -c requirements/edx-sandbox/../constraints.txt - # -r requirements/edx-sandbox/py38.in +openedx-calc==3.0.0 + # via -r requirements/edx-sandbox/py38.in pillow==9.0.1 # via matplotlib pycparser==2.21 @@ -89,6 +88,6 @@ sympy==1.6.2 # via # -c requirements/edx-sandbox/../constraints.txt # -r requirements/edx-sandbox/py38.in - # symmath + # openedx-calc tqdm==4.62.3 # via nltk diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index 20525958cfae..a27af7eb6f3d 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -24,8 +24,6 @@ # via -r requirements/edx/local.in -e common/lib/sandbox-packages # via -r requirements/edx/local.in --e common/lib/symmath - # via -r requirements/edx/local.in -e openedx/core/lib/xblock_builtin/xblock_discussion # via -r requirements/edx/local.in -e git+https://github.com/edx-solutions/xblock-google-drive.git@2d176468e33c0713c911b563f8f65f7cf232f5b6#egg=xblock-google-drive @@ -628,6 +626,7 @@ lxml==4.5.0 # edxval # lti-consumer-xblock # olxcleaner + # openedx-calc # ora2 # safe-lxml # xblock @@ -655,6 +654,7 @@ markupsafe==2.0.1 # chem # jinja2 # mako + # openedx-calc # xblock maxminddb==2.2.0 # via geoip2 @@ -702,10 +702,8 @@ oauthlib==3.0.1 # lti-consumer-xblock # requests-oauthlib # social-auth-core -openedx-calc==2.0.1 - # via - # -c requirements/edx/../constraints.txt - # -r requirements/edx/base.in +openedx-calc==3.0.0 + # via -r requirements/edx/base.in openedx-events==0.7.1 # via -r requirements/edx/base.in openedx-filters==0.4.3 @@ -1004,7 +1002,7 @@ super-csv==2.1.4 sympy==1.6.2 # via # -c requirements/edx/../constraints.txt - # symmath + # openedx-calc tableauserverclient==0.17.0 # via edx-enterprise testfixtures==6.18.3 diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index 89c5c90ac8e3..6e4c24179a77 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -24,8 +24,6 @@ # via -r requirements/edx/testing.txt -e common/lib/sandbox-packages # via -r requirements/edx/testing.txt --e common/lib/symmath - # via -r requirements/edx/testing.txt -e openedx/core/lib/xblock_builtin/xblock_discussion # via -r requirements/edx/testing.txt -e git+https://github.com/edx-solutions/xblock-google-drive.git@2d176468e33c0713c911b563f8f65f7cf232f5b6#egg=xblock-google-drive @@ -838,6 +836,7 @@ lxml==4.5.0 # edxval # lti-consumer-xblock # olxcleaner + # openedx-calc # ora2 # pyquery # safe-lxml @@ -870,6 +869,7 @@ markupsafe==2.0.1 # chem # jinja2 # mako + # openedx-calc # xblock maxminddb==2.2.0 # via @@ -879,8 +879,10 @@ mccabe==0.6.1 # via # -r requirements/edx/testing.txt # pylint -mistune==2.0.2 - # via m2r +mistune==0.8.4 + # via + # -c requirements/edx/../constraints.txt + # m2r mock==4.0.3 # via # -r requirements/edx/testing.txt @@ -934,10 +936,8 @@ oauthlib==3.0.1 # lti-consumer-xblock # requests-oauthlib # social-auth-core -openedx-calc==2.0.1 - # via - # -c requirements/edx/../constraints.txt - # -r requirements/edx/testing.txt +openedx-calc==3.0.0 + # via -r requirements/edx/testing.txt openedx-events==0.7.1 # via -r requirements/edx/testing.txt openedx-filters==0.4.3 @@ -1422,7 +1422,7 @@ sympy==1.6.2 # via # -c requirements/edx/../constraints.txt # -r requirements/edx/testing.txt - # symmath + # openedx-calc tableauserverclient==0.17.0 # via # -r requirements/edx/testing.txt diff --git a/requirements/edx/local.in b/requirements/edx/local.in index e0252e71cee8..d60eab5e641f 100644 --- a/requirements/edx/local.in +++ b/requirements/edx/local.in @@ -3,7 +3,6 @@ -e common/lib/capa -e common/lib/safe_lxml -e common/lib/sandbox-packages --e common/lib/symmath -e common/lib/xmodule -e openedx/core/lib/xblock_builtin/xblock_discussion diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index 2df70026f026..52e999c915de 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -24,8 +24,6 @@ # via -r requirements/edx/base.txt -e common/lib/sandbox-packages # via -r requirements/edx/base.txt --e common/lib/symmath - # via -r requirements/edx/base.txt -e openedx/core/lib/xblock_builtin/xblock_discussion # via -r requirements/edx/base.txt -e git+https://github.com/edx-solutions/xblock-google-drive.git@2d176468e33c0713c911b563f8f65f7cf232f5b6#egg=xblock-google-drive @@ -797,6 +795,7 @@ lxml==4.5.0 # edxval # lti-consumer-xblock # olxcleaner + # openedx-calc # ora2 # pyquery # safe-lxml @@ -828,6 +827,7 @@ markupsafe==2.0.1 # chem # jinja2 # mako + # openedx-calc # xblock maxminddb==2.2.0 # via @@ -884,10 +884,8 @@ oauthlib==3.0.1 # lti-consumer-xblock # requests-oauthlib # social-auth-core -openedx-calc==2.0.1 - # via - # -c requirements/edx/../constraints.txt - # -r requirements/edx/base.txt +openedx-calc==3.0.0 + # via -r requirements/edx/base.txt openedx-events==0.7.1 # via -r requirements/edx/base.txt openedx-filters==0.4.3 @@ -1317,7 +1315,7 @@ sympy==1.6.2 # via # -c requirements/edx/../constraints.txt # -r requirements/edx/base.txt - # symmath + # openedx-calc tableauserverclient==0.17.0 # via # -r requirements/edx/base.txt From 675486a174ca039a739c1b05f07d645700cafb5c Mon Sep 17 00:00:00 2001 From: Carlos Muniz Date: Fri, 4 Feb 2022 11:47:17 -0500 Subject: [PATCH 4/7] fix: Correct path to symmath function --- common/lib/capa/capa/responsetypes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py index 3e4d6fc937da..9f47f49ca41b 100644 --- a/common/lib/capa/capa/responsetypes.py +++ b/common/lib/capa/capa/responsetypes.py @@ -2546,7 +2546,7 @@ def setup_response(self): super(SymbolicResponse, self).setup_response() # lint-amnesty, pylint: disable=super-with-arguments def execute_check_function(self, idset, submission): - from symmath import symmath_check + from symmath.symmath_check import symmath_check try: # Since we have limited max_inputfields to 1, # we can assume that there is only one submission From 8245a6b1cf5b754997f86f02b9bf4d1b54f0b2a8 Mon Sep 17 00:00:00 2001 From: Carlos Muniz Date: Mon, 7 Feb 2022 12:27:02 -0500 Subject: [PATCH 5/7] docs: Update mistune constraint description --- requirements/constraints.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 705c61e059ee..1641183c4503 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -103,5 +103,8 @@ scipy<1.8.0 # mistune is a dependency of m2r (which is a dependency of sphinxcontrib-openapi) # m2r fails to specify the version of mistune that it needs leading to the error message: # AttributeError: module 'mistune' has no attribute 'BlockGrammar' -# https://github.com/miyakogi/m2r/issues/66 +# See Issue: https://github.com/miyakogi/m2r/issues/66 +# m2r is no longer actively maintained: https://github.com/miyakogi/m2r/pull/43 +# This will be fixed when sphinxcontrib-openapi depends on m2r2 instead of m2r +# See issue: https://github.com/sphinx-contrib/openapi/issues/123 mistune<2.0.0 From a01a7ad416e984a44a032f151e96add921075109 Mon Sep 17 00:00:00 2001 From: Carlos Muniz Date: Mon, 7 Feb 2022 12:41:58 -0500 Subject: [PATCH 6/7] feat: Update openedx-calc==3.0.1 This allows common/lib/capa/capa/responsetypes.py to not have to change. --- common/lib/capa/capa/responsetypes.py | 2 +- requirements/edx-sandbox/py38.txt | 2 +- requirements/edx/base.txt | 2 +- requirements/edx/development.txt | 2 +- requirements/edx/testing.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py index 9f47f49ca41b..3e4d6fc937da 100644 --- a/common/lib/capa/capa/responsetypes.py +++ b/common/lib/capa/capa/responsetypes.py @@ -2546,7 +2546,7 @@ def setup_response(self): super(SymbolicResponse, self).setup_response() # lint-amnesty, pylint: disable=super-with-arguments def execute_check_function(self, idset, submission): - from symmath.symmath_check import symmath_check + from symmath import symmath_check try: # Since we have limited max_inputfields to 1, # we can assume that there is only one submission diff --git a/requirements/edx-sandbox/py38.txt b/requirements/edx-sandbox/py38.txt index 5e20e359c2a5..69b77bc712bc 100644 --- a/requirements/edx-sandbox/py38.txt +++ b/requirements/edx-sandbox/py38.txt @@ -54,7 +54,7 @@ numpy==1.16.6 # matplotlib # openedx-calc # scipy -openedx-calc==3.0.0 +openedx-calc==3.0.1 # via -r requirements/edx-sandbox/py38.in pillow==9.0.1 # via matplotlib diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index a27af7eb6f3d..211655c8e49b 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -702,7 +702,7 @@ oauthlib==3.0.1 # lti-consumer-xblock # requests-oauthlib # social-auth-core -openedx-calc==3.0.0 +openedx-calc==3.0.1 # via -r requirements/edx/base.in openedx-events==0.7.1 # via -r requirements/edx/base.in diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index 6e4c24179a77..57d079c0b0e7 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -936,7 +936,7 @@ oauthlib==3.0.1 # lti-consumer-xblock # requests-oauthlib # social-auth-core -openedx-calc==3.0.0 +openedx-calc==3.0.1 # via -r requirements/edx/testing.txt openedx-events==0.7.1 # via -r requirements/edx/testing.txt diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index 52e999c915de..4de78457d518 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -884,7 +884,7 @@ oauthlib==3.0.1 # lti-consumer-xblock # requests-oauthlib # social-auth-core -openedx-calc==3.0.0 +openedx-calc==3.0.1 # via -r requirements/edx/base.txt openedx-events==0.7.1 # via -r requirements/edx/base.txt From d5a41ec9f31116bfddc162df810645ffbcfd3b3f Mon Sep 17 00:00:00 2001 From: Carlos Muniz Date: Mon, 7 Feb 2022 14:37:49 -0500 Subject: [PATCH 7/7] feat: Change symmath to xmodule scripts/post-pip-compile.sh uses common/lib/symmath/ as its sample directory to clean up after using pip-tools. Since symmath has been removed, xmodule, the largest of the directories in common/lib/ will replace it. --- scripts/post-pip-compile.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/post-pip-compile.sh b/scripts/post-pip-compile.sh index a87faa5984d6..71be02ac33cc 100755 --- a/scripts/post-pip-compile.sh +++ b/scripts/post-pip-compile.sh @@ -17,7 +17,7 @@ function clean_file { # Workaround for https://github.com/jazzband/pip-tools/issues/204 - # change absolute paths for local editable packages back to relative ones FILE_CONTENT=$(<${FILE_PATH}) - FILE_URL_REGEX="-e (file:///[^"$'\n'"]*)/common/lib/symmath" + FILE_URL_REGEX="-e (file:///[^"$'\n'"]*)/common/lib/xmodule" if [[ "${FILE_CONTENT}" =~ ${FILE_URL_REGEX} ]]; then BASE_FILE_URL=${BASH_REMATCH[1]} sed "s|$BASE_FILE_URL/||" ${FILE_PATH} > ${TEMP_FILE}