From 5b9eaed4634ae70c79cb4ea3539f31ddd81e8d8e Mon Sep 17 00:00:00 2001 From: Ashish Mahendra Date: Tue, 20 Aug 2024 14:27:14 +0000 Subject: [PATCH 01/17] [Draft] Updating Walker --- jaclang/runtimelib/importer.py | 6 +++++ jaclang/runtimelib/machine.py | 45 +++++++++++++++++++++++++++++++++- jaclang/tests/fixtures/bar.jac | 14 ++++++++++- jaclang/tests/fixtures/foo.jac | 15 +++++++++++- 4 files changed, 77 insertions(+), 3 deletions(-) diff --git a/jaclang/runtimelib/importer.py b/jaclang/runtimelib/importer.py index 39f1dd1cf..33b21a9e3 100644 --- a/jaclang/runtimelib/importer.py +++ b/jaclang/runtimelib/importer.py @@ -365,12 +365,17 @@ def run_import( cachable=spec.cachable, ) try: + if not codeobj: raise ImportError(f"No bytecode found for {spec.full_target}") + print(f"Compiling {spec.full_target}") with SysModulesPatch( self.jac_machine.loaded_modules ), sys_path_context(spec.caller_dir): exec(codeobj, module.__dict__) + print( + f"Module {module_name} successfully compiled. dict: {module.__dict__.keys()}" + ) except Exception as e: logger.error(dump_traceback(e)) raise e @@ -383,5 +388,6 @@ def run_import( cachable=spec.cachable, lang=spec.language, ) + # print(f"import_return: {import_return}") self.result = import_return return self.result diff --git a/jaclang/runtimelib/machine.py b/jaclang/runtimelib/machine.py index 846267bb5..3ad7f68d5 100644 --- a/jaclang/runtimelib/machine.py +++ b/jaclang/runtimelib/machine.py @@ -3,8 +3,9 @@ import inspect import marshal import os +import sys import types -from typing import Dict, Optional +from typing import Dict, Optional, Union from jaclang.compiler.absyntree import Module from jaclang.compiler.compile import compile_jac @@ -97,6 +98,48 @@ def list_edges(self, module_name: str) -> list[str]: return nodes return [] + def update_module( + self, module_name: str, items: Optional[dict[str, Union[str, Optional[str]]]] + ) -> tuple[types.ModuleType, ...]: + """Reimport the module.""" + from .importer import JacImporter, ImportPathSpec + + if module_name in self.loaded_modules: + try: + # Unload the existing module + del self.loaded_modules[module_name] + if module_name in sys.modules: + del sys.modules[module_name] + + # Re-import the module + importer = JacImporter(self) + spec = ImportPathSpec( + target=module_name, + base_path=self.base_path, + absorb=False, + cachable=True, + mdl_alias=None, + override_name=None, + lng="jac", + items=items, + ) + import_result = importer.run_import(spec, reload=True) + # print(f"received items: {import_result.ret_items}") + # Load the updated module into JacMachine + self.load_module(module_name, import_result.ret_mod) + + # print(f"Module {module_name} successfully updated.") + # print(f"imported items: {import_result.ret_items}") + return ( + (import_result.ret_mod,) + if not items + else tuple(import_result.ret_items) + ) + except Exception as e: + logger.error(f"Failed to update module {module_name}: {e}") + else: + logger.warning(f"Module {module_name} not found in loaded modules.") + class JacProgram: """Class to hold the mod_bundle and bytecode for Jac modules.""" diff --git a/jaclang/tests/fixtures/bar.jac b/jaclang/tests/fixtures/bar.jac index 73c5cb3b7..0c1fe89dc 100644 --- a/jaclang/tests/fixtures/bar.jac +++ b/jaclang/tests/fixtures/bar.jac @@ -31,4 +31,16 @@ walker bar_walk { disengage; } } -} \ No newline at end of file + # New behavior after update + can end with `root exit { + "bar_walk has been updated!" |> print; + disengage; + } +} + +walker print_walk { + can start with `root entry { + "This is a new walker, print_walk!" |> print; + disengage; + } +} diff --git a/jaclang/tests/fixtures/foo.jac b/jaclang/tests/fixtures/foo.jac index c98576529..a3524581f 100644 --- a/jaclang/tests/fixtures/foo.jac +++ b/jaclang/tests/fixtures/foo.jac @@ -1,5 +1,7 @@ import:py from jaclang.plugin.feature, JacFeature as Jac; import:jac from bar, bar_walk; +import:py from time,sleep; + # Test runner to initialize the walker @@ -35,9 +37,20 @@ can test_run { f" - Edge: {edge}" |> print; } } + print(f"bar_walk: {bar_walk}"); root spawn bar_walk(); -} + # Sleep to observe the update process + "Sleeping for 5 seconds after update..." |> print; + sleep(8); + + # Update the module + (print_walk,)=Jac.context().jac_machine.update_module("bar", items={'print_walk': None}); + print(f"print_walk: {print_walk}"); + # Run the walker again after update + "Running bar_walk after update..." |> print; + root spawn print_walk(); +} # Define the entry point to run the test with entry { test_run(); From 2011c251db3f362df1f92b701865450aa056c900 Mon Sep 17 00:00:00 2001 From: Ashish Mahendra Date: Tue, 27 Aug 2024 12:13:31 +0000 Subject: [PATCH 02/17] sys.module update --- jaclang/runtimelib/importer.py | 14 +++++++------- jaclang/runtimelib/machine.py | 1 + 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/jaclang/runtimelib/importer.py b/jaclang/runtimelib/importer.py index 33b21a9e3..32eef858f 100644 --- a/jaclang/runtimelib/importer.py +++ b/jaclang/runtimelib/importer.py @@ -202,7 +202,7 @@ def run_import(self, spec: ImportPathSpec) -> ImportReturn: def update_sys(self, module: types.ModuleType, spec: ImportPathSpec) -> None: """Update sys.modules with the newly imported module.""" if spec.module_name not in self.jac_machine.loaded_modules: - self.jac_machine.loaded_modules[spec.module_name] = module + self.jac_machine.load_module(spec.module_name, module) class PythonImporter(Importer): @@ -305,7 +305,7 @@ def handle_directory( module.__file__ = None if module_name not in self.jac_machine.loaded_modules: - self.jac_machine.loaded_modules[module_name] = module + self.jac_machine.load_module(module_name, module) return module def create_jac_py_module( @@ -331,7 +331,7 @@ def create_jac_py_module( module_name=package_name, full_mod_path=full_mod_path, ) - self.jac_machine.loaded_modules[module_name] = module + self.jac_machine.load_module(module_name, module) return module def run_import( @@ -368,14 +368,14 @@ def run_import( if not codeobj: raise ImportError(f"No bytecode found for {spec.full_target}") - print(f"Compiling {spec.full_target}") + # print(f"Compiling {spec.full_target}") with SysModulesPatch( self.jac_machine.loaded_modules ), sys_path_context(spec.caller_dir): exec(codeobj, module.__dict__) - print( - f"Module {module_name} successfully compiled. dict: {module.__dict__.keys()}" - ) + # print( + # f"Module {module_name} successfully compiled. dict: {module.__dict__.keys()}" + # ) except Exception as e: logger.error(dump_traceback(e)) raise e diff --git a/jaclang/runtimelib/machine.py b/jaclang/runtimelib/machine.py index 3ad7f68d5..620a74fbb 100644 --- a/jaclang/runtimelib/machine.py +++ b/jaclang/runtimelib/machine.py @@ -60,6 +60,7 @@ def get_bytecode( def load_module(self, module_name: str, module: types.ModuleType) -> None: """Load a module into the machine.""" self.loaded_modules[module_name] = module + sys.modules[module_name] = module def list_modules(self) -> list[str]: """List all loaded modules.""" From 69b970fe78f930fd282511944f78dae0c1f43fd6 Mon Sep 17 00:00:00 2001 From: Ashish Mahendra Date: Thu, 29 Aug 2024 14:15:35 +0000 Subject: [PATCH 03/17] Update: a working version --- jaclang/runtimelib/importer.py | 6 ++- jaclang/runtimelib/machine.py | 42 +++++++-------- jaclang/tests/fixtures/walker_reload/bar.jac | 34 ++++++++++++ jaclang/tests/fixtures/walker_reload/foo.jac | 56 ++++++++++++++++++++ jaclang/tests/test_language.py | 15 ++++++ 5 files changed, 130 insertions(+), 23 deletions(-) create mode 100644 jaclang/tests/fixtures/walker_reload/bar.jac create mode 100644 jaclang/tests/fixtures/walker_reload/foo.jac diff --git a/jaclang/runtimelib/importer.py b/jaclang/runtimelib/importer.py index 8cb3dd9d4..439798a2c 100644 --- a/jaclang/runtimelib/importer.py +++ b/jaclang/runtimelib/importer.py @@ -6,6 +6,7 @@ import os import sys import types +from importlib import util from os import getcwd, path from typing import Optional, Union @@ -195,11 +196,11 @@ def run_import(self, spec: ImportPathSpec) -> ImportReturn: if spec.target.startswith("."): spec.target = spec.target.lstrip(".") full_target = path.normpath(path.join(spec.caller_dir, spec.target)) - imp_spec = importlib.util.spec_from_file_location( + imp_spec = util.spec_from_file_location( spec.target, full_target + ".py" ) if imp_spec and imp_spec.loader: - imported_module = importlib.util.module_from_spec(imp_spec) + imported_module = util.module_from_spec(imp_spec) sys.modules[imp_spec.name] = imported_module imp_spec.loader.exec_module(imported_module) else: @@ -339,6 +340,7 @@ def run_import( spec.full_target, caller_dir=spec.caller_dir, cachable=spec.cachable, + reload=reload if reload else False, ) try: diff --git a/jaclang/runtimelib/machine.py b/jaclang/runtimelib/machine.py index c39271fbe..2927b623f 100644 --- a/jaclang/runtimelib/machine.py +++ b/jaclang/runtimelib/machine.py @@ -5,7 +5,7 @@ import os import sys import types -from typing import Optional +from typing import Optional, Union from jaclang.compiler.absyntree import Module from jaclang.compiler.compile import compile_jac @@ -49,11 +49,12 @@ def get_bytecode( full_target: str, caller_dir: str, cachable: bool = True, + reload: bool = False, ) -> Optional[types.CodeType]: """Retrieve bytecode from the attached JacProgram.""" if self.jac_program: return self.jac_program.get_bytecode( - module_name, full_target, caller_dir, cachable + module_name, full_target, caller_dir, cachable, reload=reload ) return None @@ -99,7 +100,7 @@ def list_edges(self, module_name: str) -> list[str]: return nodes return [] - def update_module( + def update_walker( self, module_name: str, items: Optional[dict[str, Union[str, Optional[str]]]] ) -> tuple[types.ModuleType, ...]: """Reimport the module.""" @@ -107,12 +108,7 @@ def update_module( if module_name in self.loaded_modules: try: - # Unload the existing module - del self.loaded_modules[module_name] - if module_name in sys.modules: - del sys.modules[module_name] - - # Re-import the module + old_module = self.loaded_modules[module_name] importer = JacImporter(self) spec = ImportPathSpec( target=module_name, @@ -125,21 +121,24 @@ def update_module( items=items, ) import_result = importer.run_import(spec, reload=True) - # print(f"received items: {import_result.ret_items}") - # Load the updated module into JacMachine - self.load_module(module_name, import_result.ret_mod) - - # print(f"Module {module_name} successfully updated.") - # print(f"imported items: {import_result.ret_items}") - return ( - (import_result.ret_mod,) - if not items - else tuple(import_result.ret_items) - ) + ret_items = [] + if items: + for item_name in items: + if hasattr(old_module, item_name): + new_attr = getattr(import_result.ret_mod, item_name, None) + if new_attr: + ret_items.append(new_attr) + setattr( + old_module, + item_name, + new_attr, + ) + return (old_module,) if not items else tuple(ret_items) except Exception as e: logger.error(f"Failed to update module {module_name}: {e}") else: logger.warning(f"Module {module_name} not found in loaded modules.") + return () class JacProgram: @@ -158,6 +157,7 @@ def get_bytecode( full_target: str, caller_dir: str, cachable: bool = True, + reload: bool = False, ) -> Optional[types.CodeType]: """Get the bytecode for a specific module.""" if self.mod_bundle and isinstance(self.mod_bundle, Module): @@ -165,7 +165,7 @@ def get_bytecode( return marshal.loads(codeobj) if isinstance(codeobj, bytes) else None gen_dir = os.path.join(caller_dir, Con.JAC_GEN_DIR) pyc_file_path = os.path.join(gen_dir, module_name + ".jbc") - if cachable and os.path.exists(pyc_file_path): + if cachable and os.path.exists(pyc_file_path) and not reload: with open(pyc_file_path, "rb") as f: return marshal.load(f) diff --git a/jaclang/tests/fixtures/walker_reload/bar.jac b/jaclang/tests/fixtures/walker_reload/bar.jac new file mode 100644 index 000000000..34394779d --- /dev/null +++ b/jaclang/tests/fixtures/walker_reload/bar.jac @@ -0,0 +1,34 @@ +# Define a simple node type called `Item` +node Item { + has value: int = 0; +} + +# Define an edge type called `Link` +edge Link {} + +# Define the `bar` walker +walker bar_walk { + has count: int = 0; + + # Start walking from the root node or an Item node + can start with `root | Item entry { + here ++> Item(); + if self.count < 5 { + visit [-->]; + } else { + "Created 5 items." |> print; + disengage; + } + } + + # Walk over Item nodes and update their values + can walk with Item entry { + here.value = self.count; + f"Item value: {here.value}" |> print; + self.count += 1; + visit [-->] else { + "Finished walking over all items." |> print; + disengage; + } + } +} diff --git a/jaclang/tests/fixtures/walker_reload/foo.jac b/jaclang/tests/fixtures/walker_reload/foo.jac new file mode 100644 index 000000000..426b9cc59 --- /dev/null +++ b/jaclang/tests/fixtures/walker_reload/foo.jac @@ -0,0 +1,56 @@ +import:py from jaclang.plugin.feature {JacFeature as Jac} +import:jac from bar {bar_walk} +import:py from time {sleep} + + +can update_bar_walker { + new_behavior = ''' + # New behavior added during runtime + can end with `root exit { + "bar_walk has been updated with new behavior!" |> print; + disengage; + } +} + '''; + bar_file_path = '/home/ubuntu/jaclang/jaclang/tests/fixtures/walker_reload/bar.jac'; + with open(bar_file_path, 'r') as bar_file{ + original_content = bar_file.read(); + } + + with open(bar_file_path, 'r+') as bar_file { # Specify the correct path to bar.jac + content = bar_file.read(); + + # Replace the last occurrence of "}" with the new behavior + last_brace_index = content.rfind('}'); + if last_brace_index != -1{ + updated_content = content[:last_brace_index] + new_behavior; + bar_file.seek(0); + bar_file.write(updated_content); + bar_file.truncate(); + } + } + "Updated bar.jac with new behavior." |> print; + + (bar_walk_new,)=Jac.context().jac_machine.update_walker("bar", items={'bar_walk': None}); + "Running bar_walk after update..." |> print; + root spawn bar_walk_new(); + print(f"bar_walk: {bar_walk_new.__dict__}"); + with open(bar_file_path, 'w') as bar_file{ + bar_file.write(original_content); + } +} + +# Initialize the walker +can initial_run { + root spawn bar_walk(); + print(f"bar_walk: {bar_walk.__dict__}"); + +} + +# Define the entry point to run the test +with entry { + initial_run(); + + # Update the walker + update_bar_walker(); +} diff --git a/jaclang/tests/test_language.py b/jaclang/tests/test_language.py index ecc69196c..1bfdbbd52 100644 --- a/jaclang/tests/test_language.py +++ b/jaclang/tests/test_language.py @@ -977,3 +977,18 @@ def test_list_methods(self) -> None: ) self.assertIn("Item value: 0", stdout_value) self.assertIn("Created 5 items.", stdout_value) + + def test_walker_dynamic_update(self) -> None: + """Test dynamic update of a walker during runtime.""" + Jac.get_root().__jac__.edges.clear() + Jac.context().init_memory(base_path=self.fixture_abs_path("./")) + captured_output = io.StringIO() + sys.stdout = captured_output + + cli.run( + filename=self.fixture_abs_path("walker_reload/foo.jac"), + ) + sys.stdout = sys.__stdout__ + stdout_value = captured_output.getvalue() + expected_output = "bar_walk has been updated with new behavior!" + self.assertIn(expected_output, stdout_value.split("\n")) From b5cb6a4ed341c248b918bb1e99cd079a2305b1cd Mon Sep 17 00:00:00 2001 From: Ashish Mahendra Date: Thu, 29 Aug 2024 15:47:18 +0000 Subject: [PATCH 04/17] path fix --- jaclang/tests/fixtures/walker_reload/foo.jac | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jaclang/tests/fixtures/walker_reload/foo.jac b/jaclang/tests/fixtures/walker_reload/foo.jac index 426b9cc59..50e44cd7a 100644 --- a/jaclang/tests/fixtures/walker_reload/foo.jac +++ b/jaclang/tests/fixtures/walker_reload/foo.jac @@ -1,6 +1,7 @@ import:py from jaclang.plugin.feature {JacFeature as Jac} import:jac from bar {bar_walk} import:py from time {sleep} +import:py os; can update_bar_walker { @@ -12,7 +13,7 @@ can update_bar_walker { } } '''; - bar_file_path = '/home/ubuntu/jaclang/jaclang/tests/fixtures/walker_reload/bar.jac'; + bar_file_path = os.path.abspath('jaclang/tests/fixtures/walker_reload/bar.jac'); with open(bar_file_path, 'r') as bar_file{ original_content = bar_file.read(); } From 4f75369a4254d46102d533978fdfb9881bf1567a Mon Sep 17 00:00:00 2001 From: Ashish Mahendra Date: Fri, 30 Aug 2024 10:19:39 +0000 Subject: [PATCH 05/17] formatting fix --- jaclang/tests/fixtures/walker_reload/bar.jac | 7 ++- jaclang/tests/fixtures/walker_reload/foo.jac | 54 ++++++++++---------- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/jaclang/tests/fixtures/walker_reload/bar.jac b/jaclang/tests/fixtures/walker_reload/bar.jac index 34394779d..2224f1ec1 100644 --- a/jaclang/tests/fixtures/walker_reload/bar.jac +++ b/jaclang/tests/fixtures/walker_reload/bar.jac @@ -2,14 +2,13 @@ node Item { has value: int = 0; } - # Define an edge type called `Link` -edge Link {} +edge Link {} # Define the `bar` walker + walker bar_walk { has count: int = 0; - # Start walking from the root node or an Item node can start with `root | Item entry { here ++> Item(); @@ -20,8 +19,8 @@ walker bar_walk { disengage; } } - # Walk over Item nodes and update their values + can walk with Item entry { here.value = self.count; f"Item value: {here.value}" |> print; diff --git a/jaclang/tests/fixtures/walker_reload/foo.jac b/jaclang/tests/fixtures/walker_reload/foo.jac index 50e44cd7a..4ca942ad0 100644 --- a/jaclang/tests/fixtures/walker_reload/foo.jac +++ b/jaclang/tests/fixtures/walker_reload/foo.jac @@ -1,54 +1,56 @@ -import:py from jaclang.plugin.feature {JacFeature as Jac} -import:jac from bar {bar_walk} -import:py from time {sleep} -import:py os; - +import:py from jaclang.plugin.feature { JacFeature as Jac } +import:jac from bar { bar_walk } +import:py from time { sleep } +import:py os; can update_bar_walker { new_behavior = ''' # New behavior added during runtime can end with `root exit { - "bar_walk has been updated with new behavior!" |> print; - disengage; + "bar_walk has been updated with new behavior!" |> print; + disengage; + } } -} '''; - bar_file_path = os.path.abspath('jaclang/tests/fixtures/walker_reload/bar.jac'); - with open(bar_file_path, 'r') as bar_file{ + bar_file_path = os.path.abspath( + 'jaclang/tests/fixtures/walker_reload/bar.jac' + ); + with open(bar_file_path, 'r') as bar_file { original_content = bar_file.read(); } + with open(bar_file_path, 'r+') as bar_file { # Specify the correct path to bar.jac - with open(bar_file_path, 'r+') as bar_file { # Specify the correct path to bar.jac - content = bar_file.read(); + content = bar_file.read(); - # Replace the last occurrence of "}" with the new behavior - last_brace_index = content.rfind('}'); - if last_brace_index != -1{ - updated_content = content[:last_brace_index] + new_behavior; - bar_file.seek(0); - bar_file.write(updated_content); - bar_file.truncate(); - } + # Replace the last occurrence of "}" with the new behavior + last_brace_index = content.rfind('}'); + if last_brace_index != -1 { + updated_content = content[:last_brace_index] + new_behavior; + bar_file.seek(0); + bar_file.write(updated_content); + bar_file.truncate(); + } } "Updated bar.jac with new behavior." |> print; - - (bar_walk_new,)=Jac.context().jac_machine.update_walker("bar", items={'bar_walk': None}); + (bar_walk_new, ) = Jac.context().jac_machine.update_walker( + "bar", + items={'bar_walk': None} + ); "Running bar_walk after update..." |> print; root spawn bar_walk_new(); print(f"bar_walk: {bar_walk_new.__dict__}"); - with open(bar_file_path, 'w') as bar_file{ + with open(bar_file_path, 'w') as bar_file { bar_file.write(original_content); } } - # Initialize the walker + can initial_run { root spawn bar_walk(); print(f"bar_walk: {bar_walk.__dict__}"); - } - # Define the entry point to run the test + with entry { initial_run(); From 95e5a6797d2c47ab1307035fcd6d7ff10416785b Mon Sep 17 00:00:00 2001 From: Ashish Mahendra Date: Fri, 30 Aug 2024 15:57:50 +0530 Subject: [PATCH 06/17] Update foo.jac --- jaclang/tests/fixtures/foo.jac | 1 - 1 file changed, 1 deletion(-) diff --git a/jaclang/tests/fixtures/foo.jac b/jaclang/tests/fixtures/foo.jac index a3cfa6fe8..4b3d756cf 100644 --- a/jaclang/tests/fixtures/foo.jac +++ b/jaclang/tests/fixtures/foo.jac @@ -34,7 +34,6 @@ can test_run { f" - Edge: {edge}" |> print; } } - print(f"bar_walk: {bar_walk}"); root spawn bar_walk(); } # Define the entry point to run the test From ccc16dd4e24d23a26627b494303dc2e9392f52f1 Mon Sep 17 00:00:00 2001 From: Ashish Mahendra Date: Fri, 30 Aug 2024 15:58:21 +0530 Subject: [PATCH 07/17] Update bar.jac --- jaclang/tests/fixtures/bar.jac | 7 ------- 1 file changed, 7 deletions(-) diff --git a/jaclang/tests/fixtures/bar.jac b/jaclang/tests/fixtures/bar.jac index 0c1fe89dc..808f78b00 100644 --- a/jaclang/tests/fixtures/bar.jac +++ b/jaclang/tests/fixtures/bar.jac @@ -37,10 +37,3 @@ walker bar_walk { disengage; } } - -walker print_walk { - can start with `root entry { - "This is a new walker, print_walk!" |> print; - disengage; - } -} From 89b476a70cff0154d270002bf050667d2d8b1758 Mon Sep 17 00:00:00 2001 From: Ashish Mahendra Date: Fri, 30 Aug 2024 15:58:43 +0530 Subject: [PATCH 08/17] Update bar.jac --- jaclang/tests/fixtures/bar.jac | 5 ----- 1 file changed, 5 deletions(-) diff --git a/jaclang/tests/fixtures/bar.jac b/jaclang/tests/fixtures/bar.jac index 808f78b00..34394779d 100644 --- a/jaclang/tests/fixtures/bar.jac +++ b/jaclang/tests/fixtures/bar.jac @@ -31,9 +31,4 @@ walker bar_walk { disengage; } } - # New behavior after update - can end with `root exit { - "bar_walk has been updated!" |> print; - disengage; - } } From 69dada09874e51dc8a7fe4e67c9bf538dba3ad71 Mon Sep 17 00:00:00 2001 From: Ashish Mahendra Date: Fri, 30 Aug 2024 10:51:40 +0000 Subject: [PATCH 09/17] code udpates --- jaclang/runtimelib/importer.py | 4 ---- jaclang/runtimelib/machine.py | 1 - jaclang/tests/fixtures/bar.jac | 14 +------------- jaclang/tests/fixtures/foo.jac | 1 - jaclang/tests/fixtures/walker_reload/bar.jac | 15 ++++++++++++++- jaclang/tests/fixtures/walker_reload/foo.jac | 9 +++++---- jaclang/tests/test_language.py | 10 +++++----- 7 files changed, 25 insertions(+), 29 deletions(-) diff --git a/jaclang/runtimelib/importer.py b/jaclang/runtimelib/importer.py index 439798a2c..603af17f9 100644 --- a/jaclang/runtimelib/importer.py +++ b/jaclang/runtimelib/importer.py @@ -348,9 +348,6 @@ def run_import( raise ImportError(f"No bytecode found for {spec.full_target}") with sys_path_context(spec.caller_dir): exec(codeobj, module.__dict__) - # print( - # f"Module {module_name} successfully compiled. dict: {module.__dict__.keys()}" - # ) except Exception as e: logger.error(dump_traceback(e)) raise e @@ -363,6 +360,5 @@ def run_import( cachable=spec.cachable, lang=spec.language, ) - # print(f"import_return: {import_return}") self.result = import_return return self.result diff --git a/jaclang/runtimelib/machine.py b/jaclang/runtimelib/machine.py index e78f8599e..e485cba66 100644 --- a/jaclang/runtimelib/machine.py +++ b/jaclang/runtimelib/machine.py @@ -7,7 +7,6 @@ import types from typing import Optional, Union from contextvars import ContextVar -from typing import Optional from jaclang.compiler.absyntree import Module from jaclang.compiler.compile import compile_jac diff --git a/jaclang/tests/fixtures/bar.jac b/jaclang/tests/fixtures/bar.jac index 0c1fe89dc..73c5cb3b7 100644 --- a/jaclang/tests/fixtures/bar.jac +++ b/jaclang/tests/fixtures/bar.jac @@ -31,16 +31,4 @@ walker bar_walk { disengage; } } - # New behavior after update - can end with `root exit { - "bar_walk has been updated!" |> print; - disengage; - } -} - -walker print_walk { - can start with `root entry { - "This is a new walker, print_walk!" |> print; - disengage; - } -} +} \ No newline at end of file diff --git a/jaclang/tests/fixtures/foo.jac b/jaclang/tests/fixtures/foo.jac index a3cfa6fe8..609f04430 100644 --- a/jaclang/tests/fixtures/foo.jac +++ b/jaclang/tests/fixtures/foo.jac @@ -1,4 +1,3 @@ -import:py from jaclang.plugin.feature, JacFeature as Jac; import:py from jaclang.runtimelib.machine, JacMachine; import:jac from bar, bar_walk; # Test runner to initialize the walker diff --git a/jaclang/tests/fixtures/walker_reload/bar.jac b/jaclang/tests/fixtures/walker_reload/bar.jac index 2224f1ec1..6031dc2eb 100644 --- a/jaclang/tests/fixtures/walker_reload/bar.jac +++ b/jaclang/tests/fixtures/walker_reload/bar.jac @@ -30,4 +30,17 @@ walker bar_walk { disengage; } } -} + + # New behavior added during runtime + can end with `root exit { + "bar_walk has been updated with new behavior!" |> print; + disengage; + } + + # New behavior added during runtime + can end with `root exit { + "bar_walk has been updated with new behavior!" |> print; + disengage; + } + } + \ No newline at end of file diff --git a/jaclang/tests/fixtures/walker_reload/foo.jac b/jaclang/tests/fixtures/walker_reload/foo.jac index 4ca942ad0..8bd7d3934 100644 --- a/jaclang/tests/fixtures/walker_reload/foo.jac +++ b/jaclang/tests/fixtures/walker_reload/foo.jac @@ -1,6 +1,7 @@ -import:py from jaclang.plugin.feature { JacFeature as Jac } import:jac from bar { bar_walk } import:py from time { sleep } +import:py from jaclang.runtimelib.machine {JacMachine} + import:py os; can update_bar_walker { @@ -18,7 +19,7 @@ can update_bar_walker { with open(bar_file_path, 'r') as bar_file { original_content = bar_file.read(); } - with open(bar_file_path, 'r+') as bar_file { # Specify the correct path to bar.jac + with open(bar_file_path, 'r+') as bar_file { content = bar_file.read(); @@ -31,8 +32,8 @@ can update_bar_walker { bar_file.truncate(); } } - "Updated bar.jac with new behavior." |> print; - (bar_walk_new, ) = Jac.context().jac_machine.update_walker( + "Updating bar.jac with new behavior." |> print; + (bar_walk_new, ) = JacMachine.get().update_walker( "bar", items={'bar_walk': None} ); diff --git a/jaclang/tests/test_language.py b/jaclang/tests/test_language.py index 1dbdeedcb..23489d548 100644 --- a/jaclang/tests/test_language.py +++ b/jaclang/tests/test_language.py @@ -954,15 +954,15 @@ def test_list_methods(self) -> None: def test_walker_dynamic_update(self) -> None: """Test dynamic update of a walker during runtime.""" - Jac.get_root().__jac__.edges.clear() - Jac.context().init_memory(base_path=self.fixture_abs_path("./")) captured_output = io.StringIO() sys.stdout = captured_output - cli.run( - filename=self.fixture_abs_path("walker_reload/foo.jac"), - ) + # cli.run( + # filename=self.fixture_abs_path("walker_reload/foo.jac"), + # ) + jac_import("foo", base_path=self.fixture_abs_path("walker_reload")) sys.stdout = sys.__stdout__ stdout_value = captured_output.getvalue() + print(f"Stdout: {stdout_value}") expected_output = "bar_walk has been updated with new behavior!" self.assertIn(expected_output, stdout_value.split("\n")) From 14531f38078267060acd693802ba002f3da0c59c Mon Sep 17 00:00:00 2001 From: Ashish Mahendra Date: Fri, 30 Aug 2024 11:16:26 +0000 Subject: [PATCH 10/17] test_fix for walker update --- jaclang/tests/fixtures/walker_reload/bar.jac | 14 +------------- jaclang/tests/fixtures/walker_reload/foo.jac | 2 +- jaclang/tests/test_language.py | 7 +++---- 3 files changed, 5 insertions(+), 18 deletions(-) diff --git a/jaclang/tests/fixtures/walker_reload/bar.jac b/jaclang/tests/fixtures/walker_reload/bar.jac index 6031dc2eb..3535f31c4 100644 --- a/jaclang/tests/fixtures/walker_reload/bar.jac +++ b/jaclang/tests/fixtures/walker_reload/bar.jac @@ -30,17 +30,5 @@ walker bar_walk { disengage; } } - - # New behavior added during runtime - can end with `root exit { - "bar_walk has been updated with new behavior!" |> print; - disengage; - } - - # New behavior added during runtime - can end with `root exit { - "bar_walk has been updated with new behavior!" |> print; - disengage; - } - } +} \ No newline at end of file diff --git a/jaclang/tests/fixtures/walker_reload/foo.jac b/jaclang/tests/fixtures/walker_reload/foo.jac index 8bd7d3934..1f584c519 100644 --- a/jaclang/tests/fixtures/walker_reload/foo.jac +++ b/jaclang/tests/fixtures/walker_reload/foo.jac @@ -13,7 +13,7 @@ can update_bar_walker { } } '''; - bar_file_path = os.path.abspath( + bar_file_path = os.path.join(os.path.abspath("."), 'jaclang/tests/fixtures/walker_reload/bar.jac' ); with open(bar_file_path, 'r') as bar_file { diff --git a/jaclang/tests/test_language.py b/jaclang/tests/test_language.py index 23489d548..24be7aee2 100644 --- a/jaclang/tests/test_language.py +++ b/jaclang/tests/test_language.py @@ -957,10 +957,9 @@ def test_walker_dynamic_update(self) -> None: captured_output = io.StringIO() sys.stdout = captured_output - # cli.run( - # filename=self.fixture_abs_path("walker_reload/foo.jac"), - # ) - jac_import("foo", base_path=self.fixture_abs_path("walker_reload")) + cli.run( + filename=self.fixture_abs_path("walker_reload/foo.jac"), + ) sys.stdout = sys.__stdout__ stdout_value = captured_output.getvalue() print(f"Stdout: {stdout_value}") From 33cf8a691f0647e337aca77c8ccba51acff28ca0 Mon Sep 17 00:00:00 2001 From: Ashish Mahendra Date: Fri, 30 Aug 2024 11:17:58 +0000 Subject: [PATCH 11/17] lint_fix: machine.py --- jaclang/runtimelib/machine.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jaclang/runtimelib/machine.py b/jaclang/runtimelib/machine.py index e485cba66..545f8a2a5 100644 --- a/jaclang/runtimelib/machine.py +++ b/jaclang/runtimelib/machine.py @@ -5,8 +5,8 @@ import os import sys import types -from typing import Optional, Union from contextvars import ContextVar +from typing import Optional, Union from jaclang.compiler.absyntree import Module from jaclang.compiler.compile import compile_jac From d8267809976ae0152c0cd1bba6d28094835356dd Mon Sep 17 00:00:00 2001 From: Ashish Mahendra Date: Fri, 30 Aug 2024 11:27:38 +0000 Subject: [PATCH 12/17] bar.jac fix white space --- jaclang/tests/fixtures/walker_reload/bar.jac | 1 - 1 file changed, 1 deletion(-) diff --git a/jaclang/tests/fixtures/walker_reload/bar.jac b/jaclang/tests/fixtures/walker_reload/bar.jac index 3535f31c4..2224f1ec1 100644 --- a/jaclang/tests/fixtures/walker_reload/bar.jac +++ b/jaclang/tests/fixtures/walker_reload/bar.jac @@ -31,4 +31,3 @@ walker bar_walk { } } } - \ No newline at end of file From 2cd617961a0fb71ec3a4fe67d64991e32afa6fa0 Mon Sep 17 00:00:00 2001 From: Ashish Mahendra Date: Fri, 30 Aug 2024 11:56:53 +0000 Subject: [PATCH 13/17] path_fix --- jaclang/tests/fixtures/walker_reload/foo.jac | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/jaclang/tests/fixtures/walker_reload/foo.jac b/jaclang/tests/fixtures/walker_reload/foo.jac index 1f584c519..5ebb81687 100644 --- a/jaclang/tests/fixtures/walker_reload/foo.jac +++ b/jaclang/tests/fixtures/walker_reload/foo.jac @@ -13,9 +13,8 @@ can update_bar_walker { } } '''; - bar_file_path = os.path.join(os.path.abspath("."), - 'jaclang/tests/fixtures/walker_reload/bar.jac' - ); + script_dir = os.path.dirname(os.path.abspath(__file__)); + bar_file_path = os.path.join(script_dir,'bar.jac'); with open(bar_file_path, 'r') as bar_file { original_content = bar_file.read(); } From e5fa3dcb8b427950319569ea65ac86428fd02bb7 Mon Sep 17 00:00:00 2001 From: Ashish Mahendra Date: Fri, 30 Aug 2024 14:57:36 +0000 Subject: [PATCH 14/17] update: test has update walker feature --- jaclang/tests/test_language.py | 56 +++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/jaclang/tests/test_language.py b/jaclang/tests/test_language.py index 24be7aee2..598b30b55 100644 --- a/jaclang/tests/test_language.py +++ b/jaclang/tests/test_language.py @@ -954,14 +954,60 @@ def test_list_methods(self) -> None: def test_walker_dynamic_update(self) -> None: """Test dynamic update of a walker during runtime.""" + session = self.fixture_abs_path("bar_walk.session") + bar_file_path = self.fixture_abs_path("bar.jac") captured_output = io.StringIO() sys.stdout = captured_output - - cli.run( - filename=self.fixture_abs_path("walker_reload/foo.jac"), + cli.enter( + filename=bar_file_path, + session=session, + entrypoint="bar_walk", + args=[], ) sys.stdout = sys.__stdout__ stdout_value = captured_output.getvalue() - print(f"Stdout: {stdout_value}") - expected_output = "bar_walk has been updated with new behavior!" + expected_output = "Created 5 items." self.assertIn(expected_output, stdout_value.split("\n")) + # Define the new behavior to be added + new_behavior = """ + # New behavior added during runtime + can end with `root exit { + "bar_walk has been updated with new behavior!" |> print; + disengage; + } + } + """ + + # Backup the original file content + with open(bar_file_path, "r") as bar_file: + original_content = bar_file.read() + + # Update the bar.jac file with new behavior + with open(bar_file_path, "r+") as bar_file: + content = bar_file.read() + last_brace_index = content.rfind("}") + if last_brace_index != -1: + updated_content = content[:last_brace_index] + new_behavior + bar_file.seek(0) + bar_file.write(updated_content) + bar_file.truncate() + + captured_output = io.StringIO() + sys.stdout = captured_output + + try: + cli.enter( + filename=bar_file_path, + session=session, + entrypoint="bar_walk", + args=[], + ) + sys.stdout = sys.__stdout__ + stdout_value = captured_output.getvalue() + expected_output = "bar_walk has been updated with new behavior!" + self.assertIn(expected_output, stdout_value.split("\n")) + finally: + # Restore the original content of bar.jac + with open(bar_file_path, "w") as bar_file: + + bar_file.write(original_content) From 356179aa077536a95c26d618b8f78ed847b97ddf Mon Sep 17 00:00:00 2001 From: Ashish Mahendra Date: Fri, 30 Aug 2024 20:28:21 +0530 Subject: [PATCH 15/17] Delete jaclang/tests/fixtures/walker_reload/bar.jac --- jaclang/tests/fixtures/walker_reload/bar.jac | 33 -------------------- 1 file changed, 33 deletions(-) delete mode 100644 jaclang/tests/fixtures/walker_reload/bar.jac diff --git a/jaclang/tests/fixtures/walker_reload/bar.jac b/jaclang/tests/fixtures/walker_reload/bar.jac deleted file mode 100644 index 2224f1ec1..000000000 --- a/jaclang/tests/fixtures/walker_reload/bar.jac +++ /dev/null @@ -1,33 +0,0 @@ -# Define a simple node type called `Item` -node Item { - has value: int = 0; -} -# Define an edge type called `Link` - -edge Link {} -# Define the `bar` walker - -walker bar_walk { - has count: int = 0; - # Start walking from the root node or an Item node - can start with `root | Item entry { - here ++> Item(); - if self.count < 5 { - visit [-->]; - } else { - "Created 5 items." |> print; - disengage; - } - } - # Walk over Item nodes and update their values - - can walk with Item entry { - here.value = self.count; - f"Item value: {here.value}" |> print; - self.count += 1; - visit [-->] else { - "Finished walking over all items." |> print; - disengage; - } - } -} From 6c91b771cf4460d2a8e6cc6805b7b07ab31ad650 Mon Sep 17 00:00:00 2001 From: Ashish Mahendra Date: Fri, 30 Aug 2024 20:28:36 +0530 Subject: [PATCH 16/17] Delete jaclang/tests/fixtures/walker_reload/foo.jac --- jaclang/tests/fixtures/walker_reload/foo.jac | 59 -------------------- 1 file changed, 59 deletions(-) delete mode 100644 jaclang/tests/fixtures/walker_reload/foo.jac diff --git a/jaclang/tests/fixtures/walker_reload/foo.jac b/jaclang/tests/fixtures/walker_reload/foo.jac deleted file mode 100644 index 5ebb81687..000000000 --- a/jaclang/tests/fixtures/walker_reload/foo.jac +++ /dev/null @@ -1,59 +0,0 @@ -import:jac from bar { bar_walk } -import:py from time { sleep } -import:py from jaclang.runtimelib.machine {JacMachine} - -import:py os; - -can update_bar_walker { - new_behavior = ''' - # New behavior added during runtime - can end with `root exit { - "bar_walk has been updated with new behavior!" |> print; - disengage; - } - } - '''; - script_dir = os.path.dirname(os.path.abspath(__file__)); - bar_file_path = os.path.join(script_dir,'bar.jac'); - with open(bar_file_path, 'r') as bar_file { - original_content = bar_file.read(); - } - with open(bar_file_path, 'r+') as bar_file { - - content = bar_file.read(); - - # Replace the last occurrence of "}" with the new behavior - last_brace_index = content.rfind('}'); - if last_brace_index != -1 { - updated_content = content[:last_brace_index] + new_behavior; - bar_file.seek(0); - bar_file.write(updated_content); - bar_file.truncate(); - } - } - "Updating bar.jac with new behavior." |> print; - (bar_walk_new, ) = JacMachine.get().update_walker( - "bar", - items={'bar_walk': None} - ); - "Running bar_walk after update..." |> print; - root spawn bar_walk_new(); - print(f"bar_walk: {bar_walk_new.__dict__}"); - with open(bar_file_path, 'w') as bar_file { - bar_file.write(original_content); - } -} -# Initialize the walker - -can initial_run { - root spawn bar_walk(); - print(f"bar_walk: {bar_walk.__dict__}"); -} -# Define the entry point to run the test - -with entry { - initial_run(); - - # Update the walker - update_bar_walker(); -} From cc00d19b3d1302784063d0d63759b0a9fed33782 Mon Sep 17 00:00:00 2001 From: Ashish Mahendra Date: Fri, 6 Sep 2024 15:00:19 +0000 Subject: [PATCH 17/17] updated test_language and add walker update code --- jaclang/tests/fixtures/walker_update.jac | 19 +++++++++++++++++++ jaclang/tests/test_language.py | 8 +++----- 2 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 jaclang/tests/fixtures/walker_update.jac diff --git a/jaclang/tests/fixtures/walker_update.jac b/jaclang/tests/fixtures/walker_update.jac new file mode 100644 index 000000000..011454303 --- /dev/null +++ b/jaclang/tests/fixtures/walker_update.jac @@ -0,0 +1,19 @@ +import:jac from bar { bar_walk } +import:py from jaclang.runtimelib.machine { JacMachine } +import:py os; + +can update_bar_walker { + "Updating bar.jac with new behavior." |> print; + (bar_walk_new, ) = JacMachine.get().update_walker( + "bar", + items={'bar_walk': None} + ); + "Running bar_walk after update..." |> print; + root spawn bar_walk_new(); + print(f"bar_walk: {bar_walk_new.__dict__}"); +} + + +with entry { + update_bar_walker(); +} \ No newline at end of file diff --git a/jaclang/tests/test_language.py b/jaclang/tests/test_language.py index ed85b3cd3..7a312c38d 100644 --- a/jaclang/tests/test_language.py +++ b/jaclang/tests/test_language.py @@ -956,6 +956,7 @@ def test_walker_dynamic_update(self) -> None: """Test dynamic update of a walker during runtime.""" session = self.fixture_abs_path("bar_walk.session") bar_file_path = self.fixture_abs_path("bar.jac") + update_file_path = self.fixture_abs_path("walker_update.jac") captured_output = io.StringIO() sys.stdout = captured_output cli.enter( @@ -996,11 +997,8 @@ def test_walker_dynamic_update(self) -> None: sys.stdout = captured_output try: - cli.enter( - filename=bar_file_path, - session=session, - entrypoint="bar_walk", - args=[], + cli.run( + filename=update_file_path, ) sys.stdout = sys.__stdout__ stdout_value = captured_output.getvalue()