setta 0.0.2.dev0__py3-none-any.whl → 0.0.3__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. setta/__init__.py +1 -1
  2. setta/cli/__init__.py +1 -1
  3. setta/cli/logger.py +1 -3
  4. setta/code_gen/create_runnable_scripts.py +87 -34
  5. setta/code_gen/export_selected.py +1 -5
  6. setta/code_gen/find_placeholders.py +13 -2
  7. setta/database/backup.py +1 -2
  8. setta/database/db/artifacts/save.py +20 -4
  9. setta/database/db/projects/save.py +1 -1
  10. setta/database/db/sections/load.py +5 -2
  11. setta/database/db/sections/save.py +2 -2
  12. setta/database/db_objs.py +6 -0
  13. setta/database/export_db/export_raw.py +3 -3
  14. setta/database/export_db/utils.py +2 -3
  15. setta/database/settings_file.py +3 -3
  16. setta/lsp/file_watcher.py +24 -23
  17. setta/lsp/server.py +1 -1
  18. setta/lsp/writer.py +2 -2
  19. setta/routers/interactive.py +38 -20
  20. setta/server.py +3 -4
  21. setta/start.py +4 -3
  22. setta/static/constants/constants.json +4 -0
  23. setta/static/constants/db_init.sql +2 -1
  24. setta/static/constants/defaultValues.json +2 -1
  25. setta/static/constants/settingsProject.json +31 -31
  26. setta/static/frontend/assets/index-03be034e.css +32 -0
  27. setta/static/frontend/assets/{index-ee99dc72.js → index-59443547.js} +171 -171
  28. setta/static/frontend/index.html +2 -2
  29. setta/tasks/tasks.py +166 -98
  30. setta/tasks/utils.py +108 -21
  31. setta/terminals/terminals.py +7 -6
  32. setta/utils/constants.py +5 -2
  33. setta/utils/websocket_manager.py +8 -3
  34. setta-0.0.3.dist-info/METADATA +146 -0
  35. {setta-0.0.2.dev0.dist-info → setta-0.0.3.dist-info}/RECORD +39 -40
  36. setta/database/db_path.py +0 -8
  37. setta/static/frontend/assets/index-1d4b4ecf.css +0 -32
  38. setta-0.0.2.dev0.dist-info/METADATA +0 -24
  39. {setta-0.0.2.dev0.dist-info → setta-0.0.3.dist-info}/LICENSE +0 -0
  40. {setta-0.0.2.dev0.dist-info → setta-0.0.3.dist-info}/WHEEL +0 -0
  41. {setta-0.0.2.dev0.dist-info → setta-0.0.3.dist-info}/entry_points.txt +0 -0
  42. {setta-0.0.2.dev0.dist-info → setta-0.0.3.dist-info}/top_level.txt +0 -0
setta/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.0.2.dev0"
1
+ __version__ = "0.0.3"
setta/cli/__init__.py CHANGED
@@ -1 +1 @@
1
- from .logger import Setta
1
+ from .logger import Setta, SettaImg, SettaList
setta/cli/logger.py CHANGED
@@ -26,9 +26,7 @@ class Setta:
26
26
  if root_path:
27
27
  self.root_path = Path(root_path)
28
28
  else:
29
- self.root_path = Path(
30
- os.environ.get(CODE_FOLDER_ENV_VARIABLE, os.path.relpath(os.getcwd()))
31
- )
29
+ self.root_path = Path(os.environ.get(CODE_FOLDER_ENV_VARIABLE, "."))
32
30
 
33
31
  self.name_path_type_to_id = {}
34
32
 
@@ -1,15 +1,15 @@
1
1
  import copy
2
- import os
3
2
  from datetime import datetime
4
3
  from pathlib import Path
5
4
 
6
5
  from setta.code_gen.export_selected import (
7
6
  export_selected,
8
7
  get_gen_code_template_var,
8
+ get_section_name,
9
9
  get_section_type,
10
10
  get_selected_section_variant,
11
11
  )
12
- from setta.code_gen.find_placeholders import remove_tp, tp
12
+ from setta.code_gen.find_placeholders import parse_template_var, tp
13
13
  from setta.code_gen.python.generate_code import (
14
14
  convert_var_names_to_readable_form,
15
15
  generate_code,
@@ -23,6 +23,7 @@ from setta.code_gen.utils import process_refs
23
23
  from setta.utils.constants import (
24
24
  CODE_FOLDER,
25
25
  CODE_FOLDER_ENV_VARIABLE,
26
+ CWD,
26
27
  USER_SETTINGS,
27
28
  C,
28
29
  )
@@ -56,13 +57,13 @@ async def runCode(message, lsp_writers):
56
57
  id_to_relpath = {}
57
58
  for sid in to_write:
58
59
  curr_code = code_dict[sid]
59
- rel_path = write_code_to_file(
60
+ rel_path_str = write_code_to_file(
60
61
  folder_path,
61
62
  curr_code["sanitized_full_name"],
62
63
  curr_code["code"],
63
64
  curr_code["codeLanguage"],
64
65
  )
65
- id_to_relpath[sid] = rel_path
66
+ id_to_relpath[sid] = rel_path_str
66
67
 
67
68
  # Only run code that isn't referenced by other code
68
69
  run_order = [
@@ -71,8 +72,8 @@ async def runCode(message, lsp_writers):
71
72
 
72
73
  # create a wrapper script if there are multiple files to run
73
74
  if len(run_order) > 1:
74
- rel_path = create_wrapper_bash_script(folder_path, run_order, code_dict)
75
- command = codeCallStr(rel_path, "bash")
75
+ rel_path_str = create_wrapper_bash_script(folder_path, run_order, code_dict)
76
+ command = codeCallStr(rel_path_str, "bash")
76
77
  else:
77
78
  sid = run_order[0]
78
79
  command = codeCallStr(id_to_relpath[sid], code_dict[sid]["codeLanguage"])
@@ -280,17 +281,41 @@ def get_template_var_replacement_value_fn(
280
281
  chars_before_template_var,
281
282
  )
282
283
  else:
283
- section_dependencies.append(template_var["sectionId"])
284
- keyword = remove_tp(keyword)
285
- return construct_module_path(folder_path, keyword)
284
+ return process_non_hardcoded_template_var(
285
+ keyword,
286
+ template_var,
287
+ exporter_obj,
288
+ section_dependencies,
289
+ folder_path,
290
+ )
291
+
286
292
  elif codeLanguage == "bash":
287
- section_dependencies.append(template_var["sectionId"])
288
- keyword = sanitize_section_path_full_name(remove_tp(keyword))
289
- return codePathStr(Path(os.path.relpath(folder_path)), keyword, "python")
293
+ return process_non_hardcoded_template_var(
294
+ keyword, template_var, exporter_obj, section_dependencies, folder_path
295
+ )
290
296
 
291
297
  return get_template_var_replacement_value
292
298
 
293
299
 
300
+ def process_non_hardcoded_template_var(
301
+ keyword, template_var, exporter_obj, section_dependencies, folder_path
302
+ ):
303
+ keyword, keyword_type = parse_template_var(keyword)
304
+ if keyword_type == C.TEMPLATE_VAR_IMPORT_PATH_SUFFIX:
305
+ section_dependencies.append(template_var["sectionId"])
306
+ return construct_module_path(folder_path, keyword)
307
+ elif keyword_type == C.TEMPLATE_VAR_VERSION_SUFFIX:
308
+ version_name = get_selected_section_variant(
309
+ exporter_obj.p, template_var["sectionId"]
310
+ )["name"]
311
+ section_name = get_section_name(exporter_obj.p, template_var["sectionId"])
312
+ return f'"{section_name}@{version_name}"'
313
+ elif keyword_type == C.TEMPLATE_VAR_FILE_PATH_SUFFIX:
314
+ section_dependencies.append(template_var["sectionId"])
315
+ keyword = sanitize_section_path_full_name(keyword)
316
+ return f'"{codePathStr(folder_path, keyword, "python")}"'
317
+
318
+
294
319
  def get_absolute_decl_position_from_rel_position(
295
320
  template_var, var_name_to_decl_rel_position_dict
296
321
  ):
@@ -337,7 +362,7 @@ def preprocess_template_vars(code, evRefs, templateVars, cursor_position):
337
362
 
338
363
 
339
364
  def convert_folder_path_to_module_path(folder_path):
340
- return os.path.relpath(folder_path).replace("/", ".").replace("\\", ".")
365
+ return folder_path.relative_to(CWD).as_posix().replace("/", ".").replace("\\", ".")
341
366
 
342
367
 
343
368
  def languageToExtension(x):
@@ -351,7 +376,7 @@ def languageToCall(x):
351
376
  def codePathStr(folder_path, filename, codeLanguage):
352
377
  extension = languageToExtension(codeLanguage)
353
378
  output = folder_path / f"{filename}{extension}"
354
- return os.path.relpath(output).replace(os.sep, "/")
379
+ return output.relative_to(CWD).as_posix()
355
380
 
356
381
 
357
382
  def codeCallStr(filepath, codeLanguage):
@@ -377,8 +402,46 @@ def prune_and_topological_sort(code_dict, to_keep):
377
402
  return topological_sort(section_dependencies), section_dependencies
378
403
 
379
404
 
405
+ # TODO: eliminate redundancy between this and prune_and_topological_sort
406
+ def prune_and_find_top_nodes(code_dict, to_keep):
407
+ section_dependencies = {k: v["section_dependencies"] for k, v in code_dict.items()}
408
+ section_dependencies = prune_dict(section_dependencies, to_keep)
409
+ return find_top_nodes(section_dependencies), section_dependencies
410
+
411
+
412
+ def get_import_order_for_top_node(top_node, dependency_dict):
413
+ # Build a subgraph consisting of top_node and all its descendants.
414
+ subgraph = get_subgraph(top_node, dependency_dict)
415
+ # Get a topologically sorted list where each dependency comes before the node that depends on it.
416
+ sorted_nodes = topological_sort(subgraph)
417
+ # Because you plan to import starting from the end of the list and work backwards,
418
+ # reverse the topological order so that the deepest dependency is imported first.
419
+ import_order = sorted_nodes[::-1]
420
+ return import_order
421
+
422
+
423
+ def find_top_nodes(dependency_dict):
424
+ all_nodes = set(dependency_dict.keys())
425
+ all_deps = {dep for deps in dependency_dict.values() for dep in deps}
426
+ return all_nodes - all_deps
427
+
428
+
429
+ def get_subgraph(top_node, dependency_dict):
430
+ visited = set()
431
+
432
+ def dfs(node):
433
+ if node not in visited:
434
+ visited.add(node)
435
+ for dep in dependency_dict.get(node, []):
436
+ dfs(dep)
437
+
438
+ dfs(top_node)
439
+ return {node: dependency_dict.get(node, []) for node in visited}
440
+
441
+
442
+ # TODO: is this function actually necessary anymore?
380
443
  def topological_sort(objects):
381
- graph = {id: refs for id, refs in objects.items()}
444
+ graph = {node: refs for node, refs in objects.items()}
382
445
  visiting, visited = set(), set()
383
446
  order = []
384
447
 
@@ -387,16 +450,15 @@ def topological_sort(objects):
387
450
  raise ValueError("Circular reference detected")
388
451
  if node not in visited:
389
452
  visiting.add(node)
390
- for neighbour in graph[node]:
391
- dfs(neighbour)
453
+ for neighbor in graph.get(node, []):
454
+ dfs(neighbor)
392
455
  visiting.remove(node)
393
456
  visited.add(node)
394
457
  order.append(node)
395
458
 
396
- for id in objects.keys():
397
- if id not in visited:
398
- dfs(id)
399
-
459
+ for node in graph.keys():
460
+ if node not in visited:
461
+ dfs(node)
400
462
  return order[::-1]
401
463
 
402
464
 
@@ -407,28 +469,19 @@ def create_timestamped_folder(base_dir, prefix=""):
407
469
  folder_name = f"_{prefix}{timestamp}"
408
470
  # Full path for the new folder
409
471
  folder_path = Path(base_dir) / folder_name
410
-
411
- if not os.path.exists(folder_path):
412
- try:
413
- os.makedirs(folder_path)
414
- except FileExistsError:
415
- pass
416
-
472
+ folder_path.mkdir(parents=True, exist_ok=True)
417
473
  return folder_path
418
474
 
419
475
 
420
476
  def write_code_to_file(folder_path, filename, code, codeLanguage):
421
477
  extension = languageToExtension(codeLanguage)
422
478
  filepath = write_string_to_file(folder_path, f"{filename}{extension}", code)
423
- return os.path.relpath(filepath).replace(os.sep, "/")
479
+ return filepath.relative_to(CWD).as_posix()
424
480
 
425
481
 
426
482
  def write_string_to_file(folder, filename, content):
427
- # Full path to the file
428
- file_path = os.path.join(folder, filename)
429
- # Write the content to the file
430
- with open(file_path, "w") as file:
431
- file.write(content)
483
+ file_path = Path(folder) / filename
484
+ file_path.write_text(content)
432
485
  return file_path
433
486
 
434
487
 
@@ -11,10 +11,6 @@ from .find_placeholders import tp
11
11
  from .utils import process_refs
12
12
 
13
13
 
14
- def get_args_var_name(var_name):
15
- return f"{C.ARGS_PREFIX}{var_name}"
16
-
17
-
18
14
  def get_section_type(p, id):
19
15
  ui_type_id = p["sections"][id]["uiTypeId"]
20
16
  ui_type = p["uiTypes"].get(ui_type_id) or BASE_UI_TYPES[ui_type_id]
@@ -144,7 +140,7 @@ def no_entered_value(value):
144
140
 
145
141
 
146
142
  def get_var_name(var_name_mapping):
147
- return f"__x{len(var_name_mapping)}"
143
+ return f"{C.ARGS_PREFIX}x{len(var_name_mapping)}"
148
144
 
149
145
 
150
146
  def get_for_section_id(p, section_id):
@@ -9,5 +9,16 @@ def remove_tp(x):
9
9
  return x.lstrip(C.TEMPLATE_PREFIX)
10
10
 
11
11
 
12
- def needs_yaml(script):
13
- return tp("SETTA_YAML_FILE") in script
12
+ def parse_template_var(template_str: str) -> tuple[str, str | None]:
13
+ suffixes = [
14
+ f"{tp(C.TEMPLATE_VAR_IMPORT_PATH_SUFFIX)}",
15
+ f"{tp(C.TEMPLATE_VAR_VERSION_SUFFIX)}",
16
+ f"{tp(C.TEMPLATE_VAR_FILE_PATH_SUFFIX)}",
17
+ ]
18
+
19
+ for suffix in suffixes:
20
+ if template_str.endswith(suffix):
21
+ base_str = remove_tp(template_str[: -len(suffix)])
22
+ return base_str, remove_tp(suffix)
23
+
24
+ return template_str, None
setta/database/backup.py CHANGED
@@ -1,5 +1,4 @@
1
1
  import logging
2
- import os
3
2
  import shutil
4
3
  import time
5
4
  from datetime import datetime
@@ -54,7 +53,7 @@ def create_backup(db_path):
54
53
  ensure_backup_dir_exists()
55
54
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
56
55
  backup_filename = f"backup_{timestamp}.db"
57
- backup_path = os.path.join(DB_BACKUP_FOLDER, backup_filename)
56
+ backup_path = DB_BACKUP_FOLDER / backup_filename
58
57
  logger.debug(f"creating backup: {backup_path}")
59
58
 
60
59
  shutil.copy2(db_path, backup_path)
@@ -35,23 +35,28 @@ def save_artifacts(db, artifacts):
35
35
  db.executemany(query, query_params)
36
36
 
37
37
 
38
- def save_artifact_groups(db, artifact_groups):
38
+ def save_artifact_groups(db, artifact_groups, sections):
39
39
  query = """
40
- INSERT INTO ArtifactGroupId (id, name, data)
41
- VALUES (:id, :name, :data)
40
+ INSERT INTO ArtifactGroupId (id, name, data, "order")
41
+ VALUES (:id, :name, :data, :order)
42
42
  ON CONFLICT (id)
43
43
  DO UPDATE SET
44
44
  name = :name,
45
- data = :data
45
+ data = :data,
46
+ "order" = :order
46
47
  """
47
48
  query_params = []
48
49
  for k, group in artifact_groups.items():
50
+ order = get_artifact_group_idx(k, sections)
51
+ if order is None:
52
+ continue
49
53
  data_keys = group.keys() - {"name", "artifactTransforms"}
50
54
  query_params.append(
51
55
  {
52
56
  "id": k,
53
57
  "name": group["name"],
54
58
  "data": json.dumps(filter_dict(group, data_keys)),
59
+ "order": order,
55
60
  }
56
61
  )
57
62
  db.executemany(query, query_params)
@@ -83,3 +88,14 @@ def save_artifact_groups(db, artifact_groups):
83
88
  )
84
89
 
85
90
  db.executemany(query, query_params)
91
+
92
+
93
+ def get_artifact_group_idx(artifact_group_id, sections):
94
+ for section in sections.values():
95
+ try:
96
+ if "artifactGroupIds" in section:
97
+ idx = section["artifactGroupIds"].index(artifact_group_id)
98
+ return idx
99
+ except ValueError:
100
+ continue
101
+ return None
@@ -25,7 +25,7 @@ def save_project_details(db, p):
25
25
  save_json_source_data(p)
26
26
  remove_json_source_data(p)
27
27
  save_artifacts(db, p["artifacts"])
28
- save_artifact_groups(db, p["artifactGroups"])
28
+ save_artifact_groups(db, p["artifactGroups"], p["sections"])
29
29
  save_code_info(db, p["codeInfo"])
30
30
  save_code_info_col(db, p["codeInfoCols"])
31
31
  save_ui_types(db, p["uiTypes"])
@@ -57,8 +57,11 @@ def load_sections(db, ids):
57
57
  SELECT
58
58
  originSectionId,
59
59
  GROUP_CONCAT(id) AS artifactGroupIds
60
- FROM
61
- ArtifactGroupId
60
+ FROM (
61
+ SELECT id, originSectionId
62
+ FROM ArtifactGroupId
63
+ ORDER BY "order"
64
+ )
62
65
  GROUP BY
63
66
  originSectionId
64
67
  ) AS A ON S.id = A.originSectionId
@@ -110,14 +110,14 @@ def save_sections(db, sections, section_variants):
110
110
  query = """
111
111
  UPDATE ArtifactGroupId
112
112
  SET originSectionId = :sectionId
113
- WHERE id = :artifactId
113
+ WHERE id = :artifactGroupId
114
114
  """
115
115
  for s in sections.values():
116
116
  for a in s["artifactGroupIds"]:
117
117
  query_params.append(
118
118
  {
119
119
  "sectionId": s["id"],
120
- "artifactId": a,
120
+ "artifactGroupId": a,
121
121
  }
122
122
  )
123
123
  db.executemany(query, query_params)
setta/database/db_objs.py CHANGED
@@ -1,6 +1,8 @@
1
1
  import queue
2
2
  import sqlite3
3
3
 
4
+ from setta.utils.constants import SETTA_FILES_FOLDER
5
+
4
6
 
5
7
  class DB:
6
8
  def __init__(self, path):
@@ -100,3 +102,7 @@ class DBQueue:
100
102
  def __exit__(self, *args, **kwargs):
101
103
  self.curr.__exit__(*args, **kwargs)
102
104
  self.put_back_curr()
105
+
106
+
107
+ def get_default_db_path():
108
+ return SETTA_FILES_FOLDER / "setta.db"
@@ -1,9 +1,9 @@
1
1
  import logging
2
- import os
3
- from pathlib import Path
4
2
 
5
3
  import yaml
6
4
 
5
+ from setta.utils.constants import SETTA_FILES_FOLDER
6
+
7
7
  logger = logging.getLogger(__name__)
8
8
 
9
9
 
@@ -26,7 +26,7 @@ def export_raw(db, filename):
26
26
  yaml.add_representer(str, literal_presenter)
27
27
 
28
28
  # For YAML export, PyYAML is used; ensure it's installed
29
- filepath = Path(os.getcwd()) / f"{filename}_export.yaml"
29
+ filepath = SETTA_FILES_FOLDER / f"{filename}_export.yaml"
30
30
  logger.debug(f"saving raw db to {filepath}")
31
31
  with open(filepath, "w") as file:
32
32
  yaml.dump(database_export, file, allow_unicode=True, sort_keys=False)
@@ -1,16 +1,15 @@
1
- import os
2
1
  from collections import defaultdict
3
- from pathlib import Path
4
2
 
5
3
  import yaml
6
4
  from yaml.representer import Representer
7
5
 
8
6
  from setta.database.db.projects.load import load_full_project
7
+ from setta.utils.constants import SETTA_FILES_FOLDER
9
8
 
10
9
 
11
10
  def get_configs_and_root_folder(db, filename):
12
11
  # export defaultdict the same way as dict
13
12
  yaml.add_representer(defaultdict, Representer.represent_dict)
14
13
  configs = load_full_project(db)
15
- root_folder = Path(os.getcwd()) / f"{filename}_export"
14
+ root_folder = SETTA_FILES_FOLDER / f"{filename}_export"
16
15
  return configs, root_folder
@@ -11,7 +11,7 @@ from setta.database.db.sections.jsonSource import (
11
11
  remove_json_source_data,
12
12
  save_json_source_data,
13
13
  )
14
- from setta.utils.constants import CONSTANTS_FOLDER, USER_SETTINGS
14
+ from setta.utils.constants import CONSTANTS_FOLDER, SETTA_FILES_FOLDER, USER_SETTINGS
15
15
  from setta.utils.utils import get_absolute_path, save_json_to_file
16
16
 
17
17
  logger = logging.getLogger(__name__)
@@ -63,8 +63,8 @@ class MetaSettingsFile:
63
63
  self.path_seed_meta_settings = get_absolute_path(
64
64
  __file__, CONSTANTS_FOLDER / "settingsProject.json"
65
65
  )
66
- self.path_default_settings = "setta-settings.json"
67
- self.path_meta_settings = "setta-meta-settings.json"
66
+ self.path_default_settings = SETTA_FILES_FOLDER / "setta-settings.json"
67
+ self.path_meta_settings = SETTA_FILES_FOLDER / "setta-meta-settings.json"
68
68
  self.load_seed_files()
69
69
 
70
70
  def get_settings_paths(self):
setta/lsp/file_watcher.py CHANGED
@@ -75,17 +75,34 @@ class LSPEventHandler(FileSystemEventHandler):
75
75
  self.callback = callback
76
76
  self.loop = loop
77
77
 
78
- def on_any_event(self, event: FileSystemEvent):
78
+ def on_created(self, event: FileSystemEvent):
79
79
  if event.is_directory:
80
80
  return
81
+ changes = [{"uri": self._path_to_uri(event.src_path), "type": 1}]
82
+ self._send_changes(changes)
81
83
 
82
- changes = [
83
- {
84
- "uri": self._path_to_uri(event.src_path),
85
- "type": self._get_change_type(event),
86
- }
87
- ]
84
+ def on_modified(self, event: FileSystemEvent):
85
+ if event.is_directory:
86
+ return
87
+ changes = [{"uri": self._path_to_uri(event.src_path), "type": 2}]
88
+ self._send_changes(changes)
89
+
90
+ def on_deleted(self, event: FileSystemEvent):
91
+ if event.is_directory:
92
+ return
93
+ changes = [{"uri": self._path_to_uri(event.src_path), "type": 3}]
94
+ self._send_changes(changes)
88
95
 
96
+ def on_moved(self, event: FileSystemEvent):
97
+ if event.is_directory:
98
+ return
99
+ # Treat move as deletion of the old path and creation of the new path.
100
+ changes_del = [{"uri": self._path_to_uri(event.src_path), "type": 3}]
101
+ changes_cre = [{"uri": self._path_to_uri(event.dest_path), "type": 1}]
102
+ self._send_changes(changes_del)
103
+ self._send_changes(changes_cre)
104
+
105
+ def _send_changes(self, changes):
89
106
  if asyncio.iscoroutinefunction(self.callback):
90
107
  self.loop.call_soon_threadsafe(
91
108
  lambda: self.loop.create_task(self.callback(changes))
@@ -94,20 +111,4 @@ class LSPEventHandler(FileSystemEventHandler):
94
111
  self.callback(changes)
95
112
 
96
113
  def _path_to_uri(self, path: str) -> str:
97
- """Convert file path to URI format."""
98
114
  return Path(path).as_uri()
99
-
100
- def _get_change_type(self, event: FileSystemEvent) -> int:
101
- """
102
- Convert watchdog event type to LSP change type.
103
- 1: Created
104
- 2: Changed
105
- 3: Deleted
106
- """
107
- if event.event_type == "created":
108
- return 1
109
- elif event.event_type == "modified":
110
- return 2
111
- elif event.event_type == "deleted":
112
- return 3
113
- return 2 # Default to changed
setta/lsp/server.py CHANGED
@@ -147,4 +147,4 @@ class LanguageServerInteractor:
147
147
  return obj["version"]
148
148
 
149
149
  def get_code_metadata(self, uri):
150
- return self.code_metadata[uri.lower()]
150
+ return self.code_metadata[uri]
setta/lsp/writer.py CHANGED
@@ -268,7 +268,7 @@ class LanguageServerWriter(LanguageServerInteractor):
268
268
  asyncio.create_task(async_task)
269
269
 
270
270
  def get_workspace_uri(self):
271
- return f"file://{self.workspace_folder}".lower()
271
+ return f"file://{self.workspace_folder}"
272
272
 
273
273
  def get_base_uri(self, code_id):
274
274
  return self.code_folder / f"{code_id}.py"
@@ -277,7 +277,7 @@ class LanguageServerWriter(LanguageServerInteractor):
277
277
  def get_uri(self, code_id, virtual):
278
278
  base_uri = self.get_base_uri(code_id)
279
279
  prefix = "untitled:" if virtual else "file://"
280
- uri = f"{prefix}{base_uri}".lower()
280
+ uri = f"{prefix}{base_uri}"
281
281
  if platform.system() == "Windows":
282
282
  uri += "/" # need trailing slash on Windows for some reason
283
283
  return uri
@@ -4,7 +4,8 @@ from pydantic import BaseModel
4
4
 
5
5
  from setta.code_gen.create_runnable_scripts import (
6
6
  generate_final_code_for_sections,
7
- prune_and_topological_sort,
7
+ get_import_order_for_top_node,
8
+ prune_and_find_top_nodes,
8
9
  sanitize_section_path_full_name,
9
10
  )
10
11
  from setta.code_gen.export_selected import (
@@ -13,6 +14,7 @@ from setta.code_gen.export_selected import (
13
14
  get_section_code,
14
15
  get_section_type,
15
16
  )
17
+ from setta.code_gen.find_placeholders import parse_template_var
16
18
  from setta.tasks.fns.utils import replace_template_vars_with_random_names
17
19
  from setta.utils.constants import C
18
20
  from setta.utils.utils import multireplace
@@ -23,7 +25,7 @@ router = APIRouter()
23
25
 
24
26
 
25
27
  class UpdateInteractiveCodeRequest(BaseModel):
26
- project: dict
28
+ projects: list
27
29
 
28
30
 
29
31
  class FormatCodeRequest(BaseModel):
@@ -37,7 +39,18 @@ async def route_update_interactive_code(
37
39
  tasks=Depends(get_tasks),
38
40
  lsp_writers=Depends(get_lsp_writers),
39
41
  ):
40
- p = x.project
42
+ idx = 0
43
+ content = []
44
+ for p in x.projects:
45
+ initialContent = await update_interactive_code(p, tasks, lsp_writers, idx)
46
+ content.extend(initialContent)
47
+ idx += 1
48
+
49
+ inMemorySubprocessInfo = tasks.getInMemorySubprocessInfo()
50
+ return {"inMemorySubprocessInfo": inMemorySubprocessInfo, "content": content}
51
+
52
+
53
+ async def update_interactive_code(p, tasks, lsp_writers, idx):
41
54
  exporter_obj = export_selected(
42
55
  p, always_export_args_objs=False, force_include_template_var=True
43
56
  )
@@ -46,11 +59,11 @@ async def route_update_interactive_code(
46
59
  template_var_replacement_values = {}
47
60
  for variant in p["sectionVariants"].values():
48
61
  for t in variant["templateVars"]:
49
- if not t["sectionId"]:
50
- continue
51
- template_var_replacement_values[
52
- t["keyword"]
53
- ] = create_in_memory_module_name(p, t["sectionId"])
62
+ _, keyword_type = parse_template_var(t["keyword"])
63
+ if t["sectionId"] and keyword_type == C.TEMPLATE_VAR_IMPORT_PATH_SUFFIX:
64
+ template_var_replacement_values[
65
+ t["keyword"]
66
+ ] = create_in_memory_module_name(p, t["sectionId"])
54
67
 
55
68
  code_dict = await generate_final_code_for_sections(
56
69
  p,
@@ -62,24 +75,29 @@ async def route_update_interactive_code(
62
75
  template_var_replacement_values=template_var_replacement_values,
63
76
  )
64
77
 
65
- to_import, _ = prune_and_topological_sort(code_dict, p["importCodeBlocks"])
66
- to_import = to_import[::-1] # we want to import the dependencies first
67
- code_list = []
68
- for section_id in to_import:
69
- v = code_dict[section_id]
70
- task_name = create_in_memory_module_name(p, section_id)
71
- code_list.append(
78
+ top_node_ids, section_dependencies = prune_and_find_top_nodes(
79
+ code_dict, p["runCodeBlocks"]
80
+ )
81
+ code_graph = []
82
+ project_config_id = p["projectConfig"]["id"]
83
+ for section_id in top_node_ids:
84
+ code_graph.append(
72
85
  {
73
- "code": v["code"],
74
- "module_name": task_name,
86
+ "subprocess_key": f"{project_config_id}-{section_id}-{idx}",
87
+ "code": code_dict[section_id]["code"],
88
+ "imports": get_import_order_for_top_node(
89
+ section_id, section_dependencies
90
+ ),
91
+ "module_name": create_in_memory_module_name(p, section_id),
75
92
  }
76
93
  )
77
94
 
78
- metadata, error_msgs, content = await tasks.add_custom_fns(
79
- code_list,
95
+ initialContent = await tasks.add_custom_fns(
96
+ code_graph,
80
97
  to_cache=exporter_obj_in_memory,
81
98
  )
82
- return {"metadata": metadata, "errorMsgs": error_msgs, "content": content}
99
+
100
+ return initialContent
83
101
 
84
102
 
85
103
  @router.post(C.ROUTE_FORMAT_CODE)