ivoryos 0.1.19__py3-none-any.whl → 0.1.21__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -24,9 +24,11 @@ class ScriptRunner:
24
24
  self.stop_current_event = threading.Event()
25
25
  self.is_running = False
26
26
  self.lock = threading.Lock()
27
+ self.paused = False
27
28
 
28
29
  def toggle_pause(self):
29
30
  """Toggles between pausing and resuming the script"""
31
+ self.paused = not self.paused
30
32
  if self.pause_event.is_set():
31
33
  self.pause_event.clear() # Pause the script
32
34
  return "Paused"
@@ -34,11 +36,18 @@ class ScriptRunner:
34
36
  self.pause_event.set() # Resume the script
35
37
  return "Resumed"
36
38
 
39
+ def pause_status(self):
40
+ """Toggles between pausing and resuming the script"""
41
+ return self.paused
42
+
37
43
  def reset_stop_event(self):
44
+ """Resets the stop event"""
38
45
  self.stop_pending_event.clear()
39
46
  self.stop_current_event.clear()
47
+ self.pause_event.set()
40
48
 
41
49
  def abort_pending(self):
50
+ """Abort the pending iteration after the current is finished"""
42
51
  self.stop_pending_event.set()
43
52
  # print("Stop pending tasks")
44
53
 
@@ -66,7 +75,7 @@ class ScriptRunner:
66
75
  thread.start()
67
76
  return thread
68
77
 
69
- def execute_function_line_by_line(self, script, section_name, logger, **kwargs):
78
+ def exex_steps(self, script, section_name, logger, socketio, **kwargs):
70
79
  """
71
80
  Executes a function defined in a string line by line.
72
81
 
@@ -74,71 +83,89 @@ class ScriptRunner:
74
83
  :param kwargs: Arguments to pass to the function
75
84
  :return: The final result of the function execution
76
85
  """
86
+ _func_str = script.compile()
87
+ step_list: list = script.convert_to_lines(_func_str).get(section_name, [])
77
88
  global deck
78
89
  if deck is None:
79
90
  deck = global_config.deck
80
91
  # func_str = script.compile()
81
92
  # Parse function body from string
82
- module = ast.parse(script)
83
- func_def = next(node for node in module.body if isinstance(node, ast.FunctionDef))
84
-
85
- # Extract function body as source lines
86
- lines = [ast.unparse(node) for node in func_def.body if not isinstance(node, ast.Return)]
93
+ temp_connections = global_config.defined_variables
87
94
  # Prepare execution environment
88
- exec_globals = {"deck": deck} # Add required global objects
95
+ exec_globals = {"deck": deck, "time":time} # Add required global objects
96
+ # exec_globals = {"deck": deck, "time": time, "registered_workflows":registered_workflows} # Add required global objects
97
+ exec_globals.update(temp_connections)
89
98
  exec_locals = {} # Local execution scope
90
99
 
91
100
  # Define function arguments manually in exec_locals
92
101
  exec_locals.update(kwargs)
102
+ index = 0
93
103
 
94
104
  # Execute each line dynamically
95
- for line in lines:
105
+ while index < len(step_list):
96
106
  if self.stop_current_event.is_set():
97
107
  logger.info(f'Stopping execution during {section_name}')
98
108
  break
109
+ line = step_list[index]
99
110
  logger.info(f"Executing: {line}") # Debugging output
100
- exec(line, exec_globals, exec_locals)
111
+ socketio.emit('execution', {'section': f"{section_name}-{index}"})
112
+ # self._emit_progress(socketio, 100)
113
+ try:
114
+ exec(line, exec_globals, exec_locals)
115
+ except Exception as e:
116
+ logger.error(f"Error during script execution: {e}")
117
+ socketio.emit('error', {'message': e.__str__()})
118
+ self.toggle_pause()
101
119
  self.pause_event.wait()
102
120
 
121
+ # todo update script during the run
122
+ # _func_str = script.compile()
123
+ # step_list: list = script.convert_to_lines(_func_str).get(section_name, [])
124
+ index += 1
103
125
  return exec_locals # Return the 'results' variable
104
126
 
105
- def _run_with_stop_check(self, script: Script, repeat_count, run_name, logger, socketio, config, bo_args,
127
+ def _run_with_stop_check(self, script: Script, repeat_count: int, run_name: str, logger, socketio, config, bo_args,
106
128
  output_path):
107
129
  time.sleep(1)
108
- func_str = script.compile()
130
+ # _func_str = script.compile()
131
+ # step_list_dict: dict = script.convert_to_lines(_func_str)
109
132
  self._emit_progress(socketio, 1)
110
- try:
111
- # Run "prep" section once
112
- script_dict = script.script_dict
113
- self._run_actions(script_dict.get("prep", []), func_str.get("prep", ''), section_name="prep", logger=logger)
114
- output_list = []
115
- _, arg_type = script.config("script")
116
- _, return_list = script.config_return()
117
- # Run "script" section multiple times
118
- if repeat_count:
119
- self._run_repeat_section(repeat_count, arg_type, bo_args, output_list, func_str.get("script", ''), run_name, return_list, logger, socketio)
120
- elif config:
121
- self._run_config_section(config, arg_type, output_list, func_str.get("script", ''), run_name, logger, socketio)
122
- # Run "cleanup" section once
123
- self._run_actions(script_dict.get("cleanup", []), func_str.get("cleanup", ''), section_name="cleanup", logger=logger)
124
- # Save results if necessary
125
- if output_list:
126
- self._save_results(run_name, arg_type, return_list, output_list, logger, output_path)
127
- except Exception as e:
128
- logger.error(f"Error during script execution: {e}")
129
- finally:
130
- with self.lock:
131
- self.is_running = False # Reset the running flag when done
132
- self._emit_progress(socketio, 100)
133
-
134
- def _run_actions(self, actions, func_str, section_name="", logger=None):
135
- logger.info(f'Executing {section_name} steps') if actions else logger.info(f'No {section_name} steps')
133
+
134
+ # Run "prep" section once
135
+ script_dict = script.script_dict
136
+ self._run_actions(script, section_name="prep", logger=logger, socketio=socketio)
137
+ output_list = []
138
+ _, arg_type = script.config("script")
139
+ _, return_list = script.config_return()
140
+
141
+ # Run "script" section multiple times
142
+ if repeat_count:
143
+ self._run_repeat_section(repeat_count, arg_type, bo_args, output_list, script,
144
+ run_name, return_list, logger, socketio)
145
+ elif config:
146
+ self._run_config_section(config, arg_type, output_list, script, run_name, logger,
147
+ socketio)
148
+
149
+ # Run "cleanup" section once
150
+ self._run_actions(script, section_name="cleanup", logger=logger, socketio=socketio)
151
+ # Reset the running flag when done
152
+ with self.lock:
153
+ self.is_running = False
154
+ # Save results if necessary
155
+ if output_list:
156
+ self._save_results(run_name, arg_type, return_list, output_list, logger, output_path)
157
+ self._emit_progress(socketio, 100)
158
+
159
+ def _run_actions(self, script, section_name="", logger=None, socketio=None):
160
+ _func_str = script.compile()
161
+ step_list: list = script.convert_to_lines(_func_str).get(section_name, [])
162
+ logger.info(f'Executing {section_name} steps') if step_list else logger.info(f'No {section_name} steps')
136
163
  if self.stop_pending_event.is_set():
137
164
  logger.info(f"Stopping execution during {section_name} section.")
138
165
  return
139
- self.execute_function_line_by_line(func_str, section_name, logger)
166
+ self.exex_steps(script, section_name, logger, socketio)
140
167
 
141
- def _run_config_section(self, config, arg_type, output_list, func_str, run_name, logger, socketio):
168
+ def _run_config_section(self, config, arg_type, output_list, script, run_name, logger, socketio):
142
169
  compiled = True
143
170
  for i in config:
144
171
  try:
@@ -158,12 +185,13 @@ class ScriptRunner:
158
185
  self._emit_progress(socketio, progress)
159
186
  # fname = f"{run_name}_script"
160
187
  # function = self.globals_dict[fname]
161
- output = self.execute_function_line_by_line(func_str, "script", logger, **kwargs)
188
+ output = self.exex_steps(script, "script", logger, socketio, **kwargs)
162
189
  if output:
163
190
  # kwargs.update(output)
164
191
  output_list.append(output)
165
192
 
166
- def _run_repeat_section(self, repeat_count, arg_types, bo_args, output_list, func_str, run_name, return_list, logger, socketio):
193
+ def _run_repeat_section(self, repeat_count, arg_types, bo_args, output_list, script, run_name, return_list,
194
+ logger, socketio):
167
195
  if bo_args:
168
196
  logger.info('Initializing optimizer...')
169
197
  ax_client = utils.ax_initiation(bo_args, arg_types)
@@ -180,7 +208,7 @@ class ScriptRunner:
180
208
  logger.info(f'Output value: {parameters}')
181
209
  # fname = f"{run_name}_script"
182
210
  # function = self.globals_dict[fname]
183
- output = self.execute_function_line_by_line(func_str, "script", logger, **parameters)
211
+ output = self.exex_steps(script, "script", logger, socketio, **parameters)
184
212
 
185
213
  _output = {key: value for key, value in output.items() if key in return_list}
186
214
  ax_client.complete_trial(trial_index=trial_index, raw_data=_output)
@@ -191,7 +219,7 @@ class ScriptRunner:
191
219
  else:
192
220
  # fname = f"{run_name}_script"
193
221
  # function = self.globals_dict[fname]
194
- output = self.execute_function_line_by_line(func_str, "script", logger)
222
+ output = self.exex_steps(script, "script", logger, socketio)
195
223
 
196
224
  if output:
197
225
  output_list.append(output)
ivoryos/utils/utils.py CHANGED
@@ -97,7 +97,7 @@ def _inspect_class(class_object=None, debug=False):
97
97
  under_score = "_"
98
98
  if debug:
99
99
  under_score = "__"
100
- for function, method in inspect.getmembers(type(class_object), predicate=inspect.isfunction):
100
+ for function, method in inspect.getmembers(type(class_object), predicate=callable):
101
101
  if not function.startswith(under_score) and not function.isupper():
102
102
  try:
103
103
  annotation = inspect.signature(method)
ivoryos/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.19"
1
+ __version__ = "0.1.21"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ivoryos
3
- Version: 0.1.19
3
+ Version: 0.1.21
4
4
  Summary: an open-source Python package enabling Self-Driving Labs (SDLs) interoperability
5
5
  Home-page: https://gitlab.com/heingroup/ivoryos
6
6
  Author: Ivory Zhang
@@ -21,6 +21,9 @@ Requires-Dist: python-dotenv
21
21
  [![Documentation Status](https://readthedocs.org/projects/ivoryos/badge/?version=latest)](https://ivoryos.readthedocs.io/en/latest/?badge=latest)
22
22
  [![PyPI version](https://img.shields.io/pypi/v/ivoryos)](https://pypi.org/project/ivoryos/)
23
23
  ![License](https://img.shields.io/pypi/l/ivoryos)
24
+ [![YouTube](https://img.shields.io/badge/YouTube-video-red?logo=youtube)](https://youtu.be/dFfJv9I2-1g)
25
+ [![Research Square](https://img.shields.io/badge/Preprint-blue)](https://www.researchsquare.com/article/rs-5307798/v1)
26
+
24
27
 
25
28
  ![](https://gitlab.com/heingroup/ivoryos/raw/main/docs/source/_static/ivoryos.png)
26
29
  # ivoryOS: interoperable Web UI for self-driving laboratories (SDLs)
@@ -32,6 +35,7 @@ Requires-Dist: python-dotenv
32
35
  - [Installation](#installation)
33
36
  - [Instructions for use](#instructions-for-use)
34
37
  - [Demo](#demo)
38
+ - [Roadmap](#roadmap)
35
39
  - [License](#license)
36
40
 
37
41
  ## Description
@@ -160,15 +164,10 @@ When you run the application for the first time, it will automatically create th
160
164
  - **`ivoryos.db`**: Database file that stores application data locally.
161
165
 
162
166
 
163
- ### Demo video
164
- Intro + Tutorial + Demo with PurPOSE platform
165
- https://youtu.be/dFfJv9I2-1g
166
-
167
-
168
167
  ## Roadmap
169
168
 
170
169
  - [x] Allow plugin pages ✅
171
- - [ ] pause, resume, abort current and pending workflows
170
+ - [x] pause, resume, abort current and pending workflows
172
171
  - [ ] snapshot version control
173
172
  - [ ] dropdown input
174
173
  - [ ] show line number option
@@ -1,6 +1,6 @@
1
- ivoryos/__init__.py,sha256=F3KenDZX1zqei3PZ3tx1U77yXsbeytgMMhVJyJMkmJ0,6071
1
+ ivoryos/__init__.py,sha256=bx8EuJ5ObYwuwlW3KnWQwXG-AdWWyi-F0kaOLe7WeUo,5978
2
2
  ivoryos/config.py,sha256=3FPBYTIBhQTKDvsEoR8ZeTmg65D-CSFEdGmOuIL4pSI,1311
3
- ivoryos/version.py,sha256=cAJAbAh288a9AL-3yxwFzEM1L26izSJ6wma5aiml_9Y,23
3
+ ivoryos/version.py,sha256=qEmNtjnOwhDYQ0cHPPtUkUaghzD2xl0thJEznl4giYw,23
4
4
  ivoryos/routes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  ivoryos/routes/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  ivoryos/routes/auth/auth.py,sha256=7CdXjGAr1B_xsmwweakTWOoROgsOJf0MNTzlMP_5Nus,3240
@@ -12,35 +12,35 @@ ivoryos/routes/control/templates/control/controllers.html,sha256=iIp0h6WA68gQj9O
12
12
  ivoryos/routes/control/templates/control/controllers_home.html,sha256=xFnBXTWMlyi2naMSsCEqFlX-Z1HJJDDtzITPvq-3nng,2733
13
13
  ivoryos/routes/control/templates/control/controllers_new.html,sha256=uOQo9kYmwX2jk3KZDkMUF_ylfNUIs_oIWb_kk_MMVDM,4921
14
14
  ivoryos/routes/database/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- ivoryos/routes/database/database.py,sha256=IOrdtdsZ28bV7R1ZQfcwAeS5JDCkX-QYsP9i8BseD38,5684
16
- ivoryos/routes/database/templates/database/experiment_database.html,sha256=ABchwHYDovzwECc7UD4Mh8C_9JUl0YZpAAF9sFjNCs0,3434
15
+ ivoryos/routes/database/database.py,sha256=h4b5r1K5W3psqze0v2QF_jX0ADppy8XdFBQLJb41LOU,5808
16
+ ivoryos/routes/database/templates/database/experiment_database.html,sha256=GeZjx220PdAt_dVN6kigsaNzvaruPlrSSii1cka56NE,3530
17
17
  ivoryos/routes/design/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- ivoryos/routes/design/design.py,sha256=hV8rtyIgbw5HsDY9h_Xtq96NIoaZB8XXLKyxaxbnYEA,21361
19
- ivoryos/routes/design/templates/design/experiment_builder.html,sha256=-C84nHcK1grC8b_B5Lgbg6StJcP8x1oj2XAjRPj1oU0,28239
20
- ivoryos/routes/design/templates/design/experiment_run.html,sha256=JY44cyWyi470Z_GeDGROU6cyFsTlbTYtlFVXmPjf4XA,23944
18
+ ivoryos/routes/design/design.py,sha256=rIy6LCMw6ck4bxuPqoxIV6F1vZxY8aZ3Au5pm74dfr8,23934
19
+ ivoryos/routes/design/templates/design/experiment_builder.html,sha256=2cT25URI6NoRDKuU6xl8Vsv4pqyQhhx-Fu8JOfPIP8k,27110
20
+ ivoryos/routes/design/templates/design/experiment_run.html,sha256=t4Pq6uIltm3sq5cDb4zQa9i98iOQFNumkSa24vzCZrY,26001
21
21
  ivoryos/routes/main/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  ivoryos/routes/main/main.py,sha256=yuVJzXAob1kc1dfflkTBIZQ0tdf6kChfuq-uQlN1e9Q,957
23
23
  ivoryos/routes/main/templates/main/help.html,sha256=IOktMEsOPk0SCiMBXZ4mpffClERAyX8W82fel71M3M0,9370
24
24
  ivoryos/routes/main/templates/main/home.html,sha256=ujM0YC0yrHhCfkuprNnZZZd8XEEguS_6NjrY5ktfctg,3356
25
25
  ivoryos/static/favicon.ico,sha256=RhlrPtfITOkzC9BjP1UB1V5L9Oyp6NwNtWeMcGOnpyc,15406
26
26
  ivoryos/static/logo.webp,sha256=lXgfQR-4mHTH83k7VV9iB54-oC2ipe6uZvbwdOnLETc,14974
27
- ivoryos/static/style.css,sha256=cbQ8T8h42smwuyF5qQ_pNhlptDXGcuyK2u4sUppqnyI,3717
27
+ ivoryos/static/style.css,sha256=zQVx35A5g6JMJ-K84-6fSKtzXGjp_p5ZVG6KLHPM2IE,4021
28
28
  ivoryos/static/gui_annotation/Slide1.png,sha256=Lm4gdOkUF5HIUFaB94tl6koQVkzpitKj43GXV_XYMMc,121727
29
29
  ivoryos/static/gui_annotation/Slide2.PNG,sha256=z3wQ9oVgg4JTWVLQGKK_KhtepRHUYP1e05XUWGT2A0I,118761
30
30
  ivoryos/static/js/overlay.js,sha256=dPxop19es0E0ZUSY3d_4exIk7CJuQEnlW5uTt5fZfzI,483
31
- ivoryos/static/js/socket_handler.js,sha256=psIz61RgTzFk7B9SBZWtHWpG9vJjaRxEDTueFDKQcO4,2580
31
+ ivoryos/static/js/socket_handler.js,sha256=fJ_SKCfS2ZejnECPKZuD8DTEPhBxmm0m-yCvxf2UtIQ,4885
32
32
  ivoryos/static/js/sortable_card.js,sha256=ifmlGe3yy0U_KzMphV4ClRhK2DLOvkELYMlq1vECuac,807
33
- ivoryos/static/js/sortable_design.js,sha256=1lI1I8FbL66tv6n-SX2FkbHNDYo36xVo2qDBKVLmxnQ,1120
34
- ivoryos/templates/base.html,sha256=8HDi7I74ugcCAV4c3ha4C__-7xopt4ZsC0OQ2E_AsQ8,8313
33
+ ivoryos/static/js/sortable_design.js,sha256=wwpKfIzZGDxfX3moNz0cvPvm9YyHmopZK3wmkUdnBiw,4333
34
+ ivoryos/templates/base.html,sha256=OM4D_1A_RawOLBRhiCbKVn7a1UgPxji0BZlnCURl5WM,8325
35
35
  ivoryos/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
- ivoryos/utils/db_models.py,sha256=GiBvsQqleXa7sXjh-118Q1rDTWYKrBsz25fben3aKC4,23712
37
- ivoryos/utils/form.py,sha256=1h6eytHz1tv5_CIc_C6KGYn5JEPYniIOxo49yzM37o0,17149
38
- ivoryos/utils/global_config.py,sha256=Ojcz6xKATSbMLnTT0kiKqSnu_bNqCMyIAnZyHaKxJns,1589
36
+ ivoryos/utils/db_models.py,sha256=LG78TSc9VAEELH9pgTVRRpmyEsCjtxkQOM176N0HAw8,25179
37
+ ivoryos/utils/form.py,sha256=Eqrg4G44hoG6TizxTEP4DNd-rEoyN77t9Y7cB1WdrcQ,19205
38
+ ivoryos/utils/global_config.py,sha256=P0xs_33bZfNQ-D71lCkq7HJyT4ngQWPqUKnkoMrmM8c,1908
39
39
  ivoryos/utils/llm_agent.py,sha256=DTf-AF56vy1Em1fUKagl5NURKittmNoxTKIw1PlyC2o,6413
40
- ivoryos/utils/script_runner.py,sha256=fa35LmNHUXdU0R-_e2Wadok4GjQ_MaYNMvOFJfxSdCI,9308
41
- ivoryos/utils/utils.py,sha256=nQbGSR_FmlZyBb9lwKy3ws4FDAWWfDyu96hZg2DVIeI,14081
42
- ivoryos-0.1.19.dist-info/LICENSE,sha256=p2c8S8i-8YqMpZCJnadLz1-ofxnRMILzz6NCMIypRag,1084
43
- ivoryos-0.1.19.dist-info/METADATA,sha256=z55ToyL0h0YAkAS9pEeaXrG2TFT-fSWeJBsaKto3Xc0,6539
44
- ivoryos-0.1.19.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
45
- ivoryos-0.1.19.dist-info/top_level.txt,sha256=FRIWWdiEvRKqw-XfF_UK3XV0CrnNb6EmVbEgjaVazRM,8
46
- ivoryos-0.1.19.dist-info/RECORD,,
40
+ ivoryos/utils/script_runner.py,sha256=1B6iImRvJ--VSD036p5YSjl76yxMRqDa1OCoRi9ZMM4,10304
41
+ ivoryos/utils/utils.py,sha256=SWVlMjpcUnQ2hgVasXwKugX781eiq78Dv6oZa21lg5Q,14071
42
+ ivoryos-0.1.21.dist-info/LICENSE,sha256=p2c8S8i-8YqMpZCJnadLz1-ofxnRMILzz6NCMIypRag,1084
43
+ ivoryos-0.1.21.dist-info/METADATA,sha256=3eE6xsyC4fYuUeXA5OsAhWbdrRPCGXaxAFbTTLbCCNA,6697
44
+ ivoryos-0.1.21.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
45
+ ivoryos-0.1.21.dist-info/top_level.txt,sha256=FRIWWdiEvRKqw-XfF_UK3XV0CrnNb6EmVbEgjaVazRM,8
46
+ ivoryos-0.1.21.dist-info/RECORD,,