setta 0.0.1__py3-none-any.whl → 0.0.2__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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.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.dist-info/METADATA +0 -18
- setta-0.0.1.dist-info/RECORD +0 -5
- {setta-0.0.1.dist-info → setta-0.0.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,242 @@
|
|
1
|
+
import asyncio
|
2
|
+
import errno
|
3
|
+
import json
|
4
|
+
import logging
|
5
|
+
import platform
|
6
|
+
import select
|
7
|
+
import time
|
8
|
+
import traceback
|
9
|
+
from asyncio import CancelledError
|
10
|
+
from collections import defaultdict, deque
|
11
|
+
|
12
|
+
import psutil
|
13
|
+
from fastapi import WebSocketDisconnect
|
14
|
+
from websockets.exceptions import ConnectionClosedOK
|
15
|
+
|
16
|
+
from setta.code_gen.create_runnable_scripts import runCode
|
17
|
+
from setta.utils.constants import USER_SETTINGS, C
|
18
|
+
|
19
|
+
logger = logging.getLogger(__name__)
|
20
|
+
|
21
|
+
|
22
|
+
SETTA_CLEAR_CMD = "SETTA_CLEAR_CMD"
|
23
|
+
IS_WINDOWS = platform.system() == "Windows"
|
24
|
+
IS_MACOS = platform.system() == "Darwin"
|
25
|
+
|
26
|
+
|
27
|
+
if IS_WINDOWS:
|
28
|
+
from winpty import PtyProcess
|
29
|
+
|
30
|
+
from .utils import windows_has_child_processes as has_child_processes
|
31
|
+
|
32
|
+
else:
|
33
|
+
from ptyprocess import PtyProcessUnicode as PtyProcess
|
34
|
+
|
35
|
+
from .utils import linux_has_child_processes as has_child_processes
|
36
|
+
|
37
|
+
|
38
|
+
def is_command_running_in_pty(pid):
|
39
|
+
try:
|
40
|
+
return has_child_processes(pid)
|
41
|
+
except psutil.NoSuchProcess:
|
42
|
+
return False
|
43
|
+
|
44
|
+
|
45
|
+
def get_terminal_shell():
|
46
|
+
if USER_SETTINGS["backend"]["defaultTerminalShell"]:
|
47
|
+
return USER_SETTINGS["backend"]["defaultTerminalShell"]
|
48
|
+
if IS_WINDOWS:
|
49
|
+
return "bash.exe"
|
50
|
+
if IS_MACOS:
|
51
|
+
return "zsh"
|
52
|
+
return "bash"
|
53
|
+
|
54
|
+
|
55
|
+
class TerminalWebsockets:
|
56
|
+
def __init__(self):
|
57
|
+
self.section_to_socket_ids = defaultdict(set)
|
58
|
+
self.sockets = {}
|
59
|
+
self.PTY_PIDS = {}
|
60
|
+
|
61
|
+
def new_terminal(self, projectConfigId, sectionId, isTemporary):
|
62
|
+
if sectionId not in self.PTY_PIDS:
|
63
|
+
terminal_shell = get_terminal_shell()
|
64
|
+
if not IS_WINDOWS:
|
65
|
+
terminal_shell = [terminal_shell]
|
66
|
+
pty_process = PtyProcess.spawn(terminal_shell)
|
67
|
+
self.PTY_PIDS[sectionId] = {
|
68
|
+
"pid": pty_process.pid,
|
69
|
+
"start": time.time(),
|
70
|
+
"projectConfigId": projectConfigId,
|
71
|
+
"process": pty_process,
|
72
|
+
"history": deque(maxlen=1000),
|
73
|
+
"wasJustResized": False,
|
74
|
+
"terminalPromptIsReady": asyncio.Event(),
|
75
|
+
"isTemporary": isTemporary,
|
76
|
+
}
|
77
|
+
pty_info = self.PTY_PIDS[sectionId]
|
78
|
+
if (
|
79
|
+
IS_WINDOWS
|
80
|
+
or not USER_SETTINGS["backend"]["clearTerminalBeforeMarkingAsReady"]
|
81
|
+
):
|
82
|
+
pty_info["terminalPromptIsReady"].set()
|
83
|
+
|
84
|
+
logger.debug(f"created new pty_process {sectionId}")
|
85
|
+
if not pty_info["terminalPromptIsReady"].is_set():
|
86
|
+
pty_process.write(
|
87
|
+
f"alias {SETTA_CLEAR_CMD}='clear'\n{SETTA_CLEAR_CMD}\n"
|
88
|
+
)
|
89
|
+
return True
|
90
|
+
return False
|
91
|
+
|
92
|
+
async def connect(self, websocket, sectionId, id):
|
93
|
+
await websocket.accept()
|
94
|
+
self.sockets[id] = {"ws": websocket, "sentReadyMsg": False}
|
95
|
+
self.section_to_socket_ids[sectionId].add(id)
|
96
|
+
await self.PTY_PIDS[sectionId]["terminalPromptIsReady"].wait()
|
97
|
+
if not self.sockets[id]["sentReadyMsg"]:
|
98
|
+
await websocket.send_text(C.SETTA_TERMINAL_READY)
|
99
|
+
self.sockets[id]["sentReadyMsg"] = True
|
100
|
+
await websocket.send_text("".join(self.PTY_PIDS[sectionId]["history"]))
|
101
|
+
|
102
|
+
def disconnect(self, sectionId, id):
|
103
|
+
logger.debug(f"disconnecting {id}")
|
104
|
+
try:
|
105
|
+
del self.sockets[id]
|
106
|
+
except KeyError:
|
107
|
+
pass
|
108
|
+
self.section_to_socket_ids[sectionId].discard(id)
|
109
|
+
|
110
|
+
async def delete_terminal(self, sectionId):
|
111
|
+
logger.debug(f"delete terminal {sectionId}")
|
112
|
+
if sectionId in self.PTY_PIDS:
|
113
|
+
try:
|
114
|
+
await asyncio.to_thread(
|
115
|
+
self.PTY_PIDS[sectionId]["process"].terminate, force=True
|
116
|
+
)
|
117
|
+
del self.PTY_PIDS[sectionId]
|
118
|
+
except KeyError:
|
119
|
+
pass
|
120
|
+
|
121
|
+
# make copy to avoid set size changing while iterating
|
122
|
+
to_disconnect = set(self.section_to_socket_ids[sectionId])
|
123
|
+
for id in to_disconnect:
|
124
|
+
self.disconnect(sectionId, id)
|
125
|
+
|
126
|
+
async def delete_terminals(self, sectionIds):
|
127
|
+
await asyncio.gather(*(self.delete_terminal(s) for s in sectionIds))
|
128
|
+
|
129
|
+
async def delete_all_terminals(self):
|
130
|
+
await self.delete_terminals(self.PTY_PIDS.keys())
|
131
|
+
|
132
|
+
# Function to read from the terminal
|
133
|
+
async def read_from_terminal(self, sectionId):
|
134
|
+
pty_info = self.PTY_PIDS[sectionId]
|
135
|
+
pty_process = pty_info["process"]
|
136
|
+
history = pty_info["history"]
|
137
|
+
initial_buffer = ""
|
138
|
+
while True:
|
139
|
+
try:
|
140
|
+
output, _, _ = await asyncio.get_running_loop().run_in_executor(
|
141
|
+
None,
|
142
|
+
select.select,
|
143
|
+
[pty_process.fd],
|
144
|
+
[],
|
145
|
+
[],
|
146
|
+
None, # timeout of 0 makes this non-blocking
|
147
|
+
)
|
148
|
+
if not output:
|
149
|
+
continue
|
150
|
+
output = pty_process.read(1024)
|
151
|
+
if not pty_info["terminalPromptIsReady"].is_set():
|
152
|
+
initial_buffer += output
|
153
|
+
# We wait for the SETTA_CLEAR_CMD string to appear four times
|
154
|
+
# before we consider the terminal ready.
|
155
|
+
# This is just to make the terminal look less cluttered and annoying on the frontend.
|
156
|
+
if initial_buffer.count(SETTA_CLEAR_CMD) >= 4:
|
157
|
+
pty_info["terminalPromptIsReady"].set()
|
158
|
+
await self.send_output_to_sectionId(
|
159
|
+
sectionId, C.SETTA_TERMINAL_READY, setSentReadyMsg=True
|
160
|
+
)
|
161
|
+
continue
|
162
|
+
if not pty_info["wasJustResized"]:
|
163
|
+
history.append(output)
|
164
|
+
pty_info["wasJustResized"] = False
|
165
|
+
await self.send_output_to_sectionId(sectionId, output)
|
166
|
+
except (WebSocketDisconnect, CancelledError, ConnectionClosedOK):
|
167
|
+
break
|
168
|
+
except EOFError:
|
169
|
+
break
|
170
|
+
except OSError as e:
|
171
|
+
if e.errno == errno.EIO: # Input/output error
|
172
|
+
break
|
173
|
+
except Exception as e:
|
174
|
+
logger.debug(f"Exception details:\n{traceback.format_exc()}")
|
175
|
+
logger.debug(e)
|
176
|
+
logger.debug("read_from_terminal caught the exception!")
|
177
|
+
|
178
|
+
# Function to write to the terminal
|
179
|
+
async def write_to_terminal(self, websocket, lsp_writers, sectionId):
|
180
|
+
pty_info = self.PTY_PIDS[sectionId]
|
181
|
+
pty_process = pty_info["process"]
|
182
|
+
while True:
|
183
|
+
try:
|
184
|
+
input_data = await websocket.receive_text()
|
185
|
+
try:
|
186
|
+
json_val = json.loads(input_data)
|
187
|
+
except json.decoder.JSONDecodeError:
|
188
|
+
json_val = None
|
189
|
+
if isinstance(json_val, dict):
|
190
|
+
doResize = json_val["messageType"] == C.WS_TERMINAL_RESIZE
|
191
|
+
if doResize:
|
192
|
+
pty_process.setwinsize(json_val["rows"], json_val["cols"])
|
193
|
+
elif json_val["messageType"] == C.WS_RUN_CODE:
|
194
|
+
command = await runCode(json_val, lsp_writers)
|
195
|
+
pty_process.write(f"\x15{command}\n")
|
196
|
+
self.set_terminal_start_time(sectionId)
|
197
|
+
pty_info["wasJustResized"] = doResize
|
198
|
+
continue
|
199
|
+
elif input_data:
|
200
|
+
pty_process.write(input_data)
|
201
|
+
self.set_terminal_start_time(sectionId)
|
202
|
+
except (WebSocketDisconnect, CancelledError, ConnectionClosedOK):
|
203
|
+
break
|
204
|
+
except EOFError:
|
205
|
+
break
|
206
|
+
except OSError as e:
|
207
|
+
if e.errno == errno.EIO: # Input/output error
|
208
|
+
break
|
209
|
+
except Exception as e:
|
210
|
+
logger.debug(f"Exception details:\n{traceback.format_exc()}")
|
211
|
+
logger.debug(e)
|
212
|
+
logger.debug("write_to_terminal caught the exception!")
|
213
|
+
|
214
|
+
def set_terminal_start_time(self, id):
|
215
|
+
self.PTY_PIDS[id]["start"] = time.time()
|
216
|
+
|
217
|
+
def get_free_terminal(self, projectConfigId):
|
218
|
+
for k, v in self.PTY_PIDS.items():
|
219
|
+
if (
|
220
|
+
v["projectConfigId"] == projectConfigId
|
221
|
+
and not is_command_running_in_pty(v["pid"])
|
222
|
+
and time.time() - v["start"] > 1
|
223
|
+
):
|
224
|
+
return k
|
225
|
+
return None
|
226
|
+
|
227
|
+
def get_existing_terminals(self, projectConfigId):
|
228
|
+
output = []
|
229
|
+
for k, v in self.PTY_PIDS.items():
|
230
|
+
if v["projectConfigId"] == projectConfigId:
|
231
|
+
output.append({"id": k, "isTemporary": v["isTemporary"]})
|
232
|
+
return output
|
233
|
+
|
234
|
+
async def send_output_to_sectionId(self, sectionId, output, setSentReadyMsg=False):
|
235
|
+
for wsId in self.section_to_socket_ids[sectionId]:
|
236
|
+
ws = self.sockets[wsId]["ws"]
|
237
|
+
try:
|
238
|
+
await ws.send_text(output)
|
239
|
+
if setSentReadyMsg:
|
240
|
+
self.sockets[wsId]["sentReadyMsg"] = True
|
241
|
+
except:
|
242
|
+
continue
|
setta/terminals/utils.py
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
import re
|
2
|
+
import subprocess
|
3
|
+
|
4
|
+
import psutil
|
5
|
+
|
6
|
+
|
7
|
+
def windows_has_child_processes(input_winpid):
|
8
|
+
if len(psutil.Process(input_winpid).children()) > 0:
|
9
|
+
return True
|
10
|
+
|
11
|
+
# when running in bash.exe, sometimes the bash process children
|
12
|
+
# are not visible when querying using the windows pid
|
13
|
+
# so this finds the corresponding bash pid for the input winpid
|
14
|
+
# and finds its children
|
15
|
+
output = subprocess.check_output("ps", shell=True).decode()
|
16
|
+
processes = []
|
17
|
+
bash_pid = None
|
18
|
+
pid_idx, ppid_idx, winpid_idx = None, None, None
|
19
|
+
for i, line in enumerate(output.split("\n")): # Skip the header line
|
20
|
+
columns = re.split(r"\s+", line)
|
21
|
+
if i == 0:
|
22
|
+
pid_idx = columns.index("PID")
|
23
|
+
ppid_idx = columns.index("PPID")
|
24
|
+
winpid_idx = columns.index("WINPID")
|
25
|
+
elif columns != [""]:
|
26
|
+
pid = int(columns[pid_idx])
|
27
|
+
ppid = int(columns[ppid_idx])
|
28
|
+
winpid = int(columns[winpid_idx])
|
29
|
+
processes.append({"pid": pid, "ppid": ppid, "winpid": winpid})
|
30
|
+
if winpid == input_winpid:
|
31
|
+
bash_pid = pid
|
32
|
+
|
33
|
+
return len([p for p in processes if p["ppid"] == bash_pid]) > 0
|
34
|
+
|
35
|
+
|
36
|
+
def linux_has_child_processes(pid):
|
37
|
+
return len(psutil.Process(pid).children()) > 0
|
setta/utils/__init__.py
ADDED
File without changes
|
setta/utils/constants.py
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
import json
|
2
|
+
import logging
|
3
|
+
from pathlib import Path
|
4
|
+
|
5
|
+
from .utils import get_absolute_path, is_dev_mode
|
6
|
+
|
7
|
+
logger = logging.getLogger(__name__)
|
8
|
+
|
9
|
+
CONSTANTS_FOLDER = (
|
10
|
+
Path("../../../constants") if is_dev_mode() else Path("../static/constants")
|
11
|
+
)
|
12
|
+
SEED_FOLDER = Path("../../../seed") if is_dev_mode() else Path("../static/seed")
|
13
|
+
CODE_FOLDER = "setta_code"
|
14
|
+
DB_BACKUP_FOLDER = ".setta_backups"
|
15
|
+
CODE_FOLDER_ENV_VARIABLE = "SETTA_CODE_FOLDER"
|
16
|
+
HOST_ENV_VARIABLE = "SETTA_HOST"
|
17
|
+
PORT_ENV_VARIABLE = "SETTA_PORT"
|
18
|
+
|
19
|
+
|
20
|
+
class Constants:
|
21
|
+
def __init__(self):
|
22
|
+
self._initialized = False
|
23
|
+
|
24
|
+
def _ensure_initialized(self):
|
25
|
+
if not self._initialized:
|
26
|
+
load_constants()
|
27
|
+
self._initialized = True
|
28
|
+
|
29
|
+
def __getattr__(self, name):
|
30
|
+
if name == "_initialized":
|
31
|
+
return object.__getattribute__(self, name)
|
32
|
+
self._ensure_initialized()
|
33
|
+
return object.__getattribute__(self, name)
|
34
|
+
|
35
|
+
@property
|
36
|
+
def FRONTEND_ORIGINS(self):
|
37
|
+
self._ensure_initialized()
|
38
|
+
if is_dev_mode():
|
39
|
+
output = [f"http://{self.HOST}:{self.DEV_MODE_FRONTEND_PORT}"]
|
40
|
+
else:
|
41
|
+
output = [self.BACKEND]
|
42
|
+
if self.HOST == "127.0.0.1":
|
43
|
+
output.append(output[0].replace("127.0.0.1", "localhost"))
|
44
|
+
return output
|
45
|
+
|
46
|
+
@property
|
47
|
+
def BACKEND(self):
|
48
|
+
self._ensure_initialized()
|
49
|
+
return f"http://{self.HOST}:{self.PORT}"
|
50
|
+
|
51
|
+
@property
|
52
|
+
def WEBSOCKET(self):
|
53
|
+
self._ensure_initialized()
|
54
|
+
return f"ws://{self.HOST}:{self.PORT}{self.ROUTE_PREFIX}{self.ROUTE_WEBSOCKET_MANAGER}"
|
55
|
+
|
56
|
+
|
57
|
+
C = Constants()
|
58
|
+
USER_SETTINGS = {}
|
59
|
+
BASE_UI_TYPES = {}
|
60
|
+
BASE_UI_TYPE_IDS = {}
|
61
|
+
DEFAULT_VALUES = {}
|
62
|
+
|
63
|
+
|
64
|
+
def set_constants(**kwargs):
|
65
|
+
for k, v in kwargs.items():
|
66
|
+
setattr(C, k.upper(), v)
|
67
|
+
|
68
|
+
|
69
|
+
def load_constants():
|
70
|
+
logger.debug("Loading constants")
|
71
|
+
with open(
|
72
|
+
get_absolute_path(__file__, CONSTANTS_FOLDER / "constants.json"), "r"
|
73
|
+
) as f:
|
74
|
+
set_constants(**json.load(f))
|
75
|
+
|
76
|
+
with open(
|
77
|
+
get_absolute_path(__file__, CONSTANTS_FOLDER / "BaseUITypes.json"), "r"
|
78
|
+
) as f:
|
79
|
+
BASE_UI_TYPES.update(json.load(f))
|
80
|
+
for k, v in BASE_UI_TYPES.items():
|
81
|
+
BASE_UI_TYPE_IDS[v["type"]] = k
|
82
|
+
|
83
|
+
with open(
|
84
|
+
get_absolute_path(__file__, CONSTANTS_FOLDER / "defaultValues.json"), "r"
|
85
|
+
) as f:
|
86
|
+
DEFAULT_VALUES.update(json.load(f))
|
87
|
+
|
88
|
+
|
89
|
+
def is_from_json_source(id):
|
90
|
+
return id and id.startswith(C.JSON_SOURCE_PREFIX)
|
91
|
+
|
92
|
+
|
93
|
+
CODE_INFO_TABLE_DATA_JSON_FIELDS = set(
|
94
|
+
(
|
95
|
+
"editable",
|
96
|
+
"rcType",
|
97
|
+
"defaultVal",
|
98
|
+
"description",
|
99
|
+
"positionalOnly",
|
100
|
+
"isPinned",
|
101
|
+
"isFrozen",
|
102
|
+
)
|
103
|
+
)
|
104
|
+
|
105
|
+
SECTION_TABLE_DATA_JSON_FIELDS = set(
|
106
|
+
(
|
107
|
+
"social",
|
108
|
+
"codeLanguage",
|
109
|
+
"configLanguage",
|
110
|
+
"runInMemory",
|
111
|
+
"isFrozen",
|
112
|
+
"jsonSource",
|
113
|
+
"hideUnpinnedParams",
|
114
|
+
"displayMode",
|
115
|
+
"canvasSettings",
|
116
|
+
"chartSettings",
|
117
|
+
"renderMarkdown",
|
118
|
+
"paramFilter",
|
119
|
+
"columnWidth",
|
120
|
+
"renderedValue",
|
121
|
+
"isReadOnlyTerminal",
|
122
|
+
)
|
123
|
+
)
|
124
|
+
|
125
|
+
SECTION_CONFIG_TABLE_DATA_JSON_FIELDS = set(
|
126
|
+
(
|
127
|
+
"size",
|
128
|
+
"isHorizontalOrientation",
|
129
|
+
"pinnedAreaHeight",
|
130
|
+
"isMinimized",
|
131
|
+
"positionAndSizeLocked",
|
132
|
+
)
|
133
|
+
)
|
134
|
+
|
135
|
+
SECTION_VARIANT_ID_TABLE_DATA_JSON_FIELDS = set(["code", "description", "isFrozen"])
|
136
|
+
|
137
|
+
SECTION_VARIANT_EV_TABLE_DATA_JSON_FIELDS = set(["value"])
|
138
|
+
|
139
|
+
|
140
|
+
UI_TYPE_TABLE_DATA_JSON_FIELDS = set(
|
141
|
+
[
|
142
|
+
"name",
|
143
|
+
"presetType",
|
144
|
+
"config",
|
145
|
+
]
|
146
|
+
)
|
147
|
+
|
148
|
+
PROJECT_CONFIG_TABLE_DATA_JSON_FIELDS = set(["viewport", "previewImgColor"])
|