diff --git a/mathicsscript/__main__.py b/mathicsscript/__main__.py index 428428e..3f9a834 100755 --- a/mathicsscript/__main__.py +++ b/mathicsscript/__main__.py @@ -140,7 +140,12 @@ def out(self, out): def interactive_eval_loop( - shell: TerminalShellCommon, unicode, prompt, strict_wl_output: bool + shell: TerminalShellCommon, + unicode, + prompt, + matplotlib: bool, + asymptote: bool, + strict_wl_output: bool, ): def identity(x: Any) -> Any: return x @@ -337,6 +342,25 @@ def fmt_fun(query: Any) -> Any: help=("Most WL-output compatible (at the expense of usability)."), required=False, ) +@click.option( + "--asymptote/--no-asymptote", + default=True, + show_default=True, + help=( + "Use asymptote for 2D and 3D Graphics; " + "you need a working asymptote for this option." + ), +) +@click.option( + "--matplotlib/--no-matplotlib", + default=True, + show_default=True, + help=( + "Use matplotlib for 2D Graphics; " + "you need a working matplotlib for this option. " + "If set, this will take precedence over asymptote for 2D Graphics." + ), +) @click.argument("file", nargs=1, type=click.Path(readable=True), required=False) def main( full_form, @@ -353,6 +377,8 @@ def main( style, pygments_tokens, strict_wl_output, + asymptote, + matplotlib, file, ) -> int: """A command-line interface to Mathics. @@ -372,12 +398,14 @@ def main( # Set a default value for $ShowFullFormInput to False. # Then, it can be changed by the settings file (in WL) # and overwritten by the command line parameter. - definitions.set_ownvalue( - "Settings`$ShowFullFormInput", from_python(True if full_form else False) - ) - definitions.set_ownvalue( - "Settings`$PygmentsShowTokens", from_python(True if pygments_tokens else False) - ) + for setting_name, setting_value in ( + ("$ShowFullFormInput", full_form), + ("$UseAsymptote", asymptote), + ("$UseMatplotlib", matplotlib), + ): + definitions.set_ownvalue( + f"Settings`{setting_name}", from_python(True if setting_value else False) + ) if post_mortem: try: @@ -475,9 +503,6 @@ def main( "Settings`$ShowFullFormInput", SymbolTrue if full_form else SymbolFalse ) - definitions.set_ownvalue( - "Settings`$PygmentsStyle", from_python(shell.pygments_style) - ) definitions.set_ownvalue( "Settings`$PygmentsShowTokens", from_python(pygments_tokens) ) @@ -490,7 +515,9 @@ def main( ) definitions.set_line_no(0) - interactive_eval_loop(shell, unicode, prompt, strict_wl_output) + interactive_eval_loop( + shell, unicode, prompt, asymptote, matplotlib, strict_wl_output + ) return exit_rc diff --git a/mathicsscript/asymptote.py b/mathicsscript/asymptote.py index d7aae97..24c515f 100644 --- a/mathicsscript/asymptote.py +++ b/mathicsscript/asymptote.py @@ -6,8 +6,10 @@ import mathics import os import os.path as osp +import subprocess from subprocess import Popen, PIPE, run +from tempfile import NamedTemporaryFile from typing import Optional asy_program = os.environ.get("ASY_PROG", "asy") @@ -95,6 +97,16 @@ def __del__(self): self.session.wait() +def write_asy_and_view(asy_string: str): + # TODO: add option to let user decide whether or not to delete + # image afterwards. + with NamedTemporaryFile( + mode="w", prefix="Mathics3-Graph-", suffix=".asy", delete=False + ) as asy_fp: + asy_fp.write(asy_string + "\n") + subprocess.run(args=[asy_program, "-View", asy_fp.name]) + + if __name__ == "__main__": g = Asy() g.size(200) diff --git a/mathicsscript/autoload/settings.m b/mathicsscript/autoload/settings.m index d265f4b..f09e17c 100644 --- a/mathicsscript/autoload/settings.m +++ b/mathicsscript/autoload/settings.m @@ -1,8 +1,10 @@ +(* -*- wolfram -*- *) (**********************************************************************) (* *) (* Settings for mathicsscript *) (* *) -(* *) +(* Values that can be set from the command line should not have a *) +(* default value set here, just the "usage" value. *) (* *) (**********************************************************************) @@ -24,11 +26,11 @@ Settings`$UseUnicode::usage = "This Boolean variable sets whether Unicode is used in terminal input and output." Settings`$UseUnicode = True -Settings`$UseAsymptote::usage = "This Boolean variable sets whether 3D Graphics should render using Asymptote." -Settings`$UseAsymptote = True +Settings`$UseAsymptote::usage = "This Boolean variable sets whether 2D and 3D Graphics should render using Asymptote." +Settings`$UseMatplotlib::usage = "This Boolean variable sets whether 2D Graphics should render using Matplotlib. -Settings`$UseMatplotlib::usage = "This Boolean variable sets whether 2D Graphics should render using Matplotlib." -Settings`$UseMatplotlib = True +If set, and $UseAsymptote is also set, matplotlib will take precedence for 2D graphics. +" Settings`MathicsScriptVersion::usage = "This string is the version of MathicsScript we are running." diff --git a/mathicsscript/data/inputrc-no-unicode b/mathicsscript/data/inputrc-no-unicode index 23fdc01..d5c5cbe 100644 --- a/mathicsscript/data/inputrc-no-unicode +++ b/mathicsscript/data/inputrc-no-unicode @@ -1,6 +1,3 @@ -# GNU Readline input unicode translations -# Autogenerated from mathics_scanner.generate.rl_inputrc on Wed Oct 26 04:49:35 PM EDT 2022 - "\ea'\e": "á" "\ea-\e": "ā" "\eau\e": "ă" @@ -504,9 +501,11 @@ "\e<--\e": "⟵" "\e<-->\e": "⟷" "\e-->\e": "⟶" +"\eMlim\e": "\\[MaxLimit]" "\emath\e": "\\[MathematicaIcon]" "\emho\e": "℧" "\emi\e": "µ" +"\emlim\e": "lim" "\e-+\e": "∓" "\em\e": "μ" "\env\e": "ň" diff --git a/mathicsscript/format.py b/mathicsscript/format.py index 3ea618e..56dc854 100644 --- a/mathicsscript/format.py +++ b/mathicsscript/format.py @@ -47,7 +47,7 @@ except ImportError: svg2png = None -from mathicsscript.asymptote import Asy +from mathicsscript.asymptote import Asy, write_asy_and_view have_asymptote = False try: @@ -92,6 +92,7 @@ def eval_boxes(result, fn: Callable, obj, **options): expr_type = expr.get_head_name() expr_head = expr.get_head() + if expr_head is SymbolMathMLForm: format = "xml" elements = expr.elements @@ -102,57 +103,62 @@ def eval_boxes(result, fn: Callable, obj, **options): elements = expr.elements if len(elements) == 1: expr = elements[0] - elif expr_head is SymbolImage: - if get_settings_value(obj.definitions, "Settings`$UseMatplotlib") and plt: - temp_png = NamedTemporaryFile( - mode="w+b", suffix=".png", prefix="mathicsscript-" + elif ( + expr_head is SymbolImage + and get_settings_value(obj.definitions, "Settings`$UseMatplotlib") + and plt + ): + temp_png = NamedTemporaryFile( + mode="w+b", suffix=".png", prefix="mathicsscript-" + ) + try: + png_expr = Expression( + SymbolExport, String(temp_png.name), expr, String("PNG") ) - try: - png_expr = Expression( - SymbolExport, String(temp_png.name), expr, String("PNG") - ) - result = png_expr.evaluate(obj) - plt.axes().set_axis_off() - img = mpimg.imread(temp_png) - cmap = "gray" if expr.color_space == "Grayscale" else None - plt.imshow(img, cmap=cmap) - plt.show() - except: # noqa - pass - temp_png.close() - + result = png_expr.evaluate(obj) + plt.axes().set_axis_off() + img = mpimg.imread(temp_png) + cmap = "gray" if expr.color_space == "Grayscale" else None + plt.imshow(img, cmap=cmap) + plt.show() + except: # noqa pass + temp_png.close() - elif expr_head in (SymbolGraphics, SymbolPlot): - if ( - get_settings_value(obj.definitions, "Settings`$UseMatplotlib") - and plt - and svg2png - ): - svg_expr = Expression(SymbolExportString, expr, String("SVG")) - svg_str = svg_expr.evaluate(obj).to_python(string_quotes=False) - temp_png = NamedTemporaryFile( - mode="w+b", suffix=".png", prefix="mathicsscript-" - ) - try: - svg2png(bytestring=svg_str, write_to=temp_png.name) - plt.axes().set_axis_off() - img = mpimg.imread(temp_png) - plt.imshow(img) - plt.show() - temp_png.close() - except: # noqa - pass + elif ( + expr_head in (SymbolGraphics, SymbolPlot) + and get_settings_value(obj.definitions, "Settings`$UseMatplotlib") + and plt + and svg2png + ): + svg_expr = Expression(SymbolExportString, expr, String("SVG")) + svg_str = svg_expr.evaluate(obj).to_python(string_quotes=False) + temp_png = NamedTemporaryFile( + mode="w+b", suffix=".png", prefix="mathicsscript-" + ) + try: + svg2png(bytestring=svg_str, write_to=temp_png.name) + plt.axes().set_axis_off() + img = mpimg.imread(temp_png) + plt.imshow(img) + plt.show() + temp_png.close() + except: # noqa + pass return expr_type elif ( - expr_head in (SymbolGraphics3D,) + expr_head in (SymbolGraphics, SymbolPlot, SymbolGraphics3D) and have_asymptote and get_settings_value(obj.definitions, "Settings`$UseAsymptote") ): asy_expr = Expression(SymbolExportString, expr, String("asy")) asy_str = asy_expr.evaluate(obj).to_python(string_quotes=False) - asymptote_graph.erase() - asymptote_graph.send(asy_str) + + # Alternate older version + # asymptote_graph.erase() + # asymptote_graph.send(asy_str) + + write_asy_and_view(asy_str) return expr_type if format == "text": diff --git a/mathicsscript/termshell.py b/mathicsscript/termshell.py index 15a0e84..6bf8e73 100644 --- a/mathicsscript/termshell.py +++ b/mathicsscript/termshell.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2020-2022 Rocky Bernstein +# Copyright (C) 2020-2022, 2024 Rocky Bernstein from columnize import columnize @@ -115,18 +115,13 @@ def __init__( try: self.terminal_formatter = Terminal256Formatter(style=style) except ClassNotFound: - print( - "Pygments style name '%s' not found; No pygments style set" % style - ) + print(f"Pygments style name '{style}' not found; No pygments style set") self.pygments_style = style self.definitions = definitions set_settings_value( self.definitions, "Settings`$PygmentsShowTokens", from_python(False) ) - set_settings_value( - self.definitions, "Settings`$PygmentsStyle", from_python(style) - ) set_settings_value( self.definitions, "Settings`$UseUnicode", from_python(use_unicode) ) diff --git a/mathicsscript/user-settings.m b/mathicsscript/user-settings.m index a20f0e4..9d81d17 100644 --- a/mathicsscript/user-settings.m +++ b/mathicsscript/user-settings.m @@ -1,3 +1,4 @@ +(* -*- wolfram -*- *) (***********************************************************) (* User settings config for mathicsscript *) (* Customize this with Mathics definitions as desired. *)