kalong 0.5.3__py3-none-any.whl → 0.5.5__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.
- kalong/__init__.py +16 -19
- kalong/communication.py +40 -36
- kalong/config.py +47 -6
- kalong/loops.py +1 -1
- kalong/server.py +50 -29
- kalong/static/assets/index-ByEA1zci.js +166 -0
- kalong/static/index.html +1 -1
- kalong/stepping.py +1 -2
- kalong/utils/__init__.py +3 -3
- kalong/websockets.py +27 -17
- {kalong-0.5.3.dist-info → kalong-0.5.5.dist-info}/METADATA +1 -1
- {kalong-0.5.3.dist-info → kalong-0.5.5.dist-info}/RECORD +15 -15
- {kalong-0.5.3.dist-info → kalong-0.5.5.dist-info}/WHEEL +1 -1
- kalong/static/assets/index-B6z5WP1H.js +0 -226
- {kalong-0.5.3.dist-info → kalong-0.5.5.dist-info}/entry_points.txt +0 -0
- {kalong-0.5.3.dist-info → kalong-0.5.5.dist-info}/licenses/LICENSE +0 -0
kalong/__init__.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""A new take on debugging"""
|
|
2
2
|
|
|
3
|
-
__version__ = "0.5.
|
|
3
|
+
__version__ = "0.5.5"
|
|
4
4
|
import os
|
|
5
5
|
import sys
|
|
6
6
|
from pathlib import Path
|
|
@@ -111,25 +111,22 @@ def main():
|
|
|
111
111
|
|
|
112
112
|
elif config.inject:
|
|
113
113
|
kalong_dir = Path(__file__).resolve().parent.parent
|
|
114
|
-
gdb_command = (
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
"
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
# Releasing the GIL with the PyGILState_Ensure handle:
|
|
128
|
-
"(void) PyGILState_Release($1)",
|
|
129
|
-
]
|
|
114
|
+
gdb_command = ["gdb", "-p", str(config.inject), "-batch"] + [
|
|
115
|
+
"-eval-command=call %s" % hook
|
|
116
|
+
for hook in [
|
|
117
|
+
"(int) PyGILState_Ensure()", # Getting the GIL
|
|
118
|
+
'(int) PyRun_SimpleString("'
|
|
119
|
+
f"print('* Kalong injection from {os.getpid()} *');"
|
|
120
|
+
"import sys;" # Putting kalong project directory in sys path:
|
|
121
|
+
f"sys.path.insert(0, '{kalong_dir}');"
|
|
122
|
+
"import kalong;" # Setting breakpoint:
|
|
123
|
+
"kalong.break_above(2);"
|
|
124
|
+
'")',
|
|
125
|
+
# Releasing the GIL with the PyGILState_Ensure handle:
|
|
126
|
+
"(void) PyGILState_Release($1)",
|
|
130
127
|
]
|
|
131
|
-
|
|
132
|
-
print(f
|
|
128
|
+
]
|
|
129
|
+
print(f"Running: {' '.join(gdb_command)}")
|
|
133
130
|
run(gdb_command)
|
|
134
131
|
|
|
135
132
|
else:
|
kalong/communication.py
CHANGED
|
@@ -25,7 +25,7 @@ from .errors import SetFrameError
|
|
|
25
25
|
from .loops import get_loop
|
|
26
26
|
from .stepping import add_step, clear_step, stop_trace
|
|
27
27
|
from .utils import basicConfig, get_file_from_code
|
|
28
|
-
from .websockets import die, websocket_state
|
|
28
|
+
from .websockets import adie, die, websocket_state
|
|
29
29
|
|
|
30
30
|
log = logging.getLogger(__name__)
|
|
31
31
|
basicConfig(level=config.log_level)
|
|
@@ -161,7 +161,7 @@ async def handle_message(ws, data, frame, event, arg):
|
|
|
161
161
|
elif command == "stop":
|
|
162
162
|
clear_step()
|
|
163
163
|
stop_trace(frame)
|
|
164
|
-
|
|
164
|
+
await adie()
|
|
165
165
|
else:
|
|
166
166
|
step_frame = get_frame(frame, data.get("frame"), tb)
|
|
167
167
|
add_step(command, step_frame)
|
|
@@ -194,42 +194,46 @@ async def communication_loop(frame_, event_, arg_, pending=None):
|
|
|
194
194
|
# Otherwise if it's new, just wait for HELLO to answer current state
|
|
195
195
|
|
|
196
196
|
stop = False
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
197
|
+
try:
|
|
198
|
+
async for msg in ws:
|
|
199
|
+
if msg.type == WSMsgType.TEXT:
|
|
200
|
+
data = json.loads(msg.data)
|
|
201
|
+
try:
|
|
202
|
+
response = await handle_message(ws, data, frame, event, arg)
|
|
203
|
+
except Exception as e:
|
|
204
|
+
log.error(f"Error handling message {data}", exc_info=e)
|
|
205
|
+
response = {
|
|
206
|
+
"type": "SET_ANSWER",
|
|
207
|
+
"prompt": data.get("prompt", "?").strip(),
|
|
208
|
+
"key": data.get("key"),
|
|
209
|
+
"command": data.get("command"),
|
|
210
|
+
"frame": data.get("frame"),
|
|
211
|
+
"answer": [serialize_exception(*sys.exc_info(), "internal")],
|
|
212
|
+
}
|
|
213
|
+
log.debug(f"Got {data} answering with {response}")
|
|
214
|
+
response["local"] = True
|
|
215
|
+
|
|
216
|
+
if response.pop("recursive", False):
|
|
217
|
+
await ws.send_json({"type": "PAUSE", "recursive": True})
|
|
218
|
+
await init(ws, frame, event, arg)
|
|
219
|
+
|
|
220
|
+
stop = response.pop("stop", False)
|
|
221
|
+
|
|
222
|
+
try:
|
|
223
|
+
await ws.send_json(response)
|
|
224
|
+
except ConnectionResetError:
|
|
225
|
+
break
|
|
226
|
+
|
|
227
|
+
if stop:
|
|
228
|
+
break
|
|
229
|
+
|
|
230
|
+
elif msg.type == WSMsgType.ERROR:
|
|
231
|
+
log.error("WebSocket closed", exc_info=ws.exception())
|
|
227
232
|
break
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
log.error("WebSocket closed", exc_info=ws.exception())
|
|
231
|
-
break
|
|
233
|
+
except asyncio.CancelledError:
|
|
234
|
+
log.error("WebSocket communication cancelled")
|
|
232
235
|
|
|
233
236
|
# Browser exited, stopping debug if we are not stepping
|
|
234
237
|
if not stop:
|
|
235
238
|
stop_trace(frame)
|
|
239
|
+
await adie()
|
kalong/config.py
CHANGED
|
@@ -7,11 +7,15 @@ defaults = {
|
|
|
7
7
|
"protocol": "http",
|
|
8
8
|
"host": "localhost",
|
|
9
9
|
"port": 59999,
|
|
10
|
+
"front_host": "localhost",
|
|
10
11
|
"front_port": 59999,
|
|
12
|
+
"ws_host": "localhost",
|
|
13
|
+
"ws_port": 59999,
|
|
11
14
|
"log": "warn",
|
|
12
15
|
"detached": False,
|
|
13
16
|
"command": [],
|
|
14
17
|
"inject": None,
|
|
18
|
+
"urlsocket": False,
|
|
15
19
|
}
|
|
16
20
|
|
|
17
21
|
|
|
@@ -24,25 +28,45 @@ class Config:
|
|
|
24
28
|
parser.add_argument(
|
|
25
29
|
"--server",
|
|
26
30
|
action="store_true",
|
|
27
|
-
help="Launch the kalong server.
|
|
31
|
+
help="Launch the kalong server. This option is used by kalong itself",
|
|
28
32
|
)
|
|
29
33
|
parser.add_argument(
|
|
30
34
|
"--protocol",
|
|
31
35
|
default=self.protocol,
|
|
32
36
|
help="Protocol for contacting kalong server",
|
|
33
37
|
)
|
|
34
|
-
parser.add_argument("--host", default=self.host, help="Host of kalong server")
|
|
35
38
|
parser.add_argument(
|
|
36
|
-
"--
|
|
39
|
+
"--host",
|
|
40
|
+
default=self.host,
|
|
41
|
+
help="Host of kalong server",
|
|
37
42
|
)
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
parser.add_argument(
|
|
44
|
+
"--port",
|
|
45
|
+
type=int,
|
|
46
|
+
default=self.port,
|
|
47
|
+
help="Port of kalong server",
|
|
48
|
+
)
|
|
49
|
+
parser.add_argument(
|
|
50
|
+
"--front-host",
|
|
51
|
+
default=self.front_host,
|
|
52
|
+
help="Host of kalong frontend, defaults to host option",
|
|
40
53
|
)
|
|
41
54
|
parser.add_argument(
|
|
42
55
|
"--front-port",
|
|
43
56
|
type=int,
|
|
57
|
+
default=self.front_port,
|
|
44
58
|
help="Port of kalong frontend, defaults to port option",
|
|
45
|
-
|
|
59
|
+
)
|
|
60
|
+
parser.add_argument(
|
|
61
|
+
"--ws-host",
|
|
62
|
+
default=self.ws_host,
|
|
63
|
+
help="Host of kalong websocket server, defaults to host option",
|
|
64
|
+
)
|
|
65
|
+
parser.add_argument(
|
|
66
|
+
"--ws-port",
|
|
67
|
+
type=int,
|
|
68
|
+
default=self.ws_port,
|
|
69
|
+
help="Port of kalong websocket server, defaults to port option",
|
|
46
70
|
)
|
|
47
71
|
parser.add_argument(
|
|
48
72
|
"--log",
|
|
@@ -66,6 +90,11 @@ class Config:
|
|
|
66
90
|
action="store_true",
|
|
67
91
|
help="Break at the start of the python file",
|
|
68
92
|
)
|
|
93
|
+
parser.add_argument(
|
|
94
|
+
"--urlsocket",
|
|
95
|
+
type=str,
|
|
96
|
+
help="Path of the socket into which to feed the url for docker browser opening",
|
|
97
|
+
)
|
|
69
98
|
parser.add_argument(
|
|
70
99
|
"command",
|
|
71
100
|
nargs=REMAINDER,
|
|
@@ -76,8 +105,14 @@ class Config:
|
|
|
76
105
|
|
|
77
106
|
def parse_args(self):
|
|
78
107
|
args = self.get_parser().parse_args()
|
|
108
|
+
if not args.front_host:
|
|
109
|
+
args.front_host = args.host
|
|
79
110
|
if not args.front_port:
|
|
80
111
|
args.front_port = args.port
|
|
112
|
+
if not args.ws_host:
|
|
113
|
+
args.ws_host = args.host
|
|
114
|
+
if not args.ws_port:
|
|
115
|
+
args.ws_port = args.port
|
|
81
116
|
return args
|
|
82
117
|
|
|
83
118
|
def from_args(self):
|
|
@@ -90,8 +125,14 @@ class Config:
|
|
|
90
125
|
if value:
|
|
91
126
|
setattr(self, name, type(name)(value))
|
|
92
127
|
|
|
128
|
+
if os.getenv("KALONG_HOST") and not os.getenv("KALONG_FRONT_HOST"):
|
|
129
|
+
self.front_host = self.host
|
|
93
130
|
if os.getenv("KALONG_PORT") and not os.getenv("KALONG_FRONT_PORT"):
|
|
94
131
|
self.front_port = self.port
|
|
132
|
+
if os.getenv("KALONG_HOST") and not os.getenv("KALONG_WS_HOST"):
|
|
133
|
+
self.ws_host = self.host
|
|
134
|
+
if os.getenv("KALONG_PORT") and not os.getenv("KALONG_WS_PORT"):
|
|
135
|
+
self.ws_port = self.port
|
|
95
136
|
|
|
96
137
|
def get_args_for_server(self):
|
|
97
138
|
yield "--server"
|
kalong/loops.py
CHANGED
kalong/server.py
CHANGED
|
@@ -29,6 +29,25 @@ def maybe_bail(app):
|
|
|
29
29
|
)
|
|
30
30
|
|
|
31
31
|
|
|
32
|
+
def index():
|
|
33
|
+
index.__content__ = getattr(index, "__content__", None)
|
|
34
|
+
if not index.__content__:
|
|
35
|
+
with open(Path(__file__).parent / "static" / "index.html", "rb") as f:
|
|
36
|
+
index.__content__ = f.read()
|
|
37
|
+
if config.ws_port != config.port or config.ws_host != config.host:
|
|
38
|
+
index.__content__ = index.__content__.replace(
|
|
39
|
+
b"</body>",
|
|
40
|
+
f"""
|
|
41
|
+
<script>
|
|
42
|
+
window.KALONG_WS_HOST = "{config.ws_host}";
|
|
43
|
+
window.KALONG_WS_PORT = {config.ws_port};
|
|
44
|
+
</script>
|
|
45
|
+
</body>
|
|
46
|
+
""".encode(),
|
|
47
|
+
)
|
|
48
|
+
return web.Response(body=index.__content__, content_type="text/html")
|
|
49
|
+
|
|
50
|
+
|
|
32
51
|
async def shutdown(app):
|
|
33
52
|
log.info("App shutdown, closing remaining websockets.")
|
|
34
53
|
await asyncio.gather(
|
|
@@ -60,51 +79,53 @@ async def websocket(request):
|
|
|
60
79
|
state = ws.can_prepare(request)
|
|
61
80
|
if not state.ok:
|
|
62
81
|
log.debug(f"Sending {side} app for {origin}")
|
|
63
|
-
return
|
|
82
|
+
return index()
|
|
64
83
|
|
|
65
84
|
await ws.prepare(request)
|
|
66
85
|
|
|
67
86
|
log.debug(f"{side.title()} app connected for {origin}")
|
|
68
87
|
request.app[side][origin] = ws
|
|
69
88
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
break
|
|
81
|
-
if data["type"] == "DO_COMMAND":
|
|
82
|
-
if data["command"] in ["pause", "kill"]:
|
|
83
|
-
_, pid, _ = parse_origin(origin)
|
|
84
|
-
os.kill(
|
|
85
|
-
pid,
|
|
86
|
-
signal.SIGTERM if data["command"] == "kill" else USER_SIGNAL,
|
|
89
|
+
try:
|
|
90
|
+
async for msg in ws:
|
|
91
|
+
if msg.type == WSMsgType.TEXT:
|
|
92
|
+
data = json.loads(msg.data)
|
|
93
|
+
log.debug(f"{side} -> {other_side}: {data}")
|
|
94
|
+
try:
|
|
95
|
+
pair = await peer(request.app, other_side, origin)
|
|
96
|
+
except NoClientFoundError:
|
|
97
|
+
log.error(
|
|
98
|
+
f"No {human_readable_side(other_side)} connection was found, aborting debugger"
|
|
87
99
|
)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
100
|
+
break
|
|
101
|
+
if data["type"] == "DO_COMMAND":
|
|
102
|
+
if data["command"] in ["pause", "kill"]:
|
|
103
|
+
_, pid, _ = parse_origin(origin)
|
|
104
|
+
sign = (
|
|
105
|
+
signal.SIGKILL if data["command"] == "kill" else USER_SIGNAL
|
|
106
|
+
)
|
|
107
|
+
log.info(f"Sending signal {sign} to {pid}")
|
|
108
|
+
os.kill(pid, sign)
|
|
109
|
+
continue
|
|
110
|
+
|
|
111
|
+
await pair.send_json(data)
|
|
112
|
+
elif msg.type == WSMsgType.ERROR:
|
|
113
|
+
log.error(f"{side.title()} closed", exc_info=ws.exception())
|
|
114
|
+
except asyncio.CancelledError:
|
|
115
|
+
log.error(f"{side.title()} WebSocket communication cancelled")
|
|
93
116
|
|
|
94
117
|
log.debug(f"Closing {side}")
|
|
95
|
-
await ws.close()
|
|
96
118
|
|
|
97
119
|
if side in request.app and origin in request.app[side]:
|
|
98
|
-
|
|
120
|
+
request.app[side].pop(origin)
|
|
99
121
|
|
|
100
122
|
if not config.detached:
|
|
101
123
|
if origin in request.app[other_side]:
|
|
102
124
|
log.debug(f"Closing {other_side} due to {side} closing.")
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if origin in request.app[other_side]:
|
|
106
|
-
del request.app[other_side][origin]
|
|
125
|
+
s = request.app[other_side].pop(origin)
|
|
126
|
+
await s.close()
|
|
107
127
|
|
|
128
|
+
await ws.close()
|
|
108
129
|
maybe_bail(request.app)
|
|
109
130
|
return ws
|
|
110
131
|
|