zspeedtest 0.1.1__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.
zspeedtest/__init__.py ADDED
@@ -0,0 +1,5 @@
1
+ """Core zspeedtest components for the zspeedtest library."""
2
+
3
+ from importlib.metadata import version as get_version
4
+
5
+ __version__ = get_version("zspeedtest")
zspeedtest/main.py ADDED
@@ -0,0 +1,149 @@
1
+ """Internet speed tester.
2
+
3
+ Usage: zspeedtest <URL> [--requests N]
4
+ """
5
+
6
+ import argparse
7
+ import sys
8
+ import textwrap
9
+ import time
10
+ from typing import NamedTuple
11
+ from urllib.request import Request, urlopen
12
+
13
+ KB = 1024
14
+ MB = KB**2
15
+ CHUNK_SIZE = KB * 64
16
+ DEFAULT_URL = "http://ipv4.download.thinkbroadband.com/10MB.zip"
17
+
18
+
19
+ class SpeedTestArgs(NamedTuple):
20
+ url: str
21
+ requests: int
22
+ timeout: int
23
+
24
+ @classmethod
25
+ def from_cli(cls) -> "SpeedTestArgs":
26
+ parser = argparse.ArgumentParser(
27
+ description="Internet speed tester",
28
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
29
+ )
30
+ parser.add_argument(
31
+ "url",
32
+ nargs="?",
33
+ default=DEFAULT_URL,
34
+ help="URL of a large file to download",
35
+ )
36
+ parser.add_argument(
37
+ "--requests",
38
+ "-n",
39
+ type=int,
40
+ default=10,
41
+ metavar="N",
42
+ help="Number of requests",
43
+ )
44
+ parser.add_argument(
45
+ "--timeout",
46
+ "-t",
47
+ type=int,
48
+ default=30,
49
+ metavar="N",
50
+ help="Request timeout",
51
+ )
52
+ namespace = parser.parse_args()
53
+
54
+ if not namespace.url.startswith(("http:", "https:")):
55
+ msg = "URL must start with 'http:' or 'https:'"
56
+ raise ValueError(msg)
57
+
58
+ return cls(**namespace.__dict__)
59
+
60
+
61
+ class RequestResult(NamedTuple):
62
+ duration_seconds: float
63
+ bytes_downloaded: int
64
+
65
+
66
+ def download_url(req: Request, timeout: int) -> RequestResult:
67
+ start = time.perf_counter()
68
+ total_bytes: int = 0
69
+
70
+ # nosemgrep: python.lang.security.audit.dynamic-urllib-use-detected.dynamic-urllib-use-detected # noqa: E501, ERA001
71
+ with urlopen(req, timeout=timeout) as response: # nosec
72
+ while chunk := response.read(CHUNK_SIZE):
73
+ total_bytes += len(chunk)
74
+
75
+ duration = time.perf_counter() - start
76
+ return RequestResult(
77
+ duration_seconds=duration,
78
+ bytes_downloaded=total_bytes,
79
+ )
80
+
81
+
82
+ def format_size(bytes_count: float) -> str:
83
+ for unit in ("B", "KB", "MB", "GB"):
84
+ if bytes_count < KB:
85
+ return f"{bytes_count:.1f} {unit}"
86
+ bytes_count /= KB
87
+ return f"{bytes_count:.1f} TB"
88
+
89
+
90
+ def run() -> None:
91
+ args = SpeedTestArgs.from_cli()
92
+ start_info = textwrap.dedent(f"""\
93
+ URL: {args.url}
94
+ Requests: {args.requests}
95
+ {"-" * 52}
96
+ {"#":>3} {"Size":>10} {"Time":>8} {"Speed":>12}
97
+ {"-" * 52}
98
+ """)
99
+ print(start_info)
100
+
101
+ results: list[RequestResult] = []
102
+
103
+ req = Request(args.url)
104
+ req.add_header("User-Agent", "zspeedtest/1.0")
105
+
106
+ for i in range(1, args.requests + 1):
107
+ try:
108
+ r = download_url(req, timeout=args.timeout)
109
+ except Exception as e: # noqa: BLE001
110
+ print(f"{i:>3} ERROR: {e}")
111
+ continue
112
+
113
+ results.append(r)
114
+
115
+ speed_mbps = (r.bytes_downloaded / r.duration_seconds) / MB
116
+ print(
117
+ f"{i:>3} {format_size(r.bytes_downloaded):>10}"
118
+ f" {r.duration_seconds:>7.2f}s {speed_mbps:>10.2f} MB/s"
119
+ )
120
+
121
+ if not results:
122
+ print("\nNo requests completed successfully.")
123
+ sys.exit(1)
124
+
125
+ print("=" * 52)
126
+
127
+ total_bytes = sum(r.bytes_downloaded for r in results)
128
+ total_time = sum(r.duration_seconds for r in results)
129
+ avg_time = total_time / len(results)
130
+ avg_speed = (total_bytes / total_time) / MB
131
+ min_speed = min(
132
+ (r.bytes_downloaded / r.duration_seconds) / MB for r in results
133
+ )
134
+ max_speed = max(
135
+ (r.bytes_downloaded / r.duration_seconds) / MB for r in results
136
+ )
137
+
138
+ end_info = textwrap.dedent(f"""\
139
+ Successful requests : {len(results)} / {args.requests}
140
+ Total downloaded : {format_size(total_bytes)}
141
+ Average time : {avg_time:.2f} s
142
+ Average speed : {avg_speed:.2f} MB/s
143
+ Min / Max : {min_speed:.2f} / {max_speed:.2f} MB/s
144
+ """)
145
+ print(end_info)
146
+
147
+
148
+ if __name__ == "__main__":
149
+ run()
@@ -0,0 +1,84 @@
1
+ Metadata-Version: 2.4
2
+ Name: zspeedtest
3
+ Version: 0.1.1
4
+ Summary: zspeedtest
5
+ Keywords: zspeedtest
6
+ Author: Sergey
7
+ Author-email: Sergey <kava.develop@protonmail.com>
8
+ License-Expression: MIT
9
+ Classifier: Typing :: Typed
10
+ Classifier: Framework :: Pytest
11
+ Classifier: Framework :: AsyncIO
12
+ Classifier: Natural Language :: English
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Classifier: Topic :: Utilities
24
+ Requires-Python: >=3.10, <3.15
25
+ Project-URL: Homepage, https://theseriff.github.io/zspeedtest/
26
+ Project-URL: Repository, https://github.com/theseriff/zspeedtest
27
+ Project-URL: Documentation, https://theseriff.github.io/zspeedtest/
28
+ Project-URL: Changelog, https://github.com/theseriff/zspeedtest/blob/main/docs/CHANGELOG.md
29
+ Project-URL: Issues, https://github.com/theseriff/zspeedtest/issues
30
+ Description-Content-Type: text/markdown
31
+
32
+ <div align="center">
33
+
34
+ <h1>zspeedtest</h1>
35
+ <p><strong>Simple CLI internet speed tester. Downloads a file and measures throughput.</strong></p>
36
+
37
+ [![Supported Python versions](https://img.shields.io/pypi/pyversions/zspeedtest.svg)](https://pypi.org/project/zspeedtest)
38
+ [![PyPI version](https://badge.fury.io/py/zspeedtest.svg)](https://pypi.python.org/pypi/zspeedtest)
39
+ [![Tests](https://github.com/theseriff/zspeedtest/actions/workflows/pr_tests.yaml/badge.svg)](https://github.com/theseriff/zspeedtest/actions/workflows/pr_tests.yaml)
40
+ [![Coverage](https://coverage-badge.samuelcolvin.workers.dev/theseriff/zspeedtest.svg)](https://coverage-badge.samuelcolvin.workers.dev/redirect/theseriff/zspeedtest)
41
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
42
+
43
+ </div>
44
+
45
+ ```bash
46
+ uv add zspeedtest
47
+ zspeedtest
48
+ ```
49
+
50
+ ## Usage
51
+
52
+ ```
53
+ zspeedtest [URL] [--requests N] [--timeout N]
54
+ ```
55
+
56
+ - `URL` — large file to download (default: 10MB test file from ThinkBroadband)
57
+ - `--requests`/`-n` — number of test requests (default: 10)
58
+ - `--timeout`/`-t` — per-request timeout in seconds (default: 30)
59
+
60
+ ### Examples
61
+
62
+ ```bash
63
+ zspeedtest
64
+ zspeedtest http://example.com/file.bin --requests 5
65
+ zspeedtest http://example.com/file.bin -n 3 -t 15
66
+ ```
67
+
68
+ Output:
69
+
70
+ ```bash
71
+ URL: http://ipv4.download.thinkbroadband.com/10MB.zip
72
+ Requests: 1
73
+ ----------------------------------------------------
74
+ # Size Time Speed
75
+ ----------------------------------------------------
76
+
77
+ 1 10.0 MB 29.95s 0.33 MB/s
78
+ ====================================================
79
+ Successful requests : 1 / 1
80
+ Total downloaded : 10.0 MB
81
+ Average time : 29.95 s
82
+ Average speed : 0.33 MB/s
83
+ Min / Max : 0.33 / 0.33 MB/s
84
+ ```
@@ -0,0 +1,6 @@
1
+ zspeedtest/__init__.py,sha256=g67SDd0H4atCLjpFUcESP0JlWXyOkYMmf5nLAavR5us,157
2
+ zspeedtest/main.py,sha256=nKlFO_VFdM6iUlYo5-rHSiRGVCrGuI4zgou4cNp6yCI,4053
3
+ zspeedtest-0.1.1.dist-info/WHEEL,sha256=s49dN1sxqzkgPplo4QuUaKomil-_cbDzeLK4-pZKD-A,81
4
+ zspeedtest-0.1.1.dist-info/entry_points.txt,sha256=y1ChsvhdTkNwup6Cgwm0VRE5wCRZgzxdHySE6qXc9K0,52
5
+ zspeedtest-0.1.1.dist-info/METADATA,sha256=OIKwxSHlUT8JRGZU-u9PNQCsM9692l2spWHkBrttYSQ,3090
6
+ zspeedtest-0.1.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.11.24
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ zspeedtest = zspeedtest.main:run
3
+