From a62cb5190efda9a58d07227cacb387cb7c7cebce Mon Sep 17 00:00:00 2001 From: rocky Date: Sun, 13 Aug 2023 10:12:47 -0400 Subject: [PATCH 1/3] Allow Builtin processing by a single module --- mathics/core/load_builtin.py | 105 ++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 45 deletions(-) diff --git a/mathics/core/load_builtin.py b/mathics/core/load_builtin.py index ba9dfaf61..f168383fd 100755 --- a/mathics/core/load_builtin.py +++ b/mathics/core/load_builtin.py @@ -33,7 +33,8 @@ # e.g. 'mathics.builtin.arithmetic' to the list of Builtin class instances # that appear inside that module, e.g. for key 'mathics.builtin.arithmetic' we # have: -# [, , Dict[str, list]: + """ + Process modules listed in ``modules` which contain Builtin classes so that + for each module the classes contained in them are is imported in the Python sense, + but also that we have information added to module variable ``builtins_by_module``. + """ for module in modules: - add_builtins_from_builtin_module(module, builtins_list) - add_builtins(builtins_list) + add_builtins_from_builtin_module(module) return builtins_by_module -# The fact that we are importing inside here, suggests add_builtins -# should get moved elsewhere. -def add_builtins(new_builtins): - from mathics.core.builtin import ( - Operator, - PatternObject, - SympyObject, - mathics_to_python, - ) - - for _, builtin in new_builtins: - name = builtin.get_name() - if hasattr(builtin, "python_equivalent"): - # print("XXX0", builtin.python_equivalent) - mathics_to_python[name] = builtin.python_equivalent - - if isinstance(builtin, SympyObject): - mathics_to_sympy[name] = builtin - for sympy_name in builtin.get_sympy_names(): - # print("XXX1", sympy_name) - sympy_to_mathics[sympy_name] = builtin - if isinstance(builtin, Operator): - assert builtin.precedence is not None - builtins_precedence[Symbol(name)] = builtin.precedence - if isinstance(builtin, PatternObject): - pattern_objects[name] = builtin.__class__ - _builtins.update(dict(new_builtins)) - - def builtins_dict(builtins_by_module): return { builtin.get_name(): builtin @@ -151,7 +126,7 @@ def import_and_load_builtins(): "..", "builtin", ) - exclude_files = {"codetables", "base"} + exclude_files = {"codetables"} module_names = get_module_names(builtin_path, exclude_files) import_builtins(module_names, mathics3_builtins_modules) @@ -172,7 +147,9 @@ def import_and_load_builtins(): add_builtins_from_builtin_modules(mathics3_builtins_modules) -def import_builtin_module(import_name: str, modules: List[ModuleType]): +def import_builtin_module( + import_name: str, modules: List[ModuleType] +) -> Optional[ModuleType]: """ Imports ``the list of Mathics3 Built-in modules so that inside Mathics3 Builtin Functions, like Plus[], List[] are defined. @@ -189,6 +166,7 @@ def import_builtin_module(import_name: str, modules: List[ModuleType]): if module: modules.append(module) + return module # TODO: When we drop Python 3.7, @@ -218,7 +196,7 @@ def import_builtins( def import_builtin_subdirectories( - subdirectories: Set[str], disable_file_module_names: set, modules + subdirectories: Set[str], disable_file_module_names: set, modules: List[ModuleType] ): """ Runs import_builtisn on the each subdirectory in ``subdirectories`` that inside @@ -299,11 +277,48 @@ def name_is_builtin_symbol(module: ModuleType, name: str) -> Optional[type]: return module_object -def update_display_operators_set(builtin_instance): +def update_builtin_properties(builtin_instance): """ + Update, where appropriate, various internal builtin tables: + * mathics_to_python[name], + * mathics_to_sympy[name], + * builtins_precedence[name] + * pattern_object[name] + + Information to store comes from ``builtin``. + If builtin_instance is an operator of some kind, add that to the set of opererator strings ``display_operators_set``. + + Finally add {name: builtin} to global _builtins """ + # The fact that we are importing inside here, suggests + # this function might get moved elsewhere. + from mathics.core.builtin import ( + Operator, + PatternObject, + SympyObject, + mathics_to_python, + ) + + name = builtin_instance.get_name() + if hasattr(builtin_instance, "python_equivalent"): + # print("XXX0", builtin_instance.python_equivalent) + mathics_to_python[name] = builtin_instance.python_equivalent + + if isinstance(builtin_instance, SympyObject): + mathics_to_sympy[name] = builtin_instance + for sympy_name in builtin_instance.get_sympy_names(): + # print("XXX1", sympy_name) + sympy_to_mathics[sympy_name] = builtin_instance + if isinstance(builtin_instance, Operator): + assert builtin_instance.precedence is not None + builtins_precedence[Symbol(name)] = builtin_instance.precedence + if isinstance(builtin_instance, PatternObject): + pattern_objects[name] = builtin_instance.__class__ + operator = builtin_instance.get_operator_display() if operator is not None: display_operators_set.add(operator) + + _builtins.update({name: builtin_instance}) From 1b3e806bfa2300cb44eba63b8ee88e871bc8e80c Mon Sep 17 00:00:00 2001 From: rocky Date: Sun, 13 Aug 2023 19:22:51 -0400 Subject: [PATCH 2/3] First working load_builtin test.. that demonstrates this works. --- mathics/core/load_builtin.py | 10 ++++++--- test/core/test_load_builtin.py | 40 ++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 test/core/test_load_builtin.py diff --git a/mathics/core/load_builtin.py b/mathics/core/load_builtin.py index f168383fd..eeb453ee0 100755 --- a/mathics/core/load_builtin.py +++ b/mathics/core/load_builtin.py @@ -27,6 +27,7 @@ # is initialized via below import_builtins modules mathics3_builtins_modules: List[ModuleType] = [] +# Used in definition_contribute _builtins = {} # builtins_by_module gives a way of mapping a Python module name @@ -115,7 +116,7 @@ def get_module_names(builtin_path: str, exclude_files: set) -> list: return [f for f in py_files if f not in exclude_files] -def import_and_load_builtins(): +def import_and_load_builtins(exclude_files: Optional[Set[str]] = None): """ Imports Builtin modules in mathics.builtin and add rules, and definitions from that. """ @@ -126,7 +127,10 @@ def import_and_load_builtins(): "..", "builtin", ) - exclude_files = {"codetables"} + + if exclude_files is None: + exclude_files = {"codetables"} + module_names = get_module_names(builtin_path, exclude_files) import_builtins(module_names, mathics3_builtins_modules) @@ -139,7 +143,7 @@ def import_and_load_builtins(): disable_file_module_names = set() if ENABLE_FILES_MODULE else {"files_io"} subdirectory_list = next(os.walk(builtin_path))[1] - subdirectories = set(subdirectory_list) - set("__pycache__") + subdirectories = set(subdirectory_list) - (set("__pycache__") | exclude_files) import_builtin_subdirectories( subdirectories, disable_file_module_names, mathics3_builtins_modules ) diff --git a/test/core/test_load_builtin.py b/test/core/test_load_builtin.py new file mode 100644 index 000000000..b46f24f73 --- /dev/null +++ b/test/core/test_load_builtin.py @@ -0,0 +1,40 @@ +import importlib + +from mathics.core.load_builtin import ( + add_builtins_from_builtin_module, + import_and_load_builtins, +) +from mathics.session import MathicsSession + + +def test_add_builtins_from_builtin_module(): + """ + Test that add_builtins_from_module() loads a single Builtin module + and updates definitions. + """ + # Set up a session with all but one module. + # Then evaluate a builtin in that module and see that we + # now have the function defined. + + # First, load in many modules except quantum_mechanics. + import_and_load_builtins(exclude_files={"quantum_mechanics"}) + + # Create a session, evaluate an expression using a missing Builtin function + # and see that it is not defined... + session = MathicsSession(character_encoding="ASCII") + assert str(session.evaluate("PauliMatrix[0]")) == "Global`PauliMatrix[0]" + assert ( + str(session.evaluate("SixJSymbol[{1,2,3}, {1,2,3}]")) + == "Global`SixJSymbol[{1,2,3}, {1,2,3}]" + ) + # Finally add in the module and see that when we use Builtin functions + # in that module work. + angular_module = importlib.import_module( + "mathics.builtin.quantum_mechanics.angular" + ) + add_builtins_from_builtin_module(angular_module) + + # Note that adding more builtins does not update the session, so we need a new one. + session = MathicsSession(character_encoding="ASCII") + assert str(session.evaluate("PauliMatrix[0]")) == "{{1,0},{0,1}}" + assert str(session.evaluate("SixJSymbol[{1, 2, 3}, {1, 2, 3}]")) == "1/105" From f35bc4b2cefddeb0dac86e7858f15c46ca420133 Mon Sep 17 00:00:00 2001 From: mmatera Date: Sat, 19 Aug 2023 20:21:18 -0300 Subject: [PATCH 3/3] allow to clean all before import_and_load_modules --- mathics/core/load_builtin.py | 11 ++++++++++- test/core/test_load_builtin.py | 4 +++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/mathics/core/load_builtin.py b/mathics/core/load_builtin.py index eeb453ee0..ddb4f6da6 100755 --- a/mathics/core/load_builtin.py +++ b/mathics/core/load_builtin.py @@ -116,10 +116,19 @@ def get_module_names(builtin_path: str, exclude_files: set) -> list: return [f for f in py_files if f not in exclude_files] -def import_and_load_builtins(exclude_files: Optional[Set[str]] = None): +def import_and_load_builtins(exclude_files: Optional[Set[str]] = None, clean_all=False): """ Imports Builtin modules in mathics.builtin and add rules, and definitions from that. """ + + # If clean_all, clean the _builtin and builtins_by_module to start + # with a fresh instance of this module. + # TODO: check if we need to clean the other dicts and lists. + if clean_all: + global mathics3_builtins_modules, _builtins + _builtins = {} + mathics3_builtins_modules = [] + builtin_path = osp.join( osp.dirname( __file__, diff --git a/test/core/test_load_builtin.py b/test/core/test_load_builtin.py index b46f24f73..21b6349ab 100644 --- a/test/core/test_load_builtin.py +++ b/test/core/test_load_builtin.py @@ -1,6 +1,7 @@ import importlib from mathics.core.load_builtin import ( + _builtins, add_builtins_from_builtin_module, import_and_load_builtins, ) @@ -17,7 +18,8 @@ def test_add_builtins_from_builtin_module(): # now have the function defined. # First, load in many modules except quantum_mechanics. - import_and_load_builtins(exclude_files={"quantum_mechanics"}) + _builtins = {} + import_and_load_builtins(exclude_files={"quantum_mechanics"}, clean_all=True) # Create a session, evaluate an expression using a missing Builtin function # and see that it is not defined...