diff --git a/mathics/builtin/base.py b/mathics/builtin/base.py index cee799cc3..d62cca191 100644 --- a/mathics/builtin/base.py +++ b/mathics/builtin/base.py @@ -3,6 +3,7 @@ import importlib import re + from functools import lru_cache, total_ordering from itertools import chain from typing import Any, Callable, Dict, Iterable, List, Optional, Union, cast @@ -191,7 +192,12 @@ def __new__(cls, *args, **kwargs): # mathics.builtin.inout if kwargs.get("expression", None) is not False: - return to_expression(cls.get_name(), *args) + try: + return to_expression(cls.get_name(), *args) + except: + from trepan.api import debug + + debug() else: instance = super().__new__(cls) if not instance.formats: @@ -286,7 +292,13 @@ def check_options(options_to_check, evaluation): ) box_rules = [] - # FIXME: Why a special case for System`MakeBoxes? Remove this + + # System`MakeBoxes comes first because all the other symbols contribute to it. + # Then, a definition of this symbol must be available from the begining. + # Probably, this is not a good approach, since it makes that MakeBoxes has + # a very large number of inespecific rules. A better approach would imply at least + # to store the rules as upvalues of the symbols associated to these rules. + # if name != "System`MakeBoxes": new_rules = [] for rule in rules: @@ -384,13 +396,32 @@ def contextify_form_name(f): ) if is_pymodule: definitions.pymathics[name] = definition + try: + makeboxes_def = definitions.pymathics["System`MakeBoxes"] + except KeyError: + builtin_mb_def = definitions.builtin["System`MakeBoxes"] + makeboxes_def = Definition( + "System`MakeBoxes", + builtin=builtin_mb_def.builtin, + attributes=builtin_mb_def.attributes, + is_numeric=builtin_mb_def.is_numeric, + ) + definitions.pymathics["System`MakeBoxes"] = makeboxes_def else: definitions.builtin[name] = definition + makeboxes_def = definitions.builtin["System`MakeBoxes"] - makeboxes_def = definitions.builtin["System`MakeBoxes"] for rule in box_rules: makeboxes_def.add_rule(rule) + # If a pymathics module was loaded, then there are at least two + # definitions for MakeBoxes, the builtin, the pymathics, and the + # cached from combining the former two. Rules are added to one of + # both, which are not going to be in the cached version that + # Definitions.get_definition provides. To make the new rules + # accesible, we need to clear the definitions cache. + definitions.clear_cache("System`MakeBoxes") + @classmethod def get_name(cls, short=False) -> str: if cls.name is None: diff --git a/mathics/builtin/system_init.py b/mathics/builtin/system_init.py index 213a16fe1..e306d1b70 100644 --- a/mathics/builtin/system_init.py +++ b/mathics/builtin/system_init.py @@ -16,6 +16,8 @@ from mathics.core.expression import ensure_context from mathics.core.parser import all_operator_names +from mathics.settings import ROOT_DIR + mathics_to_sympy = {} # here we have: name -> sympy object sympy_to_mathics = {} @@ -213,3 +215,41 @@ def update_builtin_definitions(builtins: dict, definitions): operator = builtin.get_operator_display() if operator is not None: display_operators_set.add(operator) + + +def _initialize_system_definitions(): + from mathics.core.definitions import system_definitions + + # print("Initializing definitions") + # Importing "mathics.format" populates the Symbol of the + # PrintForms and OutputForms sets. + # + # If "importlib" is used instead of "import", then we get: + # TypeError: boxes_to_text() takes 1 positional argument but + # 2 were given + # Rocky: this smells of something not quite right in terms of + # modularity. + + import mathics.format # noqa + + update_builtin_definitions(system_builtins_dict, system_definitions) + + autoload_files(system_definitions, ROOT_DIR, "autoload") + + # Move any user definitions created by autoloaded files to + # builtins, and clear out the user definitions list. This + # means that any autoloaded definitions become shared + # between users and no longer disappear after a Quit[]. + # + # Autoloads that accidentally define a name in Global` + # could cause confusion, so check for this. + # + for name in system_definitions.user: + if name.startswith("Global`"): + raise ValueError("autoload defined %s." % name) + + system_definitions.builtin.update(system_definitions.user) + system_definitions.user = {} + + +_initialize_system_definitions() diff --git a/mathics/core/definitions.py b/mathics/core/definitions.py index 8576bb25c..a3c4c401b 100644 --- a/mathics/core/definitions.py +++ b/mathics/core/definitions.py @@ -783,44 +783,3 @@ def __repr__(self) -> str: system_definitions = Definitions(add_builtin=False) - - -def initialize_system_definitions(): - from mathics.builtin.system_init import ( - autoload_files, - update_builtin_definitions, - system_builtins_dict, - ) - - # Importing "mathics.format" populates the Symbol of the - # PrintForms and OutputForms sets. - # - # If "importlib" is used instead of "import", then we get: - # TypeError: boxes_to_text() takes 1 positional argument but - # 2 were given - # Rocky: this smells of something not quite right in terms of - # modularity. - - import mathics.format # noqa - - update_builtin_definitions(system_builtins_dict, system_definitions) - - autoload_files(system_definitions, ROOT_DIR, "autoload") - - # Move any user definitions created by autoloaded files to - # builtins, and clear out the user definitions list. This - # means that any autoloaded definitions become shared - # between users and no longer disappear after a Quit[]. - # - # Autoloads that accidentally define a name in Global` - # could cause confusion, so check for this. - # - for name in system_definitions.user: - if name.startswith("Global`"): - raise ValueError("autoload defined %s." % name) - - system_definitions.builtin.update(system_definitions.user) - system_definitions.user = {} - - -system_definitions = Definitions(add_builtin=False) diff --git a/mathics/docpipeline.py b/mathics/docpipeline.py index 826d54066..29629af22 100644 --- a/mathics/docpipeline.py +++ b/mathics/docpipeline.py @@ -21,7 +21,7 @@ import mathics import mathics.settings -from mathics.core.definitions import Definitions, initialize_system_definitions +from mathics.core.definitions import Definitions from mathics.core.evaluation import Evaluation, Output from mathics.core.parser import MathicsSingleLineFeeder from mathics.builtin.system_init import builtins_dict @@ -446,7 +446,7 @@ def main(): global definitions global logfile global check_partial_enlapsed_time - initialize_system_definitions() + definitions = Definitions(add_builtin=True) parser = ArgumentParser(description="Mathics test suite.", add_help=False) diff --git a/mathics/main.py b/mathics/main.py old mode 100755 new mode 100644 index 7ac446f8e..b5b36a24f --- a/mathics/main.py +++ b/mathics/main.py @@ -16,7 +16,8 @@ from mathics.builtin.system_init import autoload_files from mathics.builtin.trace import TraceBuiltins, traced_do_replace from mathics.core.atoms import String -from mathics.core.definitions import Definitions, Symbol, initialize_system_definitions + +from mathics.core.definitions import Definitions, Symbol from mathics.core.evaluation import Evaluation, Output from mathics.core.expression import Expression from mathics.core.parser import MathicsFileLineFeeder, MathicsLineFeeder @@ -370,7 +371,6 @@ def dump_tracing_stats(): if args.show_statistics: atexit.register(show_lru_cache_statistics) - initialize_system_definitions() definitions = Definitions(add_builtin=True, extension_modules=extension_modules) definitions.set_line_no(0) diff --git a/test/helper.py b/test/helper.py index 7e8a8ae39..89aa0a2cd 100644 --- a/test/helper.py +++ b/test/helper.py @@ -4,9 +4,7 @@ from typing import Optional from mathics.session import MathicsSession -from mathics.core.definitions import initialize_system_definitions -initialize_system_definitions() # Set up a Mathics session with definitions. # For consistency set the character encoding ASCII which is