diff --git a/Makefile b/Makefile index ffad84fd6..95fa2df8a 100644 --- a/Makefile +++ b/Makefile @@ -44,13 +44,13 @@ MATHICS3_MODULE_OPTION ?= --load-module pymathics.graph,pymathics.natlang test \ texdoc -SANDBOX ?= +MATHICS3_SANDBOX ?= ifeq ($(OS),Windows_NT) - SANDBOX = t + MATHICS3_SANDBOX = t else UNAME_S := $(shell uname -s) ifeq ($(UNAME_S),Darwin) - SANDBOX = t + MATHICS3_SANDBOX = t endif endif @@ -155,7 +155,7 @@ doctest-data: mathics/builtin/*.py mathics/doc/documentation/*.mdoc mathics/doc/ #: Run tests that appear in docstring in the code. Use environment variable "DOCTEST_OPTIONS" for doctest options doctest: - MATHICS_CHARACTER_ENCODING="ASCII" SANDBOX=$(SANDBOX) $(PYTHON) mathics/docpipeline.py $(DOCTEST_OPTIONS) + MATHICS_CHARACTER_ENCODING="ASCII" MATHICS3_SANDBOX=$(MATHICS3_SANDBOX) $(PYTHON) mathics/docpipeline.py $(DOCTEST_OPTIONS) #: Run tests that appear in docstring in the code, stopping on the first error. doctest-x: diff --git a/mathics/__main__.py b/mathics/__main__.py index b24825ebd..28ac37327 100755 --- a/mathics/__main__.py +++ b/mathics/__main__.py @@ -357,7 +357,10 @@ def interactive_eval_loop(shell, full_form: bool, strict_wl_output: bool): show_echo(source_code, evaluation) if len(source_code) and source_code[0] == "!": - subprocess.run(source_code[1:], shell=True) + if not settings.ENABLE_SYSTEM_COMMANDS: + evaluation.message("Run", "dis") + else: + subprocess.run(source_code[1:], shell=True) shell.definitions.increment_line_no(1) continue if query is None: diff --git a/mathics/builtin/system.py b/mathics/builtin/system.py index 397ebdf56..96d32e9a1 100644 --- a/mathics/builtin/system.py +++ b/mathics/builtin/system.py @@ -63,20 +63,31 @@ class Breakpoint(Builtin): Here is how to use 'mathics.disabled_breakpoint': - >> SetEnvironment["PYTHONBREAKPOINT" -> "mathics.disabled_breakpoint"]; + X> SetEnvironment["PYTHONBREAKPOINT" -> "mathics.disabled_breakpoint"]; - >> Breakpoint[] - = Hit disabled breakpoint. + X> Breakpoint[] + | Hit disabled breakpoint. = Breakpoint[] + X> Breakpoint[] + : Breakpoint::dis: Execution of external commands is disabled. + = Null + The environment variable 'PYTHONBREAKPOINT' can be changed at runtime to switch \ 'breakpoint()' and 'Breakpoint[]' behavior. """ summary_text = "invoke Python breakpoint()" + messages = {"dis": "Execution of external commands is disabled."} + def eval(self, evaluation: Evaluation): "Breakpoint[]" + from mathics import settings + + if not settings.ENABLE_SYSTEM_COMMANDS: + evaluation.message("Breakpoint", "dis") + return SymbolNull breakpoint() @@ -88,17 +99,25 @@ class CommandLine(Predefined):
is a list of strings passed on the command line to launch the Mathics3 session. - >> $CommandLine + S> $CommandLine = {...} + + S> $CommandLine + = {} """ name = "$CommandLine" + messages = {"dis": "Execution of external commands is disabled."} summary_text = ( "get the command line arguments passed when the current Mathics3 " "session was launched" ) def evaluate(self, evaluation: Evaluation) -> Expression: + from mathics import settings + + if not settings.ENABLE_SYSTEM_COMMANDS: + return ListExpression() return ListExpression(*(String(arg) for arg in sys.argv)) @@ -114,17 +133,21 @@ class Environment(Builtin): S> Environment["HOME"] = ... - See also - :'GetEnvironment': - /doc/reference-of-built-in-symbols/global-system-information/getenvironment/ and - :'SetEnvironment': - /doc/reference-of-built-in-symbols/global-system-information/setenvironment/. + S> Environment["HOME"] + : Environment::dis: Execution of external commands is disabled. + = $Failed """ + messages = {"dis": "Execution of external commands is disabled."} summary_text = "list the system environment variables" def eval(self, var, evaluation: Evaluation): "Environment[var_String]" + from mathics import settings + + if not settings.ENABLE_SYSTEM_COMMANDS: + evaluation.message("Environment", "dis") + return SymbolFailed env_var = var.get_string_value() if env_var not in os.environ: return SymbolFailed @@ -150,29 +173,26 @@ class GetEnvironment(Builtin): On POSIX systems, the following gets the users HOME directory: S> GetEnvironment["HOME"] - = ... - - We can get both the HOME directory and the user name in one go: - S> GetEnvironment[{"HOME", "USER"}] - = ... - - Arguments however must be strings: - S> GetEnvironment[HOME] - : HOME is not ALL or a string or a list of strings. - = GetEnvironment[HOME] + = ... - See also - :'Environment': - /doc/reference-of-built-in-symbols/global-system-information/environment/ and - :'SetEnvironment': - /doc/reference-of-built-in-symbols/global-system-information/setenvironment/. + S> GetEnvironment["HOME"] + : GetEnvironment::dis: Execution of external commands is disabled. + = $Failed """ - messages = {"name": "`1` is not ALL or a string or a list of strings."} + messages = { + "name": "`1` is not ALL or a string or a list of strings.", + "dis": "Execution of external commands is disabled.", + } summary_text = "retrieve the value of a system environment variable" def eval(self, var, evaluation: Evaluation): "GetEnvironment[var___]" + from mathics import settings + + if not settings.ENABLE_SYSTEM_COMMANDS: + evaluation.message("GetEnvironment", "dis") + return SymbolFailed if isinstance(var, String): env_var = var.value tup = ( @@ -289,7 +309,7 @@ def evaluate(self, evaluation: Evaluation) -> String: class MachineName(Predefined): - """ + r""" :WMA link:https://reference.wolfram.com/language/ref/\\$MachineName.html
@@ -300,13 +320,26 @@ class MachineName(Predefined): S> $MachineName = ... + + S> $MachineName + : $MachineName::dis: Execution of external commands is disabled. + = $Failed """ name = "$MachineName" + messages = {"dis": "Execution of external commands is disabled."} summary_text = "get the name of computer that Mathics3 is running" def evaluate(self, evaluation: Evaluation) -> String: - return String(platform.uname().node) + from mathics import settings + + if not settings.ENABLE_SYSTEM_COMMANDS: + evaluation.message("$MachineName", "dis") + return SymbolFailed + try: + return String(platform.uname().node) + except Exception: + return String("unknown") class MathicsVersion(Predefined): @@ -477,15 +510,24 @@ class ParentProcessID(Predefined): system under which it is run.
- >> $ParentProcessID + S> $ParentProcessID = ... + S> $ParentProcessID + : $ParentProcessID::dis: Execution of external commands is disabled. + = $Failed """ name = "$ParentProcessID" + messages = {"dis": "Execution of external commands is disabled."} summary_text = "get process id of the process that invoked Mathics3" def evaluate(self, evaluation: Evaluation) -> Integer: + from mathics import settings + + if not settings.ENABLE_SYSTEM_COMMANDS: + evaluation.message("$ParentProcessID", "dis") + return SymbolFailed return Integer(os.getppid()) @@ -499,14 +541,24 @@ class ProcessID(Predefined): which it is run. - >> $ProcessID + S> $ProcessID = ... + + S> $ProcessID + : $ProcessID::dis: Execution of external commands is disabled. + = $Failed """ name = "$ProcessID" + messages = {"dis": "Execution of external commands is disabled."} summary_text = "get process id of the Mathics process" def evaluate(self, evaluation: Evaluation) -> Integer: + from mathics import settings + + if not settings.ENABLE_SYSTEM_COMMANDS: + evaluation.message("$ProcessID", "dis") + return SymbolFailed return Integer(os.getpid()) @@ -571,10 +623,16 @@ class Run(Builtin): = ... """ + messages = {"dis": "Execution of external commands is disabled."} summary_text = "run a system command" def eval(self, command, evaluation: Evaluation): "Run[command_String]" + from mathics import settings + + if not settings.ENABLE_SYSTEM_COMMANDS: + evaluation.message("Run", "dis") + return SymbolFailed command_str = command.to_python() return Integer(subprocess.call(command_str, shell=True)) @@ -588,14 +646,22 @@ class ScriptCommandLine(Predefined):
is a list of string arguments when running the kernel is script mode. - >> $ScriptCommandLine - = {...} + S> $ScriptCommandLine + = {} + + S> $ScriptCommandLine + = {} """ summary_text = "list of command line arguments" name = "$ScriptCommandLine" + messages = {"dis": "Execution of external commands is disabled."} def evaluate(self, evaluation: Evaluation): + from mathics import settings + + if not settings.ENABLE_SYSTEM_COMMANDS: + return ListExpression() try: dash_index = sys.argv.index("--") except ValueError: @@ -649,6 +715,10 @@ class SetEnvironment(Builtin): Set a single environment variable: S> SetEnvironment["FOO" -> "bar"] + S> SetEnvironment["FOO" -> "bar"] + : SetEnvironment::dis: Execution of external commands is disabled. + = $Failed + See that the environment variable has changed: S> GetEnvironment["FOO"] = FOO -> bar @@ -681,11 +751,20 @@ class SetEnvironment(Builtin): /doc/reference-of-built-in-symbols/global-system-information/getenvironment/. """ - messages = {"value": "`1` must be a string or None."} + messages = { + "value": "`1` must be a string or None.", + "dis": "Execution of external commands is disabled.", + } summary_text = "set system environment variable(s)" def eval(self, rule, evaluation): "SetEnvironment[rule_Rule]" + from mathics import settings + + if not settings.ENABLE_SYSTEM_COMMANDS: + evaluation.message("SetEnvironment", "dis") + return SymbolFailed + env_var_name, env_var_value = rule.elements # WMA does not give an error message if env_var_name is not a String - weird. if not isinstance(env_var_name, String): @@ -695,13 +774,19 @@ def eval(self, rule, evaluation): evaluation.message("SetEnvironment", "value", env_var_value) return SymbolFailed - os.environ[env_var_name.value] = ( - None if None is SymbolNone else env_var_value.value - ) + if env_var_value is SymbolNone: + os.environ.pop(env_var_name.value, None) + else: + os.environ[env_var_name.value] = env_var_value.value return SymbolNull def eval_list(self, rules: Expression, evaluation: Evaluation): "SetEnvironment[{rules__}]" + from mathics import settings + + if not settings.ENABLE_SYSTEM_COMMANDS: + evaluation.message("SetEnvironment", "dis") + return SymbolFailed # All the rules must be of the form for rule in rules.elements: @@ -825,14 +910,24 @@ class UserName(Predefined): \Mathics session. - X> $UserName + S> $UserName = ... + + S> $UserName + : $UserName::dis: Execution of external commands is disabled. + = $Failed """ name = "$UserName" + messages = {"dis": "Execution of external commands is disabled."} summary_text = "get login name of the user that invoked the current session" def evaluate(self, evaluation: Evaluation) -> String: + from mathics import settings + + if not settings.ENABLE_SYSTEM_COMMANDS: + evaluation.message("$UserName", "dis") + return SymbolFailed try: user = os.getlogin() except Exception: @@ -896,14 +991,24 @@ class SystemMemory(Predefined):
Returns the total amount of physical memory. - >> $SystemMemory + S> $SystemMemory = ... + + S> $SystemMemory + : $SystemMemory::dis: Execution of external commands is disabled. + = $Failed """ name = "$SystemMemory" summary_text = "get the total amount of physical memory in the system" + messages = {"dis": "Execution of external commands is disabled."} def evaluate(self, evaluation: Evaluation) -> Integer: + from mathics import settings + + if not settings.ENABLE_SYSTEM_COMMANDS: + evaluation.message("$SystemMemory", "dis") + return SymbolFailed totalmem = psutil.virtual_memory().total return Integer(totalmem) @@ -916,18 +1021,28 @@ class MemoryAvailable(Builtin):
Returns the amount of the available physical memory. - >> MemoryAvailable[] + S> MemoryAvailable[] = ... + S> MemoryAvailable[] + : MemoryAvailable::dis: Execution of external commands is disabled. + = $Failed + The relationship between \\$SystemMemory, MemoryAvailable, and MemoryInUse: - >> $SystemMemory > MemoryAvailable[] > MemoryInUse[] + S> $SystemMemory > MemoryAvailable[] > MemoryInUse[] = True """ summary_text = "get the available amount of physical memory in the system" + messages = {"dis": "Execution of external commands is disabled."} def eval(self, evaluation: Evaluation) -> Integer: """MemoryAvailable[]""" + from mathics import settings + + if not settings.ENABLE_SYSTEM_COMMANDS: + evaluation.message("MemoryAvailable", "dis") + return SymbolFailed totalmem = psutil.virtual_memory().available return Integer(totalmem) @@ -943,14 +1058,24 @@ class SystemMemory(Predefined): This system however doesn't have that installed, so -1 is returned instead. - >> $SystemMemory + S> $SystemMemory = -1 + + S> $SystemMemory + : $SystemMemory::dis: Execution of external commands is disabled. + = $Failed """ summary_text = "the total amount of physical memory in the system" name = "$SystemMemory" + messages = {"dis": "Execution of external commands is disabled."} def evaluate(self, evaluation: Evaluation) -> Integer: + from mathics import settings + + if not settings.ENABLE_SYSTEM_COMMANDS: + evaluation.message("$SystemMemory", "dis") + return SymbolFailed return IntegerM1 class MemoryAvailable(Builtin): @@ -959,16 +1084,26 @@ class MemoryAvailable(Builtin):
'MemoryAvailable' -
Returns the amount of the available physical when Python module "psutil" is installed. +
Returns the amount of the available physical memory when Python module "psutil" is installed. This system however doesn't have that installed, so -1 is returned instead.
- >> MemoryAvailable[] + S> MemoryAvailable[] = -1 + + S> MemoryAvailable[] + : MemoryAvailable::dis: Execution of external commands is disabled. + = $Failed """ summary_text = "get the available amount of physical memory in the system" + messages = {"dis": "Execution of external commands is disabled."} def eval(self, evaluation: Evaluation) -> Integer: """MemoryAvailable[]""" + from mathics import settings + + if not settings.ENABLE_SYSTEM_COMMANDS: + evaluation.message("MemoryAvailable", "dis") + return SymbolFailed return IntegerM1 diff --git a/mathics/core/convert/op.py b/mathics/core/convert/op.py index c0fad95b1..8ec8e1e8e 100644 --- a/mathics/core/convert/op.py +++ b/mathics/core/convert/op.py @@ -30,8 +30,8 @@ val: operator_to_ascii[key] for key, val in operator_to_unicode.items() } -UNICODE_TO_AMSLATEX = OPERATOR_CONVERSION_TABLES["unicode-to-amslatex"] -UNICODE_TO_LATEX = OPERATOR_CONVERSION_TABLES["unicode-to-latex"] +UNICODE_TO_AMSLATEX = OPERATOR_CONVERSION_TABLES.get("unicode-to-amslatex", {}) +UNICODE_TO_LATEX = OPERATOR_CONVERSION_TABLES.get("unicode-to-latex", {}) AMSTEX_OPERATORS = { diff --git a/mathics/core/parser/operators.py b/mathics/core/parser/operators.py index 7ebc1216c..6418f5187 100644 --- a/mathics/core/parser/operators.py +++ b/mathics/core/parser/operators.py @@ -35,7 +35,9 @@ nonassoc_binary_operators = OPERATOR_DATA["non-associative-binary-operators"] operator_precedences = OPERATOR_DATA["operator-precedences"] operator_to_amslatex = OPERATOR_DATA["operator-to-amslatex"] -operator_to_string = OPERATOR_DATA["operator-to-string"] +operator_to_string = OPERATOR_DATA.get( + "operator-to-string", OPERATOR_DATA.get("operator-to_string", {}) +) postfix_operators = OPERATOR_DATA["postfix-operators"] prefix_operators = OPERATOR_DATA["prefix-operators"] right_binary_operators = OPERATOR_DATA["right-binary-operators"] diff --git a/mathics/doc/doc_entries.py b/mathics/doc/doc_entries.py index e6dd99474..707fbf3b6 100644 --- a/mathics/doc/doc_entries.py +++ b/mathics/doc/doc_entries.py @@ -317,7 +317,7 @@ class DocTest: the documentation. * `X>` Shows the example in the docs, but disables testing the example. * `S>` Shows the example in the docs, but disables testing if environment - variable SANDBOX is set. + variable MATHICS3_SANDBOX is set. * `=` Compares the result text. * `:` Compares an (error) message. `|` Prints output. @@ -362,8 +362,14 @@ def strip_sentinal(line: str): self.private = testcase[0] == "#" # Ignored test cases are NOT executed, but shown as part of the docs - # Sandboxed test cases are NOT executed if environment SANDBOX is set - if testcase[0] == "X" or (testcase[0] == "S" and getenv("SANDBOX", False)): + # Sandboxed test cases are NOT executed if environment MATHICS3_SANDBOX is set + from mathics import settings + + is_sandbox = not settings.ENABLE_SYSTEM_COMMANDS + if ( + testcase[0] == "X" + or (testcase[0] == "S" and is_sandbox) + ): self.ignore = True # substitute '>' again so we get the correct formatting testcase[0] = ">" diff --git a/mathics/doc/latex_doc.py b/mathics/doc/latex_doc.py index 968aa9a11..41408d101 100644 --- a/mathics/doc/latex_doc.py +++ b/mathics/doc/latex_doc.py @@ -540,7 +540,7 @@ class LaTeXDocTest(DocTest): the documentation. * `X>` Shows the example in the docs, but disables testing the example. * `S>` Shows the example in the docs, but disables testing if environment - variable SANDBOX is set. + variable MATHICS3_SANDBOX is set. * `=` Compares the result text. * `:` Compares an (error) message. `|` Prints output. diff --git a/mathics/format/form/util.py b/mathics/format/form/util.py index 82679f6f0..a3a7a7584 100644 --- a/mathics/format/form/util.py +++ b/mathics/format/form/util.py @@ -35,10 +35,10 @@ class _WrongFormattedExpression(Exception): ARITHMETIC_OPERATOR_STRINGS: Final[FrozenSet[str]] = frozenset( [ - *operator_to_string["Divide"], - *operator_to_string["NonCommutativeMultiply"], - *operator_to_string["Power"], - *operator_to_string["Times"], + *operator_to_string.get("Divide", ["/"]), + *operator_to_string.get("NonCommutativeMultiply", ["**"]), + *operator_to_string.get("Power", ["^"]), + *operator_to_string.get("Times", ["*"]), " ", ] ) @@ -109,12 +109,13 @@ def collect_in_pre_post_arguments( operator_spec = render_function(head, evaluation, **kwargs) if head is SymbolInfix: operator_spec = [ - f"{operator_to_string['Infix']}{operator_spec}{operator_to_string['Infix']}" + f"{operator_to_string.get('Infix', '')}{operator_spec}" + f"{operator_to_string.get('Infix', '')}" ] elif head is SymbolPrefix: - operator_spec = f"{operator_spec}{operator_to_string['Prefix']}" + operator_spec = f"{operator_spec}{operator_to_string.get('Prefix', '')}" elif head is SymbolPostfix: - operator_spec = f"{operator_to_string['Postfix']}{operator_spec}" + operator_spec = f"{operator_to_string.get('Postfix', '')}{operator_spec}" return operands, operator_spec, precedence, group_name # At least two parameters: get the operator spec. diff --git a/mathics/interrupt.py b/mathics/interrupt.py index df70e8039..c47470565 100644 --- a/mathics/interrupt.py +++ b/mathics/interrupt.py @@ -41,7 +41,10 @@ def inspect_eval_loop(evaluation: Evaluation): query, source_code = evaluation.parse_feeder_returning_code(shell) # show_echo(source_code, evaluation) if len(source_code) and source_code[0] == "!" and shell is not None: - subprocess.run(source_code[1:], shell=True) + if not settings.ENABLE_SYSTEM_COMMANDS: + evaluation.message("Run", "dis") + else: + subprocess.run(source_code[1:], shell=True) if shell.definitions is not None: shell.definitions.increment_line_no(1) continue @@ -90,7 +93,10 @@ def Mathics3_interrupt_handler( print_fn("continuing") break elif user_input in ("debugger", "d"): - breakpoint() + if not settings.ENABLE_SYSTEM_COMMANDS: + print_fn("Execution of external commands is disabled.") + else: + breakpoint() elif user_input in ("exit", "quit"): print_fn("Mathics3 exited because of an interrupt.") sys.exit(3) diff --git a/mathics/settings.py b/mathics/settings.py index 288f1cf09..65190cebc 100644 --- a/mathics/settings.py +++ b/mathics/settings.py @@ -4,6 +4,7 @@ Some of the values can be adjusted via Environment Variables. """ + import os import os.path as osp import sys @@ -94,6 +95,23 @@ def get_srcdir(): # users to access local files. ENABLE_FILES_MODULE = True +# Leave this True unless you have specific reason for not permitting +# users to execute system commands. +# If MATHICS3_SANDBOX environment variable is set, this defaults to False. +ENABLE_SYSTEM_COMMANDS = ( + os.environ.get( + "MATHICS3_ENABLE_SYSTEM_COMMANDS", + str( + not ( + os.environ.get("MATHICS3_SANDBOX") + or sys.platform in ("emscripten", "wasi") + ) + ), + ).lower() + == "true" +) + + # Rocky: this is probably a hack. LoadModule[] needs to handle # whatever it is that setting this thing did. default_pymathics_modules: List[str] = [] diff --git a/test/builtin/drawing/test_image.py b/test/builtin/drawing/test_image.py index c7e52b74e..b6c94ca91 100644 --- a/test/builtin/drawing/test_image.py +++ b/test/builtin/drawing/test_image.py @@ -56,8 +56,8 @@ reason="Test doesn't work in a when scikit-image is not installed", ) @pytest.mark.skipif( - os.getenv("SANDBOX", False), - reason="Test doesn't work in a sandboxed environment with access to local files", + os.getenv("MATHICS3_SANDBOX"), + reason="Files module is disabled in sandbox mode", ) @pytest.mark.parametrize(("str_expr, str_expected, msg"), image_tests) def test_image(str_expr: str, str_expected: str, msg: str, message=""): diff --git a/test/builtin/test_file_operations.py b/test/builtin/test_file_operations.py index 5e9db2845..04c44e95d 100644 --- a/test/builtin/test_file_operations.py +++ b/test/builtin/test_file_operations.py @@ -100,8 +100,8 @@ ], ) @pytest.mark.skipif( - os.getenv("SANDBOX", False), - reason="Test doesn't work in a sandboxed environment with access to local files", + os.getenv("MATHICS3_SANDBOX"), + reason="Files module is disabled in sandbox mode", ) def test_private_doctests_file_properties(str_expr, msgs, str_expected, fail_msg): """file_opertions.file_properties""" diff --git a/test/builtin/test_system.py b/test/builtin/test_system.py index c48eefc5f..4ea6cb7d0 100644 --- a/test/builtin/test_system.py +++ b/test/builtin/test_system.py @@ -4,17 +4,36 @@ """ +import os from test.helper import check_evaluation import pytest +from mathics import settings + @pytest.mark.parametrize( ("str_expr", "str_expected", "assert_tag_message"), [ ('MemberQ[$Packages, "System`"]', "True", "$Packages"), - ("Head[$ParentProcessID] == Integer", "True", "$ParentProcessID"), - ("Head[$ProcessID] == Integer", "True", "$ProcessID"), + pytest.param( + "Head[$ParentProcessID] == Integer", + "True", + "$ParentProcessID", + marks=pytest.mark.skipif( + not settings.ENABLE_SYSTEM_COMMANDS, + reason="In sandbox mode, $ParentProcessID returns $Failed", + ), + ), + pytest.param( + "Head[$ProcessID] == Integer", + "True", + "$ProcessID", + marks=pytest.mark.skipif( + not settings.ENABLE_SYSTEM_COMMANDS, + reason="In sandbox mode, $ProcessID returns $Failed", + ), + ), ("Head[$SessionID] == Integer", "True", "$SessionID"), ("Head[$SystemWordLength] == Integer", "True", "$SystemWordLength"), ], diff --git a/test/doc/test_doctests.py b/test/doc/test_doctests.py index d721001e1..e04620ffc 100644 --- a/test/doc/test_doctests.py +++ b/test/doc/test_doctests.py @@ -2,6 +2,8 @@ Pytests for the documentation system. Basic functions and classes. """ +from unittest.mock import patch + from mathics.core.evaluation import Message, Print from mathics.core.load_builtin import import_and_load_builtins from mathics.doc.common_doc import MathicsMainDocumentation @@ -92,10 +94,11 @@ def test_create_doctest(): }, }, ] - for index, test_case in enumerate(test_cases): - doctest = DocTest(1, test_case["test"], key) - for property_key, value in test_case["properties"].items(): - if property_key == "result" and value is None: - assert getattr(doctest, property_key) == "" - else: - assert getattr(doctest, property_key) == value + with patch("mathics.settings.ENABLE_SYSTEM_COMMANDS", True): + for index, test_case in enumerate(test_cases): + doctest = DocTest(1, test_case["test"], key) + for property_key, value in test_case["properties"].items(): + if property_key == "result" and value is None: + assert getattr(doctest, property_key) == "" + else: + assert getattr(doctest, property_key) == value \ No newline at end of file diff --git a/test/test_evaluation.py b/test/test_evaluation.py index d21b7ac07..b4f3503f6 100644 --- a/test/test_evaluation.py +++ b/test/test_evaluation.py @@ -3,6 +3,8 @@ import pytest +from mathics import settings + from .helper import check_evaluation, evaluate, session @@ -25,15 +27,50 @@ (r"Sum[2^(-i), {i, 1, \[Infinity]}]", "1"), # Global System Information (r"Abs[$ByteOrdering]", "1"), - (r"Head[$CommandLine]", "List"), + pytest.param( + r"Head[$CommandLine]", + "List", + marks=pytest.mark.skipif( + not settings.ENABLE_SYSTEM_COMMANDS, + reason="In sandbox mode, $CommandLine returns {}", + ), + ), (r"Head[$Machine]", "String"), - (r"Head[$MachineName]", "String"), + pytest.param( + r"Head[$MachineName]", + "String", + marks=pytest.mark.skipif( + not settings.ENABLE_SYSTEM_COMMANDS, + reason="In sandbox mode, $MachineName returns $Failed", + ), + ), (r"""Length[Names["System`*"]] > 1024""", "True"), (r"Length[$Packages] >= 5", "True"), - (r"Head[$ParentProcessID]", "Integer"), - (r"Head[$ProcessID]", "Integer"), + pytest.param( + r"Head[$ParentProcessID]", + "Integer", + marks=pytest.mark.skipif( + not settings.ENABLE_SYSTEM_COMMANDS, + reason="In sandbox mode, $ParentProcessID returns $Failed", + ), + ), + pytest.param( + r"Head[$ProcessID]", + "Integer", + marks=pytest.mark.skipif( + not settings.ENABLE_SYSTEM_COMMANDS, + reason="In sandbox mode, $ProcessID returns $Failed", + ), + ), (r"Head[$ProcessorType]", "String"), - (r"Head[$ScriptCommandLine]", "List"), + pytest.param( + r"Head[$ScriptCommandLine]", + "List", + marks=pytest.mark.skipif( + not settings.ENABLE_SYSTEM_COMMANDS, + reason="In sandbox mode, $ScriptCommandLine returns {}", + ), + ), (r"Head[$SystemID]", "String"), (r"Head[$SystemWordLength]", "Integer"), # This doesn't work if not logged or in some OS's