diff --git a/mathics/builtin/lowlevelprofile.py b/mathics/builtin/lowlevelprofile.py new file mode 100644 index 000000000..6292a6779 --- /dev/null +++ b/mathics/builtin/lowlevelprofile.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- + +""" +Low-level Profiling + +Low-level (Python) profile from inside the Mathics interpreter + +""" + +import cProfile +import pstats +import sys +from io import StringIO + +from mathics.builtin import Builtin +from mathics.core.attributes import A_HOLD_ALL_COMPLETE, A_PROTECTED +from mathics.core.convert.python import from_python +from mathics.core.evaluation import Evaluation +from mathics.core.expression import Expression +from mathics.core.list import ListExpression +from mathics.core.symbols import SymbolNull + + +class PythonCProfileEvaluation(Builtin): + """ + :Python:https://docs.python.org/3/library/profile.html + +
+
'PythonProfileEvaluation[$expr$]' +
profile $expr$ with the Python's cProfiler. +
+ + >> PythonCProfileEvaluation[a + b + 1] + = ... + """ + + attributes = A_HOLD_ALL_COMPLETE | A_PROTECTED + summary_text = "profile the internal evaluation of an expression" + + def eval(self, expr: Expression, evaluation: Evaluation): + "PythonCProfileEvaluation[expr_]" + profile_result = SymbolNull + textstream = StringIO() + if sys.version_info >= (3, 8): + with cProfile.Profile() as pr: + result = expr.evaluate(evaluation) + stats = pstats.Stats(pr, stream=textstream) + stats.strip_dirs().sort_stats(-1).print_stats() + # TODO: convert the string (or the statistics) + # into something like a WL Table, by splitting the + # rows and the columns. By now, just a string + # is returned. + profile_result = from_python(textstream.getvalue()) + else: + result = expr.evaluate(evaluation) + return ListExpression(result, profile_result) diff --git a/mathics/docpipeline.py b/mathics/docpipeline.py index 0696f4e59..5fc005940 100644 --- a/mathics/docpipeline.py +++ b/mathics/docpipeline.py @@ -58,7 +58,7 @@ def print_and_log(*args): def compare(result, wanted) -> bool: - if result == wanted: + if wanted == "..." or result == wanted: return True if result is None or wanted is None: @@ -67,8 +67,10 @@ def compare(result, wanted) -> bool: wanted = wanted.splitlines() if result == [] and wanted == ["#<--#"]: return True + if len(result) != len(wanted): return False + for r, w in zip(result, wanted): wanted_re = re.escape(w.strip()) wanted_re = wanted_re.replace("\\.\\.\\.", ".*?")