diff --git a/mathics/__init__.py b/mathics/__init__.py
index b2b2dcec8..80fe909e0 100644
--- a/mathics/__init__.py
+++ b/mathics/__init__.py
@@ -5,9 +5,9 @@
from importlib import import_module
from typing import Dict
-import mpmath
-import numpy
-import sympy
+from mpmath import __version__ as mpmath_version
+from numpy import __version__ as numpy_version
+from sympy import __version__ as sympy_version
from mathics.version import __version__
@@ -17,10 +17,10 @@
# if we can't get version information.
version_info: Dict[str, str] = {
"mathics": __version__,
- "mpmath": mpmath.__version__,
- "numpy": numpy.__version__,
+ "mpmath": mpmath_version,
+ "numpy": numpy_version,
"python": platform.python_implementation() + " " + sys.version.split("\n")[0],
- "sympy": sympy.__version__,
+ "sympy": sympy_version,
}
diff --git a/mathics/builtin/system.py b/mathics/builtin/system.py
index dc23bb504..a0d81c6d9 100644
--- a/mathics/builtin/system.py
+++ b/mathics/builtin/system.py
@@ -15,9 +15,15 @@
from mathics.core.attributes import A_CONSTANT
from mathics.core.builtin import Builtin, Predefined
from mathics.core.convert.expression import to_mathics_list
+from mathics.core.evaluation import Evaluation
from mathics.core.expression import Expression
from mathics.core.list import ListExpression
-from mathics.core.systemsymbols import SymbolFailed, SymbolRule
+from mathics.core.systemsymbols import (
+ SymbolFailed,
+ SymbolNone,
+ SymbolRule,
+ SymbolSequence,
+)
from mathics.version import __version__
try:
@@ -98,7 +104,7 @@ class MaxLengthIntStringConversion(Predefined):
name = "$MaxLengthIntStringConversion"
summary_text = "the maximum length for which an integer is converted to a String"
- def evaluate(self, evaluation) -> Integer:
+ def evaluate(self, evaluation: Evaluation) -> Integer:
try:
return Integer(sys.get_int_max_str_digits())
except AttributeError:
@@ -120,7 +126,7 @@ def eval_set(self, expr, evaluation):
evaluation.message("$MaxLengthIntStringConversion", "inv", expr)
return self.evaluate(evaluation)
- def eval_setdelayed(self, expr, evaluation):
+ def eval_setdelayed(self, expr, evaluation: Evaluation):
"""SetDelayed[$MaxLengthIntStringConversion, expr_]"""
return self.eval_set(expr)
@@ -143,7 +149,7 @@ class CommandLine(Predefined):
)
name = "$CommandLine"
- def evaluate(self, evaluation) -> Expression:
+ def evaluate(self, evaluation: Evaluation) -> Expression:
return ListExpression(*(String(arg) for arg in sys.argv))
@@ -156,13 +162,19 @@ class Environment(Builtin):
gives the value of an operating system environment variable.
- X> Environment["HOME"]
+ 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/.
"""
summary_text = "list the system environment variables"
- def eval(self, var, evaluation):
+ def eval(self, var, evaluation: Evaluation):
"Environment[var_String]"
env_var = var.get_string_value()
if env_var not in os.environ:
@@ -176,36 +188,82 @@ class GetEnvironment(Builtin):
:WMA link:https://reference.wolfram.com/language/ref/GetEnvironment.html
- - 'GetEnvironment["$var$"]'
-
- gives the setting corresponding to the variable "var" in the operating system environment.
+
- 'GetEnvironment["$var$"]'
+
- gives the setting corresponding to the variable "var" in the operating \
+ system environment.
+
+
- 'GetEnvironment[{"$var1$", "$var2$", ...}]'
+
- gives a list rules for each of the environment variables listed.
+
+
- 'GetEnvironment[]'
+
- gives a list rules for all environment variables.
- X> GetEnvironment["HOME"]
+ 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/.
"""
+ messages = {"name": "`1` is not ALL or a string or a list of strings."}
summary_text = "retrieve the value of a system environment variable"
- def eval(self, var, evaluation):
+ def eval(self, var, evaluation: Evaluation):
"GetEnvironment[var___]"
if isinstance(var, String):
- env_var = var.get_string_value()
+ env_var = var.value
tup = (
- env_var,
- "System`None"
- if env_var not in os.environ
- else String(os.environ[env_var]),
+ var,
+ (
+ SymbolNone
+ if env_var not in os.environ
+ else String(os.environ[env_var])
+ ),
)
return Expression(SymbolRule, *tup)
- env_vars = var.get_sequence()
- if len(env_vars) == 0:
- rules = [
- Expression(SymbolRule, name, value)
- for name, value in os.environ.items()
- ]
- return ListExpression(*rules)
+ if (
+ isinstance(var, ListExpression)
+ or hasattr(var, "head")
+ and var.head == SymbolSequence
+ ):
+ if len(var.elements) == 0:
+ rules = [
+ Expression(SymbolRule, String(name), String(value))
+ for name, value in os.environ.items()
+ ]
+ return ListExpression(*rules)
+ else:
+ rules = []
+ for env_var in var.elements:
+ if not isinstance(env_var, String):
+ evaluation.message("GetEnvironment", "name", var)
+ return None
+ rules.append(
+ Expression(
+ SymbolRule,
+ env_var,
+ String(os.environ.get(env_var.value, "")),
+ )
+ )
+ return ListExpression(*rules)
+ else:
+ evaluation.message("GetEnvironment", "name", var)
class Machine(Predefined):
@@ -218,14 +276,14 @@ class Machine(Predefined):
Mathics3 is being run.
- X> $Machine
- = linux
+ S> $Machine
+ = ...
"""
summary_text = "the type of computer system over with Mathics is running"
name = "$Machine"
- def evaluate(self, evaluation) -> String:
+ def evaluate(self, evaluation: Evaluation) -> String:
return String(sys.platform)
@@ -239,14 +297,14 @@ class MachineName(Predefined):
is being run, if such a name is defined.
- X> $MachineName
- = buster
+ S> $MachineName
+ = ...
"""
summary_text = "the name of computer over with Mathics is running"
name = "$MachineName"
- def evaluate(self, evaluation) -> String:
+ def evaluate(self, evaluation: Evaluation) -> String:
return String(platform.uname().node)
@@ -262,9 +320,10 @@ class MathicsVersion(Predefined):
>> MathicsVersion
= ...
"""
+
summary_text = "the version of the mathics core"
- def evaluate(self, evaluation) -> String:
+ def evaluate(self, evaluation: Evaluation) -> String:
return String(__version__)
@@ -278,8 +337,8 @@ class Packages(Predefined):
been loaded into Mathics.
- X> $Packages
- = {ImportExport`,XML`,Internal`,System`,Global`}
+ S> $Packages
+ = {ImportExport`, XML`, Internal`, System`, Global`}
"""
summary_text = "list the packages loaded in the current session"
@@ -303,10 +362,11 @@ class ParentProcessID(Predefined):
= ...
"""
+
summary_text = "id of the process that invoked Mathics"
name = "$ParentProcessID"
- def evaluate(self, evaluation) -> Integer:
+ def evaluate(self, evaluation: Evaluation) -> Integer:
return Integer(os.getppid())
@@ -323,10 +383,11 @@ class ProcessID(Predefined):
>> $ProcessID
= ...
"""
+
summary_text = "id of the Mathics process"
name = "$ProcessID"
- def evaluate(self, evaluation) -> Integer:
+ def evaluate(self, evaluation: Evaluation) -> Integer:
return Integer(os.getpid())
@@ -368,11 +429,12 @@ class PythonImplementation(Predefined):
>> $PythonImplementation
= ...
"""
+
name = "$PythonImplementation"
summary_text = "name of the Python implementation running Mathics3"
- def evaluate(self, evaluation):
+ def evaluate(self, evaluation: Evaluation):
from mathics.system_info import python_implementation
return String(python_implementation())
@@ -394,7 +456,7 @@ class ScriptCommandLine(Predefined):
summary_text = "list of command line arguments"
name = "$ScriptCommandLine"
- def evaluate(self, evaluation):
+ def evaluate(self, evaluation: Evaluation):
try:
dash_index = sys.argv.index("--")
except ValueError:
@@ -405,6 +467,82 @@ def evaluate(self, evaluation):
return to_mathics_list(*params, elements_conversion_fn=String)
+class SetEnvironment(Builtin):
+ """
+ :WMA link:https://reference.wolfram.com/language/ref/SetEnvironment.html
+
+
+ - 'SetEnvironment["$var$" -> $value"]'
+
- sets the value of an operating system environment variable.
+
+
- 'SetEnvironment[{"$var$" -> $value", ...}]'
+
- sets more than one environment variable.
+
+
+ Set a single environment variable:
+ S> SetEnvironment["FOO" -> "bar"]
+ = SetEnvironment[FOO -> bar]
+
+ See that the environment variable has changed:
+ S> GetEnvironment["FOO"]
+ = FOO -> bar
+
+ Set two environment variables:
+ S> SetEnvironment[{"FOO" -> "baz", "A" -> "B"}]
+ = SetEnvironment[{FOO -> baz, A -> B}]
+
+ See that the environment variable has changed:
+ S> GetEnvironment["FOO"]
+ = FOO -> baz
+
+ Environment values must be strings:
+
+ S> SetEnvironment["FOO" -> 5]
+ : 5 must be a string or None.
+ = SetEnvironment[FOO -> 5]
+
+ S> GetEnvironment["FOO"]
+ = FOO -> baz
+
+ If the environment name is not a string, the evaluation fails without a message.
+
+ S> SetEnvironment[FOO -> "bar"]
+ = SetEnvironment[FOO -> bar]
+
+ S> GetEnvironment["FOO"]
+ = FOO -> baz
+
+ See also
+ :'Environment':
+ /doc/reference-of-built-in-symbols/global-system-information/environment/ and
+ :'GeEnvironment':
+ /doc/reference-of-built-in-symbols/global-system-information/getenvironment/.
+ """
+
+ messages = {"value": "`1` must be a string or None."}
+ summary_text = "set system environment variable(s)"
+
+ def eval(self, rule, evaluation):
+ "SetEnvironment[rule_]"
+ env_var_name, env_var_value = rule.elements
+ if not (env_var_value is SymbolNone or isinstance(env_var_value, String)):
+ evaluation.message("SetEnvironment", "value", env_var_value)
+ return None
+
+ if isinstance(env_var_name, String):
+ # WMA does not give an error message if env_var_name is not a String - weird.
+ os.environ[env_var_name.value] = (
+ None if None is SymbolNone else env_var_value.value
+ )
+ return None
+
+ def eval_list(self, rules: Expression, evaluation: Evaluation):
+ "SetEnvironment[{rules__}]"
+ for rule in rules.elements:
+ self.eval(rule, evaluation)
+ return None
+
+
class Run(Builtin):
"""
:WMA link:https://reference.wolfram.com/language/ref/Run.html
@@ -421,7 +559,7 @@ class Run(Builtin):
summary_text = "run a system command"
- def eval(self, command, evaluation):
+ def eval(self, command, evaluation: Evaluation):
"Run[command_String]"
command_str = command.to_python()
return Integer(subprocess.call(command_str, shell=True))
@@ -439,10 +577,11 @@ class SystemID(Predefined):
X> $SystemID
= linux
"""
+
summary_text = "id for the type of computer system"
name = "$SystemID"
- def evaluate(self, evaluation) -> String:
+ def evaluate(self, evaluation: Evaluation) -> String:
return String(sys.platform)
@@ -459,10 +598,11 @@ class SystemWordLength(Predefined):
X> $SystemWordLength
= 64
"""
+
summary_text = "word length of computer system"
name = "$SystemWordLength"
- def evaluate(self, evaluation) -> Integer:
+ def evaluate(self, evaluation: Evaluation) -> Integer:
# https://docs.python.org/3/library/platform.html#module-platform
# says it is more reliable to get bits using sys.maxsize
# than platform.architecture()[0]
@@ -485,10 +625,11 @@ class UserName(Predefined):
X> $UserName
= ...
"""
+
summary_text = "login name of the user that invoked the current session"
name = "$UserName"
- def evaluate(self, evaluation) -> String:
+ def evaluate(self, evaluation: Evaluation) -> String:
try:
user = os.getlogin()
except Exception:
@@ -530,11 +671,12 @@ class VersionNumber(Predefined):
>> $VersionNumber
= ...
"""
+
summary_text = "the version number of the current Mathics core"
name = "$VersionNumber"
value = 10.0
- def evaluate(self, evaluation) -> Real:
+ def evaluate(self, evaluation: Evaluation) -> Real:
# Make this be whatever the latest Mathematica release is,
# assuming we are trying to be compatible with this.
return Real(self.value)
@@ -558,7 +700,7 @@ class SystemMemory(Predefined):
summary_text = "the total amount of physical memory in the system"
name = "$SystemMemory"
- def evaluate(self, evaluation) -> Integer:
+ def evaluate(self, evaluation: Evaluation) -> Integer:
totalmem = psutil.virtual_memory().total
return Integer(totalmem)
@@ -581,7 +723,7 @@ class MemoryAvailable(Builtin):
summary_text = "the available amount of physical memory in the system"
- def eval(self, evaluation) -> Integer:
+ def eval(self, evaluation: Evaluation) -> Integer:
"""MemoryAvailable[]"""
totalmem = psutil.virtual_memory().available
return Integer(totalmem)
@@ -605,7 +747,7 @@ class SystemMemory(Predefined):
summary_text = "the total amount of physical memory in the system"
name = "$SystemMemory"
- def evaluate(self, evaluation) -> Integer:
+ def evaluate(self, evaluation: Evaluation) -> Integer:
return IntegerM1
class MemoryAvailable(Builtin):
@@ -624,9 +766,9 @@ class MemoryAvailable(Builtin):
summary_text = "the available amount of physical memory in the system"
- def eval(self, evaluation) -> Integer:
+ def eval(self, evaluation: Evaluation) -> Integer:
"""MemoryAvailable[]"""
- return Integer(-1)
+ return IntegerM1
class MemoryInUse(Builtin):
@@ -702,7 +844,7 @@ class Share(Builtin):
summary_text = "force Python garbage collection"
- def eval(self, evaluation) -> Integer:
+ def eval(self, evaluation: Evaluation) -> Integer:
"""Share[]"""
# TODO: implement a routine that swap all the definitions,
# collecting repeated symbols and expressions, and then
@@ -716,7 +858,7 @@ def eval(self, evaluation) -> Integer:
gc.collect()
return Integer0
- def eval_with_symbol(self, symbol, evaluation) -> Integer:
+ def eval_with_symbol(self, symbol, evaluation: Evaluation) -> Integer:
"""Share[symbol_Symbol]"""
# TODO: implement a routine that swap all the definitions,
# collecting repeated symbols and expressions, and then
diff --git a/test/package/__init__.py b/test/package/__init__.py
new file mode 100644
index 000000000..e69de29bb