setta 0.0.1.dev0__py3-none-any.whl → 0.0.2.dev0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (265) hide show
  1. setta/__init__.py +1 -1
  2. setta/cli/__init__.py +1 -0
  3. setta/cli/connect.py +43 -0
  4. setta/cli/logger.py +225 -0
  5. setta/code_gen/__init__.py +0 -0
  6. setta/code_gen/create_runnable_scripts.py +466 -0
  7. setta/code_gen/export_selected.py +776 -0
  8. setta/code_gen/find_placeholders.py +13 -0
  9. setta/code_gen/python/__init__.py +0 -0
  10. setta/code_gen/python/ast_utils.py +183 -0
  11. setta/code_gen/python/check_scope.py +187 -0
  12. setta/code_gen/python/generate_code.py +280 -0
  13. setta/code_gen/python/make_parseable.py +97 -0
  14. setta/code_gen/python/position_line_col.py +33 -0
  15. setta/code_gen/python/validate_imports.py +87 -0
  16. setta/code_gen/utils.py +120 -0
  17. setta/code_gen/yaml/__init__.py +0 -0
  18. setta/code_gen/yaml/generate_yaml.py +23 -0
  19. setta/code_gen/yaml/section_dict.py +93 -0
  20. setta/database/__init__.py +0 -0
  21. setta/database/backup.py +80 -0
  22. setta/database/db/__init__.py +0 -0
  23. setta/database/db/artifacts/__init__.py +0 -0
  24. setta/database/db/artifacts/load.py +93 -0
  25. setta/database/db/artifacts/save.py +85 -0
  26. setta/database/db/artifacts/save_or_create.py +68 -0
  27. setta/database/db/artifacts/utils.py +13 -0
  28. setta/database/db/codeInfo/__init__.py +0 -0
  29. setta/database/db/codeInfo/copy.py +26 -0
  30. setta/database/db/codeInfo/load.py +65 -0
  31. setta/database/db/codeInfo/save.py +75 -0
  32. setta/database/db/codeInfo/utils.py +33 -0
  33. setta/database/db/evRefs/__init__.py +0 -0
  34. setta/database/db/evRefs/load.py +45 -0
  35. setta/database/db/evRefs/save.py +95 -0
  36. setta/database/db/projects/__init__.py +0 -0
  37. setta/database/db/projects/copy.py +36 -0
  38. setta/database/db/projects/delete.py +7 -0
  39. setta/database/db/projects/load.py +184 -0
  40. setta/database/db/projects/save.py +267 -0
  41. setta/database/db/projects/saveAs.py +40 -0
  42. setta/database/db/projects/utils.py +135 -0
  43. setta/database/db/sectionVariants/__init__.py +0 -0
  44. setta/database/db/sectionVariants/copy.py +28 -0
  45. setta/database/db/sectionVariants/load.py +139 -0
  46. setta/database/db/sectionVariants/save.py +140 -0
  47. setta/database/db/sectionVariants/utils.py +44 -0
  48. setta/database/db/sections/__init__.py +0 -0
  49. setta/database/db/sections/copy.py +70 -0
  50. setta/database/db/sections/jsonSource.py +119 -0
  51. setta/database/db/sections/load.py +350 -0
  52. setta/database/db/sections/save.py +204 -0
  53. setta/database/db/sections/utils.py +13 -0
  54. setta/database/db/uiTypes/__init__.py +0 -0
  55. setta/database/db/uiTypes/copy.py +33 -0
  56. setta/database/db/uiTypes/load.py +51 -0
  57. setta/database/db/uiTypes/save.py +99 -0
  58. setta/database/db/uiTypes/utils.py +27 -0
  59. setta/database/db_init.py +36 -0
  60. setta/database/db_objs.py +102 -0
  61. setta/database/db_path.py +8 -0
  62. setta/database/export_db/__init__.py +0 -0
  63. setta/database/export_db/export_db.py +43 -0
  64. setta/database/export_db/export_raw.py +53 -0
  65. setta/database/export_db/export_readable.py +242 -0
  66. setta/database/export_db/utils.py +16 -0
  67. setta/database/import_db.py +28 -0
  68. setta/database/seed.py +41 -0
  69. setta/database/settings_file.py +118 -0
  70. setta/database/utils.py +32 -0
  71. setta/lsp/__init__.py +0 -0
  72. setta/lsp/file_watcher.py +113 -0
  73. setta/lsp/reader.py +184 -0
  74. setta/lsp/reader_fns/__init__.py +0 -0
  75. setta/lsp/reader_fns/completion.py +84 -0
  76. setta/lsp/reader_fns/definition.py +2 -0
  77. setta/lsp/reader_fns/diagnostics.py +99 -0
  78. setta/lsp/reader_fns/documentHighlight.py +25 -0
  79. setta/lsp/reader_fns/references.py +34 -0
  80. setta/lsp/reader_fns/signatureHelp.py +99 -0
  81. setta/lsp/server.py +150 -0
  82. setta/lsp/utils.py +60 -0
  83. setta/lsp/writer.py +306 -0
  84. setta/routers/__init__.py +11 -0
  85. setta/routers/artifact.py +105 -0
  86. setta/routers/code_info.py +32 -0
  87. setta/routers/dependencies.py +49 -0
  88. setta/routers/in_memory_fn_stdout_websocket.py +21 -0
  89. setta/routers/interactive.py +119 -0
  90. setta/routers/lsp.py +14 -0
  91. setta/routers/projects.py +188 -0
  92. setta/routers/reference_renaming.py +111 -0
  93. setta/routers/sections.py +174 -0
  94. setta/routers/settings.py +40 -0
  95. setta/routers/terminals.py +83 -0
  96. setta/routers/websocket.py +36 -0
  97. setta/server.py +141 -0
  98. setta/start.py +112 -0
  99. setta/static/constants/BaseUITypes.json +153 -0
  100. setta/static/constants/Settings.json +113 -0
  101. setta/static/constants/constants.json +117 -0
  102. setta/static/constants/db_init.sql +249 -0
  103. setta/static/constants/defaultValues.json +125 -0
  104. setta/static/constants/settingsProject.json +276 -0
  105. setta/static/frontend/android-chrome-192x192.png +0 -0
  106. setta/static/frontend/android-chrome-512x512.png +0 -0
  107. setta/static/frontend/apple-touch-icon.png +0 -0
  108. setta/static/frontend/assets/KaTeX_AMS-Regular-0cdd387c.woff2 +0 -0
  109. setta/static/frontend/assets/KaTeX_AMS-Regular-30da91e8.woff +0 -0
  110. setta/static/frontend/assets/KaTeX_AMS-Regular-68534840.ttf +0 -0
  111. setta/static/frontend/assets/KaTeX_Caligraphic-Bold-07d8e303.ttf +0 -0
  112. setta/static/frontend/assets/KaTeX_Caligraphic-Bold-1ae6bd74.woff +0 -0
  113. setta/static/frontend/assets/KaTeX_Caligraphic-Bold-de7701e4.woff2 +0 -0
  114. setta/static/frontend/assets/KaTeX_Caligraphic-Regular-3398dd02.woff +0 -0
  115. setta/static/frontend/assets/KaTeX_Caligraphic-Regular-5d53e70a.woff2 +0 -0
  116. setta/static/frontend/assets/KaTeX_Caligraphic-Regular-ed0b7437.ttf +0 -0
  117. setta/static/frontend/assets/KaTeX_Fraktur-Bold-74444efd.woff2 +0 -0
  118. setta/static/frontend/assets/KaTeX_Fraktur-Bold-9163df9c.ttf +0 -0
  119. setta/static/frontend/assets/KaTeX_Fraktur-Bold-9be7ceb8.woff +0 -0
  120. setta/static/frontend/assets/KaTeX_Fraktur-Regular-1e6f9579.ttf +0 -0
  121. setta/static/frontend/assets/KaTeX_Fraktur-Regular-51814d27.woff2 +0 -0
  122. setta/static/frontend/assets/KaTeX_Fraktur-Regular-5e28753b.woff +0 -0
  123. setta/static/frontend/assets/KaTeX_Main-Bold-0f60d1b8.woff2 +0 -0
  124. setta/static/frontend/assets/KaTeX_Main-Bold-138ac28d.ttf +0 -0
  125. setta/static/frontend/assets/KaTeX_Main-Bold-c76c5d69.woff +0 -0
  126. setta/static/frontend/assets/KaTeX_Main-BoldItalic-70ee1f64.ttf +0 -0
  127. setta/static/frontend/assets/KaTeX_Main-BoldItalic-99cd42a3.woff2 +0 -0
  128. setta/static/frontend/assets/KaTeX_Main-BoldItalic-a6f7ec0d.woff +0 -0
  129. setta/static/frontend/assets/KaTeX_Main-Italic-0d85ae7c.ttf +0 -0
  130. setta/static/frontend/assets/KaTeX_Main-Italic-97479ca6.woff2 +0 -0
  131. setta/static/frontend/assets/KaTeX_Main-Italic-f1d6ef86.woff +0 -0
  132. setta/static/frontend/assets/KaTeX_Main-Regular-c2342cd8.woff2 +0 -0
  133. setta/static/frontend/assets/KaTeX_Main-Regular-c6368d87.woff +0 -0
  134. setta/static/frontend/assets/KaTeX_Main-Regular-d0332f52.ttf +0 -0
  135. setta/static/frontend/assets/KaTeX_Math-BoldItalic-850c0af5.woff +0 -0
  136. setta/static/frontend/assets/KaTeX_Math-BoldItalic-dc47344d.woff2 +0 -0
  137. setta/static/frontend/assets/KaTeX_Math-BoldItalic-f9377ab0.ttf +0 -0
  138. setta/static/frontend/assets/KaTeX_Math-Italic-08ce98e5.ttf +0 -0
  139. setta/static/frontend/assets/KaTeX_Math-Italic-7af58c5e.woff2 +0 -0
  140. setta/static/frontend/assets/KaTeX_Math-Italic-8a8d2445.woff +0 -0
  141. setta/static/frontend/assets/KaTeX_SansSerif-Bold-1ece03f7.ttf +0 -0
  142. setta/static/frontend/assets/KaTeX_SansSerif-Bold-e99ae511.woff2 +0 -0
  143. setta/static/frontend/assets/KaTeX_SansSerif-Bold-ece03cfd.woff +0 -0
  144. setta/static/frontend/assets/KaTeX_SansSerif-Italic-00b26ac8.woff2 +0 -0
  145. setta/static/frontend/assets/KaTeX_SansSerif-Italic-3931dd81.ttf +0 -0
  146. setta/static/frontend/assets/KaTeX_SansSerif-Italic-91ee6750.woff +0 -0
  147. setta/static/frontend/assets/KaTeX_SansSerif-Regular-11e4dc8a.woff +0 -0
  148. setta/static/frontend/assets/KaTeX_SansSerif-Regular-68e8c73e.woff2 +0 -0
  149. setta/static/frontend/assets/KaTeX_SansSerif-Regular-f36ea897.ttf +0 -0
  150. setta/static/frontend/assets/KaTeX_Script-Regular-036d4e95.woff2 +0 -0
  151. setta/static/frontend/assets/KaTeX_Script-Regular-1c67f068.ttf +0 -0
  152. setta/static/frontend/assets/KaTeX_Script-Regular-d96cdf2b.woff +0 -0
  153. setta/static/frontend/assets/KaTeX_Size1-Regular-6b47c401.woff2 +0 -0
  154. setta/static/frontend/assets/KaTeX_Size1-Regular-95b6d2f1.ttf +0 -0
  155. setta/static/frontend/assets/KaTeX_Size1-Regular-c943cc98.woff +0 -0
  156. setta/static/frontend/assets/KaTeX_Size2-Regular-2014c523.woff +0 -0
  157. setta/static/frontend/assets/KaTeX_Size2-Regular-a6b2099f.ttf +0 -0
  158. setta/static/frontend/assets/KaTeX_Size2-Regular-d04c5421.woff2 +0 -0
  159. setta/static/frontend/assets/KaTeX_Size3-Regular-500e04d5.ttf +0 -0
  160. setta/static/frontend/assets/KaTeX_Size3-Regular-6ab6b62e.woff +0 -0
  161. setta/static/frontend/assets/KaTeX_Size4-Regular-99f9c675.woff +0 -0
  162. setta/static/frontend/assets/KaTeX_Size4-Regular-a4af7d41.woff2 +0 -0
  163. setta/static/frontend/assets/KaTeX_Size4-Regular-c647367d.ttf +0 -0
  164. setta/static/frontend/assets/KaTeX_Typewriter-Regular-71d517d6.woff2 +0 -0
  165. setta/static/frontend/assets/KaTeX_Typewriter-Regular-e14fed02.woff +0 -0
  166. setta/static/frontend/assets/KaTeX_Typewriter-Regular-f01f3e87.ttf +0 -0
  167. setta/static/frontend/assets/cormorant-garamond-all-700-italic-c9b58582.woff +0 -0
  168. setta/static/frontend/assets/cormorant-garamond-cyrillic-700-italic-9101ad5f.woff2 +0 -0
  169. setta/static/frontend/assets/cormorant-garamond-cyrillic-ext-700-italic-950de0d6.woff2 +0 -0
  170. setta/static/frontend/assets/cormorant-garamond-latin-700-italic-0bc53e12.woff2 +0 -0
  171. setta/static/frontend/assets/cormorant-garamond-latin-ext-700-italic-525738e0.woff2 +0 -0
  172. setta/static/frontend/assets/cormorant-garamond-vietnamese-700-italic-99563037.woff2 +0 -0
  173. setta/static/frontend/assets/erase-5e0448ea.svg +15 -0
  174. setta/static/frontend/assets/index-1d4b4ecf.css +32 -0
  175. setta/static/frontend/assets/index-ee99dc72.js +678 -0
  176. setta/static/frontend/assets/inter-all-400-normal-054f12d0.woff +0 -0
  177. setta/static/frontend/assets/inter-all-600-normal-c03769e5.woff +0 -0
  178. setta/static/frontend/assets/inter-all-800-normal-15dc6e4b.woff +0 -0
  179. setta/static/frontend/assets/inter-cyrillic-400-normal-a4eee61a.woff2 +0 -0
  180. setta/static/frontend/assets/inter-cyrillic-600-normal-8b14f703.woff2 +0 -0
  181. setta/static/frontend/assets/inter-cyrillic-800-normal-e706eaaa.woff2 +0 -0
  182. setta/static/frontend/assets/inter-cyrillic-ext-400-normal-70047a3b.woff2 +0 -0
  183. setta/static/frontend/assets/inter-cyrillic-ext-600-normal-d4ab9bc4.woff2 +0 -0
  184. setta/static/frontend/assets/inter-cyrillic-ext-800-normal-eae7515a.woff2 +0 -0
  185. setta/static/frontend/assets/inter-greek-400-normal-381ea30d.woff2 +0 -0
  186. setta/static/frontend/assets/inter-greek-600-normal-601f93a2.woff2 +0 -0
  187. setta/static/frontend/assets/inter-greek-800-normal-7af4fb64.woff2 +0 -0
  188. setta/static/frontend/assets/inter-greek-ext-400-normal-27027b17.woff2 +0 -0
  189. setta/static/frontend/assets/inter-greek-ext-600-normal-f2ddf9de.woff2 +0 -0
  190. setta/static/frontend/assets/inter-greek-ext-800-normal-4cb6189e.woff2 +0 -0
  191. setta/static/frontend/assets/inter-latin-400-normal-d56fec21.woff2 +0 -0
  192. setta/static/frontend/assets/inter-latin-600-normal-ff769fa6.woff2 +0 -0
  193. setta/static/frontend/assets/inter-latin-800-normal-5eea1309.woff2 +0 -0
  194. setta/static/frontend/assets/inter-latin-ext-400-normal-bb698d85.woff2 +0 -0
  195. setta/static/frontend/assets/inter-latin-ext-600-normal-ca4808f9.woff2 +0 -0
  196. setta/static/frontend/assets/inter-latin-ext-800-normal-ebdacc0f.woff2 +0 -0
  197. setta/static/frontend/assets/logo/logo.svg +8 -0
  198. setta/static/frontend/assets/pen-455d7d8a.svg +19 -0
  199. setta/static/frontend/assets/source-code-pro-all-500-normal-6bdaa03b.woff +0 -0
  200. setta/static/frontend/assets/source-code-pro-cyrillic-500-normal-288a0d68.woff2 +0 -0
  201. setta/static/frontend/assets/source-code-pro-cyrillic-ext-500-normal-b110a13b.woff2 +0 -0
  202. setta/static/frontend/assets/source-code-pro-greek-500-normal-04328acb.woff2 +0 -0
  203. setta/static/frontend/assets/source-code-pro-latin-500-normal-06edef1e.woff2 +0 -0
  204. setta/static/frontend/assets/source-code-pro-latin-ext-500-normal-6dc60d5e.woff2 +0 -0
  205. setta/static/frontend/assets/web-vitals-44a8e082.js +1 -0
  206. setta/static/frontend/assets/work-sans-all-400-normal-38034a3c.woff +0 -0
  207. setta/static/frontend/assets/work-sans-all-500-normal-550d64e5.woff +0 -0
  208. setta/static/frontend/assets/work-sans-all-600-normal-ccf14060.woff +0 -0
  209. setta/static/frontend/assets/work-sans-all-700-normal-494c2971.woff +0 -0
  210. setta/static/frontend/assets/work-sans-latin-400-normal-36735bc1.woff2 +0 -0
  211. setta/static/frontend/assets/work-sans-latin-500-normal-3790bfda.woff2 +0 -0
  212. setta/static/frontend/assets/work-sans-latin-600-normal-5fba494e.woff2 +0 -0
  213. setta/static/frontend/assets/work-sans-latin-700-normal-a5033d0a.woff2 +0 -0
  214. setta/static/frontend/assets/work-sans-latin-ext-400-normal-c20f571a.woff2 +0 -0
  215. setta/static/frontend/assets/work-sans-latin-ext-500-normal-0f5ac96c.woff2 +0 -0
  216. setta/static/frontend/assets/work-sans-latin-ext-600-normal-97a237d1.woff2 +0 -0
  217. setta/static/frontend/assets/work-sans-latin-ext-700-normal-103e112c.woff2 +0 -0
  218. setta/static/frontend/browserconfig.xml +9 -0
  219. setta/static/frontend/favicon-16x16.png +0 -0
  220. setta/static/frontend/favicon-32x32.png +0 -0
  221. setta/static/frontend/favicon.ico +0 -0
  222. setta/static/frontend/index.html +30 -0
  223. setta/static/frontend/manifest.json +25 -0
  224. setta/static/frontend/mstile-144x144.png +0 -0
  225. setta/static/frontend/mstile-150x150.png +0 -0
  226. setta/static/frontend/mstile-310x150.png +0 -0
  227. setta/static/frontend/mstile-310x310.png +0 -0
  228. setta/static/frontend/mstile-70x70.png +0 -0
  229. setta/static/frontend/robots.txt +3 -0
  230. setta/static/frontend/safari-pinned-tab.svg +18 -0
  231. setta/static/frontend/site.webmanifest +19 -0
  232. setta/static/seed/.DS_Store +0 -0
  233. setta/static/seed/examples/.DS_Store +0 -0
  234. setta/tasks/__init__.py +0 -0
  235. setta/tasks/fns/__init__.py +9 -0
  236. setta/tasks/fns/codeAreaAutocomplete.py +209 -0
  237. setta/tasks/fns/codeAreaFindTemplateVars.py +128 -0
  238. setta/tasks/fns/codeAreaInitializeCode.py +98 -0
  239. setta/tasks/fns/findEVRefs.py +236 -0
  240. setta/tasks/fns/parametersRequest.py +71 -0
  241. setta/tasks/fns/textFieldAutocomplete.py +210 -0
  242. setta/tasks/fns/textFieldInitializeCode.py +99 -0
  243. setta/tasks/fns/typeCheck.py +40 -0
  244. setta/tasks/fns/utils.py +134 -0
  245. setta/tasks/task_runner.py +29 -0
  246. setta/tasks/tasks.py +152 -0
  247. setta/tasks/utils.py +178 -0
  248. setta/terminals/__init__.py +0 -0
  249. setta/terminals/terminals.py +242 -0
  250. setta/terminals/utils.py +37 -0
  251. setta/utils/__init__.py +0 -0
  252. setta/utils/constants.py +148 -0
  253. setta/utils/generate_memorable_string.py +431 -0
  254. setta/utils/generate_new_filename.py +80 -0
  255. setta/utils/section_contents.py +133 -0
  256. setta/utils/utils.py +271 -0
  257. setta/utils/websocket_manager.py +91 -0
  258. setta-0.0.2.dev0.dist-info/LICENSE +201 -0
  259. setta-0.0.2.dev0.dist-info/METADATA +24 -0
  260. setta-0.0.2.dev0.dist-info/RECORD +263 -0
  261. {setta-0.0.1.dev0.dist-info → setta-0.0.2.dev0.dist-info}/WHEEL +1 -1
  262. setta-0.0.2.dev0.dist-info/entry_points.txt +2 -0
  263. setta-0.0.1.dev0.dist-info/METADATA +0 -18
  264. setta-0.0.1.dev0.dist-info/RECORD +0 -5
  265. {setta-0.0.1.dev0.dist-info → setta-0.0.2.dev0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,97 @@
1
+ import ast
2
+ import re
3
+
4
+
5
+ def make_parseable(code: str, var_name: str) -> str:
6
+ """
7
+ Make minimal modifications to a potentially incomplete Python variable declaration
8
+ to make it parseable by ast.parse.
9
+
10
+ Args:
11
+ code (str): The potentially incomplete Python code
12
+
13
+ Returns:
14
+ str: Modified code that can be parsed by ast.parse
15
+ """
16
+ # No need to extract var_name as it's passed in
17
+ if is_parseable(code):
18
+ return code
19
+
20
+ code = code.strip()
21
+
22
+ # Strategy 1: Try removing the last character if it's a bracket or operator
23
+ if code[-1] in "([{=+-*/,":
24
+ modified = code[:-1]
25
+ if is_parseable(modified):
26
+ return modified
27
+
28
+ # Strategy 2: Try balancing parentheses/brackets/braces
29
+ opening = "([{"
30
+ closing = ")]}"
31
+ brackets_map = dict(zip(opening, closing))
32
+ stack = []
33
+
34
+ for char in code:
35
+ if char in opening:
36
+ stack.append(char)
37
+ elif char in closing:
38
+ if not stack or brackets_map[stack[-1]] != char:
39
+ # Mismatched bracket - remove this character
40
+ modified = code.replace(char, "", 1)
41
+ if is_parseable(modified):
42
+ return modified
43
+ else:
44
+ stack.pop()
45
+
46
+ # Add missing closing brackets
47
+ while stack:
48
+ code += brackets_map[stack.pop()]
49
+ if is_parseable(code):
50
+ return code
51
+
52
+ # Strategy 3: Handle incomplete string literals
53
+ if re.search(r'[\'"]', code):
54
+ # Remove incomplete string if quote is the last character
55
+ if code[-1] in "\"'":
56
+ modified = code[:-1]
57
+ if is_parseable(modified):
58
+ return modified
59
+ # Try to close the string
60
+ for quote in ['"', "'"]:
61
+ modified = code + quote
62
+ if is_parseable(modified):
63
+ return modified
64
+
65
+ # Strategy 4: Remove trailing operators
66
+ operators = ["+", "-", "*", "/", "=", "&", "|", "^", "%"]
67
+ for op in operators:
68
+ if code.rstrip().endswith(op):
69
+ modified = code.rstrip()[:-1]
70
+ if is_parseable(modified):
71
+ return modified
72
+
73
+ # If nothing else works, try removing characters from the end until it parses
74
+ while len(code) > 0:
75
+ if is_parseable(code):
76
+ return code
77
+ code = code[:-1]
78
+
79
+ # this means it failed
80
+ return None
81
+
82
+
83
+ def is_parseable(code: str) -> bool:
84
+ """
85
+ Check if the given code can be parsed by ast.parse
86
+
87
+ Args:
88
+ code (str): Python code to check
89
+
90
+ Returns:
91
+ bool: True if code can be parsed, False otherwise
92
+ """
93
+ try:
94
+ ast.parse(code)
95
+ return True
96
+ except SyntaxError:
97
+ return False
@@ -0,0 +1,33 @@
1
+ def position_to_line_col(text, position):
2
+ if position < 0 or position > len(text):
3
+ return None
4
+
5
+ lines = text.split("\n")
6
+ current_pos = 0
7
+
8
+ for line_num, line in enumerate(lines):
9
+ line_length = len(line) + 1 # +1 for the newline character
10
+ if current_pos + line_length > position:
11
+ col = position - current_pos
12
+ return line_num, col
13
+ current_pos += line_length
14
+
15
+ # If we get here, the position is at the very end of the text
16
+ return len(lines) - 1, len(lines[-1])
17
+
18
+
19
+ def line_col_to_position_batch(text, line_col_pairs):
20
+ lines = text.split("\n")
21
+ line_starts = [0]
22
+ for line in lines[:-1]:
23
+ line_starts.append(line_starts[-1] + len(line) + 1)
24
+
25
+ results = []
26
+ for line, col in line_col_pairs:
27
+ if line < 0 or line >= len(lines) or col < 0:
28
+ results.append(None)
29
+ else:
30
+ position = line_starts[line] + min(col, len(lines[line]))
31
+ results.append(position)
32
+
33
+ return results
@@ -0,0 +1,87 @@
1
+ import asyncio
2
+ import copy
3
+ import logging
4
+
5
+ from setta.code_gen.export_selected import get_specific_template_var
6
+ from setta.code_gen.find_placeholders import tp
7
+ from setta.database.utils import create_new_id
8
+ from setta.utils.constants import C
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ async def maybe_validate_imports_and_update_code(
14
+ sectionId, code, ref_template_var_positions, lsp_writers
15
+ ):
16
+ templateVars = ref_template_var_positions["templateVars"]
17
+ generated_imports_info, idx = get_specific_template_var(
18
+ templateVars, tp(C.SETTA_GENERATED_PYTHON_IMPORTS), return_index=True
19
+ )
20
+ if not generated_imports_info:
21
+ return code, templateVars
22
+ old_value = generated_imports_info["value"]
23
+ new_value, did_change = await validate_imports(sectionId, old_value, lsp_writers)
24
+ if not did_change:
25
+ return code, templateVars
26
+
27
+ # this shouldn't happen
28
+ if len(new_value) != len(old_value):
29
+ logger.debug("new imports have different length from old imports")
30
+ return code, templateVars
31
+
32
+ templateVars = copy.deepcopy(ref_template_var_positions["templateVars"])
33
+ generated_imports_info = templateVars[idx]
34
+ generated_imports_info["value"] = new_value
35
+ startPos = generated_imports_info["startPos"]
36
+ endPos = generated_imports_info["startPos"] + len(new_value)
37
+ code = code[:startPos] + new_value + code[endPos:]
38
+ return code, templateVars
39
+
40
+
41
+ async def validate_imports(sectionId, generated_imports, lsp_writers):
42
+ if not generated_imports:
43
+ return generated_imports, False
44
+
45
+ code_id = f"{sectionId}-validate-imports"
46
+ await lsp_writers["full"].send_document_open_request(
47
+ code_id=code_id, code=generated_imports, virtual=True
48
+ )
49
+
50
+ lines = generated_imports.split("\n")
51
+ message_id = create_new_id()
52
+
53
+ # First, send all requests and store the futures
54
+ request_futures = []
55
+ line_indices = [] # Keep track of which line each request is for
56
+ for idx, line in enumerate(lines):
57
+ if not line:
58
+ continue
59
+ request_id = f"{message_id}-{idx}"
60
+ # Send request without awaiting
61
+ lsp_writers["full"].create_asyncio_task(
62
+ lsp_writers["full"].send_definition_request(
63
+ idx, len(line) - 1, code_id, request_id, virtual=True
64
+ ),
65
+ request_id,
66
+ local=True,
67
+ )
68
+ # Store the future for waiting later
69
+ future = lsp_writers["full"].wait_for_response(request_id)
70
+ request_futures.append(future)
71
+ line_indices.append(idx) # Store the original line index
72
+
73
+ # Then await all responses concurrently
74
+ responses = await asyncio.gather(*request_futures)
75
+
76
+ did_change = False
77
+ # Use line_indices to map responses back to correct lines
78
+ for response_idx, response in enumerate(responses):
79
+ original_idx = line_indices[response_idx]
80
+ if not response["data"]:
81
+ # replace with equal length comment so that we don't have
82
+ # to change any position information
83
+ lines[original_idx] = "#" * len(lines[original_idx])
84
+ did_change = True
85
+
86
+ generated_imports = "\n".join(lines)
87
+ return generated_imports, did_change
@@ -0,0 +1,120 @@
1
+ import copy
2
+
3
+
4
+ def push_var_deep(objects, target_var_name):
5
+ def find_dependents(obj_list, var_name):
6
+ """Find all objects that directly or indirectly depend on the given object."""
7
+ dependents = set()
8
+ for obj in obj_list:
9
+ if var_name in obj["dependencies"]:
10
+ dependents.add(obj["var_name"])
11
+ dependents.update(find_dependents(obj_list, obj["var_name"]))
12
+ return dependents
13
+
14
+ dependents = find_dependents(objects, target_var_name)
15
+ everything_else = []
16
+ target_and_dependents = []
17
+ for obj in objects:
18
+ if obj["var_name"] != target_var_name and obj["var_name"] not in dependents:
19
+ everything_else.append(obj)
20
+ else:
21
+ target_and_dependents.append(obj)
22
+
23
+ return everything_else + target_and_dependents
24
+
25
+
26
+ def process_refs(
27
+ code,
28
+ refs,
29
+ get_ref_var_name,
30
+ cursor_position=None,
31
+ templateVars=None,
32
+ get_template_var_replacement_value=None,
33
+ ):
34
+ positions = {
35
+ "refs": [], # list of dicts, where each dict is {"keyword", "startPos", "value"}
36
+ "templateVars": [], # list of dicts, where each tuple is {"keyword", "startPos", "value"}
37
+ "cursor": cursor_position,
38
+ }
39
+ if templateVars is None:
40
+ templateVars = []
41
+
42
+ refs = copy.deepcopy(refs)
43
+ templateVars = copy.deepcopy(templateVars)
44
+ refs_set = set(id(x) for x in refs)
45
+ to_process = sorted(refs + templateVars, key=lambda x: x["startPos"])
46
+ args = [
47
+ positions,
48
+ to_process,
49
+ refs_set,
50
+ get_ref_var_name,
51
+ get_template_var_replacement_value,
52
+ ]
53
+ code = process_refs_helper(code, *args, process_last_ones=False)
54
+ code = process_refs_helper(code, *args, process_last_ones=True)
55
+ return code, positions
56
+
57
+
58
+ def process_refs_helper(
59
+ code,
60
+ positions,
61
+ to_process,
62
+ refs_set,
63
+ get_ref_var_name,
64
+ get_template_var_replacement_value,
65
+ process_last_ones,
66
+ ):
67
+ input_positions_len = {k: len(v) for k, v in positions.items() if k != "cursor"}
68
+
69
+ for i, x in enumerate(to_process):
70
+ if x.get("processLast", False) != process_last_ones:
71
+ continue
72
+
73
+ if id(x) in refs_set:
74
+ replacement_value = get_ref_var_name(
75
+ x["sectionId"], x["paramInfoId"], x["isArgsObj"]
76
+ )
77
+ type = "refs"
78
+ else:
79
+ replacement_value = get_template_var_replacement_value(code, x, positions)
80
+ type = "templateVars"
81
+
82
+ startPos = x["startPos"]
83
+ endPos = startPos + len(x["keyword"])
84
+ length_diff = len(replacement_value) - (endPos - startPos)
85
+
86
+ # Update positions for all future replacements
87
+ for future_x in to_process[i + 1 :]:
88
+ future_x["startPos"] += length_diff
89
+
90
+ # Update positions for all positions that already existed when this function was called.
91
+ update_existing_positions(positions, input_positions_len, startPos, length_diff)
92
+
93
+ if positions["cursor"] is not None:
94
+ positions["cursor"] = get_new_cursor_position(
95
+ positions["cursor"], length_diff, startPos, endPos
96
+ )
97
+
98
+ positions[type].append(
99
+ {"keyword": x["keyword"], "startPos": startPos, "value": replacement_value}
100
+ )
101
+ code = code[:startPos] + replacement_value + code[endPos:]
102
+
103
+ return code
104
+
105
+
106
+ def update_existing_positions(positions, input_positions_len, startPos, length_diff):
107
+ for k, v in input_positions_len.items():
108
+ for i in range(v):
109
+ if startPos < positions[k][i]["startPos"]:
110
+ positions[k][i]["startPos"] += length_diff
111
+
112
+
113
+ def get_new_cursor_position(cursor_position, length_diff, startPos, endPos):
114
+ # Update position if replacement occurs before the initial position
115
+ if endPos <= cursor_position:
116
+ # All changes before the position affect its offset
117
+ return cursor_position + length_diff
118
+ elif startPos < cursor_position < endPos:
119
+ return endPos + length_diff # Reset position to after the new substring
120
+ return cursor_position
File without changes
@@ -0,0 +1,23 @@
1
+ import yaml
2
+
3
+
4
+ def generate_yaml(e):
5
+ transformed_dict = {}
6
+ for key, value in e.items():
7
+ if "value" in value and isinstance(value["value"], dict):
8
+ # Handle nested structure for keys like 'dataset' and 'algorithm'
9
+ callable_value = value["value"]["callable"]
10
+ params = value["value"].get("params", [])
11
+ param_dict = {}
12
+ for param in params:
13
+ param_value = e[param]["value"]
14
+ # Resolve references to other keys (like 'n_samples' for 'p0')
15
+ if param_value in e:
16
+ param_value = e[param_value]["value"]
17
+ param_dict[e[param]["name"]] = param_value
18
+ transformed_dict[key] = {callable_value: param_dict}
19
+ elif not value["is_global"]:
20
+ # Directly use top-level keys and values
21
+ transformed_dict[key] = value["value"]
22
+
23
+ return yaml.safe_dump(transformed_dict)
@@ -0,0 +1,93 @@
1
+ import yaml
2
+
3
+
4
+ def section_dict_to_yaml(exporter_objs, codeInfo, sectionVariant):
5
+ pinned = exporter_objs["pinned"].prettyOutput
6
+ unpinned = exporter_objs["unpinned"].prettyOutput
7
+ pinnedStr = raw_dump(pinned)
8
+ unpinnedStr = raw_dump(unpinned)
9
+
10
+ yamlValue = ""
11
+ if sectionVariant["selectedItem"]:
12
+ selectedItemName = codeInfo[sectionVariant["selectedItem"]]["name"]
13
+ yamlValue += f"{selectedItemName}\n---\n"
14
+
15
+ if len(pinned) > 0:
16
+ yamlValue += f"{pinnedStr}\n---\n"
17
+ elif yamlValue != "":
18
+ yamlValue += "\n---\n"
19
+ if len(unpinned) > 0:
20
+ yamlValue += unpinnedStr
21
+
22
+ return yamlValue
23
+
24
+
25
+ def param_sweep_dict_to_yaml(exporter_obj):
26
+ return "\n---\n".join(raw_dump(x) for x in exporter_obj.prettyOutput)
27
+
28
+
29
+ def run_group_dict_to_yaml(exporter_obj):
30
+ if len(exporter_obj.prettyOutput) == 0:
31
+ return "{}"
32
+ return yaml.dump(
33
+ exporter_obj.prettyOutput,
34
+ Dumper=RunGroupDumper,
35
+ explicit_end=False,
36
+ sort_keys=False,
37
+ )
38
+
39
+
40
+ # Register representer for dict that skips empty ones
41
+ def represent_dict_skip_empty(dumper, data):
42
+ if not data: # if dictionary is empty
43
+ return dumper.represent_scalar("tag:yaml.org,2002:null", "")
44
+ return dumper.represent_dict(data)
45
+
46
+
47
+ class RunGroupDumper(yaml.Dumper):
48
+ pass
49
+
50
+
51
+ RunGroupDumper.add_representer(dict, represent_dict_skip_empty)
52
+
53
+
54
+ def raw_dump(input_data, indent=0):
55
+ output = ""
56
+
57
+ # Handle list of dictionaries or other lists
58
+ if isinstance(input_data, list):
59
+ for item in input_data:
60
+ output += " " * indent + "-"
61
+ if isinstance(item, (dict, list)):
62
+ output += " " + raw_dump(item, indent + 2)[indent + 2 :]
63
+ else:
64
+ output += " " + str(item) + "\n"
65
+ # Handle single dictionary
66
+ elif isinstance(input_data, dict):
67
+ for k, v in input_data.items():
68
+ # Add indentation
69
+ output += " " * indent
70
+
71
+ # If value is a dictionary, recursively process it
72
+ if isinstance(v, dict):
73
+ output += f"{k}:\n"
74
+ output += raw_dump(v, indent + 2)
75
+ else:
76
+ # Check if value is a list
77
+ if isinstance(v, list):
78
+ output += f"{k}:\n"
79
+ output += raw_dump(v, indent + 2)
80
+ # Check if value contains newlines
81
+ else:
82
+ str_value = str(v)
83
+ if "\n" in str_value:
84
+ # Use literal block style
85
+ output += f"{k}: |\n"
86
+ # Indent each line of the value
87
+ for line in str_value.splitlines():
88
+ output += " " * (indent + 2) + line + "\n"
89
+ else:
90
+ # Single line value
91
+ output += f"{k}: {str_value}\n"
92
+
93
+ return output
File without changes
@@ -0,0 +1,80 @@
1
+ import logging
2
+ import os
3
+ import shutil
4
+ import time
5
+ from datetime import datetime
6
+ from pathlib import Path
7
+ from typing import List, Optional
8
+
9
+ from setta.utils.constants import DB_BACKUP_FOLDER, USER_SETTINGS
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ def ensure_backup_dir_exists():
15
+ """Create the backup directory if it doesn't exist."""
16
+ Path(DB_BACKUP_FOLDER).mkdir(parents=True, exist_ok=True)
17
+
18
+
19
+ def get_backup_files() -> List[Path]:
20
+ """Get a list of existing backup files, sorted by filename (which contains the timestamp)."""
21
+ backup_dir = Path(DB_BACKUP_FOLDER)
22
+ return sorted(
23
+ [f for f in backup_dir.glob("backup_*.db")], key=lambda x: x.name, reverse=True
24
+ )
25
+
26
+
27
+ def get_last_backup_time() -> Optional[float]:
28
+ """
29
+ Get the timestamp of the last backup from the most recent backup filename.
30
+ Returns None if no backups exist.
31
+ """
32
+ backup_files = get_backup_files()
33
+ if not backup_files:
34
+ return None
35
+
36
+ latest_backup = backup_files[0]
37
+ timestamp_str = "_".join(latest_backup.stem.split("_")[-2:])
38
+
39
+ # Convert the timestamp string to a datetime object
40
+ backup_datetime = datetime.strptime(timestamp_str, "%Y%m%d_%H%M%S")
41
+
42
+ # Convert the datetime to a Unix timestamp (seconds since epoch)
43
+ return backup_datetime.timestamp()
44
+
45
+
46
+ def cleanup_old_backups():
47
+ backup_files = get_backup_files()
48
+ for old_backup in backup_files[USER_SETTINGS["backend"]["autobackupKeepLastN"] :]:
49
+ old_backup.unlink()
50
+
51
+
52
+ def create_backup(db_path):
53
+ """Create a new backup of the database."""
54
+ ensure_backup_dir_exists()
55
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
56
+ backup_filename = f"backup_{timestamp}.db"
57
+ backup_path = os.path.join(DB_BACKUP_FOLDER, backup_filename)
58
+ logger.debug(f"creating backup: {backup_path}")
59
+
60
+ shutil.copy2(db_path, backup_path)
61
+ cleanup_old_backups()
62
+
63
+
64
+ def should_create_backup() -> bool:
65
+ """Check if it's time to create a new backup."""
66
+ if not USER_SETTINGS["backend"]["autobackup"]:
67
+ return False
68
+ last_backup_time = get_last_backup_time()
69
+ logger.debug(f"last_backup_time: {last_backup_time}")
70
+ if last_backup_time is None:
71
+ return True
72
+
73
+ current_time = time.time()
74
+ time_since_last_backup = current_time - last_backup_time
75
+ return time_since_last_backup >= USER_SETTINGS["backend"]["autobackupFreq"]
76
+
77
+
78
+ def maybe_create_backup(db_path):
79
+ if should_create_backup():
80
+ create_backup(db_path)
File without changes
File without changes
@@ -0,0 +1,93 @@
1
+ import json
2
+
3
+ from setta.cli.logger import get_wrapper_for_loading
4
+ from setta.utils.constants import C
5
+ from setta.utils.utils import try_json
6
+
7
+
8
+ def load_artifacts(db, ids):
9
+ placeholders = ", ".join(["?"] * len(ids))
10
+ query = f"""
11
+ SELECT id, name, path, value, type
12
+ FROM Artifact
13
+ WHERE id IN ({placeholders})
14
+ """
15
+
16
+ db.execute(query, ids)
17
+
18
+ output = {}
19
+ for row in db.fetchall():
20
+ output[row["id"]] = {
21
+ "name": row["name"],
22
+ "path": row["path"],
23
+ "value": try_json(row["value"]),
24
+ "type": row["type"],
25
+ }
26
+
27
+ return output
28
+
29
+
30
+ def load_artifact_groups(db, ids):
31
+ placeholders = ", ".join(["?"] * len(ids))
32
+ query = f"""
33
+ SELECT ArtifactGroupId.id, ArtifactGroupId.name, ArtifactGroupId.data, ArtifactGroup.artifactId, ArtifactGroup.data as artifactTransformData, ArtifactGroup."order"
34
+ FROM ArtifactGroupId
35
+ LEFT JOIN ArtifactGroup
36
+ ON ArtifactGroupId.id = ArtifactGroup.idid
37
+ WHERE ArtifactGroupId.id in ({placeholders})
38
+ ORDER BY ArtifactGroupId.id, ArtifactGroup."order"
39
+ """
40
+ db.execute(query, ids)
41
+ output = {}
42
+ for row in db.fetchall():
43
+ if row["id"] not in output:
44
+ output[row["id"]] = {
45
+ "name": row["name"],
46
+ "artifactTransforms": [],
47
+ **json.loads(row["data"]),
48
+ }
49
+ if row["artifactId"]:
50
+ output[row["id"]]["artifactTransforms"].append(
51
+ {
52
+ "artifactId": row["artifactId"],
53
+ **json.loads(row["artifactTransformData"]),
54
+ }
55
+ )
56
+ return output
57
+
58
+
59
+ def load_available_artifacts(db, sectionType):
60
+ allowed_types = C.ALLOWED_ARTIFACT_TYPES[sectionType]
61
+
62
+ placeholders = ", ".join(["?"] * len(allowed_types))
63
+ query = f"""
64
+ SELECT id, name, path, type
65
+ FROM Artifact
66
+ WHERE type IN ({placeholders})
67
+ """
68
+
69
+ db.execute(query, allowed_types)
70
+
71
+ output = {}
72
+ for row in db.fetchall():
73
+ output[row["id"]] = {
74
+ "name": row["name"],
75
+ "path": row["path"],
76
+ "type": row["type"],
77
+ }
78
+
79
+ return output
80
+
81
+
82
+ def load_artifact_metadata_and_maybe_value_from_disk(db, artifactIds):
83
+ artifacts = load_artifacts(db, artifactIds)
84
+ for a in artifacts.values():
85
+ if a["path"]:
86
+ a.update(load_artifact_from_disk(a["path"], a["type"]))
87
+ return artifacts
88
+
89
+
90
+ def load_artifact_from_disk(filepath, type):
91
+ wrapper = get_wrapper_for_loading(type)
92
+ wrapper.load(filepath)
93
+ return wrapper.serialize()