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 '' % (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 = ''
-
- 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 = '''
-'''.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 = '''
-'''.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 = '''
-'''.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}