setta 0.0.8.dev1__py3-none-any.whl → 0.0.9.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- setta/__init__.py +1 -1
- setta/code_gen/export_selected.py +6 -4
- setta/database/db/sections/load.py +16 -1
- setta/lsp/file_watcher.py +0 -16
- setta/lsp/specific_file_watcher.py +249 -0
- setta/lsp/utils.py +20 -0
- setta/routers/dependencies.py +4 -0
- setta/routers/sections.py +14 -1
- setta/server.py +6 -0
- setta/static/constants/constants.json +2 -0
- setta/static/frontend/assets/{index-20612afa.js → index-0134e43f.js} +145 -145
- setta/static/frontend/index.html +1 -1
- setta/utils/websocket_manager.py +5 -0
- {setta-0.0.8.dev1.dist-info → setta-0.0.9.dev0.dist-info}/METADATA +10 -5
- {setta-0.0.8.dev1.dist-info → setta-0.0.9.dev0.dist-info}/RECORD +19 -18
- {setta-0.0.8.dev1.dist-info → setta-0.0.9.dev0.dist-info}/WHEEL +1 -1
- {setta-0.0.8.dev1.dist-info → setta-0.0.9.dev0.dist-info}/LICENSE +0 -0
- {setta-0.0.8.dev1.dist-info → setta-0.0.9.dev0.dist-info}/entry_points.txt +0 -0
- {setta-0.0.8.dev1.dist-info → setta-0.0.9.dev0.dist-info}/top_level.txt +0 -0
setta/__init__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "0.0.
|
1
|
+
__version__ = "0.0.9.dev0"
|
@@ -549,7 +549,7 @@ class ExporterForInMemoryFn:
|
|
549
549
|
C.LIST_ROOT,
|
550
550
|
C.DICT_ROOT,
|
551
551
|
C.GROUP,
|
552
|
-
|
552
|
+
C.IMAGE,
|
553
553
|
# C.CHART,
|
554
554
|
C.DRAW,
|
555
555
|
C.CHAT,
|
@@ -588,9 +588,11 @@ class ExporterForInMemoryFn:
|
|
588
588
|
value[child_name] = self.export_section(
|
589
589
|
c, f'{name}["{child_name}"]'
|
590
590
|
)
|
591
|
-
|
592
|
-
|
593
|
-
|
591
|
+
elif type in [C.IMAGE]:
|
592
|
+
artifacts = get_artifacts(self.p, id)
|
593
|
+
img = artifacts[0]["value"] if len(artifacts) > 0 else None
|
594
|
+
value = {"image": img}
|
595
|
+
self.create_var_mapping((id, "image"), f'{name}["image"]')
|
594
596
|
elif type == C.DRAW:
|
595
597
|
value = {"drawing": get_drawing(self.p, id)}
|
596
598
|
self.create_var_mapping((id, "drawing"), f'{name}["drawing"]')
|
@@ -210,7 +210,11 @@ def load_json_sources_into_data_structures(
|
|
210
210
|
if v["jsonSource"] and ((not section_ids) or k in section_ids)
|
211
211
|
}
|
212
212
|
for s in sections.values():
|
213
|
+
logger.debug(
|
214
|
+
f'Attempting to read {s["jsonSource"]} with keys {s["jsonSourceKeys"]}'
|
215
|
+
)
|
213
216
|
new_data = load_json_source(s["jsonSource"], s["jsonSourceKeys"])
|
217
|
+
logger.debug(f"Loaded json: {new_data}")
|
214
218
|
for filename, data in new_data.items():
|
215
219
|
codeInfo.update(data["codeInfo"])
|
216
220
|
variantId = None
|
@@ -244,6 +248,9 @@ def load_json_sources_into_data_structures(
|
|
244
248
|
for s in sections.values():
|
245
249
|
for vid in s["variantIds"]:
|
246
250
|
if sectionVariants[vid]["name"] not in filenames_loaded:
|
251
|
+
logger.debug(
|
252
|
+
f'Removing variant {sectionVariants[vid]["name"]} because the associated json was not found'
|
253
|
+
)
|
247
254
|
to_delete.append(vid)
|
248
255
|
|
249
256
|
for vid in to_delete:
|
@@ -255,14 +262,22 @@ def load_json_sources_into_data_structures(
|
|
255
262
|
s["jsonSourceMissing"] = False
|
256
263
|
s["variantIds"] = [v for v in s["variantIds"] if v in sectionVariants]
|
257
264
|
if len(s["variantIds"]) == 0:
|
265
|
+
logger.debug("Section has no variantIds. Creating new section variant.")
|
258
266
|
variantId, variant = new_section_variant()
|
259
|
-
s["variantIds"].append(variantId)
|
260
267
|
sectionVariants[variantId] = variant
|
268
|
+
s["variantId"] = variantId
|
269
|
+
s["variantIds"].append(variantId)
|
261
270
|
s["jsonSourceMissing"] = True
|
262
271
|
elif s["variantId"] not in s["variantIds"]:
|
272
|
+
logger.debug(
|
273
|
+
"Selected variantId is not in list of variantIds. Changing selected variantId"
|
274
|
+
)
|
263
275
|
s["variantId"] = s["variantIds"][0]
|
264
276
|
|
265
277
|
if s["defaultVariantId"] not in s["variantIds"]:
|
278
|
+
logger.debug(
|
279
|
+
"Default variantId is not in list of variantIds. Changing default variantId"
|
280
|
+
)
|
266
281
|
s["defaultVariantId"] = s["variantId"]
|
267
282
|
|
268
283
|
|
setta/lsp/file_watcher.py
CHANGED
@@ -53,22 +53,6 @@ class LSPFileWatcher:
|
|
53
53
|
self.observer.stop()
|
54
54
|
self.observer.join()
|
55
55
|
|
56
|
-
def is_watching(self, path: str) -> bool:
|
57
|
-
"""
|
58
|
-
Check if a path is being watched.
|
59
|
-
|
60
|
-
Args:
|
61
|
-
path: Path to check
|
62
|
-
|
63
|
-
Returns:
|
64
|
-
bool: True if the path is being watched
|
65
|
-
"""
|
66
|
-
absolute_path = os.path.abspath(path)
|
67
|
-
return any(
|
68
|
-
absolute_path.startswith(watched_path)
|
69
|
-
for watched_path in self.watched_paths
|
70
|
-
)
|
71
|
-
|
72
56
|
|
73
57
|
class LSPEventHandler(FileSystemEventHandler):
|
74
58
|
def __init__(self, callback, loop):
|
@@ -0,0 +1,249 @@
|
|
1
|
+
import asyncio
|
2
|
+
import logging
|
3
|
+
import os
|
4
|
+
from typing import Dict, List, Set
|
5
|
+
|
6
|
+
from watchdog.events import FileSystemEvent, FileSystemEventHandler
|
7
|
+
from watchdog.observers import Observer
|
8
|
+
|
9
|
+
logger = logging.getLogger(__name__)
|
10
|
+
|
11
|
+
|
12
|
+
class SpecificFileWatcher:
|
13
|
+
"""File watcher that monitors specific files and notifies via a single callback."""
|
14
|
+
|
15
|
+
def __init__(self, callback):
|
16
|
+
"""
|
17
|
+
Initialize the file watcher for specific files.
|
18
|
+
|
19
|
+
Args:
|
20
|
+
callback: Function to call when any watched file changes
|
21
|
+
Callback receives (file_path, event_type) where event_type is
|
22
|
+
one of 'created', 'modified', 'deleted', or 'moved'
|
23
|
+
"""
|
24
|
+
self.observer = Observer()
|
25
|
+
self.watched_files: Set[str] = set()
|
26
|
+
self.handler = SpecificFileEventHandler(
|
27
|
+
callback, asyncio.get_event_loop(), self.watched_files
|
28
|
+
)
|
29
|
+
|
30
|
+
def add_file(self, file_path: str) -> bool:
|
31
|
+
"""
|
32
|
+
Add a specific file to watch.
|
33
|
+
|
34
|
+
Args:
|
35
|
+
file_path: Absolute path to the file to watch
|
36
|
+
|
37
|
+
Returns:
|
38
|
+
bool: True if the file was added, False if it doesn't exist
|
39
|
+
"""
|
40
|
+
if not os.path.exists(file_path):
|
41
|
+
return False
|
42
|
+
|
43
|
+
absolute_path = os.path.abspath(file_path)
|
44
|
+
dir_path = os.path.dirname(absolute_path)
|
45
|
+
|
46
|
+
# Add file to watched files if not already there
|
47
|
+
if absolute_path not in self.watched_files:
|
48
|
+
self.watched_files.add(absolute_path)
|
49
|
+
else:
|
50
|
+
# Already watching this file
|
51
|
+
return True
|
52
|
+
|
53
|
+
# Schedule the directory for watching if needed
|
54
|
+
if not self.observer.emitters:
|
55
|
+
# No directories are being watched yet
|
56
|
+
self.observer.schedule(self.handler, dir_path, recursive=False)
|
57
|
+
else:
|
58
|
+
# Check if the directory is already being watched
|
59
|
+
is_dir_watched = False
|
60
|
+
for emitter in self.observer.emitters:
|
61
|
+
if emitter.watch.path == dir_path:
|
62
|
+
is_dir_watched = True
|
63
|
+
break
|
64
|
+
|
65
|
+
if not is_dir_watched:
|
66
|
+
self.observer.schedule(self.handler, dir_path, recursive=False)
|
67
|
+
|
68
|
+
return True
|
69
|
+
|
70
|
+
def remove_file(self, file_path: str) -> None:
|
71
|
+
"""
|
72
|
+
Remove a file from being watched.
|
73
|
+
|
74
|
+
Args:
|
75
|
+
file_path: Path to the file
|
76
|
+
"""
|
77
|
+
absolute_path = os.path.abspath(file_path)
|
78
|
+
|
79
|
+
if absolute_path in self.watched_files:
|
80
|
+
self.watched_files.remove(absolute_path)
|
81
|
+
|
82
|
+
def update_watch_list(self, file_paths: List[str]) -> Dict[str, List[str]]:
|
83
|
+
"""
|
84
|
+
Update the entire list of files being watched with a single function call.
|
85
|
+
This efficiently handles adding new files and removing files that are no longer needed.
|
86
|
+
|
87
|
+
Args:
|
88
|
+
file_paths: List of file paths that should be watched
|
89
|
+
|
90
|
+
Returns:
|
91
|
+
Dict containing 'added' and 'removed' lists of file paths
|
92
|
+
"""
|
93
|
+
# Convert all input paths to absolute paths (only if they exist)
|
94
|
+
absolute_paths = [
|
95
|
+
os.path.abspath(path) for path in file_paths if os.path.exists(path)
|
96
|
+
]
|
97
|
+
absolute_paths_set = set(absolute_paths)
|
98
|
+
|
99
|
+
# Calculate differences
|
100
|
+
files_to_add = absolute_paths_set - self.watched_files
|
101
|
+
files_to_remove = self.watched_files - absolute_paths_set
|
102
|
+
|
103
|
+
# Track which files were successfully added
|
104
|
+
added_files = []
|
105
|
+
|
106
|
+
# Add new files
|
107
|
+
for file_path in files_to_add:
|
108
|
+
success = self.add_file(file_path)
|
109
|
+
if success:
|
110
|
+
added_files.append(file_path)
|
111
|
+
|
112
|
+
# Remove files no longer in the list
|
113
|
+
for file_path in files_to_remove:
|
114
|
+
self.remove_file(file_path)
|
115
|
+
|
116
|
+
# Return information about what changed
|
117
|
+
return {"added": added_files, "removed": list(files_to_remove)}
|
118
|
+
|
119
|
+
def start(self) -> None:
|
120
|
+
"""Start the file watcher."""
|
121
|
+
self.observer.start()
|
122
|
+
self.started = True
|
123
|
+
|
124
|
+
def stop(self) -> None:
|
125
|
+
"""Stop the file watcher."""
|
126
|
+
self.observer.stop()
|
127
|
+
self.observer.join()
|
128
|
+
self.started = False
|
129
|
+
|
130
|
+
|
131
|
+
class SpecificFileEventHandler(FileSystemEventHandler):
|
132
|
+
"""Event handler for specific file events."""
|
133
|
+
|
134
|
+
def __init__(self, callback, loop, watched_files_ref):
|
135
|
+
"""
|
136
|
+
Initialize the event handler.
|
137
|
+
|
138
|
+
Args:
|
139
|
+
callback: Function to call when a watched file changes
|
140
|
+
loop: Asyncio event loop for async callbacks
|
141
|
+
watched_files_ref: Reference to the set of files being watched
|
142
|
+
"""
|
143
|
+
self.callback = callback
|
144
|
+
self.loop = loop
|
145
|
+
self.watched_files_ref = watched_files_ref
|
146
|
+
|
147
|
+
def on_created(self, event: FileSystemEvent):
|
148
|
+
"""Handle file creation event."""
|
149
|
+
if not event.is_directory:
|
150
|
+
file_path = os.path.abspath(event.src_path)
|
151
|
+
if file_path in self.watched_files_ref:
|
152
|
+
self._send_event(event, "created")
|
153
|
+
|
154
|
+
def on_modified(self, event: FileSystemEvent):
|
155
|
+
"""Handle file modification event."""
|
156
|
+
if not event.is_directory:
|
157
|
+
file_path = os.path.abspath(event.src_path)
|
158
|
+
if file_path in self.watched_files_ref:
|
159
|
+
self._send_event(event, "modified")
|
160
|
+
|
161
|
+
def on_deleted(self, event: FileSystemEvent):
|
162
|
+
"""Handle file deletion event."""
|
163
|
+
if not event.is_directory:
|
164
|
+
file_path = os.path.abspath(event.src_path)
|
165
|
+
if file_path in self.watched_files_ref:
|
166
|
+
self._send_event(event, "deleted")
|
167
|
+
|
168
|
+
def on_moved(self, event: FileSystemEvent):
|
169
|
+
"""Handle file move event."""
|
170
|
+
if not event.is_directory:
|
171
|
+
# For moved events, we need to check both source and destination
|
172
|
+
src_path = os.path.abspath(event.src_path)
|
173
|
+
dest_path = (
|
174
|
+
os.path.abspath(event.dest_path)
|
175
|
+
if hasattr(event, "dest_path")
|
176
|
+
else None
|
177
|
+
)
|
178
|
+
|
179
|
+
# If the source was being watched, notify about the move
|
180
|
+
if src_path in self.watched_files_ref:
|
181
|
+
self._send_event(event, "moved")
|
182
|
+
|
183
|
+
# If the destination is also being watched, notify about modification
|
184
|
+
if (
|
185
|
+
dest_path
|
186
|
+
and dest_path in self.watched_files_ref
|
187
|
+
and dest_path != src_path
|
188
|
+
):
|
189
|
+
# Create a modified event for the destination
|
190
|
+
self._send_event(event, "modified")
|
191
|
+
|
192
|
+
def _send_event(self, event: FileSystemEvent, event_type: str):
|
193
|
+
"""
|
194
|
+
Process and send the event to the callback.
|
195
|
+
|
196
|
+
Args:
|
197
|
+
event: The file system event
|
198
|
+
event_type: The type of event ('created', 'modified', 'deleted', 'moved')
|
199
|
+
"""
|
200
|
+
logger.debug("_send_event")
|
201
|
+
# Get absolute path
|
202
|
+
abs_path = os.path.abspath(event.src_path)
|
203
|
+
|
204
|
+
# Get relative path to current working directory
|
205
|
+
rel_path = os.path.relpath(abs_path)
|
206
|
+
|
207
|
+
# Get file contents for created and modified events
|
208
|
+
file_content = None
|
209
|
+
if event_type in ("created", "modified"):
|
210
|
+
try:
|
211
|
+
with open(abs_path, "r", encoding="utf-8") as f:
|
212
|
+
file_content = f.read()
|
213
|
+
except Exception as e:
|
214
|
+
logger.debug(f"Error reading file {abs_path}: {e}")
|
215
|
+
file_content = None
|
216
|
+
|
217
|
+
# For moved events, get the content of the destination file
|
218
|
+
dest_abs_path = None
|
219
|
+
dest_rel_path = None
|
220
|
+
if event_type == "moved" and hasattr(event, "dest_path"):
|
221
|
+
dest_abs_path = os.path.abspath(event.dest_path)
|
222
|
+
dest_rel_path = os.path.relpath(dest_abs_path)
|
223
|
+
try:
|
224
|
+
with open(dest_abs_path, "r", encoding="utf-8") as f:
|
225
|
+
file_content = f.read()
|
226
|
+
except Exception as e:
|
227
|
+
logger.debug(f"Error reading destination file {dest_abs_path}: {e}")
|
228
|
+
file_content = None
|
229
|
+
|
230
|
+
# Prepare event info object
|
231
|
+
event_info = {
|
232
|
+
"absPath": abs_path,
|
233
|
+
"relPath": rel_path,
|
234
|
+
"eventType": event_type,
|
235
|
+
"fileContent": file_content,
|
236
|
+
}
|
237
|
+
|
238
|
+
# Add destination paths for moved events
|
239
|
+
if event_type == "moved" and dest_abs_path:
|
240
|
+
event_info["destAbsPath"] = dest_abs_path
|
241
|
+
event_info["destRelPath"] = dest_rel_path
|
242
|
+
|
243
|
+
logger.debug(f"will send {event_info}")
|
244
|
+
if asyncio.iscoroutinefunction(self.callback):
|
245
|
+
self.loop.call_soon_threadsafe(
|
246
|
+
lambda: self.loop.create_task(self.callback(event_info))
|
247
|
+
)
|
248
|
+
else:
|
249
|
+
self.callback(event_info)
|
setta/lsp/utils.py
CHANGED
@@ -1,8 +1,15 @@
|
|
1
|
+
import logging
|
2
|
+
|
3
|
+
from setta.utils.constants import C
|
4
|
+
|
1
5
|
from .file_watcher import LSPFileWatcher
|
2
6
|
from .reader import LanguageServerReader
|
3
7
|
from .server import LanguageServer
|
8
|
+
from .specific_file_watcher import SpecificFileWatcher
|
4
9
|
from .writer import LanguageServerWriter
|
5
10
|
|
11
|
+
logger = logging.getLogger(__name__)
|
12
|
+
|
6
13
|
|
7
14
|
def create_lsps(
|
8
15
|
workspace_folder,
|
@@ -42,6 +49,19 @@ def create_file_watcher(lsps, lsp_writers):
|
|
42
49
|
return file_watcher
|
43
50
|
|
44
51
|
|
52
|
+
def create_specific_file_watcher(websocket_manager):
|
53
|
+
async def callback(event_info):
|
54
|
+
logger.debug(f"callback {event_info}")
|
55
|
+
await websocket_manager.broadcast(
|
56
|
+
{
|
57
|
+
"content": event_info,
|
58
|
+
"messageType": C.WS_SPECIFIC_FILE_WATCHER_UPDATE,
|
59
|
+
}
|
60
|
+
)
|
61
|
+
|
62
|
+
return SpecificFileWatcher(callback)
|
63
|
+
|
64
|
+
|
45
65
|
async def start_lsps(lsps, lsp_readers, lsp_writers):
|
46
66
|
for k, v in lsps.items():
|
47
67
|
await v.start_server()
|
setta/routers/dependencies.py
CHANGED
setta/routers/sections.py
CHANGED
@@ -2,7 +2,7 @@ import os
|
|
2
2
|
from pathlib import Path
|
3
3
|
from typing import Dict, List, Optional
|
4
4
|
|
5
|
-
from fastapi import APIRouter, HTTPException, status
|
5
|
+
from fastapi import APIRouter, Depends, HTTPException, status
|
6
6
|
from pydantic import BaseModel
|
7
7
|
|
8
8
|
from setta.code_gen.export_selected import (
|
@@ -21,6 +21,7 @@ from setta.database.db.sections.copy import (
|
|
21
21
|
)
|
22
22
|
from setta.database.db.sections.jsonSource import save_json_source_data
|
23
23
|
from setta.database.db.sections.load import load_json_sources_into_data_structures
|
24
|
+
from setta.routers.dependencies import get_specific_file_watcher
|
24
25
|
from setta.utils.constants import C
|
25
26
|
from setta.utils.generate_new_filename import generate_new_filename
|
26
27
|
|
@@ -75,6 +76,10 @@ class DeleteFileRequest(BaseModel):
|
|
75
76
|
filepath: str
|
76
77
|
|
77
78
|
|
79
|
+
class FileWatchListRequest(BaseModel):
|
80
|
+
filepaths: List[str]
|
81
|
+
|
82
|
+
|
78
83
|
@router.post(C.ROUTE_COPY_SECTIONS)
|
79
84
|
def route_sections_make_copy(x: SectionsMakeCopyRequest):
|
80
85
|
output = copy_sections_and_other_info(x.sectionsAndOtherInfo)
|
@@ -172,3 +177,11 @@ def route_delete_file(x: DeleteFileRequest):
|
|
172
177
|
status_code=status.HTTP_404_NOT_FOUND,
|
173
178
|
detail=f"Failed to delete file: {str(e)}",
|
174
179
|
)
|
180
|
+
|
181
|
+
|
182
|
+
@router.post(C.ROUTE_FILE_WATCH_LIST)
|
183
|
+
def route_file_watch_list(
|
184
|
+
x: FileWatchListRequest, specific_file_watcher=Depends(get_specific_file_watcher)
|
185
|
+
):
|
186
|
+
# x.filepaths is the current list of file paths that should be watched
|
187
|
+
specific_file_watcher.update_watch_list(x.filepaths)
|
setta/server.py
CHANGED
@@ -16,6 +16,7 @@ from setta.lsp.utils import (
|
|
16
16
|
create_lsp_readers,
|
17
17
|
create_lsp_writers,
|
18
18
|
create_lsps,
|
19
|
+
create_specific_file_watcher,
|
19
20
|
kill_lsps,
|
20
21
|
start_lsps,
|
21
22
|
)
|
@@ -52,6 +53,9 @@ async def lifespan(app: FastAPI):
|
|
52
53
|
app.state.file_watcher = create_file_watcher(app.state.lsps, app.state.lsp_writers)
|
53
54
|
app.state.terminal_websockets = TerminalWebsockets()
|
54
55
|
app.state.websocket_manager = WebsocketManager()
|
56
|
+
app.state.specific_file_watcher = create_specific_file_watcher(
|
57
|
+
app.state.websocket_manager
|
58
|
+
)
|
55
59
|
app.state.tasks = Tasks(app.state.lsp_writers)
|
56
60
|
app.state.lsp_readers = create_lsp_readers(
|
57
61
|
app.state.lsps, app.state.websocket_manager
|
@@ -73,6 +77,7 @@ async def lifespan(app: FastAPI):
|
|
73
77
|
app.state.lsp_writers,
|
74
78
|
)
|
75
79
|
app.state.file_watcher.start()
|
80
|
+
app.state.specific_file_watcher.start()
|
76
81
|
|
77
82
|
if not is_dev_mode():
|
78
83
|
# Mount the 'frontend/dist' directory at '/static'
|
@@ -98,6 +103,7 @@ async def lifespan(app: FastAPI):
|
|
98
103
|
finally:
|
99
104
|
app.state.tasks.close()
|
100
105
|
app.state.file_watcher.stop()
|
106
|
+
app.state.specific_file_watcher.stop()
|
101
107
|
await kill_lsps(app.state.lsps, app.state.lsp_readers)
|
102
108
|
|
103
109
|
|
@@ -93,6 +93,7 @@
|
|
93
93
|
"ROUTE_CHECK_IF_FILE_EXISTS": "/checkIfFileExists",
|
94
94
|
"ROUTE_LOAD_ARTIFACT_FROM_DISK": "/loadArtifactFromDisk",
|
95
95
|
"ROUTE_RESTART_LANGUAGE_SERVER": "/restartLanguageServer",
|
96
|
+
"ROUTE_FILE_WATCH_LIST": "/fileWatchList",
|
96
97
|
"JSON_SOURCE_PREFIX": "JSON-",
|
97
98
|
"NESTED_PARAM": "NESTED_PARAM",
|
98
99
|
"ARGS_PREFIX": "__",
|
@@ -105,6 +106,7 @@
|
|
105
106
|
"WS_TERMINAL_RESIZE": "terminalResize",
|
106
107
|
"WS_LSP_STATUS": "lspStatus",
|
107
108
|
"WS_IN_MEMORY_FN_AVG_RUN_TIME": "inMemoryFnAvgRunTime",
|
109
|
+
"WS_SPECIFIC_FILE_WATCHER_UPDATE": "specificFileWatcherUpdate",
|
108
110
|
"SETTA_GENERATED_PYTHON": "SETTA_GENERATED_PYTHON",
|
109
111
|
"SETTA_GENERATED_PYTHON_IMPORTS": "SETTA_GENERATED_PYTHON_IMPORTS",
|
110
112
|
"TEMPLATE_VAR_IMPORT_PATH_SUFFIX": "import_path",
|