ladyrick 0.5.4__py3-none-any.whl → 0.5.6__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.
ladyrick/cli/multi_ssh.py CHANGED
@@ -1,5 +1,6 @@
1
1
  import json
2
2
  import os
3
+ import re
3
4
  import select
4
5
  import signal
5
6
  import subprocess
@@ -110,15 +111,20 @@ class Host:
110
111
 
111
112
 
112
113
  class RemoteExecutor:
113
- def __init__(self, host: Host, command: list[str]):
114
+ def __init__(self, host: Host, command: list[str], envs: dict | None = None):
114
115
  self.host = host
115
116
  self.command = command
116
117
  self.envs = {}
118
+ if envs:
119
+ for k, v in envs.items():
120
+ if not re.match(r"^[a-zA-Z_][a-zA-Z0-9_]*$", k):
121
+ raise ValueError(f"invalid env name: {k!r}")
122
+ self.envs[k] = v
117
123
  self.process = None
118
124
 
119
125
  @classmethod
120
126
  def make_ssh_cmd(cls, host: Host, cmd: str):
121
- opts = ["/usr/bin/env", "ssh", "-qT", "-oStrictHostKeyChecking=no"]
127
+ opts = ["/usr/bin/env", "ssh", "-T", "-oStrictHostKeyChecking=no"]
122
128
  if host.config_file is not None:
123
129
  opts.append(f"-F{host.config_file}")
124
130
  if host.User is not None:
@@ -134,6 +140,7 @@ class RemoteExecutor:
134
140
  return opts
135
141
 
136
142
  def start(self):
143
+ assert self.process is None
137
144
  code = pathlib.Path(__file__).read_text().split("# ----- remote_head end ----- #")[0].strip()
138
145
  remote_cmd = shlex.join(
139
146
  [
@@ -185,18 +192,27 @@ class RemoteExecutor:
185
192
  e.envs["RANK"] = str(i)
186
193
 
187
194
  def send_signal(self, sig):
188
- if self.process:
195
+ assert self.process is not None
196
+ if self.process.poll() is None and self.process.stdin and not self.process.stdin.closed:
189
197
  sig_name = signal.Signals(sig).name
190
- if self.process.stdin and not self.process.stdin.closed:
191
- log(f"writing to stdin: SIGNAL {sig_name}")
192
- try:
193
- self.process.stdin.write(f"SIGNAL {sig_name}\n".encode())
194
- self.process.stdin.flush()
195
- except (BrokenPipeError, OSError) as e:
196
- log(e)
198
+ log(f"writing to stdin: SIGNAL {sig_name}")
199
+ try:
200
+ self.process.stdin.write(f"SIGNAL {sig_name}\n".encode())
201
+ self.process.stdin.flush()
202
+ except (BrokenPipeError, OSError) as e:
203
+ log(e)
204
+
205
+ def terminate(self):
206
+ if self.process is not None and self.process.poll() is None:
207
+ log("terminate RemoteExecutor")
208
+ self.process.terminate()
209
+
210
+ def poll(self):
211
+ assert self.process is not None
212
+ return self.process.poll()
197
213
 
198
214
 
199
- def signal_repeat_checker(sig_to_check, count, duration):
215
+ def signal_repeat_checker(sig_to_check, duration: float):
200
216
  last_int_signal_time = []
201
217
 
202
218
  def checker(sig: signal.Signals):
@@ -206,11 +222,8 @@ def signal_repeat_checker(sig_to_check, count, duration):
206
222
  threadhold = cur_time - duration
207
223
  last_int_signal_time = [t for t in last_int_signal_time if t >= threadhold]
208
224
  last_int_signal_time.append(cur_time)
209
-
210
- if len(last_int_signal_time) >= count:
211
- log(f"received {sig_to_check.name} for {count} times or more in {duration} second(s)")
212
- return True
213
- return False
225
+ return len(last_int_signal_time)
226
+ return 0
214
227
 
215
228
  return checker
216
229
 
@@ -223,6 +236,7 @@ def main():
223
236
  parser.add_argument("-l", type=str, help="ssh login User")
224
237
  parser.add_argument("-o", type=str, action="append", help="ssh options")
225
238
  parser.add_argument("-F", type=str, help="ssh config file")
239
+ parser.add_argument("-e", "--env", type=str, action="append", help="extra envs")
226
240
  parser.add_argument("--hosts-config", type=str, action="append", help="hosts config string. order is 2")
227
241
  parser.add_argument("--hosts-config-file", type=str, action="append", help="hosts config file. order is 3")
228
242
  parser.add_argument("--help", action="help", default=argparse.SUPPRESS, help="show this help message and exit")
@@ -263,28 +277,47 @@ def main():
263
277
  parser.print_help()
264
278
  sys.exit(1)
265
279
 
266
- executors = [RemoteExecutor(host, args.cmd) for host in hosts]
280
+ envs = {}
281
+ if args.env:
282
+ for e in args.env:
283
+ p = e.split("=", 1)
284
+ if len(p) == 1:
285
+ p.append("")
286
+ envs[p[0]] = p[1]
287
+
288
+ executors = [RemoteExecutor(host, args.cmd, envs) for host in hosts]
267
289
 
268
290
  RemoteExecutor.set_envs(executors)
269
291
 
270
292
  for executor in executors:
271
293
  executor.start()
272
294
 
273
- checker = signal_repeat_checker(signal.SIGINT, count=3, duration=1)
295
+ import rich
296
+
297
+ checker = signal_repeat_checker(signal.SIGINT, duration=1)
274
298
 
275
299
  def handle_signal(sig, frame):
276
- if checker(sig):
300
+ log(f"received signal {sig}")
301
+ sig_count = checker(sig)
302
+ if sig_count >= 3:
277
303
  sig = signal.SIGUSR2
278
- log("\nTry to froce kill remote processes")
304
+ if sig_count == 3:
305
+ rich.print("\n[bold magenta]Can't wait. Try to froce kill remote processes...[/bold magenta]")
279
306
  else:
280
- log(f"\nReceived {signal.Signals(sig).name}, forwarding...")
307
+ rich.print(
308
+ f"\n[bold green]Received {signal.Signals(sig).name}, forwarding to remote processes...[/bold green]"
309
+ )
281
310
  for executor in executors:
282
311
  executor.send_signal(sig)
312
+ if sig_count >= 4:
313
+ rich.print("\n[bold red]Really Can't wait!!! Froce kill local processes and exiting right now![/bold red]")
314
+ for executor in executors:
315
+ executor.terminate()
283
316
 
284
317
  for sig in [signal.SIGHUP, signal.SIGINT, signal.SIGTERM, signal.SIGUSR1, signal.SIGUSR2]:
285
318
  signal.signal(sig, handle_signal)
286
319
 
287
- while any(e.process.poll() is None for e in executors if e.process):
320
+ while any([e.poll() is None for e in executors if e.process]):
288
321
  time.sleep(0.5)
289
322
  log("finished")
290
323
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ladyrick
3
- Version: 0.5.4
3
+ Version: 0.5.6
4
4
  Summary: ladyrick's tools
5
5
  Author-email: ladyrick <ladyrick@qq.com>
6
6
  License-Expression: MIT
@@ -10,7 +10,7 @@ ladyrick/typing.py,sha256=YQeApe63dk7yL4NS5ytlR6v3dLCii2-qsXNlUvjK-zw,203
10
10
  ladyrick/utils.py,sha256=jRRaqC6kNbCJPGeE0YisFgis-wiuINLik1mcUQtytow,608
11
11
  ladyrick/vars.py,sha256=VbFh2u7XybUaBuiYEXBa4sOmoS99vc2AIXdYLBh8vjk,3763
12
12
  ladyrick/cli/calc.py,sha256=5_UhSSaL_K9FBCHg3zuCk41CHJXy3QAh__qCabbffQY,1438
13
- ladyrick/cli/multi_ssh.py,sha256=wbO7woW0roAd6Q5LW861c8h5r7sfkCKW2qvCq-HssTY,9230
13
+ ladyrick/cli/multi_ssh.py,sha256=d_7zPvmfVUQZVcxhCFj37ktTOb0-AzxSEr0z6ipMY9U,10403
14
14
  ladyrick/cli/psf.py,sha256=JLk3gbPn7E3uuPBbzGvLgJmFQlilA6zg_Xlg7xW5jik,1146
15
15
  ladyrick/cli/tee.py,sha256=UMJxSJLOEfbV43auVKRTIJ5ZAMAkAfj8byiFLk5PUHE,3579
16
16
  ladyrick/cli/test_args.py,sha256=f5sUPDlcf6nbNf6UfLwZQI5g5LN8wlFBQZ10GLw22cg,212
@@ -21,9 +21,9 @@ ladyrick/patch/rich_print.py,sha256=z3Ea1VCunXZvNvEDFHpoyWc8ydINmh-gOIJ1ssscs6s,
21
21
  ladyrick/patch/python/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  ladyrick/patch/python/__main__.py,sha256=BAGy1phd26WWcGM9TKHbqIpeZliVofopBndtMIPtDQ0,651
23
23
  ladyrick/patch/python/usercustomize.py,sha256=8mYpcZ8p-l41fiSJue727n8cAmcEmUktObDYZDdLJfs,218
24
- ladyrick-0.5.4.dist-info/licenses/LICENSE,sha256=EeNAFxYAOYEmo2YEM7Zk5Oknq4RI0XMAbk4Rgoem6fs,1065
25
- ladyrick-0.5.4.dist-info/METADATA,sha256=lAnPzvF3RUZk9942XC-UDUMZuiD77o_h6LFRZx2jj-c,883
26
- ladyrick-0.5.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
27
- ladyrick-0.5.4.dist-info/entry_points.txt,sha256=28kidI1OCOAi7S1aHBr8veak49VNZ0tWFvf-Ty8UmSU,225
28
- ladyrick-0.5.4.dist-info/top_level.txt,sha256=RIC3-Jty2qzLYXSOr7fOu1loTwlMU9cF6MFeGIROxWU,9
29
- ladyrick-0.5.4.dist-info/RECORD,,
24
+ ladyrick-0.5.6.dist-info/licenses/LICENSE,sha256=EeNAFxYAOYEmo2YEM7Zk5Oknq4RI0XMAbk4Rgoem6fs,1065
25
+ ladyrick-0.5.6.dist-info/METADATA,sha256=s-vKvkA34C7DS3PXt2QygfVzhmBxSJC42bawDe_QC8Q,883
26
+ ladyrick-0.5.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
27
+ ladyrick-0.5.6.dist-info/entry_points.txt,sha256=28kidI1OCOAi7S1aHBr8veak49VNZ0tWFvf-Ty8UmSU,225
28
+ ladyrick-0.5.6.dist-info/top_level.txt,sha256=RIC3-Jty2qzLYXSOr7fOu1loTwlMU9cF6MFeGIROxWU,9
29
+ ladyrick-0.5.6.dist-info/RECORD,,