setta 0.0.1__py3-none-any.whl → 0.0.2__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.dist-info/LICENSE +201 -0
  259. setta-0.0.2.dist-info/METADATA +24 -0
  260. setta-0.0.2.dist-info/RECORD +263 -0
  261. {setta-0.0.1.dist-info → setta-0.0.2.dist-info}/WHEEL +1 -1
  262. setta-0.0.2.dist-info/entry_points.txt +2 -0
  263. setta-0.0.1.dist-info/METADATA +0 -18
  264. setta-0.0.1.dist-info/RECORD +0 -5
  265. {setta-0.0.1.dist-info → setta-0.0.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,13 @@
1
+ from setta.utils.constants import C
2
+
3
+
4
+ def tp(x):
5
+ return f"{C.TEMPLATE_PREFIX}{x}"
6
+
7
+
8
+ def remove_tp(x):
9
+ return x.lstrip(C.TEMPLATE_PREFIX)
10
+
11
+
12
+ def needs_yaml(script):
13
+ return tp("SETTA_YAML_FILE") in script
File without changes
@@ -0,0 +1,183 @@
1
+ import ast
2
+ import builtins
3
+ from typing import Set
4
+
5
+
6
+ def not_in_root_imports(import_path, root_imports):
7
+ return not any(
8
+ import_path.startswith(root + ".") or import_path == root
9
+ for root in root_imports
10
+ )
11
+
12
+
13
+ def is_instance_attribute(node: ast.AST) -> bool:
14
+ """
15
+ Determines if an attribute access chain involves an instance parameter (self/cls/etc).
16
+ Checks if any part of the attribute chain starts with the first parameter of a method.
17
+ """
18
+ if not isinstance(node, ast.Attribute):
19
+ return False
20
+
21
+ # Walk up the AST to find the enclosing function
22
+ current = node
23
+ while current and not isinstance(current, ast.FunctionDef):
24
+ current = getattr(current, "parent", None)
25
+
26
+ if not current: # Not in a function
27
+ return False
28
+
29
+ # Check if the function is a method (has at least one argument)
30
+ if not current.args.args:
31
+ return False
32
+
33
+ # Get the instance parameter name (traditionally 'self' but could be anything)
34
+ instance_param = current.args.args[0].arg
35
+
36
+ # Build the complete attribute chain
37
+ base = node
38
+ while isinstance(base, ast.Attribute):
39
+ base = base.value
40
+
41
+ # Check if the chain starts with the instance parameter
42
+ return isinstance(base, ast.Name) and base.id == instance_param
43
+
44
+
45
+ def find_missing_imports(code: str) -> Set[str]:
46
+ """
47
+ Analyzes Python code and returns a set of missing imports.
48
+ If a module is explicitly imported (directly or with alias), its submodules are not considered missing.
49
+
50
+ Args:
51
+ code (str): String containing Python code to analyze
52
+
53
+ Returns:
54
+ Set[str]: Set of import statements that are missing
55
+
56
+ Example:
57
+ >>> code = "import torch; x = torch.nn.CrossEntropyLoss()"
58
+ >>> find_missing_imports(code)
59
+ set() # torch.nn is not considered missing since torch is imported
60
+ """
61
+ # Get set of built-in names
62
+ builtin_names = set(dir(builtins))
63
+
64
+ # Track existing imports, defined names, and missing imports
65
+ existing_imports = set()
66
+ root_imports = set() # Track the root modules that are explicitly imported
67
+ defined_names = set()
68
+ missing_imports = set()
69
+
70
+ # Parse the code into an AST
71
+ try:
72
+ tree = ast.parse(code)
73
+ except SyntaxError as e:
74
+ raise ValueError(f"Invalid Python code: {str(e)}")
75
+
76
+ # Add parent references to make context checking easier
77
+ for parent in ast.walk(tree):
78
+ for child in ast.iter_child_nodes(parent):
79
+ child.parent = parent
80
+
81
+ # First pass: collect existing imports and defined names
82
+ for node in ast.walk(tree):
83
+ if isinstance(node, ast.Import):
84
+ for name in node.names:
85
+ if name.asname: # Alias
86
+ existing_imports.add(name.asname)
87
+ root_imports.add(name.asname) # Add alias as root
88
+ else:
89
+ existing_imports.add(name.name) # Original name
90
+ root_imports.add(name.name.split(".")[0]) # Add root module
91
+ elif isinstance(node, ast.ImportFrom):
92
+ if node.module:
93
+ for name in node.names:
94
+ if name.asname: # Alias
95
+ existing_imports.add(name.asname)
96
+ root_imports.add(name.asname)
97
+ else:
98
+ existing_imports.add(name.name)
99
+ root_imports.add(name.name.split(".")[0]) # Add root module
100
+ elif isinstance(node, ast.FunctionDef):
101
+ defined_names.add(node.name)
102
+ elif isinstance(node, ast.ClassDef):
103
+ defined_names.add(node.name)
104
+ elif isinstance(node, ast.Name) and isinstance(node.ctx, ast.Store):
105
+ defined_names.add(node.id)
106
+ elif isinstance(node, ast.arg):
107
+ defined_names.add(node.arg)
108
+
109
+ # Second pass: find potential missing imports
110
+ for node in ast.walk(tree):
111
+ if isinstance(node, ast.Name):
112
+ # Skip if it's a built-in, already imported, or locally defined
113
+ if (
114
+ node.id not in builtin_names
115
+ and node.id not in existing_imports
116
+ and node.id not in defined_names
117
+ and isinstance(node.ctx, ast.Load)
118
+ and not_in_root_imports(node.id, root_imports)
119
+ ):
120
+ missing_imports.add(node.id)
121
+ elif isinstance(node, ast.Attribute):
122
+ # Build the full attribute chain
123
+ attr_chain = []
124
+ current = node
125
+ while isinstance(current, ast.Attribute):
126
+ attr_chain.append(current.attr)
127
+ current = current.value
128
+
129
+ if isinstance(current, ast.Name):
130
+ attr_chain.append(current.id)
131
+
132
+ # Skip instance attributes (where chain starts with self/cls)
133
+ if is_instance_attribute(node):
134
+ continue
135
+
136
+ # Skip if the root is a local name
137
+ if attr_chain[-1] in defined_names:
138
+ continue
139
+
140
+ # Reverse to get correct order
141
+ attr_chain.reverse()
142
+
143
+ # Generate all possible import paths
144
+ for i in range(1, len(attr_chain)):
145
+ import_path = ".".join(attr_chain[:i])
146
+ if (
147
+ import_path not in existing_imports
148
+ and import_path not in builtin_names
149
+ and import_path not in defined_names
150
+ and not_in_root_imports(import_path, root_imports)
151
+ ):
152
+ missing_imports.add(import_path)
153
+
154
+ return missing_imports
155
+
156
+
157
+ def find_top_level_symbols(source: str) -> Set[str]:
158
+ """Find all top-level symbol names that could cause naming conflicts."""
159
+ root = ast.parse(source)
160
+ symbols = set()
161
+
162
+ # Only look at top-level nodes
163
+ for node in root.body:
164
+ # Get top-level assignments
165
+ if isinstance(node, ast.Assign):
166
+ for target in node.targets:
167
+ if isinstance(target, ast.Name):
168
+ symbols.add(target.id)
169
+
170
+ # Get function and class names
171
+ elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
172
+ symbols.add(node.name)
173
+
174
+ # Get import aliases and imported names
175
+ elif isinstance(node, ast.Import):
176
+ for alias in node.names:
177
+ symbols.add(alias.asname if alias.asname else alias.name.split(".")[0])
178
+
179
+ elif isinstance(node, ast.ImportFrom):
180
+ for alias in node.names:
181
+ symbols.add(alias.asname if alias.asname else alias.name)
182
+
183
+ return symbols
@@ -0,0 +1,187 @@
1
+ import ast
2
+ from dataclasses import dataclass
3
+ from typing import List, Set
4
+
5
+ from setta.code_gen.python.position_line_col import position_to_line_col
6
+
7
+
8
+ @dataclass
9
+ class ScopeRange:
10
+ start_line: int
11
+ start_col: int
12
+ end_line: int
13
+ end_col: int
14
+ variable_names: Set[str]
15
+
16
+
17
+ class ScopeVisitor(ast.NodeVisitor):
18
+ def __init__(self):
19
+ self.scopes: List[ScopeRange] = []
20
+ self._current_scope_vars: Set[str] = set()
21
+
22
+ def visit_Name(self, node: ast.Name):
23
+ if isinstance(node.ctx, ast.Store):
24
+ self._current_scope_vars.add(node.id)
25
+ self.generic_visit(node)
26
+
27
+ def _visit_scope_node(self, node: ast.AST):
28
+ # Save previous scope variables
29
+ prev_vars = self._current_scope_vars.copy()
30
+ self._current_scope_vars = set()
31
+
32
+ # Visit the node's children
33
+ self.generic_visit(node)
34
+
35
+ # Create scope range
36
+ scope = ScopeRange(
37
+ start_line=node.lineno,
38
+ start_col=node.col_offset,
39
+ end_line=node.end_lineno,
40
+ end_col=node.end_col_offset,
41
+ variable_names=self._current_scope_vars,
42
+ )
43
+ self.scopes.append(scope)
44
+
45
+ # Restore previous scope variables
46
+ self._current_scope_vars = prev_vars
47
+
48
+ def visit_FunctionDef(self, node: ast.FunctionDef):
49
+ self._visit_scope_node(node)
50
+
51
+ def visit_ClassDef(self, node: ast.ClassDef):
52
+ # Save previous scope variables
53
+ prev_vars = self._current_scope_vars.copy()
54
+ self._current_scope_vars = set()
55
+
56
+ # Visit the node's children
57
+ self.generic_visit(node)
58
+
59
+ # Class variables are accessible in methods, so we need to add them to method scopes
60
+ class_vars = self._current_scope_vars.copy()
61
+ for child in node.body:
62
+ if isinstance(child, ast.FunctionDef):
63
+ scope = next(
64
+ (s for s in self.scopes if s.start_line == child.lineno), None
65
+ )
66
+ if scope:
67
+ scope.variable_names.update(class_vars)
68
+
69
+ # Create scope range for the class itself
70
+ scope = ScopeRange(
71
+ start_line=node.lineno,
72
+ start_col=node.col_offset,
73
+ end_line=node.end_lineno,
74
+ end_col=node.end_col_offset,
75
+ variable_names=self._current_scope_vars,
76
+ )
77
+ self.scopes.append(scope)
78
+
79
+ # Restore previous scope variables
80
+ self._current_scope_vars = prev_vars
81
+
82
+ def visit_Module(self, node: ast.Module):
83
+ # Module node is special - it represents the entire file
84
+ self._current_scope_vars = set()
85
+
86
+ # Visit all children
87
+ self.generic_visit(node)
88
+
89
+ # For Module, use first/last line of content
90
+ first_line = float("inf")
91
+ last_line = 0
92
+ first_col = float("inf")
93
+ last_col = 0
94
+
95
+ for child in ast.walk(node):
96
+ if hasattr(child, "lineno"):
97
+ first_line = min(first_line, child.lineno)
98
+ first_col = min(first_col, child.col_offset)
99
+ if hasattr(child, "end_lineno"):
100
+ last_line = max(last_line, child.end_lineno)
101
+ last_col = max(last_col, child.end_col_offset)
102
+
103
+ # Handle empty files
104
+ if first_line == float("inf"):
105
+ first_line = 1
106
+ first_col = 0
107
+ last_line = 1
108
+ last_col = 0
109
+
110
+ scope = ScopeRange(
111
+ start_line=first_line,
112
+ start_col=first_col,
113
+ end_line=last_line,
114
+ end_col=last_col,
115
+ variable_names=self._current_scope_vars,
116
+ )
117
+ self.scopes.append(scope)
118
+
119
+
120
+ def are_positions_in_scope_with_variable(
121
+ code: str, positions: list[tuple[int, int]], variable_name: str
122
+ ) -> list[bool]:
123
+ """
124
+ Check if multiple positions in the code are in the same scope as a specific variable.
125
+
126
+ This function analyzes Python code to determine whether each given position (line, column)
127
+ falls within a scope where a specified variable is defined. A scope can be a module,
128
+ function, or class definition.
129
+
130
+ Args:
131
+ code (str): The Python source code to analyze
132
+ positions (list[tuple[int, int]]): List of (line, column) tuples representing
133
+ positions to check. Line and column numbers are 1-based.
134
+ variable_name (str): Name of the variable to check for in the scope
135
+
136
+ Returns:
137
+ list[bool]: List of boolean values corresponding to each position in the input,
138
+ where True indicates the position is in a scope where the variable is defined,
139
+ and False indicates it is not.
140
+
141
+ Raises:
142
+ SyntaxError: If the provided code contains syntax errors (handled internally
143
+ by returning all False values)
144
+
145
+ Example:
146
+ >>> code = '''
147
+ def func():
148
+ x = 1
149
+ return x
150
+ '''
151
+ >>> positions = [(2, 0), (3, 4), (4, 0)]
152
+ >>> are_positions_in_scope_with_variable(code, positions, 'x')
153
+ [True, True, False]
154
+ """
155
+ try:
156
+ tree = ast.parse(code)
157
+ except SyntaxError:
158
+ return [False] * len(positions) # Return correct number of Falses
159
+
160
+ visitor = ScopeVisitor()
161
+ visitor.visit(tree)
162
+
163
+ output = []
164
+
165
+ line_col_positions = []
166
+ for x in positions:
167
+ line, col = position_to_line_col(code, x)
168
+ line_col_positions.append((line + 1, col))
169
+
170
+ for line, col in line_col_positions:
171
+ in_scope = False
172
+ # Check each scope
173
+ for scope in visitor.scopes:
174
+ # Check if position is within this scope
175
+ if (
176
+ scope.start_line <= line <= scope.end_line
177
+ and (scope.start_line != line or scope.start_col <= col)
178
+ and (scope.end_line != line or col <= scope.end_col)
179
+ ):
180
+ # Check if variable is defined in this scope
181
+ if variable_name in scope.variable_names:
182
+ in_scope = True
183
+ break
184
+
185
+ output.append(in_scope)
186
+
187
+ return output
@@ -0,0 +1,280 @@
1
+ from setta.code_gen.export_selected import get_gen_code_template_var
2
+ from setta.code_gen.python.make_parseable import make_parseable
3
+ from setta.utils.utils import replace_at_positions
4
+
5
+ from ...utils.constants import C
6
+ from ..utils import push_var_deep
7
+ from .ast_utils import find_missing_imports, find_top_level_symbols
8
+
9
+
10
+ def get_import_text(code, chars_before_template_var):
11
+ imports = [f"import {x}" for x in find_missing_imports(code)]
12
+ return f"\n{chars_before_template_var}".join(imports)
13
+
14
+
15
+ def get_mappings(var_names, selected, is_dict, positionalOnlyParams=None):
16
+ output = ""
17
+ relative_positions = []
18
+ curr_position = 0
19
+ for idx, v in enumerate(var_names):
20
+ prefix = "" if idx == 0 else ", "
21
+ if is_dict or v not in positionalOnlyParams:
22
+ var_decl_prefix = var_declaration(
23
+ selected[v]["name"],
24
+ v,
25
+ is_dict,
26
+ return_prefix=True,
27
+ is_callable_param=True,
28
+ )
29
+ prefix += var_decl_prefix
30
+
31
+ curr_output = f"{prefix}{v}"
32
+ relative_positions.append({"startPos": curr_position + len(prefix), "value": v})
33
+ curr_position += len(curr_output)
34
+ output += curr_output
35
+
36
+ return output, relative_positions
37
+
38
+
39
+ def get_dict_contents(var_names, selected):
40
+ return get_mappings(var_names, selected, is_dict=True)
41
+
42
+
43
+ def get_callable_params(var_names, positionalOnlyParams, selected):
44
+ return get_mappings(
45
+ var_names, selected, is_dict=False, positionalOnlyParams=positionalOnlyParams
46
+ )
47
+
48
+
49
+ def get_list_of_vars(var_names):
50
+ return get_mappings(var_names, None, is_dict=False, positionalOnlyParams=var_names)
51
+
52
+
53
+ def get_boolean(value):
54
+ return "True" if value == "true" else "False"
55
+
56
+
57
+ def var_declaration(
58
+ var_name, value, is_dict=False, return_prefix=False, is_callable_param=False
59
+ ):
60
+ if is_dict:
61
+ prefix = f'"{var_name}": '
62
+ else:
63
+ prefix = f"{var_name}=" if is_callable_param else f"{var_name} = "
64
+ if return_prefix:
65
+ return prefix
66
+ return f"{prefix}{value}"
67
+
68
+
69
+ # returns the value and a relative_positions array (for populating some ref_var_name_positions) or None if not applicable
70
+ # TODO: remove str(x["value"]). I don't think it's necessary since x["value"] should always be a string already.
71
+ def get_value(x, selected):
72
+ type_value = x["type"]
73
+ if type_value == C.TEXT:
74
+ output = str(x["value"])
75
+ relative_positions = []
76
+ elif type_value == C.SLIDER or type_value == C.DROPDOWN:
77
+ output = str(x["value"])
78
+ relative_positions = []
79
+ elif type_value == C.SWITCH:
80
+ output = str(get_boolean(x["value"]))
81
+ relative_positions = []
82
+ elif type_value == C.COLOR_PICKER or type_value == C.PASSWORD:
83
+ output = f'"{x["value"]}"'
84
+ relative_positions = []
85
+ elif type_value == C.SECTION:
86
+ if not x["value"]["callable"]:
87
+ output, relative_positions = get_dict_contents(
88
+ x["value"]["usedParams"], selected
89
+ )
90
+ prefix = "{"
91
+ output = prefix + output + "}"
92
+ prefix_len = len(prefix)
93
+ else:
94
+ output, relative_positions = get_callable_params(
95
+ x["value"]["usedParams"], x["value"]["positionalOnlyParams"], selected
96
+ )
97
+ prefix = f"{x['value']['callable']}("
98
+ output = f"{prefix}{output})"
99
+ prefix_len = len(prefix)
100
+ relative_positions = [
101
+ {**r, "startPos": r["startPos"] + prefix_len} for r in relative_positions
102
+ ]
103
+ elif type_value == C.LIST_ROOT:
104
+ output, relative_positions = get_list_of_vars(x["value"])
105
+ output = f"[{output}]"
106
+ relative_positions = [
107
+ {**r, "startPos": r["startPos"] + 1} for r in relative_positions
108
+ ]
109
+ elif type_value in [C.DICT_ROOT, C.GROUP, C.NESTED_PARAM]:
110
+ output, relative_positions = get_dict_contents(x["value"], selected)
111
+ output = "{" + output + "}"
112
+ relative_positions = [
113
+ {**r, "startPos": r["startPos"] + 1} for r in relative_positions
114
+ ]
115
+ else:
116
+ output = None
117
+ relative_positions = []
118
+
119
+ return output, relative_positions
120
+
121
+
122
+ def create_line(var_name, x, selected, force_appear):
123
+ value, relative_positions = get_value(x, selected)
124
+ # include any ref positions that were calculated in export_selected,
125
+ # because they too need to be shifted by the length of the variable declaration
126
+ relative_positions.extend(x["ref_var_name_positions"])
127
+ if value or force_appear:
128
+ prefix = var_declaration(var_name, value, return_prefix=True)
129
+ prefix_len = len(prefix)
130
+ value = f"{prefix}{value}"
131
+ relative_positions = [
132
+ {**r, "startPos": r["startPos"] + prefix_len} for r in relative_positions
133
+ ]
134
+ return value, relative_positions
135
+
136
+
137
+ def generate_code(selected, chars_before_each_line, push_var_name=None):
138
+ code = ""
139
+ var_name_to_decl_position = {}
140
+ last_var_name = None
141
+ if push_var_name:
142
+ for v in selected.values():
143
+ if (
144
+ isinstance(v["value"], dict)
145
+ and push_var_name in v["value"]["unusedParams"]
146
+ ):
147
+ v["value"]["usedParams"].append(push_var_name)
148
+ v["dependencies"].append(push_var_name)
149
+ v["value"]["unusedParams"].remove(push_var_name)
150
+ selected = [{"var_name": k, **v} for k, v in selected.items()]
151
+ selected = push_var_deep(selected, push_var_name)
152
+ selected = {v.pop("var_name"): v for v in selected}
153
+
154
+ for var_name, x in selected.items():
155
+ force_appear = push_var_name == var_name
156
+ var_str, ref_var_name_positions = create_line(
157
+ var_name, x, selected, force_appear=force_appear
158
+ )
159
+ if var_str:
160
+ last_var_name = var_name
161
+ # We add the "before chars" after the new line.
162
+ # This way every var decl starts with the var instead of with the leading chars.
163
+ var_str = construct_var_decl_with_trailing_whitespace(
164
+ var_str, chars_before_each_line
165
+ )
166
+ startPos = len(code)
167
+ var_name_to_decl_position[var_name] = (
168
+ startPos,
169
+ var_str,
170
+ chars_before_each_line,
171
+ )
172
+ code += var_str
173
+ x["ref_var_name_positions"] = ref_var_name_positions
174
+
175
+ # Since each var declaration ends with the chars_before_each_line
176
+ # we need to remove the hanging one on the final variable declaration.
177
+ if last_var_name is not None:
178
+ startPos, var_str, chars_after_line = var_name_to_decl_position[last_var_name]
179
+ new_var_str = var_str.rstrip(chars_after_line)
180
+ var_name_to_decl_position[last_var_name] = startPos, new_var_str, ""
181
+ code = code.rstrip(chars_after_line)
182
+
183
+ return code, var_name_to_decl_position
184
+
185
+
186
+ def construct_var_decl_with_trailing_whitespace(var_decl, chars_after_line):
187
+ return f"{var_decl}\n{chars_after_line}"
188
+
189
+
190
+ def generate_imports(
191
+ code,
192
+ push_var_name,
193
+ var_name_to_decl_position,
194
+ template_var,
195
+ chars_before_template_var,
196
+ ):
197
+ if push_var_name:
198
+ startPos, var_decl, chars_after_line = var_name_to_decl_position[push_var_name]
199
+ endPos = startPos + len(var_decl)
200
+ parseable_str = make_parseable(code[startPos:endPos], push_var_name)
201
+ if parseable_str is None:
202
+ parseable_str = var_declaration(push_var_name, "None")
203
+ var_decl = construct_var_decl_with_trailing_whitespace(
204
+ parseable_str, chars_after_line
205
+ )
206
+ code = code[:startPos] + var_decl + code[endPos:]
207
+ startPos = template_var["startPos"]
208
+ endPos = startPos + len(template_var["keyword"])
209
+ # remove template var to infer imports, because ast will raise syntax error
210
+ code = code[:startPos] + code[endPos:]
211
+ imports = get_import_text(code, chars_before_template_var)
212
+ if len(imports) > 0:
213
+ imports += "\n"
214
+ return imports
215
+
216
+
217
+ def convert_var_names_to_readable_form(
218
+ generated_code,
219
+ var_name_to_decl_rel_position,
220
+ exporter_obj,
221
+ ref_template_var_positions,
222
+ setup_code,
223
+ ):
224
+ # need to adjust line numbers and positions with prepended setup code length
225
+ # because the setup code has been prepended to the generated code
226
+ # after the line numbers and positions are calculated.
227
+ setup_code_len = len(setup_code)
228
+
229
+ all_ref_var_name_positions = [
230
+ (r["startPos"] + setup_code_len, r["value"])
231
+ for r in ref_template_var_positions["refs"]
232
+ ]
233
+
234
+ gen_code_template_var = get_gen_code_template_var(
235
+ ref_template_var_positions["templateVars"]
236
+ )
237
+
238
+ # convert to global positions
239
+ for var_name, (startPos, _, _) in var_name_to_decl_rel_position.items():
240
+ newStartPos = startPos + setup_code_len
241
+ if gen_code_template_var:
242
+ newStartPos += gen_code_template_var["startPos"]
243
+ all_ref_var_name_positions.append((newStartPos, var_name))
244
+ for r in exporter_obj.output[var_name]["ref_var_name_positions"]:
245
+ all_ref_var_name_positions.append((newStartPos + r["startPos"], r["value"]))
246
+
247
+ # create nice var names for references
248
+ top_level_symbols = find_top_level_symbols(generated_code)
249
+ var_name_to_nice_var_name_mapping = {}
250
+ for idx in range(len(all_ref_var_name_positions)):
251
+ position, var_name = all_ref_var_name_positions[idx]
252
+
253
+ # only create nice_var_name if we haven't already created it for this var_name
254
+ if var_name not in var_name_to_nice_var_name_mapping:
255
+ nice_name = exporter_obj.output[var_name]["name"]
256
+ nice_var_name = create_nice_var_name(top_level_symbols, nice_name)
257
+ top_level_symbols.add(nice_var_name)
258
+ var_name_to_nice_var_name_mapping[var_name] = nice_var_name
259
+
260
+ nice_var_name = var_name_to_nice_var_name_mapping[var_name]
261
+ all_ref_var_name_positions[idx] = position, var_name, nice_var_name
262
+
263
+ generated_code = replace_at_positions(generated_code, all_ref_var_name_positions)
264
+ return generated_code
265
+
266
+
267
+ def create_nice_var_name(top_level_symbols, nice_name):
268
+ trial_name = nice_name
269
+ counter = 2
270
+ while trial_name in top_level_symbols:
271
+ trial_name = f"{nice_name}_{counter}"
272
+ counter += 1
273
+ return trial_name
274
+
275
+
276
+ def get_chars_in_line_before_position(string, position):
277
+ last_newline = string.rfind("\n", 0, position)
278
+ if last_newline != -1:
279
+ return string[last_newline + 1 : position]
280
+ return ""