ssh-handler 1.4.0__tar.gz → 1.4.2__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.
Files changed (29) hide show
  1. {ssh_handler-1.4.0/ssh_handler.egg-info → ssh_handler-1.4.2}/PKG-INFO +1 -1
  2. {ssh_handler-1.4.0 → ssh_handler-1.4.2}/pyproject.toml +1 -1
  3. {ssh_handler-1.4.0 → ssh_handler-1.4.2}/ssh_handler/__init__.py +1 -1
  4. {ssh_handler-1.4.0 → ssh_handler-1.4.2}/ssh_handler/core.py +19 -7
  5. {ssh_handler-1.4.0 → ssh_handler-1.4.2}/ssh_handler/results.py +17 -0
  6. {ssh_handler-1.4.0 → ssh_handler-1.4.2}/ssh_handler/serial_handler.py +8 -2
  7. {ssh_handler-1.4.0 → ssh_handler-1.4.2/ssh_handler.egg-info}/PKG-INFO +1 -1
  8. {ssh_handler-1.4.0 → ssh_handler-1.4.2}/LICENSE +0 -0
  9. {ssh_handler-1.4.0 → ssh_handler-1.4.2}/README.md +0 -0
  10. {ssh_handler-1.4.0 → ssh_handler-1.4.2}/setup.cfg +0 -0
  11. {ssh_handler-1.4.0 → ssh_handler-1.4.2}/ssh_handler/__main__.py +0 -0
  12. {ssh_handler-1.4.0 → ssh_handler-1.4.2}/ssh_handler/cli.py +0 -0
  13. {ssh_handler-1.4.0 → ssh_handler-1.4.2}/ssh_handler/config.py +0 -0
  14. {ssh_handler-1.4.0 → ssh_handler-1.4.2}/ssh_handler/credentials.py +0 -0
  15. {ssh_handler-1.4.0 → ssh_handler-1.4.2}/ssh_handler/exceptions.py +0 -0
  16. {ssh_handler-1.4.0 → ssh_handler-1.4.2}/ssh_handler/ftp.py +0 -0
  17. {ssh_handler-1.4.0 → ssh_handler-1.4.2}/ssh_handler/openssh/OpenSSH-ARM64.zip +0 -0
  18. {ssh_handler-1.4.0 → ssh_handler-1.4.2}/ssh_handler/openssh/OpenSSH-Win32.zip +0 -0
  19. {ssh_handler-1.4.0 → ssh_handler-1.4.2}/ssh_handler/openssh/OpenSSH-Win64.zip +0 -0
  20. {ssh_handler-1.4.0 → ssh_handler-1.4.2}/ssh_handler/pool.py +0 -0
  21. {ssh_handler-1.4.0 → ssh_handler-1.4.2}/ssh_handler/pyqt_worker.py +0 -0
  22. {ssh_handler-1.4.0 → ssh_handler-1.4.2}/ssh_handler/setup_openssh_server.ps1 +0 -0
  23. {ssh_handler-1.4.0 → ssh_handler-1.4.2}/ssh_handler/winrm_bootstrap.py +0 -0
  24. {ssh_handler-1.4.0 → ssh_handler-1.4.2}/ssh_handler.egg-info/SOURCES.txt +0 -0
  25. {ssh_handler-1.4.0 → ssh_handler-1.4.2}/ssh_handler.egg-info/dependency_links.txt +0 -0
  26. {ssh_handler-1.4.0 → ssh_handler-1.4.2}/ssh_handler.egg-info/entry_points.txt +0 -0
  27. {ssh_handler-1.4.0 → ssh_handler-1.4.2}/ssh_handler.egg-info/requires.txt +0 -0
  28. {ssh_handler-1.4.0 → ssh_handler-1.4.2}/ssh_handler.egg-info/top_level.txt +0 -0
  29. {ssh_handler-1.4.0 → ssh_handler-1.4.2}/tests/test_offline.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ssh-handler
3
- Version: 1.4.0
3
+ Version: 1.4.2
4
4
  Summary: Extensive SSH/SFTP/SCP/FTP handler built on Paramiko, for test automation, CLIs and PyQt5 tools.
5
5
  Author: ssh-handler contributors
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ssh-handler"
7
- version = "1.4.0"
7
+ version = "1.4.2"
8
8
  description = "Extensive SSH/SFTP/SCP/FTP handler built on Paramiko, for test automation, CLIs and PyQt5 tools."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -22,7 +22,7 @@ dependencies (PyQt5 / scp) don't break the core package.
22
22
 
23
23
  from __future__ import annotations
24
24
 
25
- __version__ = "1.4.0"
25
+ __version__ = "1.4.2"
26
26
 
27
27
  from .config import SSHConfig, FTPConfig
28
28
  from .core import SSHHandler, ShellSession
@@ -655,7 +655,8 @@ class SSHHandler:
655
655
 
656
656
  def stream(self, command: str, *, on_line=None, on_match=None, match=None,
657
657
  stop_on_match: bool = False, save_to: Optional[str] = None,
658
- append: bool = True, timeout: Optional[float] = None,
658
+ append: bool = True, clean: bool = True,
659
+ timeout: Optional[float] = None,
659
660
  stop_event=None, encoding: str = "utf-8", safe: Optional[bool] = None):
660
661
  """
661
662
  Consume a streaming command with built-in matching and file logging.
@@ -669,6 +670,7 @@ class SSHHandler:
669
670
  :returns: dict with 'lines' (count) and 'matches' (list).
670
671
  """
671
672
  import re
673
+ from .results import strip_ansi
672
674
 
673
675
  def _do():
674
676
  pat = re.compile(match) if isinstance(match, str) else match
@@ -678,6 +680,8 @@ class SSHHandler:
678
680
  try:
679
681
  for line in self.iter_lines(command, timeout=timeout,
680
682
  stop_event=stop_event, encoding=encoding):
683
+ if clean:
684
+ line = strip_ansi(line)
681
685
  count += 1
682
686
  if fh:
683
687
  fh.write(line + "\n")
@@ -708,8 +712,9 @@ class SSHHandler:
708
712
  def serial_stream(self, device: str = "/dev/ttyUSB0", *, baudrate: int = 115200,
709
713
  mode: str = "auto", on_line=None, on_match=None, match=None,
710
714
  stop_on_match: bool = False, save_to: Optional[str] = None,
711
- timeout: Optional[float] = None, stop_event=None,
712
- configure: bool = True, safe: Optional[bool] = None):
715
+ clean: bool = True, timeout: Optional[float] = None,
716
+ stop_event=None, configure: bool = True,
717
+ safe: Optional[bool] = None):
713
718
  """
714
719
  Stream a serial port attached to the **remote** host, over SSH — so it
715
720
  works through a jump host (laptop -> RDP machine -> target). Same live
@@ -729,13 +734,20 @@ class SSHHandler:
729
734
  is_windows = (mode == "windows" or
730
735
  (mode == "auto" and device.upper().startswith("COM")))
731
736
  if is_windows:
737
+ # Force UTF-8 console output so our UTF-8 reader doesn't get mojibake,
738
+ # and read whatever bytes are available (robust to any line ending)
739
+ # rather than ReadLine (which hangs if the device's EOL differs).
732
740
  ps = (
733
741
  "$ErrorActionPreference='SilentlyContinue';"
742
+ "[Console]::OutputEncoding=[System.Text.Encoding]::UTF8;"
734
743
  f"$p=New-Object System.IO.Ports.SerialPort '{device}',{int(baudrate)},"
735
744
  "'None',8,'One';"
736
- "$p.ReadTimeout=1000;$p.Open();"
737
- "while($true){try{$l=$p.ReadLine();if($l){Write-Output $l}}"
738
- "catch [TimeoutException]{}}"
745
+ "$p.Encoding=[System.Text.Encoding]::UTF8;"
746
+ "$p.Open();"
747
+ "while($true){"
748
+ "if($p.BytesToRead -gt 0){"
749
+ "$s=$p.ReadExisting();[Console]::Out.Write($s);[Console]::Out.Flush()}"
750
+ "else{Start-Sleep -Milliseconds 30}}"
739
751
  )
740
752
  cmd = f"powershell -NoProfile -EncodedCommand {self._ps_encode(ps)}"
741
753
  else:
@@ -746,7 +758,7 @@ class SSHHandler:
746
758
  else:
747
759
  cmd = f"cat {dev}"
748
760
  return self.stream(cmd, on_line=on_line, on_match=on_match, match=match,
749
- stop_on_match=stop_on_match, save_to=save_to,
761
+ stop_on_match=stop_on_match, save_to=save_to, clean=clean,
750
762
  timeout=timeout, stop_event=stop_event, safe=safe)
751
763
 
752
764
  def serial_write(self, device: str, data: str, *, baudrate: int = 115200,
@@ -2,11 +2,28 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import re
5
6
  import time
6
7
  from dataclasses import dataclass, field, asdict
7
8
  from typing import Optional
8
9
 
9
10
 
11
+ # ANSI/VT escape sequences (CSI like ESC[23;80H, OSC like ESC]0;title BEL, etc.)
12
+ _ANSI_RE = re.compile(r"\x1b(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]|\][^\x07\x1b]*(?:\x07|\x1b\\))")
13
+ # control chars except tab(09), newline(0a), carriage-return(0d handled separately)
14
+ _CTRL_RE = re.compile(r"[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]")
15
+
16
+
17
+ def strip_ansi(text: str) -> str:
18
+ """Remove ANSI/VT escape codes, carriage returns, and other control chars,
19
+ so streamed console output is clean to save, match, and read."""
20
+ if not text:
21
+ return text
22
+ text = _ANSI_RE.sub("", text)
23
+ text = text.replace("\r", "")
24
+ return _CTRL_RE.sub("", text)
25
+
26
+
10
27
  def _human_size(num: float) -> str:
11
28
  for unit in ("B", "KB", "MB", "GB", "TB"):
12
29
  if abs(num) < 1024.0:
@@ -153,12 +153,16 @@ class SerialHandler:
153
153
 
154
154
  def stream(self, *, on_line=None, on_match=None, match=None,
155
155
  stop_on_match: bool = False, save_to: Optional[str] = None,
156
- append: bool = True, timeout: Optional[float] = None, stop_event=None,
156
+ append: bool = True, clean: bool = True,
157
+ timeout: Optional[float] = None, stop_event=None,
157
158
  encoding: str = "utf-8", safe=None):
158
159
  """
159
160
  Read the serial console continuously with built-in matching + file
160
- logging. Same signature/semantics as SSHHandler.stream.
161
+ logging. Same signature/semantics as SSHHandler.stream. ``clean=True``
162
+ strips ANSI escape codes and control chars from each line.
161
163
  """
164
+ from .results import strip_ansi
165
+
162
166
  def _do():
163
167
  pat = re.compile(match) if isinstance(match, str) else match
164
168
  matches, count = [], 0
@@ -167,6 +171,8 @@ class SerialHandler:
167
171
  try:
168
172
  for line in self.iter_lines(stop_event=stop_event, timeout=timeout,
169
173
  encoding=encoding):
174
+ if clean:
175
+ line = strip_ansi(line)
170
176
  count += 1
171
177
  if fh:
172
178
  fh.write(line + "\n")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ssh-handler
3
- Version: 1.4.0
3
+ Version: 1.4.2
4
4
  Summary: Extensive SSH/SFTP/SCP/FTP handler built on Paramiko, for test automation, CLIs and PyQt5 tools.
5
5
  Author: ssh-handler contributors
6
6
  License-Expression: MIT
File without changes
File without changes
File without changes