setta 0.0.1.dev0__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.
- setta/__init__.py +1 -1
- setta/cli/__init__.py +1 -0
- setta/cli/connect.py +43 -0
- setta/cli/logger.py +225 -0
- setta/code_gen/__init__.py +0 -0
- setta/code_gen/create_runnable_scripts.py +466 -0
- setta/code_gen/export_selected.py +776 -0
- setta/code_gen/find_placeholders.py +13 -0
- setta/code_gen/python/__init__.py +0 -0
- setta/code_gen/python/ast_utils.py +183 -0
- setta/code_gen/python/check_scope.py +187 -0
- setta/code_gen/python/generate_code.py +280 -0
- setta/code_gen/python/make_parseable.py +97 -0
- setta/code_gen/python/position_line_col.py +33 -0
- setta/code_gen/python/validate_imports.py +87 -0
- setta/code_gen/utils.py +120 -0
- setta/code_gen/yaml/__init__.py +0 -0
- setta/code_gen/yaml/generate_yaml.py +23 -0
- setta/code_gen/yaml/section_dict.py +93 -0
- setta/database/__init__.py +0 -0
- setta/database/backup.py +80 -0
- setta/database/db/__init__.py +0 -0
- setta/database/db/artifacts/__init__.py +0 -0
- setta/database/db/artifacts/load.py +93 -0
- setta/database/db/artifacts/save.py +85 -0
- setta/database/db/artifacts/save_or_create.py +68 -0
- setta/database/db/artifacts/utils.py +13 -0
- setta/database/db/codeInfo/__init__.py +0 -0
- setta/database/db/codeInfo/copy.py +26 -0
- setta/database/db/codeInfo/load.py +65 -0
- setta/database/db/codeInfo/save.py +75 -0
- setta/database/db/codeInfo/utils.py +33 -0
- setta/database/db/evRefs/__init__.py +0 -0
- setta/database/db/evRefs/load.py +45 -0
- setta/database/db/evRefs/save.py +95 -0
- setta/database/db/projects/__init__.py +0 -0
- setta/database/db/projects/copy.py +36 -0
- setta/database/db/projects/delete.py +7 -0
- setta/database/db/projects/load.py +184 -0
- setta/database/db/projects/save.py +267 -0
- setta/database/db/projects/saveAs.py +40 -0
- setta/database/db/projects/utils.py +135 -0
- setta/database/db/sectionVariants/__init__.py +0 -0
- setta/database/db/sectionVariants/copy.py +28 -0
- setta/database/db/sectionVariants/load.py +139 -0
- setta/database/db/sectionVariants/save.py +140 -0
- setta/database/db/sectionVariants/utils.py +44 -0
- setta/database/db/sections/__init__.py +0 -0
- setta/database/db/sections/copy.py +70 -0
- setta/database/db/sections/jsonSource.py +119 -0
- setta/database/db/sections/load.py +350 -0
- setta/database/db/sections/save.py +204 -0
- setta/database/db/sections/utils.py +13 -0
- setta/database/db/uiTypes/__init__.py +0 -0
- setta/database/db/uiTypes/copy.py +33 -0
- setta/database/db/uiTypes/load.py +51 -0
- setta/database/db/uiTypes/save.py +99 -0
- setta/database/db/uiTypes/utils.py +27 -0
- setta/database/db_init.py +36 -0
- setta/database/db_objs.py +102 -0
- setta/database/db_path.py +8 -0
- setta/database/export_db/__init__.py +0 -0
- setta/database/export_db/export_db.py +43 -0
- setta/database/export_db/export_raw.py +53 -0
- setta/database/export_db/export_readable.py +242 -0
- setta/database/export_db/utils.py +16 -0
- setta/database/import_db.py +28 -0
- setta/database/seed.py +41 -0
- setta/database/settings_file.py +118 -0
- setta/database/utils.py +32 -0
- setta/lsp/__init__.py +0 -0
- setta/lsp/file_watcher.py +113 -0
- setta/lsp/reader.py +184 -0
- setta/lsp/reader_fns/__init__.py +0 -0
- setta/lsp/reader_fns/completion.py +84 -0
- setta/lsp/reader_fns/definition.py +2 -0
- setta/lsp/reader_fns/diagnostics.py +99 -0
- setta/lsp/reader_fns/documentHighlight.py +25 -0
- setta/lsp/reader_fns/references.py +34 -0
- setta/lsp/reader_fns/signatureHelp.py +99 -0
- setta/lsp/server.py +150 -0
- setta/lsp/utils.py +60 -0
- setta/lsp/writer.py +306 -0
- setta/routers/__init__.py +11 -0
- setta/routers/artifact.py +105 -0
- setta/routers/code_info.py +32 -0
- setta/routers/dependencies.py +49 -0
- setta/routers/in_memory_fn_stdout_websocket.py +21 -0
- setta/routers/interactive.py +119 -0
- setta/routers/lsp.py +14 -0
- setta/routers/projects.py +188 -0
- setta/routers/reference_renaming.py +111 -0
- setta/routers/sections.py +174 -0
- setta/routers/settings.py +40 -0
- setta/routers/terminals.py +83 -0
- setta/routers/websocket.py +36 -0
- setta/server.py +141 -0
- setta/start.py +112 -0
- setta/static/constants/BaseUITypes.json +153 -0
- setta/static/constants/Settings.json +113 -0
- setta/static/constants/constants.json +117 -0
- setta/static/constants/db_init.sql +249 -0
- setta/static/constants/defaultValues.json +125 -0
- setta/static/constants/settingsProject.json +276 -0
- setta/static/frontend/android-chrome-192x192.png +0 -0
- setta/static/frontend/android-chrome-512x512.png +0 -0
- setta/static/frontend/apple-touch-icon.png +0 -0
- setta/static/frontend/assets/KaTeX_AMS-Regular-0cdd387c.woff2 +0 -0
- setta/static/frontend/assets/KaTeX_AMS-Regular-30da91e8.woff +0 -0
- setta/static/frontend/assets/KaTeX_AMS-Regular-68534840.ttf +0 -0
- setta/static/frontend/assets/KaTeX_Caligraphic-Bold-07d8e303.ttf +0 -0
- setta/static/frontend/assets/KaTeX_Caligraphic-Bold-1ae6bd74.woff +0 -0
- setta/static/frontend/assets/KaTeX_Caligraphic-Bold-de7701e4.woff2 +0 -0
- setta/static/frontend/assets/KaTeX_Caligraphic-Regular-3398dd02.woff +0 -0
- setta/static/frontend/assets/KaTeX_Caligraphic-Regular-5d53e70a.woff2 +0 -0
- setta/static/frontend/assets/KaTeX_Caligraphic-Regular-ed0b7437.ttf +0 -0
- setta/static/frontend/assets/KaTeX_Fraktur-Bold-74444efd.woff2 +0 -0
- setta/static/frontend/assets/KaTeX_Fraktur-Bold-9163df9c.ttf +0 -0
- setta/static/frontend/assets/KaTeX_Fraktur-Bold-9be7ceb8.woff +0 -0
- setta/static/frontend/assets/KaTeX_Fraktur-Regular-1e6f9579.ttf +0 -0
- setta/static/frontend/assets/KaTeX_Fraktur-Regular-51814d27.woff2 +0 -0
- setta/static/frontend/assets/KaTeX_Fraktur-Regular-5e28753b.woff +0 -0
- setta/static/frontend/assets/KaTeX_Main-Bold-0f60d1b8.woff2 +0 -0
- setta/static/frontend/assets/KaTeX_Main-Bold-138ac28d.ttf +0 -0
- setta/static/frontend/assets/KaTeX_Main-Bold-c76c5d69.woff +0 -0
- setta/static/frontend/assets/KaTeX_Main-BoldItalic-70ee1f64.ttf +0 -0
- setta/static/frontend/assets/KaTeX_Main-BoldItalic-99cd42a3.woff2 +0 -0
- setta/static/frontend/assets/KaTeX_Main-BoldItalic-a6f7ec0d.woff +0 -0
- setta/static/frontend/assets/KaTeX_Main-Italic-0d85ae7c.ttf +0 -0
- setta/static/frontend/assets/KaTeX_Main-Italic-97479ca6.woff2 +0 -0
- setta/static/frontend/assets/KaTeX_Main-Italic-f1d6ef86.woff +0 -0
- setta/static/frontend/assets/KaTeX_Main-Regular-c2342cd8.woff2 +0 -0
- setta/static/frontend/assets/KaTeX_Main-Regular-c6368d87.woff +0 -0
- setta/static/frontend/assets/KaTeX_Main-Regular-d0332f52.ttf +0 -0
- setta/static/frontend/assets/KaTeX_Math-BoldItalic-850c0af5.woff +0 -0
- setta/static/frontend/assets/KaTeX_Math-BoldItalic-dc47344d.woff2 +0 -0
- setta/static/frontend/assets/KaTeX_Math-BoldItalic-f9377ab0.ttf +0 -0
- setta/static/frontend/assets/KaTeX_Math-Italic-08ce98e5.ttf +0 -0
- setta/static/frontend/assets/KaTeX_Math-Italic-7af58c5e.woff2 +0 -0
- setta/static/frontend/assets/KaTeX_Math-Italic-8a8d2445.woff +0 -0
- setta/static/frontend/assets/KaTeX_SansSerif-Bold-1ece03f7.ttf +0 -0
- setta/static/frontend/assets/KaTeX_SansSerif-Bold-e99ae511.woff2 +0 -0
- setta/static/frontend/assets/KaTeX_SansSerif-Bold-ece03cfd.woff +0 -0
- setta/static/frontend/assets/KaTeX_SansSerif-Italic-00b26ac8.woff2 +0 -0
- setta/static/frontend/assets/KaTeX_SansSerif-Italic-3931dd81.ttf +0 -0
- setta/static/frontend/assets/KaTeX_SansSerif-Italic-91ee6750.woff +0 -0
- setta/static/frontend/assets/KaTeX_SansSerif-Regular-11e4dc8a.woff +0 -0
- setta/static/frontend/assets/KaTeX_SansSerif-Regular-68e8c73e.woff2 +0 -0
- setta/static/frontend/assets/KaTeX_SansSerif-Regular-f36ea897.ttf +0 -0
- setta/static/frontend/assets/KaTeX_Script-Regular-036d4e95.woff2 +0 -0
- setta/static/frontend/assets/KaTeX_Script-Regular-1c67f068.ttf +0 -0
- setta/static/frontend/assets/KaTeX_Script-Regular-d96cdf2b.woff +0 -0
- setta/static/frontend/assets/KaTeX_Size1-Regular-6b47c401.woff2 +0 -0
- setta/static/frontend/assets/KaTeX_Size1-Regular-95b6d2f1.ttf +0 -0
- setta/static/frontend/assets/KaTeX_Size1-Regular-c943cc98.woff +0 -0
- setta/static/frontend/assets/KaTeX_Size2-Regular-2014c523.woff +0 -0
- setta/static/frontend/assets/KaTeX_Size2-Regular-a6b2099f.ttf +0 -0
- setta/static/frontend/assets/KaTeX_Size2-Regular-d04c5421.woff2 +0 -0
- setta/static/frontend/assets/KaTeX_Size3-Regular-500e04d5.ttf +0 -0
- setta/static/frontend/assets/KaTeX_Size3-Regular-6ab6b62e.woff +0 -0
- setta/static/frontend/assets/KaTeX_Size4-Regular-99f9c675.woff +0 -0
- setta/static/frontend/assets/KaTeX_Size4-Regular-a4af7d41.woff2 +0 -0
- setta/static/frontend/assets/KaTeX_Size4-Regular-c647367d.ttf +0 -0
- setta/static/frontend/assets/KaTeX_Typewriter-Regular-71d517d6.woff2 +0 -0
- setta/static/frontend/assets/KaTeX_Typewriter-Regular-e14fed02.woff +0 -0
- setta/static/frontend/assets/KaTeX_Typewriter-Regular-f01f3e87.ttf +0 -0
- setta/static/frontend/assets/cormorant-garamond-all-700-italic-c9b58582.woff +0 -0
- setta/static/frontend/assets/cormorant-garamond-cyrillic-700-italic-9101ad5f.woff2 +0 -0
- setta/static/frontend/assets/cormorant-garamond-cyrillic-ext-700-italic-950de0d6.woff2 +0 -0
- setta/static/frontend/assets/cormorant-garamond-latin-700-italic-0bc53e12.woff2 +0 -0
- setta/static/frontend/assets/cormorant-garamond-latin-ext-700-italic-525738e0.woff2 +0 -0
- setta/static/frontend/assets/cormorant-garamond-vietnamese-700-italic-99563037.woff2 +0 -0
- setta/static/frontend/assets/erase-5e0448ea.svg +15 -0
- setta/static/frontend/assets/index-1d4b4ecf.css +32 -0
- setta/static/frontend/assets/index-ee99dc72.js +678 -0
- setta/static/frontend/assets/inter-all-400-normal-054f12d0.woff +0 -0
- setta/static/frontend/assets/inter-all-600-normal-c03769e5.woff +0 -0
- setta/static/frontend/assets/inter-all-800-normal-15dc6e4b.woff +0 -0
- setta/static/frontend/assets/inter-cyrillic-400-normal-a4eee61a.woff2 +0 -0
- setta/static/frontend/assets/inter-cyrillic-600-normal-8b14f703.woff2 +0 -0
- setta/static/frontend/assets/inter-cyrillic-800-normal-e706eaaa.woff2 +0 -0
- setta/static/frontend/assets/inter-cyrillic-ext-400-normal-70047a3b.woff2 +0 -0
- setta/static/frontend/assets/inter-cyrillic-ext-600-normal-d4ab9bc4.woff2 +0 -0
- setta/static/frontend/assets/inter-cyrillic-ext-800-normal-eae7515a.woff2 +0 -0
- setta/static/frontend/assets/inter-greek-400-normal-381ea30d.woff2 +0 -0
- setta/static/frontend/assets/inter-greek-600-normal-601f93a2.woff2 +0 -0
- setta/static/frontend/assets/inter-greek-800-normal-7af4fb64.woff2 +0 -0
- setta/static/frontend/assets/inter-greek-ext-400-normal-27027b17.woff2 +0 -0
- setta/static/frontend/assets/inter-greek-ext-600-normal-f2ddf9de.woff2 +0 -0
- setta/static/frontend/assets/inter-greek-ext-800-normal-4cb6189e.woff2 +0 -0
- setta/static/frontend/assets/inter-latin-400-normal-d56fec21.woff2 +0 -0
- setta/static/frontend/assets/inter-latin-600-normal-ff769fa6.woff2 +0 -0
- setta/static/frontend/assets/inter-latin-800-normal-5eea1309.woff2 +0 -0
- setta/static/frontend/assets/inter-latin-ext-400-normal-bb698d85.woff2 +0 -0
- setta/static/frontend/assets/inter-latin-ext-600-normal-ca4808f9.woff2 +0 -0
- setta/static/frontend/assets/inter-latin-ext-800-normal-ebdacc0f.woff2 +0 -0
- setta/static/frontend/assets/logo/logo.svg +8 -0
- setta/static/frontend/assets/pen-455d7d8a.svg +19 -0
- setta/static/frontend/assets/source-code-pro-all-500-normal-6bdaa03b.woff +0 -0
- setta/static/frontend/assets/source-code-pro-cyrillic-500-normal-288a0d68.woff2 +0 -0
- setta/static/frontend/assets/source-code-pro-cyrillic-ext-500-normal-b110a13b.woff2 +0 -0
- setta/static/frontend/assets/source-code-pro-greek-500-normal-04328acb.woff2 +0 -0
- setta/static/frontend/assets/source-code-pro-latin-500-normal-06edef1e.woff2 +0 -0
- setta/static/frontend/assets/source-code-pro-latin-ext-500-normal-6dc60d5e.woff2 +0 -0
- setta/static/frontend/assets/web-vitals-44a8e082.js +1 -0
- setta/static/frontend/assets/work-sans-all-400-normal-38034a3c.woff +0 -0
- setta/static/frontend/assets/work-sans-all-500-normal-550d64e5.woff +0 -0
- setta/static/frontend/assets/work-sans-all-600-normal-ccf14060.woff +0 -0
- setta/static/frontend/assets/work-sans-all-700-normal-494c2971.woff +0 -0
- setta/static/frontend/assets/work-sans-latin-400-normal-36735bc1.woff2 +0 -0
- setta/static/frontend/assets/work-sans-latin-500-normal-3790bfda.woff2 +0 -0
- setta/static/frontend/assets/work-sans-latin-600-normal-5fba494e.woff2 +0 -0
- setta/static/frontend/assets/work-sans-latin-700-normal-a5033d0a.woff2 +0 -0
- setta/static/frontend/assets/work-sans-latin-ext-400-normal-c20f571a.woff2 +0 -0
- setta/static/frontend/assets/work-sans-latin-ext-500-normal-0f5ac96c.woff2 +0 -0
- setta/static/frontend/assets/work-sans-latin-ext-600-normal-97a237d1.woff2 +0 -0
- setta/static/frontend/assets/work-sans-latin-ext-700-normal-103e112c.woff2 +0 -0
- setta/static/frontend/browserconfig.xml +9 -0
- setta/static/frontend/favicon-16x16.png +0 -0
- setta/static/frontend/favicon-32x32.png +0 -0
- setta/static/frontend/favicon.ico +0 -0
- setta/static/frontend/index.html +30 -0
- setta/static/frontend/manifest.json +25 -0
- setta/static/frontend/mstile-144x144.png +0 -0
- setta/static/frontend/mstile-150x150.png +0 -0
- setta/static/frontend/mstile-310x150.png +0 -0
- setta/static/frontend/mstile-310x310.png +0 -0
- setta/static/frontend/mstile-70x70.png +0 -0
- setta/static/frontend/robots.txt +3 -0
- setta/static/frontend/safari-pinned-tab.svg +18 -0
- setta/static/frontend/site.webmanifest +19 -0
- setta/static/seed/.DS_Store +0 -0
- setta/static/seed/examples/.DS_Store +0 -0
- setta/tasks/__init__.py +0 -0
- setta/tasks/fns/__init__.py +9 -0
- setta/tasks/fns/codeAreaAutocomplete.py +209 -0
- setta/tasks/fns/codeAreaFindTemplateVars.py +128 -0
- setta/tasks/fns/codeAreaInitializeCode.py +98 -0
- setta/tasks/fns/findEVRefs.py +236 -0
- setta/tasks/fns/parametersRequest.py +71 -0
- setta/tasks/fns/textFieldAutocomplete.py +210 -0
- setta/tasks/fns/textFieldInitializeCode.py +99 -0
- setta/tasks/fns/typeCheck.py +40 -0
- setta/tasks/fns/utils.py +134 -0
- setta/tasks/task_runner.py +29 -0
- setta/tasks/tasks.py +152 -0
- setta/tasks/utils.py +178 -0
- setta/terminals/__init__.py +0 -0
- setta/terminals/terminals.py +242 -0
- setta/terminals/utils.py +37 -0
- setta/utils/__init__.py +0 -0
- setta/utils/constants.py +148 -0
- setta/utils/generate_memorable_string.py +431 -0
- setta/utils/generate_new_filename.py +80 -0
- setta/utils/section_contents.py +133 -0
- setta/utils/utils.py +271 -0
- setta/utils/websocket_manager.py +91 -0
- setta-0.0.2.dist-info/LICENSE +201 -0
- setta-0.0.2.dist-info/METADATA +24 -0
- setta-0.0.2.dist-info/RECORD +263 -0
- {setta-0.0.1.dev0.dist-info → setta-0.0.2.dist-info}/WHEEL +1 -1
- setta-0.0.2.dist-info/entry_points.txt +2 -0
- setta-0.0.1.dev0.dist-info/METADATA +0 -18
- setta-0.0.1.dev0.dist-info/RECORD +0 -5
- {setta-0.0.1.dev0.dist-info → setta-0.0.2.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
|
setta/code_gen/utils.py
ADDED
@@ -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
|
setta/database/backup.py
ADDED
@@ -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()
|