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,99 @@
1
+ import json
2
+
3
+ from pydantic import BaseModel
4
+
5
+ from setta.code_gen.create_runnable_scripts import generate_final_code_for_sections
6
+ from setta.code_gen.export_selected import export_selected
7
+ from setta.code_gen.find_placeholders import tp
8
+ from setta.tasks.task_runner import RunType
9
+ from setta.utils.constants import C
10
+
11
+ from .utils import (
12
+ TaskDefinition,
13
+ send_document_open_request_with_diagnostics_with_code_dict,
14
+ )
15
+
16
+
17
+ class TextFieldInitializeCodeContent(BaseModel):
18
+ project: dict
19
+ sectionId: str
20
+ paramInfoId: str | None
21
+
22
+
23
+ async def _textFieldInitializeCode(message, lsp_writers):
24
+ message = TextFieldInitializeCodeContent.parse_obj(message.content)
25
+ project = message.project
26
+ exporter_obj = export_selected(
27
+ project, always_export_args_objs=True, force_include_template_var=True
28
+ )
29
+
30
+ var_name = exporter_obj.var_name_mapping[
31
+ (message.sectionId, message.paramInfoId, False)
32
+ ]
33
+ projectConfigName = project["projectConfig"]["name"]
34
+ code_dict = await generate_final_code_for_sections(
35
+ project,
36
+ exporter_obj,
37
+ lsp_writers=lsp_writers,
38
+ folder_path=lsp_writers["full"].project_code_folder(projectConfigName),
39
+ push_var_name=var_name,
40
+ )
41
+ send_document_open_request_with_diagnostics_with_code_dict(
42
+ lsp_writers["full"],
43
+ code_dict,
44
+ exporter_obj,
45
+ projectConfigName,
46
+ )
47
+
48
+ gen_code_section_details = exporter_obj.code_gen_template_var_section_details[
49
+ "section"
50
+ ]
51
+ gen_code_dict = code_dict[gen_code_section_details["id"]]
52
+ var_name_to_decl_position = gen_code_dict["var_name_to_decl_rel_position_dict"][
53
+ tp(C.SETTA_GENERATED_PYTHON)
54
+ ]
55
+ pushed_var_name_position = var_name_to_decl_position[var_name]
56
+ generated_var_names = list(var_name_to_decl_position.keys())
57
+ infoToFullName = {
58
+ tuple(json.loads(k)): v for k, v in project["infoToFullName"].items()
59
+ }
60
+ referencable_var_names = [
61
+ infoToFullName[exporter_obj.var_name_reverse_mapping[v]]
62
+ for v, (startPos, _, _) in var_name_to_decl_position.items()
63
+ if startPos < pushed_var_name_position[0]
64
+ ]
65
+
66
+ return {
67
+ "cache": [
68
+ gen_code_dict,
69
+ var_name,
70
+ referencable_var_names,
71
+ generated_var_names,
72
+ exporter_obj,
73
+ project["fullNameToInfo"],
74
+ ],
75
+ }
76
+
77
+
78
+ def get_cache_key(projectConfigId, sectionId, paramInfoId):
79
+ return f"textFieldInitializeCode-{projectConfigId}-{sectionId}-{paramInfoId}"
80
+
81
+
82
+ def write_cache(cache, message, result):
83
+ if result.get("cache") is None:
84
+ return
85
+ message = TextFieldInitializeCodeContent.parse_obj(message.content)
86
+ key = get_cache_key(
87
+ message.project["projectConfig"]["id"], message.sectionId, message.paramInfoId
88
+ )
89
+ cache[key] = result["cache"]
90
+
91
+
92
+ textFieldInitializeCode = TaskDefinition(
93
+ name="textFieldInitializeCode",
94
+ return_message_type="textFieldInitializeCodeReturn",
95
+ fn=_textFieldInitializeCode,
96
+ write_cache=write_cache,
97
+ run_as=RunType.NONE,
98
+ server_dependencies=["lsp_writers"],
99
+ )
@@ -0,0 +1,40 @@
1
+ from setta.code_gen.create_runnable_scripts import generate_final_code_for_sections
2
+ from setta.code_gen.export_selected import export_selected
3
+ from setta.tasks.task_runner import RunType
4
+
5
+ from .utils import (
6
+ TaskDefinition,
7
+ send_document_open_request_with_diagnostics_with_code_dict,
8
+ )
9
+
10
+
11
+ # this just initializes a document which gets the lsp to send back diagnostics
12
+ async def _typeCheck(message, lsp_writers):
13
+ project = message.content
14
+ exporter_obj = export_selected(
15
+ project, always_export_args_objs=False, force_include_template_var=True
16
+ )
17
+ projectConfigName = project["projectConfig"]["name"]
18
+ code_dict = await generate_final_code_for_sections(
19
+ project,
20
+ exporter_obj,
21
+ lsp_writers=lsp_writers,
22
+ folder_path=lsp_writers["full"].project_code_folder(projectConfigName),
23
+ )
24
+ send_document_open_request_with_diagnostics_with_code_dict(
25
+ lsp_writers["full"],
26
+ code_dict,
27
+ exporter_obj,
28
+ projectConfigName,
29
+ )
30
+
31
+ return None
32
+
33
+
34
+ typeCheck = TaskDefinition(
35
+ name="typeCheck",
36
+ return_message_type="typeCheckReturn",
37
+ fn=_typeCheck,
38
+ run_as=RunType.NONE,
39
+ server_dependencies=["lsp_writers"],
40
+ )
@@ -0,0 +1,134 @@
1
+ import asyncio
2
+ import random
3
+ import string
4
+ from typing import Any, Callable, List, Optional
5
+
6
+ from pydantic import BaseModel
7
+
8
+ from setta.tasks.task_runner import RunType
9
+ from setta.utils.constants import C
10
+ from setta.utils.utils import multireplace
11
+
12
+
13
+ def send_document_open_request_with_diagnostics_with_code_dict(
14
+ lsp_writer,
15
+ code_dict,
16
+ exporter_obj,
17
+ projectConfigName,
18
+ ):
19
+ for codeSectionId, details in code_dict.items():
20
+ if details["codeLanguage"] != "python":
21
+ continue
22
+ asyncio.create_task(
23
+ send_document_open_request_with_diagnostics(
24
+ lsp_writer,
25
+ details["code"],
26
+ exporter_obj,
27
+ details["ref_template_var_positions"],
28
+ projectConfigName,
29
+ details["sanitized_full_name"],
30
+ codeSectionId,
31
+ )
32
+ )
33
+
34
+
35
+ async def send_document_open_request_with_diagnostics(
36
+ lsp_writer,
37
+ code,
38
+ exporter_obj,
39
+ ref_template_var_positions,
40
+ projectConfigName,
41
+ sectionName,
42
+ codeSectionId,
43
+ ):
44
+ code_id = lsp_writer.construct_code_id(
45
+ projectConfigName,
46
+ sectionName,
47
+ )
48
+ await lsp_writer.send_document_open_request(
49
+ code_id=code_id,
50
+ code=code,
51
+ codeJson=exporter_obj.output if exporter_obj else None,
52
+ ref_template_var_positions=ref_template_var_positions,
53
+ needsDiagnostics=True,
54
+ projectConfigId=exporter_obj.p["projectConfig"]["id"],
55
+ codeSectionId=codeSectionId,
56
+ )
57
+
58
+ return code_id
59
+
60
+
61
+ def prepare_code_for_scope_checking(fullText, templateVars, target_template_var):
62
+ the_special_var_name = None
63
+ for t in templateVars:
64
+ startPos = t["startPos"]
65
+ keyword = t["keyword"]
66
+ endPos = startPos + len(keyword)
67
+ if t is target_template_var:
68
+ # need to assign something to the replacement variable so that ast considers it a variable
69
+ decl = "=None"
70
+ # the [:-len(decl)] is to keep the full declaration the same length as the original keyword
71
+ the_special_var_name = generate_unique_variable(fullText, keyword)[
72
+ : -len(decl)
73
+ ]
74
+ replacement = the_special_var_name + decl
75
+ else:
76
+ # variable assignment not necessary for the keywords that we don't care about
77
+ replacement = generate_unique_variable(fullText, keyword)
78
+ fullText = fullText[:startPos] + replacement + fullText[endPos:]
79
+
80
+ return fullText, the_special_var_name
81
+
82
+
83
+ # generate valid variable names because for example $SETTA_GENERATED_PYTHON is not valid
84
+ def replace_template_vars_with_random_names(candidateTemplateVars, fullText):
85
+ var_name_mapping = {}
86
+ reverse_var_name_mapping = {}
87
+ keyword_to_pos = {}
88
+ for keyword, pos in candidateTemplateVars:
89
+ new_keyword = generate_unique_variable(fullText, keyword)
90
+ var_name_mapping[keyword] = new_keyword
91
+ reverse_var_name_mapping[new_keyword] = keyword
92
+ # we only need 1 position of the keyword, even if there are multiple positions
93
+ keyword_to_pos[keyword] = pos
94
+
95
+ code = multireplace(fullText, var_name_mapping)
96
+ return code, list(keyword_to_pos.values()), reverse_var_name_mapping
97
+
98
+
99
+ def generate_unique_variable(code, keyword):
100
+ length = len(keyword)
101
+ chars = string.ascii_lowercase + string.digits + "_"
102
+
103
+ while True:
104
+ # Ensure the variable starts with a letter or underscore
105
+ new_var = random.choice(string.ascii_lowercase + "_")
106
+ # Fill the rest with random characters
107
+ new_var += "".join(random.choice(chars) for _ in range(length - 1))
108
+
109
+ if new_var.isidentifier() and new_var not in code:
110
+ return new_var
111
+
112
+
113
+ def do_nothing(*args, **kwargs):
114
+ pass
115
+
116
+
117
+ class TaskDefinition(BaseModel):
118
+ name: Optional[str] = ""
119
+ return_message_type: str
120
+ fn: Callable
121
+ read_cache: Optional[Callable] = do_nothing
122
+ write_cache: Optional[Callable] = do_nothing
123
+ run_as: Optional[RunType] = RunType.NONE
124
+ dependencies: Optional[List[str]] = None
125
+ server_dependencies: Optional[List[str]] = []
126
+
127
+
128
+ class TaskMessage(BaseModel):
129
+ id: str
130
+ content: Any
131
+
132
+
133
+ class SettaInMemoryFn(TaskDefinition):
134
+ return_message_type: str = C.WS_IN_MEMORY_FN_RETURN
@@ -0,0 +1,29 @@
1
+ import asyncio
2
+ import inspect
3
+ from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
4
+ from enum import Enum
5
+
6
+
7
+ class RunType(Enum):
8
+ SUBPROCESS = "SUBPROCESS"
9
+ THREAD = "THREAD"
10
+ NONE = "NONE"
11
+
12
+
13
+ class TaskRunner:
14
+ def __init__(self):
15
+ self.thread_executor = ThreadPoolExecutor(max_workers=2)
16
+ self.process_executor = ProcessPoolExecutor(max_workers=2)
17
+
18
+ async def run(self, fn, fn_args, run_as):
19
+ if inspect.iscoroutinefunction(fn):
20
+ return await fn(*fn_args)
21
+
22
+ if run_as == RunType.NONE:
23
+ return fn(*fn_args)
24
+ elif run_as == RunType.THREAD:
25
+ executor = self.thread_executor
26
+ elif run_as == RunType.SUBPROCESS:
27
+ executor = self.process_executor
28
+
29
+ return await asyncio.get_running_loop().run_in_executor(executor, fn, *fn_args)
setta/tasks/tasks.py ADDED
@@ -0,0 +1,152 @@
1
+ import asyncio
2
+ import logging
3
+ import queue
4
+ import threading
5
+ from typing import Dict
6
+
7
+ from setta.database.utils import create_new_id
8
+
9
+ from . import fns
10
+ from .fns.utils import TaskDefinition, TaskMessage
11
+ from .task_runner import RunType, TaskRunner
12
+ from .utils import SettaInMemoryFnSubprocess, add_fns_from_module
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class Tasks:
18
+ def __init__(self, lsp_writers):
19
+ self.lsp_writers = lsp_writers
20
+ self.task_runner = TaskRunner()
21
+ self.cache = {}
22
+ self.fns: Dict[str, TaskDefinition] = {}
23
+ self.in_memory_subprocess = SettaInMemoryFnSubprocess()
24
+ self.websockets = [] # Store the websocket connections
25
+ add_fns_from_module(self.fns, fns)
26
+
27
+ # Start stdout listener thread
28
+ self._stop_event = asyncio.Event()
29
+ self.stdout_queue = queue.Queue() # regular Queue
30
+ self._stdout_processor_task = None
31
+ self.stdout_thread = threading.Thread(target=self._stdout_listener, daemon=True)
32
+ self.stdout_thread.start()
33
+
34
+ # Backend Changes (Tasks class)
35
+ async def connect(self, websocket):
36
+ # Accept the new connection
37
+ await websocket.accept()
38
+ self.websockets.append(websocket)
39
+
40
+ # Start the processor task if it's not running
41
+ if self._stdout_processor_task is None or self._stdout_processor_task.done():
42
+ self._stdout_processor_task = asyncio.create_task(
43
+ self._process_stdout_queue()
44
+ )
45
+
46
+ async def disconnect(self, websocket):
47
+ self.websockets.remove(websocket)
48
+ if len(self.websockets) == 0:
49
+ # Cancel the processor task
50
+ if self._stdout_processor_task and not self._stdout_processor_task.done():
51
+ self._stdout_processor_task.cancel()
52
+ try:
53
+ await self._stdout_processor_task
54
+ except asyncio.CancelledError:
55
+ pass
56
+ self._stdout_processor_task = None
57
+
58
+ def _stdout_listener(self):
59
+ while not self._stop_event.is_set():
60
+ try:
61
+ stdout_data = self.in_memory_subprocess.stdout_parent_conn.recv()
62
+ self.stdout_queue.put(stdout_data) # simple put, no async needed
63
+ except Exception as e:
64
+ if self._stop_event.is_set():
65
+ break
66
+ logger.debug(f"Error in stdout listener: {e}")
67
+
68
+ async def _process_stdout_queue(self):
69
+ while not self._stop_event.is_set():
70
+ try:
71
+ if self._stop_event.is_set():
72
+ break
73
+ if len(self.websockets) > 0:
74
+ stdout_data = self.stdout_queue.get_nowait()
75
+ stdout_data = stdout_data.replace("\n", "\r\n")
76
+ for w in self.websockets:
77
+ await w.send_text(stdout_data)
78
+ self.stdout_queue.task_done()
79
+ except queue.Empty:
80
+ await asyncio.sleep(0.1) # Check for connection every 100ms
81
+ except asyncio.CancelledError:
82
+ break
83
+ except Exception as e:
84
+ if self._stop_event.is_set():
85
+ break
86
+ logger.debug(f"Error processing stdout: {e}")
87
+
88
+ async def __call__(self, fn_name, message: TaskMessage):
89
+ if fn_name in self.fns:
90
+ return await self.call_regular_fn(fn_name, message)
91
+ return await self.call_in_memory_subprocess_fn(fn_name, message)
92
+
93
+ async def call_regular_fn(self, fn_name, message: TaskMessage):
94
+ fn = self.fns[fn_name]
95
+ fn.read_cache(self.cache, message)
96
+ fn_args = [message, *(getattr(self, k) for k in fn.server_dependencies)]
97
+ result = await self.task_runner.run(fn.fn, fn_args, fn.run_as)
98
+ fn.write_cache(self.cache, message, result)
99
+ if result is None:
100
+ result = {}
101
+ result["messageType"] = fn.return_message_type
102
+ return result
103
+
104
+ async def call_in_memory_subprocess_fn(self, fn_name, message: TaskMessage):
105
+ self.in_memory_subprocess.parent_conn.send(
106
+ {"type": "call", "fn_name": fn_name, "message": message}
107
+ )
108
+ result = await self.task_runner.run(
109
+ self.in_memory_subprocess.parent_conn.recv, [], RunType.THREAD
110
+ )
111
+ # if result["status"] == "success":
112
+ # for x in result["content"]:
113
+ # x.setdefault("sectionType", default_section_type(x["type"]))
114
+ if result["status"] != "success":
115
+ result["content"] = {}
116
+
117
+ return {"content": result["content"], "messageType": result["messageType"]}
118
+
119
+ async def add_custom_fns(self, code_list, to_cache):
120
+ error_msgs = {}
121
+ task_metadata = {}
122
+ initial_content = []
123
+ for c in code_list:
124
+ # Send import request to subprocess
125
+ self.in_memory_subprocess.parent_conn.send(
126
+ {
127
+ "type": "import",
128
+ "code": c["code"],
129
+ "module_name": c["module_name"],
130
+ "to_cache": to_cache,
131
+ }
132
+ )
133
+ result = await self.task_runner.run(
134
+ self.in_memory_subprocess.parent_conn.recv, [], RunType.THREAD
135
+ )
136
+ if result["status"] == "success":
137
+ task_metadata.update(result["content"])
138
+ else:
139
+ error_msgs[c["module_name"]] = result["error"]
140
+
141
+ for k in task_metadata.keys():
142
+ task_output = await self(k, TaskMessage(id=create_new_id(), content={}))
143
+ initial_content.extend(task_output["content"])
144
+
145
+ return task_metadata, error_msgs, initial_content
146
+
147
+ def close(self):
148
+ self._stop_event.set()
149
+ self.in_memory_subprocess.close()
150
+ self.stdout_thread.join()
151
+ if self._stdout_processor_task:
152
+ self._stdout_processor_task.cancel()
setta/tasks/utils.py ADDED
@@ -0,0 +1,178 @@
1
+ import importlib.util
2
+ import json
3
+ import multiprocessing
4
+ import os
5
+ import sys
6
+ import traceback
7
+ import uuid
8
+
9
+ from setta.tasks.fns.utils import TaskDefinition
10
+ from setta.utils.utils import nested_access
11
+
12
+
13
+ def import_code_from_string(code_string, module_name=None, add_to_sys_modules=True):
14
+ # Generate a unique module name if one isn't provided
15
+ if module_name is None:
16
+ module_name = f"setta_dynamic_module_{uuid.uuid4().hex}"
17
+
18
+ # Add current directory to sys.path if it's not already there
19
+ current_dir = os.getcwd()
20
+ if current_dir not in sys.path:
21
+ sys.path.insert(0, current_dir)
22
+
23
+ spec = importlib.util.spec_from_loader(module_name, loader=None)
24
+
25
+ # Create a new module based on the spec
26
+ module = importlib.util.module_from_spec(spec)
27
+
28
+ # Optionally add the module to sys.modules
29
+ if add_to_sys_modules:
30
+ print(f"adding {module_name} to sys.modules", flush=True)
31
+ sys.modules[module_name] = module
32
+
33
+ # Compile the code string
34
+ code_object = compile(code_string, module_name, "exec")
35
+
36
+ # Execute the compiled code object in the module's namespace
37
+ exec(code_object, module.__dict__)
38
+
39
+ return module
40
+
41
+
42
+ class SettaInMemoryFnSubprocess:
43
+ def __init__(self):
44
+ self.parent_conn, self.child_conn = multiprocessing.Pipe()
45
+ self.process = multiprocessing.Process(target=self._subprocess_main)
46
+ self.stdout_parent_conn, self.stdout_child_conn = multiprocessing.Pipe()
47
+ self.process.daemon = True # Ensure process dies with parent
48
+ self.process.start()
49
+
50
+ def _subprocess_main(self):
51
+ """Main loop in subprocess that handles all requests"""
52
+ # Initialize store for imported modules
53
+ fns_dict = {}
54
+ cache = {}
55
+
56
+ class OutputCapture:
57
+ def __init__(self, stdout_pipe):
58
+ self.stdout_pipe = stdout_pipe
59
+
60
+ def write(self, text):
61
+ self.stdout_pipe.send(text)
62
+
63
+ def flush(self):
64
+ pass
65
+
66
+ # Redirect stdout as soon as subprocess starts
67
+ output_capture = OutputCapture(self.stdout_child_conn)
68
+ sys.stdout = output_capture
69
+ sys.stderr = output_capture
70
+
71
+ while True:
72
+ msg = self.child_conn.recv() # Wait for requests
73
+ msg_type = msg["type"]
74
+ return_message_type = None
75
+
76
+ if msg_type == "shutdown":
77
+ break
78
+
79
+ try:
80
+ if msg_type == "import":
81
+ code = msg["code"]
82
+ module_name = msg["module_name"]
83
+ # Import and store module
84
+ module = import_code_from_string(code, module_name)
85
+ added_fn_names = add_fns_from_module(fns_dict, module, module_name)
86
+ task_metadata = {}
87
+ for k in added_fn_names:
88
+ cache[k] = msg["to_cache"]
89
+ task_metadata[k] = get_task_metadata(fns_dict[k], cache[k])
90
+
91
+ self.child_conn.send(
92
+ {
93
+ "status": "success",
94
+ "content": task_metadata,
95
+ }
96
+ )
97
+
98
+ elif msg_type == "call":
99
+ fn_name = msg["fn_name"]
100
+ message = self.process_message(fn_name, msg["message"], cache)
101
+ fn = fns_dict[fn_name]
102
+ result = fn.fn(message)
103
+ return_message_type = fn.return_message_type
104
+
105
+ self.child_conn.send(
106
+ {
107
+ "status": "success",
108
+ "content": result,
109
+ "messageType": return_message_type,
110
+ }
111
+ )
112
+
113
+ except Exception as e:
114
+ traceback.print_exc()
115
+ self.child_conn.send(
116
+ {
117
+ "status": "error",
118
+ "error": str(e),
119
+ "messageType": return_message_type,
120
+ }
121
+ )
122
+
123
+ def close(self):
124
+ try:
125
+ self.parent_conn.send({"type": "shutdown"})
126
+ self.process.join(timeout=1.0)
127
+ except:
128
+ pass
129
+
130
+ if self.process.is_alive():
131
+ self.process.terminate()
132
+ self.process.join()
133
+
134
+ # Close both sets of connections
135
+ self.parent_conn.close()
136
+ self.child_conn.close()
137
+ self.stdout_parent_conn.close()
138
+ self.stdout_child_conn.close()
139
+
140
+ def process_message(self, fn_name, message, cache):
141
+ if fn_name in cache:
142
+ exporter_obj = cache[fn_name]
143
+ for k, v in message.content.items():
144
+ nice_str = exporter_obj.var_name_mapping[tuple(json.loads(k))]
145
+ p_dict, key = nested_access(exporter_obj.output, nice_str)
146
+ p_dict[key] = v
147
+ message.content = exporter_obj.output
148
+ return message.content
149
+
150
+
151
+ def add_fns_from_module(fns_dict, module, module_name=None):
152
+ count = 1
153
+ added_fn_names = []
154
+ for attr in vars(module).values():
155
+ if isinstance(attr, TaskDefinition):
156
+ if not attr.name:
157
+ if not module_name:
158
+ raise ValueError(
159
+ "Either TaskDefinition must be initialized with a name, or task_name must be specified"
160
+ )
161
+ attr.name = f"{module_name}-{count}"
162
+ count += 1
163
+ fns_dict[attr.name] = attr
164
+ added_fn_names.append(attr.name)
165
+ return added_fn_names
166
+
167
+
168
+ def get_task_metadata(in_memory_fn, exporter_obj):
169
+ # None means run the task on every change
170
+ if in_memory_fn.dependencies is None:
171
+ dependencies = None
172
+ # Empty array means only run when the task imported.
173
+ # Non-empty array means run when specified dependencies update.
174
+ else:
175
+ dependencies = [
176
+ exporter_obj.var_name_reverse_mapping[d] for d in in_memory_fn.dependencies
177
+ ]
178
+ return {"dependencies": dependencies}
File without changes