speedtest-ssh 2.10.2__tar.gz → 2.12.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 (22) hide show
  1. {speedtest_ssh-2.10.2/speedtest_ssh.egg-info → speedtest_ssh-2.12.0}/PKG-INFO +4 -4
  2. {speedtest_ssh-2.10.2 → speedtest_ssh-2.12.0}/pyproject.toml +11 -9
  3. speedtest_ssh-2.12.0/speedtest_ssh/_version.py +1 -0
  4. {speedtest_ssh-2.10.2 → speedtest_ssh-2.12.0}/speedtest_ssh/config.py +1 -3
  5. {speedtest_ssh-2.10.2 → speedtest_ssh-2.12.0}/speedtest_ssh/data_transfer.py +17 -26
  6. {speedtest_ssh-2.10.2 → speedtest_ssh-2.12.0}/speedtest_ssh/ping.py +6 -5
  7. speedtest_ssh-2.12.0/speedtest_ssh/py.typed +1 -0
  8. {speedtest_ssh-2.10.2 → speedtest_ssh-2.12.0}/speedtest_ssh/sftp_wrapper.py +7 -10
  9. {speedtest_ssh-2.10.2 → speedtest_ssh-2.12.0}/speedtest_ssh/speedtest_ssh.py +13 -16
  10. {speedtest_ssh-2.10.2 → speedtest_ssh-2.12.0}/speedtest_ssh/util.py +20 -11
  11. {speedtest_ssh-2.10.2 → speedtest_ssh-2.12.0/speedtest_ssh.egg-info}/PKG-INFO +4 -4
  12. speedtest_ssh-2.10.2/speedtest_ssh/_version.py +0 -1
  13. speedtest_ssh-2.10.2/speedtest_ssh/py.typed +0 -1
  14. {speedtest_ssh-2.10.2 → speedtest_ssh-2.12.0}/LICENSE +0 -0
  15. {speedtest_ssh-2.10.2 → speedtest_ssh-2.12.0}/README.md +0 -0
  16. {speedtest_ssh-2.10.2 → speedtest_ssh-2.12.0}/setup.cfg +0 -0
  17. {speedtest_ssh-2.10.2 → speedtest_ssh-2.12.0}/speedtest_ssh/__init__.py +0 -0
  18. {speedtest_ssh-2.10.2 → speedtest_ssh-2.12.0}/speedtest_ssh.egg-info/SOURCES.txt +0 -0
  19. {speedtest_ssh-2.10.2 → speedtest_ssh-2.12.0}/speedtest_ssh.egg-info/dependency_links.txt +0 -0
  20. {speedtest_ssh-2.10.2 → speedtest_ssh-2.12.0}/speedtest_ssh.egg-info/entry_points.txt +0 -0
  21. {speedtest_ssh-2.10.2 → speedtest_ssh-2.12.0}/speedtest_ssh.egg-info/requires.txt +0 -0
  22. {speedtest_ssh-2.10.2 → speedtest_ssh-2.12.0}/speedtest_ssh.egg-info/top_level.txt +0 -0
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: speedtest_ssh
3
- Version: 2.10.2
3
+ Version: 2.12.0
4
4
  Summary: A tool meant to check inter-device speeds via ssh
5
- License: GPL
5
+ License-Expression: GPL-3.0-only
6
6
  Project-URL: Homepage, https://github.com/zwimer/speedtest_ssh
7
7
  Classifier: Programming Language :: Python :: 3
8
- Classifier: Programming Language :: Python :: 3.12
9
- Requires-Python: >=3.12
8
+ Classifier: Programming Language :: Python :: 3.14
9
+ Requires-Python: >=3.14
10
10
  Description-Content-Type: text/markdown
11
11
  License-File: LICENSE
12
12
  Requires-Dist: argcomplete
@@ -1,22 +1,23 @@
1
1
  [build-system]
2
- requires = ["setuptools>=61.2"]
2
+ requires = ["setuptools>=77"]
3
3
  build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "speedtest_ssh"
7
7
  classifiers = [
8
- "Programming Language :: Python :: 3",
9
- "Programming Language :: Python :: 3.12",
8
+ "Programming Language :: Python :: 3",
9
+ "Programming Language :: Python :: 3.14",
10
10
  ]
11
- license = {text = "GPL"}
11
+ license = "GPL-3.0-only"
12
+ license-files = ["LICENSE"]
12
13
  description = "A tool meant to check inter-device speeds via ssh"
13
14
  urls = {Homepage = "https://github.com/zwimer/speedtest_ssh"}
14
- requires-python = ">= 3.12"
15
+ requires-python = ">= 3.14"
15
16
  dynamic = ["version"]
16
17
  dependencies = [
17
18
  "argcomplete",
18
- "paramiko",
19
- "tqdm",
19
+ "paramiko",
20
+ "tqdm",
20
21
  ]
21
22
 
22
23
  [project.readme]
@@ -27,7 +28,6 @@ content-type = "text/markdown"
27
28
  speedtest-ssh = "speedtest_ssh:cli"
28
29
 
29
30
  [tool.setuptools]
30
- license-files = ["LICENSE"]
31
31
  include-package-data = false
32
32
 
33
33
  [tool.setuptools.packages]
@@ -49,10 +49,12 @@ disable = [
49
49
  "line-too-long",
50
50
  "invalid-name",
51
51
  ]
52
+ [tool.pylint.main]
53
+ jobs = 0
52
54
 
53
55
  [tool.black]
54
56
  line-length = 120
55
- target-version = ["py312"]
57
+ target-version = ["py314", "py315"]
56
58
 
57
59
  [tool.ruff]
58
60
  line-length = 120
@@ -0,0 +1 @@
1
+ __version__: str = "2.12.0"
@@ -3,9 +3,7 @@ from dataclasses import dataclass
3
3
 
4
4
  @dataclass(kw_only=True)
5
5
  class Config:
6
- """
7
- A simple config dataclass that holds information used to access a remote client
8
- """
6
+ """A simple config dataclass that holds information used to access a remote client"""
9
7
 
10
8
  host: str
11
9
  user: str | None
@@ -1,4 +1,3 @@
1
- from __future__ import annotations
2
1
  from subprocess import CalledProcessError, PIPE
3
2
  from logging import getLogger, DEBUG, INFO
4
3
  from typing import TYPE_CHECKING, Self
@@ -30,17 +29,16 @@ def _rand_str(size: int) -> str:
30
29
 
31
30
 
32
31
  class ProgressBar(tqdm):
33
- """
34
- A small tqdm wrapper for paramiko's SFTP callbacks to use
35
- """
32
+ """A small tqdm wrapper for paramiko's SFTP callbacks to use"""
36
33
 
37
34
  def __init__(self, desc: str):
38
35
  super().__init__(desc=desc, unit="B", unit_scale=True, unit_divisor=1024, dynamic_ncols=True)
39
36
 
40
37
  def __call__(self, bytes_done: int, bytes_remaining: int):
41
38
  """
42
- :param bytes_done: The number of bytes transferred
43
- :param bytes_remaining: The number of bytes yet to be transferred
39
+ Args:
40
+ bytes_done: The number of bytes transferred
41
+ bytes_remaining: The number of bytes yet to be transferred
44
42
  """
45
43
  self.total: int | None
46
44
  if self.total is None:
@@ -49,8 +47,7 @@ class ProgressBar(tqdm):
49
47
 
50
48
 
51
49
  class DataTransfer:
52
- """
53
- A context manager used to send and receive data to and from the remote client
50
+ """A context manager used to send and receive data to and from the remote client
54
51
  This class is a context manager; on exit will remove the remote file on deletion
55
52
  """
56
53
 
@@ -58,7 +55,8 @@ class DataTransfer:
58
55
 
59
56
  def __init__(self, config: Config):
60
57
  """
61
- :param config: The Config object the DataTransfer instance should use
58
+ Args:
59
+ config: The Config object the DataTransfer instance should use
62
60
  """
63
61
  self._l = getLogger(self._LOG)
64
62
  self._remote_f: str = f"/tmp/speedtest-ssh_{datetime.now(tz=UTC)}_{_rand_str(8)}.tmp"
@@ -79,21 +77,15 @@ class DataTransfer:
79
77
  self._sftp_cm.__exit__(*args, **kwargs)
80
78
 
81
79
  def put(self, local: Path) -> None:
82
- """
83
- Send the local file to be saved at the remote path
84
- """
80
+ """Send the local file to be saved at the remote path"""
85
81
  raise NotImplementedError
86
82
 
87
83
  def get(self, local: Path) -> None:
88
- """
89
- Get the remote file to and save it at the local path
90
- """
84
+ """Get the remote file to and save it at the local path"""
91
85
  raise NotImplementedError
92
86
 
93
87
  def clean_remote(self) -> None:
94
- """
95
- Removes the remote file
96
- """
88
+ """Removes the remote file"""
97
89
  try:
98
90
  self._l.debug("Removing remote file (if it exists): %s", self._remote_f)
99
91
  self._sftp.remove(self._remote_f)
@@ -102,9 +94,7 @@ class DataTransfer:
102
94
 
103
95
 
104
96
  class SFTP(DataTransfer):
105
- """
106
- A DataTransfer classes that uses SFTP
107
- """
97
+ """A DataTransfer classes that uses SFTP"""
108
98
 
109
99
  def put(self, local: Path) -> None:
110
100
  with ProgressBar("Upload ") as p:
@@ -116,14 +106,14 @@ class SFTP(DataTransfer):
116
106
 
117
107
 
118
108
  class Rsync(DataTransfer):
119
- """
120
- A DataTransfer that uses rsync
109
+ """A DataTransfer that uses rsync
121
110
  Requires rsync be installed
122
111
  """
123
112
 
124
113
  def __init__(self, config: Config):
125
114
  """
126
- :param host: The hostname ssh uses for the remote client
115
+ Args:
116
+ host: The hostname ssh uses for the remote client
127
117
  """
128
118
  super().__init__(config)
129
119
  self._remote_f = re.sub(r"[^a-zA-Z\d_/-]", "_", self._remote_f) # Old rsync args suck
@@ -159,8 +149,9 @@ class Rsync(DataTransfer):
159
149
 
160
150
  def _transfer(self, src: str | Path, dst: str | Path) -> None:
161
151
  """
162
- :param src: The file to rsync to dst
163
- :param dst: The location to rsync src to
152
+ Args:
153
+ src: The file to rsync to dst
154
+ dst: The location to rsync src to
164
155
  """
165
156
  try:
166
157
  _ = run_cmd(self._rsync, *self._flags, src, dst, env=self._env, check=True)
@@ -11,15 +11,16 @@ _LOG = "Ping"
11
11
 
12
12
 
13
13
  class PingFailed(RuntimeError):
14
- """
15
- Raise on ping failure
16
- """
14
+ """Raise on ping failure"""
17
15
 
18
16
 
19
17
  def ping(host: str, n_pings: int = 10, max_wait: int = 3) -> float:
20
18
  """
21
- :param host: The host to ping
22
- :return: The number Ping host
19
+ Args:
20
+ host: The host to ping
21
+
22
+ Returns:
23
+ The number Ping host
23
24
  """
24
25
  # We use subprocess b/c ping is suid and we don't want to require root
25
26
  print("Pinging...")
@@ -21,9 +21,10 @@ _LOG = "SFTPWrapper"
21
21
 
22
22
 
23
23
  def create_paramiko_config(raw: Config) -> dict:
24
- """
25
- Create a dict for paramiko to use as its config
26
- :return: A dict containing info needed for paramiko to ssh
24
+ """Create a dict for paramiko to use as its config
25
+
26
+ Returns:
27
+ A dict containing info needed for paramiko to ssh
27
28
  """
28
29
  log = getLogger(_LOG)
29
30
  config: dict[str, str | int | None] = dataclasses.asdict(raw)
@@ -56,9 +57,7 @@ def create_paramiko_config(raw: Config) -> dict:
56
57
 
57
58
 
58
59
  def load_keys(client: SSHClient, host: str, port: int) -> None:
59
- """
60
- Load available ssh keys into the ssh client
61
- """
60
+ """Load available ssh keys into the ssh client"""
62
61
  log = getLogger(_LOG)
63
62
  hosts_f = Path.home() / ".ssh/known_hosts"
64
63
  if hosts_f.exists():
@@ -73,10 +72,8 @@ def load_keys(client: SSHClient, host: str, port: int) -> None:
73
72
 
74
73
 
75
74
  @contextmanager
76
- def sftp_wrapper(raw: Config) -> Generator["SFTPClient", None, None]:
77
- """
78
- A context manager which yields a conncted SFTPClient
79
- """
75
+ def sftp_wrapper(raw: Config) -> Generator[SFTPClient]:
76
+ """A context manager which yields a conncted SFTPClient"""
80
77
  log = getLogger(_LOG)
81
78
  log.debug("Initializing ssh client")
82
79
  with SSHClient() as client:
@@ -21,12 +21,15 @@ _LOG = "speedtest_ssh"
21
21
 
22
22
 
23
23
  def _iteration(temp: Path, client: DataTransfer, size: int) -> tuple[int, int]:
24
- """
25
- Time uploading and downloading a file of size bytes
26
- :param temp: A temporary file we have ownership of
27
- :param client: A client used to transfer the data
28
- :param size: how much data to transfer
29
- :return: <nanoseconds to upload>, <nanoseconds to download>
24
+ """Time uploading and downloading a file of size bytes
25
+
26
+ Args:
27
+ temp: A temporary file we have ownership of
28
+ client: A client used to transfer the data
29
+ size: how much data to transfer
30
+
31
+ Returns:
32
+ <nanoseconds to upload>, <nanoseconds to download>
30
33
  """
31
34
  # Create the file to upload; sendfile not always supported so we do this the hard way
32
35
  log = getLogger(_LOG)
@@ -56,18 +59,14 @@ def _iteration(temp: Path, client: DataTransfer, size: int) -> tuple[int, int]:
56
59
 
57
60
 
58
61
  def _print_results(ping_delay: float | None, up_ns: int, down_ns: int, size: int) -> None:
59
- """
60
- Print the speed test results
61
- """
62
+ """Print the speed test results"""
62
63
  prefix = "\n" + ("" if ping_delay is None else f"Ping: {ping_delay} ms\n")
63
64
  fmt = lambda s: f"{8 * (size/1024**2) / (s/1000**3):.2f} Mbit/s"
64
65
  print(f"{prefix}Upload Speed: {fmt(up_ns)}\nDownload Speed: {fmt(down_ns)}")
65
66
 
66
67
 
67
68
  def speedtest_ssh(seconds: int, ping: bool, mode: str, conf: Config) -> None:
68
- """
69
- Run speedtest_ssh
70
- """
69
+ """Run speedtest_ssh"""
71
70
  log = getLogger(_LOG)
72
71
  ping_delay: float | None = ping_test(conf.host) if ping else None
73
72
  print("Connecting...")
@@ -93,9 +92,7 @@ def speedtest_ssh(seconds: int, ping: bool, mode: str, conf: Config) -> None:
93
92
 
94
93
 
95
94
  def cli() -> None:
96
- """
97
- speedtest_ssh main
98
- """
95
+ """speedtest_ssh main"""
99
96
  parser = argparse.ArgumentParser()
100
97
  parser.add_argument("-V", "--version", action="version", version=f"{parser.prog} {__version__}")
101
98
  remote = parser.add_argument_group("SSH Options")
@@ -128,7 +125,7 @@ def cli() -> None:
128
125
  # Configure logging
129
126
  lvl = ns.pop("verbose")
130
127
  lvl = _LOG_VERBOSITY[max(i for i in _LOG_VERBOSITY if i <= lvl)]
131
- basicConfig(level=lvl, format="%(levelname)-8s %(message)s")
128
+ basicConfig(level=lvl, format="%(levelname)-7s - %(message)s")
132
129
  getLogger("main").debug("Log level set to %d", lvl)
133
130
  # Call speedtest
134
131
  ns["password"] = None
@@ -13,8 +13,11 @@ _LOG = "Util"
13
13
 
14
14
  def find_exe(name: str) -> Path:
15
15
  """
16
- :param name: The name of the executable to find
17
- :return: The path to the executable
16
+ Args:
17
+ name: The name of the executable to find
18
+
19
+ Returns:
20
+ The path to the executable
18
21
  """
19
22
  where: str | None = which(name)
20
23
  if where is None:
@@ -36,10 +39,13 @@ def _log_cmd(cmd: Path, args: tuple[str | Path, ...]) -> None:
36
39
 
37
40
  def run_cmd(cmd: Path, *args: str | Path, **kwargs) -> subprocess.CompletedProcess:
38
41
  """
39
- :param cmd: The command to run
40
- :param args: The arguments of cmd
41
- :param kwargs: Fowarded to subprocess.Popen
42
- :return: Completed process
42
+ Args:
43
+ cmd: The command to run
44
+ *args: The arguments of cmd
45
+ **kwargs: Fowarded to subprocess.Popen
46
+
47
+ Returns:
48
+ Completed process
43
49
  """
44
50
  _log_cmd(cmd, args)
45
51
  return subprocess.run((cmd, *args), **kwargs) # nosec B603 # noqa: PLW1510
@@ -47,11 +53,14 @@ def run_cmd(cmd: Path, *args: str | Path, **kwargs) -> subprocess.CompletedProce
47
53
 
48
54
  def tee_cmd(cmd: Path, *args: str | Path, level: int, **kwargs) -> tuple[subprocess.Popen, str]:
49
55
  """
50
- :param cmd: The command to run (and info log the output of)
51
- :param args: The arguments of cmd
52
- :param level: The log level to use
53
- :param kwargs: Fowarded to subprocess.Popen aside from test and stdout
54
- :return: Completed process, stdout (do not read stdout from CompletedProcess)
56
+ Args:
57
+ cmd: The command to run (and info log the output of)
58
+ *args: The arguments of cmd
59
+ level: The log level to use
60
+ **kwargs: Fowarded to subprocess.Popen aside from test and stdout
61
+
62
+ Returns:
63
+ Completed process, stdout (do not read stdout from CompletedProcess)
55
64
  """
56
65
  assert "text" not in kwargs # nosec B101
57
66
  assert "stdout" not in kwargs # nosec B101
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: speedtest_ssh
3
- Version: 2.10.2
3
+ Version: 2.12.0
4
4
  Summary: A tool meant to check inter-device speeds via ssh
5
- License: GPL
5
+ License-Expression: GPL-3.0-only
6
6
  Project-URL: Homepage, https://github.com/zwimer/speedtest_ssh
7
7
  Classifier: Programming Language :: Python :: 3
8
- Classifier: Programming Language :: Python :: 3.12
9
- Requires-Python: >=3.12
8
+ Classifier: Programming Language :: Python :: 3.14
9
+ Requires-Python: >=3.14
10
10
  Description-Content-Type: text/markdown
11
11
  License-File: LICENSE
12
12
  Requires-Dist: argcomplete
@@ -1 +0,0 @@
1
- __version__: str = "2.10.2"
@@ -1 +0,0 @@
1
- PARTIAL
File without changes
File without changes
File without changes