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
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 ""
|