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 CHANGED
@@ -1,6 +1,6 @@
1
1
  """A new take on debugging"""
2
2
 
3
- __version__ = "0.5.3"
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
- ["gdb", "-p", str(config.inject), "-batch"]
116
- + [
117
- "-eval-command=call %s" % hook
118
- for hook in [
119
- "(int) PyGILState_Ensure()", # Getting the GIL
120
- '(int) PyRun_SimpleString("'
121
- f"print('* Kalong injection from {os.getpid()} *');"
122
- "import sys;" # Putting kalong project directory in sys path:
123
- f"sys.path.insert(0, '{kalong_dir}');"
124
- "import kalong;" # Setting breakpoint:
125
- "kalong.break_above(2);"
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'Running: {" ".join(gdb_command)}')
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
- die()
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
- async for msg in ws:
198
- if msg.type == WSMsgType.TEXT:
199
- data = json.loads(msg.data)
200
- try:
201
- response = await handle_message(ws, data, frame, event, arg)
202
- except Exception as e:
203
- log.error(f"Error handling message {data}", exc_info=e)
204
- response = {
205
- "type": "SET_ANSWER",
206
- "prompt": data.get("prompt", "?").strip(),
207
- "key": data["key"],
208
- "command": data.get("command"),
209
- "frame": data.get("frame"),
210
- "answer": [serialize_exception(*sys.exc_info(), "internal")],
211
- }
212
- log.debug(f"Got {data} answering with {response}")
213
- response["local"] = True
214
-
215
- if response.pop("recursive", False):
216
- await ws.send_json({"type": "PAUSE", "recursive": True})
217
- await init(ws, frame, event, arg)
218
-
219
- stop = response.pop("stop", False)
220
-
221
- try:
222
- await ws.send_json(response)
223
- except ConnectionResetError:
224
- break
225
-
226
- if stop:
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
- elif msg.type == WSMsgType.ERROR:
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. " "This option is used by kalong itself",
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
- "--port", type=int, default=self.port, help="Port of kalong server"
39
+ "--host",
40
+ default=self.host,
41
+ help="Host of kalong server",
37
42
  )
38
- front_port_default = (
39
- {"default": self.port} if self.port != self.front_port else {}
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
- **front_port_default,
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
@@ -49,7 +49,7 @@ def close_loop():
49
49
  try:
50
50
  loops[origin].close()
51
51
  finally:
52
- del loops[origin]
52
+ loops.pop(origin, None)
53
53
 
54
54
 
55
55
  def clean_loops():
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 web.FileResponse(Path(__file__).parent / "static" / "index.html")
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
- async for msg in ws:
71
- if msg.type == WSMsgType.TEXT:
72
- data = json.loads(msg.data)
73
- log.debug(f"{side} -> {other_side}: {data}")
74
- try:
75
- pair = await peer(request.app, other_side, origin)
76
- except NoClientFoundError:
77
- log.error(
78
- f"No {human_readable_side(other_side)} connection was found, aborting debugger"
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
- continue
89
-
90
- await pair.send_json(data)
91
- elif msg.type == WSMsgType.ERROR:
92
- log.error(f"{side.title()} closed", exc_info=ws.exception())
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
- del request.app[side][origin]
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
- await request.app[other_side][origin].close()
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