setta 0.0.2__py3-none-any.whl → 0.0.3__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.
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.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.dist-info/METADATA +0 -24
  39. {setta-0.0.2.dist-info → setta-0.0.3.dist-info}/LICENSE +0 -0
  40. {setta-0.0.2.dist-info → setta-0.0.3.dist-info}/WHEEL +0 -0
  41. {setta-0.0.2.dist-info → setta-0.0.3.dist-info}/entry_points.txt +0 -0
  42. {setta-0.0.2.dist-info → setta-0.0.3.dist-info}/top_level.txt +0 -0
setta/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.0.2"
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)