ghosttrap-cli 0.3.20__tar.gz → 0.3.21__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ghosttrap-cli
3
- Version: 0.3.20
3
+ Version: 0.3.21
4
4
  Summary: Watch for errors streaming from ghosttrap.io
5
5
  Project-URL: Homepage, https://github.com/alex-rowley/ghosttrap-cli
6
6
  Requires-Python: >=3.10
@@ -4,6 +4,7 @@ import argparse
4
4
  import asyncio
5
5
  import json
6
6
  import os
7
+ import signal
7
8
  import subprocess
8
9
  import sys
9
10
  import tempfile
@@ -13,7 +14,20 @@ import urllib.request
13
14
 
14
15
  import websockets
15
16
 
16
- __version__ = "0.3.20"
17
+
18
+ def _harden_signals():
19
+ """Explicitly ignore SIGURG so no process supervisor can nudge peek out
20
+ with an out-of-band signal. POSIX default is already ignore; this makes
21
+ it defensive against layers that change the disposition.
22
+ """
23
+ s = getattr(signal, "SIGURG", None)
24
+ if s is not None:
25
+ try:
26
+ signal.signal(s, signal.SIG_IGN)
27
+ except (OSError, ValueError):
28
+ pass
29
+
30
+ __version__ = "0.3.21"
17
31
 
18
32
  GHOSTTRAP_SERVER = "wss://ghosttrap.io/stream/"
19
33
  CONFIG_DIR = os.path.expanduser("~/.ghosttrap")
@@ -416,6 +430,31 @@ async def setup(server_url, token):
416
430
  sys.exit(1)
417
431
 
418
432
 
433
+ # Exception types we knowingly retry on. All represent a transient network/transport
434
+ # blip rather than a semantic error from the server:
435
+ # - ConnectionClosed: peer closed the WebSocket (either side of the handshake)
436
+ # - InvalidStatus: non-101 HTTP response during upgrade (e.g. 502 from a proxy)
437
+ # - ConnectionError: builtin — refused, reset, unreachable
438
+ # - OSError: DNS failure, transient socket errors (gaierror is a subclass)
439
+ # Anything else escapes and prints a diagnostic line first so we can add it here
440
+ # in the next release. Semantic rejections from the server ({"type": "rejected"})
441
+ # raise SystemExit, which we deliberately do NOT catch — those are real errors.
442
+ _RETRYABLE = (
443
+ websockets.ConnectionClosed,
444
+ websockets.InvalidStatus,
445
+ ConnectionError,
446
+ OSError,
447
+ )
448
+
449
+
450
+ def _log_unexpected(e):
451
+ print(
452
+ f"unexpected {type(e).__module__}.{type(e).__name__}: {e} — "
453
+ f"not currently in the retry list; please report so we can add it.",
454
+ file=sys.stderr,
455
+ )
456
+
457
+
419
458
  async def watch(server_url, token):
420
459
  config = _load_config()
421
460
  print(f"connecting to {server_url}...", file=sys.stderr)
@@ -424,8 +463,11 @@ async def watch(server_url, token):
424
463
  try:
425
464
  await _connect_and_handle(server_url, token, config, once=False)
426
465
  print("connection closed by server, reconnecting...", file=sys.stderr)
427
- except (websockets.ConnectionClosed, websockets.InvalidStatus, ConnectionError, OSError):
466
+ except _RETRYABLE:
428
467
  print("connection lost, reconnecting...", file=sys.stderr)
468
+ except Exception as e:
469
+ _log_unexpected(e)
470
+ raise
429
471
  await asyncio.sleep(60)
430
472
 
431
473
 
@@ -438,8 +480,11 @@ async def peek(server_url, token):
438
480
  if got_error:
439
481
  return
440
482
  print("connection closed by server, reconnecting...", file=sys.stderr)
441
- except (websockets.ConnectionClosed, websockets.InvalidStatus, ConnectionError, OSError):
483
+ except _RETRYABLE:
442
484
  print("connection lost, reconnecting...", file=sys.stderr)
485
+ except Exception as e:
486
+ _log_unexpected(e)
487
+ raise
443
488
  await asyncio.sleep(60)
444
489
 
445
490
 
@@ -676,6 +721,7 @@ def last(do_clear=False, requested=None):
676
721
 
677
722
 
678
723
  def main():
724
+ _harden_signals()
679
725
  parser = argparse.ArgumentParser(prog="ghosttrap", description="Watch for errors from ghosttrap.io")
680
726
  sub = parser.add_subparsers(dest="command")
681
727
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ghosttrap-cli
3
- Version: 0.3.20
3
+ Version: 0.3.21
4
4
  Summary: Watch for errors streaming from ghosttrap.io
5
5
  Project-URL: Homepage, https://github.com/alex-rowley/ghosttrap-cli
6
6
  Requires-Python: >=3.10
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ghosttrap-cli"
7
- version = "0.3.20"
7
+ version = "0.3.21"
8
8
  description = "Watch for errors streaming from ghosttrap.io"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
File without changes
File without changes