ssh-handler 1.4.2__tar.gz → 1.5.0__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.2/ssh_handler.egg-info → ssh_handler-1.5.0}/PKG-INFO +1 -1
  2. {ssh_handler-1.4.2 → ssh_handler-1.5.0}/pyproject.toml +1 -1
  3. {ssh_handler-1.4.2 → ssh_handler-1.5.0}/ssh_handler/__init__.py +1 -1
  4. {ssh_handler-1.4.2 → ssh_handler-1.5.0}/ssh_handler/core.py +16 -2
  5. {ssh_handler-1.4.2 → ssh_handler-1.5.0}/ssh_handler/results.py +7 -9
  6. {ssh_handler-1.4.2 → ssh_handler-1.5.0/ssh_handler.egg-info}/PKG-INFO +1 -1
  7. {ssh_handler-1.4.2 → ssh_handler-1.5.0}/LICENSE +0 -0
  8. {ssh_handler-1.4.2 → ssh_handler-1.5.0}/README.md +0 -0
  9. {ssh_handler-1.4.2 → ssh_handler-1.5.0}/setup.cfg +0 -0
  10. {ssh_handler-1.4.2 → ssh_handler-1.5.0}/ssh_handler/__main__.py +0 -0
  11. {ssh_handler-1.4.2 → ssh_handler-1.5.0}/ssh_handler/cli.py +0 -0
  12. {ssh_handler-1.4.2 → ssh_handler-1.5.0}/ssh_handler/config.py +0 -0
  13. {ssh_handler-1.4.2 → ssh_handler-1.5.0}/ssh_handler/credentials.py +0 -0
  14. {ssh_handler-1.4.2 → ssh_handler-1.5.0}/ssh_handler/exceptions.py +0 -0
  15. {ssh_handler-1.4.2 → ssh_handler-1.5.0}/ssh_handler/ftp.py +0 -0
  16. {ssh_handler-1.4.2 → ssh_handler-1.5.0}/ssh_handler/openssh/OpenSSH-ARM64.zip +0 -0
  17. {ssh_handler-1.4.2 → ssh_handler-1.5.0}/ssh_handler/openssh/OpenSSH-Win32.zip +0 -0
  18. {ssh_handler-1.4.2 → ssh_handler-1.5.0}/ssh_handler/openssh/OpenSSH-Win64.zip +0 -0
  19. {ssh_handler-1.4.2 → ssh_handler-1.5.0}/ssh_handler/pool.py +0 -0
  20. {ssh_handler-1.4.2 → ssh_handler-1.5.0}/ssh_handler/pyqt_worker.py +0 -0
  21. {ssh_handler-1.4.2 → ssh_handler-1.5.0}/ssh_handler/serial_handler.py +0 -0
  22. {ssh_handler-1.4.2 → ssh_handler-1.5.0}/ssh_handler/setup_openssh_server.ps1 +0 -0
  23. {ssh_handler-1.4.2 → ssh_handler-1.5.0}/ssh_handler/winrm_bootstrap.py +0 -0
  24. {ssh_handler-1.4.2 → ssh_handler-1.5.0}/ssh_handler.egg-info/SOURCES.txt +0 -0
  25. {ssh_handler-1.4.2 → ssh_handler-1.5.0}/ssh_handler.egg-info/dependency_links.txt +0 -0
  26. {ssh_handler-1.4.2 → ssh_handler-1.5.0}/ssh_handler.egg-info/entry_points.txt +0 -0
  27. {ssh_handler-1.4.2 → ssh_handler-1.5.0}/ssh_handler.egg-info/requires.txt +0 -0
  28. {ssh_handler-1.4.2 → ssh_handler-1.5.0}/ssh_handler.egg-info/top_level.txt +0 -0
  29. {ssh_handler-1.4.2 → ssh_handler-1.5.0}/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.2
3
+ Version: 1.5.0
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.2"
7
+ version = "1.5.0"
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.2"
25
+ __version__ = "1.5.0"
26
26
 
27
27
  from .config import SSHConfig, FTPConfig
28
28
  from .core import SSHHandler, ShellSession
@@ -603,7 +603,7 @@ class SSHHandler:
603
603
  # Continuous / streaming output (tail -f, slog2info -w, journalctl -f…)
604
604
  # ------------------------------------------------------------------ #
605
605
  def iter_lines(self, command: str, *, timeout: Optional[float] = None,
606
- stop_event=None, get_pty: bool = True, encoding: str = "utf-8",
606
+ stop_event=None, get_pty: bool = False, encoding: str = "utf-8",
607
607
  idle_poll: float = 0.4):
608
608
  """
609
609
  Run a (possibly never-ending) command and yield its stdout **line by
@@ -1032,6 +1032,10 @@ class SSHHandler:
1032
1032
  local_path = os.path.expanduser(local_path)
1033
1033
  sftp = self.sftp()
1034
1034
  start = time.time()
1035
+ # Verify the remote path exists FIRST, so a missing source never leaves
1036
+ # behind an empty local file (sftp.get opens the local file before fetch).
1037
+ if not self._remote_exists(sftp, remote_path):
1038
+ raise SSHTransferError(f"Remote path does not exist: {remote_path}")
1035
1039
  try:
1036
1040
  if self._remote_is_dir(sftp, remote_path):
1037
1041
  if not recursive:
@@ -1044,7 +1048,17 @@ class SSHHandler:
1044
1048
  if parent and not os.path.exists(parent):
1045
1049
  os.makedirs(parent, exist_ok=True)
1046
1050
  self._emit(logging.INFO, f"PULL {remote_path} -> {local_path}")
1047
- sftp.get(remote_path, local_path, callback=callback)
1051
+ existed = os.path.exists(local_path)
1052
+ try:
1053
+ sftp.get(remote_path, local_path, callback=callback)
1054
+ except BaseException:
1055
+ # don't leave a partial/empty local file behind on failure
1056
+ if not existed and os.path.exists(local_path):
1057
+ try:
1058
+ os.remove(local_path)
1059
+ except OSError:
1060
+ pass
1061
+ raise
1048
1062
  size, count = os.path.getsize(local_path), 1
1049
1063
  except SSHTransferError:
1050
1064
  raise
@@ -60,10 +60,8 @@ class CommandResult:
60
60
  return asdict(self)
61
61
 
62
62
  def __str__(self) -> str:
63
- return (
64
- f"<CommandResult host={self.host} exit={self.exit_code} "
65
- f"ok={self.ok} dur={self.duration:.2f}s cmd={self.command!r}>"
66
- )
63
+ status = "ok" if self.ok else f"FAILED (exit {self.exit_code})"
64
+ return f"$ {self.command} [{status}, {self.duration:.2f}s]"
67
65
 
68
66
 
69
67
  @dataclass
@@ -96,11 +94,11 @@ class TransferResult:
96
94
  return d
97
95
 
98
96
  def __str__(self) -> str:
99
- return (
100
- f"<TransferResult {self.direction}/{self.protocol} "
101
- f"{self.source} -> {self.dest} {self.human_size} "
102
- f"in {self.duration:.2f}s ({self.human_speed}), files={self.files}>"
103
- )
97
+ verb = "Uploaded" if self.direction == "push" else "Downloaded"
98
+ files = f"{self.files} files, " if self.files != 1 else ""
99
+ return (f"{verb} {self.source} -> {self.dest} "
100
+ f"({files}{self.human_size} in {self.duration:.2f}s, "
101
+ f"{self.human_speed})")
104
102
 
105
103
 
106
104
  @dataclass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ssh-handler
3
- Version: 1.4.2
3
+ Version: 1.5.0
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