ivoryos 1.0.9__py3-none-any.whl → 1.4.4__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.
- docs/source/conf.py +84 -0
- ivoryos/__init__.py +17 -207
- ivoryos/app.py +154 -0
- ivoryos/config.py +1 -0
- ivoryos/optimizer/ax_optimizer.py +191 -0
- ivoryos/optimizer/base_optimizer.py +84 -0
- ivoryos/optimizer/baybe_optimizer.py +193 -0
- ivoryos/optimizer/nimo_optimizer.py +173 -0
- ivoryos/optimizer/registry.py +11 -0
- ivoryos/routes/auth/auth.py +43 -14
- ivoryos/routes/auth/templates/change_password.html +32 -0
- ivoryos/routes/control/control.py +101 -366
- ivoryos/routes/control/control_file.py +33 -0
- ivoryos/routes/control/control_new_device.py +152 -0
- ivoryos/routes/control/templates/controllers.html +193 -0
- ivoryos/routes/control/templates/controllers_new.html +112 -0
- ivoryos/routes/control/utils.py +40 -0
- ivoryos/routes/data/data.py +197 -0
- ivoryos/routes/data/templates/components/step_card.html +78 -0
- ivoryos/routes/{database/templates/database → data/templates}/workflow_database.html +14 -8
- ivoryos/routes/data/templates/workflow_view.html +360 -0
- ivoryos/routes/design/__init__.py +4 -0
- ivoryos/routes/design/design.py +348 -657
- ivoryos/routes/design/design_file.py +68 -0
- ivoryos/routes/design/design_step.py +171 -0
- ivoryos/routes/design/templates/components/action_form.html +53 -0
- ivoryos/routes/design/templates/components/actions_panel.html +25 -0
- ivoryos/routes/design/templates/components/autofill_toggle.html +10 -0
- ivoryos/routes/design/templates/components/canvas.html +5 -0
- ivoryos/routes/design/templates/components/canvas_footer.html +9 -0
- ivoryos/routes/design/templates/components/canvas_header.html +75 -0
- ivoryos/routes/design/templates/components/canvas_main.html +39 -0
- ivoryos/routes/design/templates/components/deck_selector.html +10 -0
- ivoryos/routes/design/templates/components/edit_action_form.html +53 -0
- ivoryos/routes/design/templates/components/info_modal.html +318 -0
- ivoryos/routes/design/templates/components/instruments_panel.html +88 -0
- ivoryos/routes/design/templates/components/modals/drop_modal.html +17 -0
- ivoryos/routes/design/templates/components/modals/json_modal.html +22 -0
- ivoryos/routes/design/templates/components/modals/new_script_modal.html +17 -0
- ivoryos/routes/design/templates/components/modals/rename_modal.html +23 -0
- ivoryos/routes/design/templates/components/modals/saveas_modal.html +27 -0
- ivoryos/routes/design/templates/components/modals.html +6 -0
- ivoryos/routes/design/templates/components/python_code_overlay.html +56 -0
- ivoryos/routes/design/templates/components/sidebar.html +15 -0
- ivoryos/routes/design/templates/components/text_to_code_panel.html +20 -0
- ivoryos/routes/design/templates/experiment_builder.html +44 -0
- ivoryos/routes/execute/__init__.py +0 -0
- ivoryos/routes/execute/execute.py +377 -0
- ivoryos/routes/execute/execute_file.py +78 -0
- ivoryos/routes/execute/templates/components/error_modal.html +20 -0
- ivoryos/routes/execute/templates/components/logging_panel.html +56 -0
- ivoryos/routes/execute/templates/components/progress_panel.html +27 -0
- ivoryos/routes/execute/templates/components/run_panel.html +9 -0
- ivoryos/routes/execute/templates/components/run_tabs.html +60 -0
- ivoryos/routes/execute/templates/components/tab_bayesian.html +520 -0
- ivoryos/routes/execute/templates/components/tab_configuration.html +383 -0
- ivoryos/routes/execute/templates/components/tab_repeat.html +18 -0
- ivoryos/routes/execute/templates/experiment_run.html +30 -0
- ivoryos/routes/library/__init__.py +0 -0
- ivoryos/routes/library/library.py +157 -0
- ivoryos/routes/{database/templates/database/scripts_database.html → library/templates/library.html} +32 -23
- ivoryos/routes/main/main.py +31 -3
- ivoryos/routes/main/templates/{main/home.html → home.html} +4 -4
- ivoryos/server.py +180 -0
- ivoryos/socket_handlers.py +52 -0
- ivoryos/static/ivoryos_logo.png +0 -0
- ivoryos/static/js/action_handlers.js +384 -0
- ivoryos/static/js/db_delete.js +23 -0
- ivoryos/static/js/script_metadata.js +39 -0
- ivoryos/static/js/socket_handler.js +40 -5
- ivoryos/static/js/sortable_design.js +107 -56
- ivoryos/static/js/ui_state.js +114 -0
- ivoryos/templates/base.html +67 -8
- ivoryos/utils/bo_campaign.py +180 -3
- ivoryos/utils/client_proxy.py +267 -36
- ivoryos/utils/db_models.py +300 -65
- ivoryos/utils/decorators.py +34 -0
- ivoryos/utils/form.py +63 -29
- ivoryos/utils/global_config.py +34 -1
- ivoryos/utils/nest_script.py +314 -0
- ivoryos/utils/py_to_json.py +295 -0
- ivoryos/utils/script_runner.py +599 -165
- ivoryos/utils/serilize.py +201 -0
- ivoryos/utils/task_runner.py +71 -21
- ivoryos/utils/utils.py +50 -6
- ivoryos/version.py +1 -1
- ivoryos-1.4.4.dist-info/METADATA +263 -0
- ivoryos-1.4.4.dist-info/RECORD +119 -0
- {ivoryos-1.0.9.dist-info → ivoryos-1.4.4.dist-info}/WHEEL +1 -1
- {ivoryos-1.0.9.dist-info → ivoryos-1.4.4.dist-info}/top_level.txt +1 -0
- tests/unit/test_type_conversion.py +42 -0
- tests/unit/test_util.py +3 -0
- ivoryos/routes/control/templates/control/controllers.html +0 -78
- ivoryos/routes/control/templates/control/controllers_home.html +0 -55
- ivoryos/routes/control/templates/control/controllers_new.html +0 -89
- ivoryos/routes/database/database.py +0 -306
- ivoryos/routes/database/templates/database/step_card.html +0 -7
- ivoryos/routes/database/templates/database/workflow_view.html +0 -130
- ivoryos/routes/design/templates/design/experiment_builder.html +0 -521
- ivoryos/routes/design/templates/design/experiment_run.html +0 -558
- ivoryos-1.0.9.dist-info/METADATA +0 -218
- ivoryos-1.0.9.dist-info/RECORD +0 -61
- /ivoryos/routes/auth/templates/{auth/login.html → login.html} +0 -0
- /ivoryos/routes/auth/templates/{auth/signup.html → signup.html} +0 -0
- /ivoryos/routes/{database → data}/__init__.py +0 -0
- /ivoryos/routes/main/templates/{main/help.html → help.html} +0 -0
- {ivoryos-1.0.9.dist-info → ivoryos-1.4.4.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import json
|
|
3
|
+
import inspect
|
|
4
|
+
import logging
|
|
5
|
+
from typing import get_type_hints, Union, Optional, get_origin, get_args
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
import flask
|
|
9
|
+
|
|
10
|
+
from example.abstract_sdl_example import abstract_sdl as deck
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ScriptAnalyzer:
|
|
15
|
+
def __init__(self):
|
|
16
|
+
self.primitive_types = {
|
|
17
|
+
'int', 'float', 'str', 'bool', 'tuple', 'list'
|
|
18
|
+
}
|
|
19
|
+
self.type_mapping = {
|
|
20
|
+
int: 'int',
|
|
21
|
+
float: 'float',
|
|
22
|
+
str: 'str',
|
|
23
|
+
bool: 'bool',
|
|
24
|
+
tuple: 'tuple',
|
|
25
|
+
list: 'list'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
def extract_type_from_hint(self, type_hint):
|
|
29
|
+
"""Extract primitive types from type hints, handling Union and Optional"""
|
|
30
|
+
if type_hint is None:
|
|
31
|
+
return None
|
|
32
|
+
|
|
33
|
+
# Handle Union types (including Optional which is Union[T, None])
|
|
34
|
+
origin = get_origin(type_hint)
|
|
35
|
+
if origin is Union:
|
|
36
|
+
args = get_args(type_hint)
|
|
37
|
+
types = []
|
|
38
|
+
for arg in args:
|
|
39
|
+
if arg is type(None): # Skip None type
|
|
40
|
+
continue
|
|
41
|
+
if arg in self.type_mapping:
|
|
42
|
+
types.append(self.type_mapping[arg])
|
|
43
|
+
elif hasattr(arg, '__name__') and arg.__name__ in self.primitive_types:
|
|
44
|
+
types.append(arg.__name__)
|
|
45
|
+
else:
|
|
46
|
+
return None # Non-primitive type found
|
|
47
|
+
return types if types else None
|
|
48
|
+
|
|
49
|
+
# Handle direct primitive types
|
|
50
|
+
if type_hint in self.type_mapping:
|
|
51
|
+
return [self.type_mapping[type_hint]]
|
|
52
|
+
elif hasattr(type_hint, '__name__') and type_hint.__name__ in self.primitive_types:
|
|
53
|
+
return [type_hint.__name__]
|
|
54
|
+
else:
|
|
55
|
+
return None # Non-primitive type
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def analyze_method(self, method):
|
|
59
|
+
"""Analyze a single method and extract its signature"""
|
|
60
|
+
try:
|
|
61
|
+
sig = inspect.signature(method)
|
|
62
|
+
type_hints = get_type_hints(method)
|
|
63
|
+
|
|
64
|
+
parameters = []
|
|
65
|
+
type_hint_warning = False
|
|
66
|
+
user_input_params = []
|
|
67
|
+
|
|
68
|
+
for param_name, param in sig.parameters.items():
|
|
69
|
+
if param_name == 'self':
|
|
70
|
+
continue
|
|
71
|
+
|
|
72
|
+
param_info = {"name": param_name}
|
|
73
|
+
|
|
74
|
+
# Get type hint
|
|
75
|
+
if param_name in type_hints:
|
|
76
|
+
type_list = self.extract_type_from_hint(type_hints[param_name])
|
|
77
|
+
if type_list is None:
|
|
78
|
+
type_hint_warning = True
|
|
79
|
+
user_input_params.append(param_name)
|
|
80
|
+
param_info["type"] = ""
|
|
81
|
+
else:
|
|
82
|
+
param_info["type"] = type_list[0] if len(type_list) == 1 else type_list
|
|
83
|
+
else:
|
|
84
|
+
type_hint_warning = True
|
|
85
|
+
user_input_params.append(param_name)
|
|
86
|
+
param_info["type"] = ""
|
|
87
|
+
|
|
88
|
+
# Get default value
|
|
89
|
+
if param.default != inspect.Parameter.empty:
|
|
90
|
+
param_info["default"] = param.default
|
|
91
|
+
|
|
92
|
+
parameters.append(param_info)
|
|
93
|
+
|
|
94
|
+
# Get docstring
|
|
95
|
+
docstring = inspect.getdoc(method)
|
|
96
|
+
|
|
97
|
+
method_info = {
|
|
98
|
+
"docstring": docstring or "",
|
|
99
|
+
"parameters": parameters
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return method_info, type_hint_warning
|
|
103
|
+
|
|
104
|
+
except Exception as e:
|
|
105
|
+
print(f"Error analyzing method {method.__name__}: {e}")
|
|
106
|
+
return None
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def analyze_module(self, module, exclude_names=[]):
|
|
110
|
+
"""Analyze module from sys.modules and extract class instances and methods"""
|
|
111
|
+
exclude_classes = (flask.Blueprint, logging.Logger)
|
|
112
|
+
# Get all variables in the module that are class instances
|
|
113
|
+
included = {}
|
|
114
|
+
excluded = {}
|
|
115
|
+
failed = {}
|
|
116
|
+
included_with_warnings = {}
|
|
117
|
+
for name, obj in vars(module).items():
|
|
118
|
+
if (
|
|
119
|
+
type(obj).__module__ == 'builtins'
|
|
120
|
+
or name[0].isupper()
|
|
121
|
+
or name.startswith("_")
|
|
122
|
+
or isinstance(obj, exclude_classes)
|
|
123
|
+
or name in exclude_names
|
|
124
|
+
or not hasattr(obj, '__class__')
|
|
125
|
+
):
|
|
126
|
+
excluded[name] = type(obj).__name__
|
|
127
|
+
continue
|
|
128
|
+
|
|
129
|
+
cls = obj.__class__
|
|
130
|
+
|
|
131
|
+
try:
|
|
132
|
+
class_methods, type_hint_warning = self.analyze_class(cls)
|
|
133
|
+
if class_methods:
|
|
134
|
+
if type_hint_warning:
|
|
135
|
+
included_with_warnings[name] = class_methods
|
|
136
|
+
included[name] = class_methods
|
|
137
|
+
except Exception as e:
|
|
138
|
+
failed[name] = str(e)
|
|
139
|
+
continue
|
|
140
|
+
return included, included_with_warnings, failed, excluded
|
|
141
|
+
|
|
142
|
+
def save_to_json(self, data, output_path):
|
|
143
|
+
"""Save analysis result to JSON file"""
|
|
144
|
+
with open(output_path, 'w') as f:
|
|
145
|
+
json.dump(data, f, indent=2)
|
|
146
|
+
|
|
147
|
+
def analyze_class(self, cls):
|
|
148
|
+
class_methods = {}
|
|
149
|
+
type_hint_flag = False
|
|
150
|
+
for method_name, method in inspect.getmembers(cls, predicate=callable):
|
|
151
|
+
if method_name.startswith("_") or method_name.isupper():
|
|
152
|
+
continue
|
|
153
|
+
method_info, type_hint_warning = self.analyze_method(method)
|
|
154
|
+
if type_hint_warning:
|
|
155
|
+
type_hint_flag = True
|
|
156
|
+
if method_info:
|
|
157
|
+
class_methods[method_name] = method_info
|
|
158
|
+
return class_methods, type_hint_flag
|
|
159
|
+
|
|
160
|
+
@staticmethod
|
|
161
|
+
def print_deck_snapshot(result, with_warnings, failed):
|
|
162
|
+
print("\nDeck Snapshot:")
|
|
163
|
+
print("Included Classes:")
|
|
164
|
+
for name, methods in result.items():
|
|
165
|
+
print(f" {name}:")
|
|
166
|
+
for method_name, method_info in methods.items():
|
|
167
|
+
print(f" {method_name}: {method_info}")
|
|
168
|
+
|
|
169
|
+
if with_warnings:
|
|
170
|
+
print("\nClasses with Type Hint Warnings:")
|
|
171
|
+
for name, methods in with_warnings.items():
|
|
172
|
+
print(f" {name}:")
|
|
173
|
+
for method_name, method_info in methods.items():
|
|
174
|
+
print(f" {method_name}: {method_info}")
|
|
175
|
+
|
|
176
|
+
if failed:
|
|
177
|
+
print("\nFailed Classes:")
|
|
178
|
+
for name, error in failed.items():
|
|
179
|
+
print(f" {name}: {error}")
|
|
180
|
+
|
|
181
|
+
if __name__ == "__main__":
|
|
182
|
+
|
|
183
|
+
_analyzer = ScriptAnalyzer()
|
|
184
|
+
# module = sys.modules[deck]
|
|
185
|
+
try:
|
|
186
|
+
|
|
187
|
+
result, with_warnings, failed, _ = _analyzer.analyze_module(deck)
|
|
188
|
+
|
|
189
|
+
output_path = f"analysis.json"
|
|
190
|
+
_analyzer.save_to_json(result, output_path)
|
|
191
|
+
|
|
192
|
+
print(f"\nAnalysis complete! Results saved to: {output_path}")
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
_analyzer.print_deck_snapshot(result, with_warnings, failed)
|
|
198
|
+
|
|
199
|
+
except Exception as e:
|
|
200
|
+
print(f"Error: {e}")
|
|
201
|
+
|
ivoryos/utils/task_runner.py
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import asyncio
|
|
1
3
|
import threading
|
|
2
4
|
import time
|
|
3
5
|
from datetime import datetime
|
|
4
6
|
|
|
7
|
+
from ivoryos.utils.decorators import BUILDING_BLOCKS
|
|
5
8
|
from ivoryos.utils.db_models import db, SingleStep
|
|
6
9
|
from ivoryos.utils.global_config import GlobalConfig
|
|
7
10
|
|
|
@@ -18,8 +21,7 @@ class TaskRunner:
|
|
|
18
21
|
self.globals_dict = globals_dict
|
|
19
22
|
self.lock = global_config.runner_lock
|
|
20
23
|
|
|
21
|
-
|
|
22
|
-
def run_single_step(self, component, method, kwargs, wait=True, current_app=None):
|
|
24
|
+
async def run_single_step(self, component, method, kwargs, wait=True, current_app=None):
|
|
23
25
|
global deck
|
|
24
26
|
if deck is None:
|
|
25
27
|
deck = global_config.deck
|
|
@@ -28,18 +30,18 @@ class TaskRunner:
|
|
|
28
30
|
if not self.lock.acquire(blocking=False):
|
|
29
31
|
current_status = global_config.runner_status
|
|
30
32
|
current_status["status"] = "busy"
|
|
33
|
+
current_status["output"] = "busy"
|
|
31
34
|
return current_status
|
|
32
35
|
|
|
33
|
-
|
|
34
36
|
if wait:
|
|
35
|
-
output = self._run_single_step(component, method, kwargs, current_app)
|
|
37
|
+
output = await self._run_single_step(component, method, kwargs, current_app)
|
|
36
38
|
else:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
# Create background task properly
|
|
40
|
+
async def background_runner():
|
|
41
|
+
await self._run_single_step(component, method, kwargs, current_app)
|
|
42
|
+
|
|
43
|
+
asyncio.create_task(background_runner())
|
|
44
|
+
await asyncio.sleep(0.1) # Change time.sleep to await asyncio.sleep
|
|
43
45
|
output = {"status": "task started", "task_id": global_config.runner_status.get("id")}
|
|
44
46
|
|
|
45
47
|
return output
|
|
@@ -48,34 +50,82 @@ class TaskRunner:
|
|
|
48
50
|
if component.startswith("deck."):
|
|
49
51
|
component = component.split(".")[1]
|
|
50
52
|
instrument = getattr(deck, component)
|
|
53
|
+
function_executable = getattr(instrument, method)
|
|
54
|
+
elif component.startswith("blocks."):
|
|
55
|
+
component = component.split(".")[1]
|
|
56
|
+
function_executable = BUILDING_BLOCKS[component][method]["func"]
|
|
51
57
|
else:
|
|
52
58
|
temp_connections = global_config.defined_variables
|
|
53
59
|
instrument = temp_connections.get(component)
|
|
54
|
-
|
|
60
|
+
function_executable = getattr(instrument, method)
|
|
55
61
|
return function_executable
|
|
56
62
|
|
|
57
|
-
def _run_single_step(self, component, method, kwargs, current_app=None):
|
|
63
|
+
async def _run_single_step(self, component, method, kwargs, current_app=None):
|
|
58
64
|
try:
|
|
59
65
|
function_executable = self._get_executable(component, deck, method)
|
|
60
|
-
method_name = f"{
|
|
66
|
+
method_name = f"{component}.{method}"
|
|
61
67
|
except Exception as e:
|
|
62
68
|
self.lock.release()
|
|
63
|
-
return {"status": "error", "msg": e
|
|
69
|
+
return {"status": "error", "msg": str(e)}
|
|
64
70
|
|
|
65
|
-
# with
|
|
71
|
+
# Flask context is NOT async → just use normal "with"
|
|
66
72
|
with current_app.app_context():
|
|
67
|
-
step = SingleStep(
|
|
73
|
+
step = SingleStep(
|
|
74
|
+
method_name=method_name,
|
|
75
|
+
kwargs=kwargs,
|
|
76
|
+
run_error=None,
|
|
77
|
+
start_time=datetime.now()
|
|
78
|
+
)
|
|
68
79
|
db.session.add(step)
|
|
69
|
-
db.session.
|
|
70
|
-
global_config.runner_status = {"id":step.id, "type": "task"}
|
|
80
|
+
db.session.flush()
|
|
81
|
+
global_config.runner_status = {"id": step.id, "type": "task"}
|
|
82
|
+
|
|
71
83
|
try:
|
|
72
|
-
|
|
84
|
+
kwargs = self._convert_kwargs_type(kwargs, function_executable)
|
|
85
|
+
|
|
86
|
+
if inspect.iscoroutinefunction(function_executable):
|
|
87
|
+
output = await function_executable(**kwargs)
|
|
88
|
+
else:
|
|
89
|
+
output = function_executable(**kwargs)
|
|
90
|
+
|
|
73
91
|
step.output = output
|
|
74
92
|
step.end_time = datetime.now()
|
|
93
|
+
success = True
|
|
75
94
|
except Exception as e:
|
|
76
|
-
step.run_error = e
|
|
95
|
+
step.run_error = str(e)
|
|
77
96
|
step.end_time = datetime.now()
|
|
97
|
+
success = False
|
|
98
|
+
output = str(e)
|
|
78
99
|
finally:
|
|
79
100
|
db.session.commit()
|
|
80
101
|
self.lock.release()
|
|
81
|
-
|
|
102
|
+
|
|
103
|
+
return dict(success=success, output=output)
|
|
104
|
+
|
|
105
|
+
@staticmethod
|
|
106
|
+
def _convert_kwargs_type(kwargs, function_executable):
|
|
107
|
+
def convert_guess(str_value):
|
|
108
|
+
str_value = str_value.strip()
|
|
109
|
+
if str_value.isdigit() or (str_value.startswith('-') and str_value[1:].isdigit()):
|
|
110
|
+
return int(str_value)
|
|
111
|
+
try:
|
|
112
|
+
return float(str_value)
|
|
113
|
+
except ValueError:
|
|
114
|
+
return str_value
|
|
115
|
+
|
|
116
|
+
sig = inspect.signature(function_executable)
|
|
117
|
+
converted = {}
|
|
118
|
+
|
|
119
|
+
for name, value in kwargs.items():
|
|
120
|
+
if name in sig.parameters:
|
|
121
|
+
param = sig.parameters[name]
|
|
122
|
+
if param.annotation != inspect.Parameter.empty:
|
|
123
|
+
# convert using type hint
|
|
124
|
+
try:
|
|
125
|
+
converted[name] = param.annotation(value)
|
|
126
|
+
except Exception:
|
|
127
|
+
converted[name] = value
|
|
128
|
+
else:
|
|
129
|
+
# no type hint → guess
|
|
130
|
+
converted[name] = convert_guess(value)
|
|
131
|
+
return converted
|
ivoryos/utils/utils.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import ast
|
|
2
2
|
import importlib
|
|
3
3
|
import inspect
|
|
4
|
+
import json
|
|
4
5
|
import logging
|
|
5
6
|
import os
|
|
6
7
|
import pickle
|
|
@@ -11,10 +12,11 @@ from collections import Counter
|
|
|
11
12
|
|
|
12
13
|
import flask
|
|
13
14
|
from flask import session
|
|
15
|
+
from flask_login import current_user
|
|
14
16
|
from flask_socketio import SocketIO
|
|
15
17
|
|
|
16
18
|
from ivoryos.utils.db_models import Script
|
|
17
|
-
|
|
19
|
+
from ivoryos.utils.decorators import BUILDING_BLOCKS
|
|
18
20
|
|
|
19
21
|
def get_script_file():
|
|
20
22
|
"""Get script from Flask session and returns the script"""
|
|
@@ -24,7 +26,7 @@ def get_script_file():
|
|
|
24
26
|
s.__dict__.update(**session_script)
|
|
25
27
|
return s
|
|
26
28
|
else:
|
|
27
|
-
return Script(author=
|
|
29
|
+
return Script(author=current_user.get_id(),)
|
|
28
30
|
|
|
29
31
|
|
|
30
32
|
def post_script_file(script, is_dict=False):
|
|
@@ -104,7 +106,8 @@ def _inspect_class(class_object=None, debug=False):
|
|
|
104
106
|
try:
|
|
105
107
|
annotation = inspect.signature(method)
|
|
106
108
|
docstring = inspect.getdoc(method)
|
|
107
|
-
|
|
109
|
+
coroutine = inspect.iscoroutinefunction(method)
|
|
110
|
+
functions[function] = dict(signature=annotation, docstring=docstring, coroutine=coroutine,)
|
|
108
111
|
|
|
109
112
|
except Exception:
|
|
110
113
|
pass
|
|
@@ -140,6 +143,7 @@ def _get_type_from_parameters(arg, parameters):
|
|
|
140
143
|
def _convert_by_str(args, arg_types):
|
|
141
144
|
"""
|
|
142
145
|
Converts a value to type through eval(f'{type}("{args}")')
|
|
146
|
+
v1.3.4 TODO try str lastly, otherwise it's always converted to str
|
|
143
147
|
"""
|
|
144
148
|
if type(arg_types) is not list:
|
|
145
149
|
arg_types = [arg_types]
|
|
@@ -150,6 +154,7 @@ def _convert_by_str(args, arg_types):
|
|
|
150
154
|
return args
|
|
151
155
|
except Exception:
|
|
152
156
|
raise TypeError(f"Input type error: cannot convert '{args}' to {arg_type}.")
|
|
157
|
+
return args
|
|
153
158
|
|
|
154
159
|
|
|
155
160
|
def _convert_by_class(args, arg_types):
|
|
@@ -347,8 +352,8 @@ def create_deck_snapshot(deck, save: bool = False, output_path: str = '', exclud
|
|
|
347
352
|
for name, class_type in items.items():
|
|
348
353
|
print(f" {name}: {class_type}")
|
|
349
354
|
|
|
350
|
-
print_section("✅ INCLUDED", deck_summary["included"])
|
|
351
|
-
print_section("❌ FAILED", deck_summary["failed"])
|
|
355
|
+
print_section("✅ INCLUDED MODULES", deck_summary["included"])
|
|
356
|
+
print_section("❌ FAILED MODULES", deck_summary["failed"])
|
|
352
357
|
print("\n")
|
|
353
358
|
|
|
354
359
|
print_deck_snapshot(deck_summary)
|
|
@@ -363,6 +368,28 @@ def create_deck_snapshot(deck, save: bool = False, output_path: str = '', exclud
|
|
|
363
368
|
return deck_snapshot
|
|
364
369
|
|
|
365
370
|
|
|
371
|
+
def create_block_snapshot(save: bool = False, output_path: str = ''):
|
|
372
|
+
block_snapshot = {}
|
|
373
|
+
included = {}
|
|
374
|
+
failed = {}
|
|
375
|
+
for category, data in BUILDING_BLOCKS.items():
|
|
376
|
+
key = f"blocks.{category}"
|
|
377
|
+
block_snapshot[key] = {}
|
|
378
|
+
|
|
379
|
+
for func_name, meta in data.items():
|
|
380
|
+
func = meta["func"]
|
|
381
|
+
block_snapshot[key][func_name] = {
|
|
382
|
+
"signature": meta["signature"],
|
|
383
|
+
"docstring": meta["docstring"],
|
|
384
|
+
"coroutine": meta["coroutine"],
|
|
385
|
+
"path": f"{func.__module__}.{func.__qualname__}"
|
|
386
|
+
}
|
|
387
|
+
if block_snapshot:
|
|
388
|
+
print(f"\n=== ✅ BUILDING_BLOCKS ({len(block_snapshot)}) ===")
|
|
389
|
+
for category, blocks in block_snapshot.items():
|
|
390
|
+
print(f" {category}: ", ",".join(blocks.keys()))
|
|
391
|
+
return block_snapshot
|
|
392
|
+
|
|
366
393
|
def load_deck(pkl_name: str):
|
|
367
394
|
"""
|
|
368
395
|
Loads a pickled deck snapshot from disk on offline mode
|
|
@@ -419,4 +446,21 @@ def get_local_ip():
|
|
|
419
446
|
ip = '127.0.0.1'
|
|
420
447
|
finally:
|
|
421
448
|
s.close()
|
|
422
|
-
return ip
|
|
449
|
+
return ip
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
def safe_dump(obj):
|
|
453
|
+
try:
|
|
454
|
+
json.dumps(obj)
|
|
455
|
+
return obj
|
|
456
|
+
except (TypeError, OverflowError):
|
|
457
|
+
return repr(obj) # store readable representation
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def create_module_snapshot(module):
|
|
461
|
+
classes = inspect.getmembers(module, inspect.isclass)
|
|
462
|
+
api_variables = {}
|
|
463
|
+
for i in classes:
|
|
464
|
+
# globals()[i[0]] = i[1]
|
|
465
|
+
api_variables[i[0]] = i[1]
|
|
466
|
+
return api_variables
|
ivoryos/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "1.
|
|
1
|
+
__version__ = "1.4.4"
|