speedtest-cli-ng 0.2.0__tar.gz → 0.3.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 (60) hide show
  1. speedtest_cli_ng-0.3.0/.gitignore +220 -0
  2. {speedtest_cli_ng-0.2.0/speedtest_cli_ng.egg-info → speedtest_cli_ng-0.3.0}/PKG-INFO +17 -13
  3. {speedtest_cli_ng-0.2.0 → speedtest_cli_ng-0.3.0}/README.md +6 -4
  4. {speedtest_cli_ng-0.2.0 → speedtest_cli_ng-0.3.0}/pyproject.toml +46 -35
  5. speedtest_cli_ng-0.3.0/src/speedtest/__init__.py +3 -0
  6. {speedtest_cli_ng-0.2.0 → speedtest_cli_ng-0.3.0/src}/speedtest/__main__.py +8 -11
  7. speedtest_cli_ng-0.3.0/src/speedtest/__version__.py +5 -0
  8. speedtest_cli_ng-0.3.0/src/speedtest/cli/__init__.py +7 -0
  9. speedtest_cli_ng-0.3.0/src/speedtest/cli/display.py +69 -0
  10. {speedtest_cli_ng-0.2.0 → speedtest_cli_ng-0.3.0/src}/speedtest/cli/parser.py +2 -8
  11. speedtest_cli_ng-0.3.0/src/speedtest/cli/runner.py +107 -0
  12. {speedtest_cli_ng-0.2.0 → speedtest_cli_ng-0.3.0/src}/speedtest/client.py +14 -18
  13. speedtest_cli_ng-0.3.0/src/speedtest/engine/__init__.py +30 -0
  14. speedtest_cli_ng-0.3.0/src/speedtest/engine/config.py +72 -0
  15. {speedtest_cli_ng-0.2.0 → speedtest_cli_ng-0.3.0/src}/speedtest/engine/network.py +30 -34
  16. {speedtest_cli_ng-0.2.0 → speedtest_cli_ng-0.3.0/src}/speedtest/engine/servers.py +10 -14
  17. {speedtest_cli_ng-0.2.0 → speedtest_cli_ng-0.3.0/src}/speedtest/engine/transfer.py +41 -27
  18. {speedtest_cli_ng-0.2.0 → speedtest_cli_ng-0.3.0/src}/speedtest/exceptions.py +10 -10
  19. speedtest_cli_ng-0.3.0/src/speedtest/models/__init__.py +17 -0
  20. speedtest_cli_ng-0.2.0/speedtest/models/config.py → speedtest_cli_ng-0.3.0/src/speedtest/models/api_config.py +3 -2
  21. {speedtest_cli_ng-0.2.0 → speedtest_cli_ng-0.3.0/src}/speedtest/models/context.py +17 -5
  22. speedtest_cli_ng-0.3.0/src/speedtest/models/location.py +25 -0
  23. {speedtest_cli_ng-0.2.0 → speedtest_cli_ng-0.3.0/src}/speedtest/models/result.py +2 -2
  24. {speedtest_cli_ng-0.2.0 → speedtest_cli_ng-0.3.0/src}/speedtest/models/server.py +3 -26
  25. speedtest_cli_ng-0.3.0/src/speedtest/utils/__init__.py +9 -0
  26. speedtest_cli_ng-0.3.0/src/speedtest/utils/constants.py +7 -0
  27. speedtest_cli_ng-0.3.0/src/speedtest/utils/logger.py +41 -0
  28. speedtest_cli_ng-0.3.0/tests/cli/test_display.py +93 -0
  29. speedtest_cli_ng-0.3.0/tests/cli/test_parser.py +84 -0
  30. speedtest_cli_ng-0.3.0/tests/cli/test_runner.py +167 -0
  31. speedtest_cli_ng-0.3.0/tests/conftest.py +88 -0
  32. speedtest_cli_ng-0.3.0/tests/engine/test_config.py +114 -0
  33. speedtest_cli_ng-0.3.0/tests/models/test_api_config.py +66 -0
  34. speedtest_cli_ng-0.3.0/tests/models/test_context.py +107 -0
  35. speedtest_cli_ng-0.3.0/tests/models/test_location.py +53 -0
  36. speedtest_cli_ng-0.3.0/tests/models/test_result.py +90 -0
  37. speedtest_cli_ng-0.3.0/tests/models/test_server.py +80 -0
  38. speedtest_cli_ng-0.3.0/tests/test_main.py +81 -0
  39. speedtest_cli_ng-0.3.0/tests/utils/test_logger.py +88 -0
  40. speedtest_cli_ng-0.3.0/uv.lock +303 -0
  41. speedtest_cli_ng-0.2.0/PKG-INFO +0 -162
  42. speedtest_cli_ng-0.2.0/extras/man/speedtest-cli.1 +0 -103
  43. speedtest_cli_ng-0.2.0/setup.cfg +0 -4
  44. speedtest_cli_ng-0.2.0/speedtest/__init__.py +0 -15
  45. speedtest_cli_ng-0.2.0/speedtest/cli/commands.py +0 -26
  46. speedtest_cli_ng-0.2.0/speedtest/cli/main.py +0 -103
  47. speedtest_cli_ng-0.2.0/speedtest/cli/output.py +0 -62
  48. speedtest_cli_ng-0.2.0/speedtest/engine/__init__.py +0 -0
  49. speedtest_cli_ng-0.2.0/speedtest/engine/config.py +0 -50
  50. speedtest_cli_ng-0.2.0/speedtest/models/__init__.py +0 -12
  51. speedtest_cli_ng-0.2.0/speedtest/utils/__init__.py +0 -0
  52. speedtest_cli_ng-0.2.0/speedtest/utils/logger.py +0 -91
  53. speedtest_cli_ng-0.2.0/speedtest_cli_ng.egg-info/SOURCES.txt +0 -32
  54. speedtest_cli_ng-0.2.0/speedtest_cli_ng.egg-info/dependency_links.txt +0 -1
  55. speedtest_cli_ng-0.2.0/speedtest_cli_ng.egg-info/entry_points.txt +0 -3
  56. speedtest_cli_ng-0.2.0/speedtest_cli_ng.egg-info/top_level.txt +0 -1
  57. {speedtest_cli_ng-0.2.0 → speedtest_cli_ng-0.3.0}/LICENSE +0 -0
  58. {speedtest_cli_ng-0.2.0 → speedtest_cli_ng-0.3.0}/NOTICE +0 -0
  59. {speedtest_cli_ng-0.2.0 → speedtest_cli_ng-0.3.0/src}/speedtest/utils/status.py +0 -0
  60. {speedtest_cli_ng-0.2.0/speedtest/cli → speedtest_cli_ng-0.3.0/tests}/__init__.py +0 -0
@@ -0,0 +1,220 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by an python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py.cover
50
+ *.lcov
51
+ .hypothesis/
52
+ .pytest_cache/
53
+ cover/
54
+
55
+ # Translations
56
+ *.mo
57
+ *.pot
58
+
59
+ # Django stuff:
60
+ *.log
61
+ local_settings.py
62
+ db.sqlite3
63
+ db.sqlite3-journal
64
+
65
+ # Flask stuff:
66
+ instance/
67
+ .webassets-cache
68
+
69
+ # Scrapy stuff:
70
+ .scrapy
71
+
72
+ # Sphinx documentation
73
+ docs/_build/
74
+
75
+ # PyBuilder
76
+ .pybuilder/
77
+ target/
78
+
79
+ # Jupyter Notebook
80
+ .ipynb_checkpoints
81
+
82
+ # IPython
83
+ profile_default/
84
+ ipython_config.py
85
+
86
+ # pyenv
87
+ # For a library or package, you might want to ignore these files since the code is
88
+ # intended to run in multiple environments; otherwise, check them in:
89
+ # .python-version
90
+
91
+ # pipenv
92
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
93
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
94
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
95
+ # install all needed dependencies.
96
+ # Pipfile.lock
97
+
98
+ # UV
99
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
100
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
101
+ # commonly ignored for libraries.
102
+ # uv.lock
103
+
104
+ # poetry
105
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
106
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
107
+ # commonly ignored for libraries.
108
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
109
+ # poetry.lock
110
+ # poetry.toml
111
+
112
+ # pdm
113
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
114
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
115
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
116
+ # pdm.lock
117
+ # pdm.toml
118
+ .pdm-python
119
+ .pdm-build/
120
+
121
+ # pixi
122
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
123
+ # pixi.lock
124
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
125
+ # in the .venv directory. It is recommended not to include this directory in version control.
126
+ .pixi/*
127
+ !.pixi/config.toml
128
+
129
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
130
+ __pypackages__/
131
+
132
+ # Celery stuff
133
+ celerybeat-schedule*
134
+ celerybeat.pid
135
+
136
+ # Redis
137
+ *.rdb
138
+ *.aof
139
+ *.pid
140
+
141
+ # RabbitMQ
142
+ mnesia/
143
+ rabbitmq/
144
+ rabbitmq-data/
145
+
146
+ # ActiveMQ
147
+ activemq-data/
148
+
149
+ # SageMath parsed files
150
+ *.sage.py
151
+
152
+ # Environments
153
+ .env
154
+ .envrc
155
+ .venv
156
+ env/
157
+ venv/
158
+ ENV/
159
+ env.bak/
160
+ venv.bak/
161
+
162
+ # Spyder project settings
163
+ .spyderproject
164
+ .spyproject
165
+
166
+ # Rope project settings
167
+ .ropeproject
168
+
169
+ # mkdocs documentation
170
+ /site
171
+
172
+ # mypy
173
+ .mypy_cache/
174
+ .dmypy.json
175
+ dmypy.json
176
+
177
+ # Pyre type checker
178
+ .pyre/
179
+
180
+ # pytype static type analyzer
181
+ .pytype/
182
+
183
+ # Cython debug symbols
184
+ cython_debug/
185
+
186
+ # PyCharm
187
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
188
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
189
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
190
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
191
+ # .idea/
192
+
193
+ # Abstra
194
+ # Abstra is an AI-powered process automation framework.
195
+ # Ignore directories containing user credentials, local state, and settings.
196
+ # Learn more at https://abstra.io/docs
197
+ .abstra/
198
+
199
+ # Visual Studio Code
200
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
201
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
202
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
203
+ # you could uncomment the following to ignore the entire vscode folder
204
+ # .vscode/
205
+ # Temporary file for partial code execution
206
+ tempCodeRunnerFile.py
207
+
208
+ # Ruff stuff:
209
+ .ruff_cache/
210
+
211
+ # PyPI configuration file
212
+ .pypirc
213
+
214
+ # Marimo
215
+ marimo/_static/
216
+ marimo/_lsp/
217
+ __marimo__/
218
+
219
+ # Streamlit
220
+ .streamlit/secrets.toml
@@ -1,28 +1,30 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: speedtest-cli-ng
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Next generation CLI for testing internet bandwidth using speedtest.net
5
- Author-email: Michał Korczak <git@omikor.in>
6
- License-Expression: Apache-2.0
7
5
  Project-URL: Homepage, https://github.com/Omikorin/speedtest-cli
8
6
  Project-URL: Source, https://github.com/Omikorin/speedtest-cli
9
7
  Project-URL: Issues, https://github.com/Omikorin/speedtest-cli/issues
10
- Keywords: speedtest,speedtest.net,bandwidth,internet,cli
8
+ Author-email: Michał Korczak <git@omikor.in>
9
+ License-Expression: Apache-2.0
10
+ License-File: LICENSE
11
+ License-File: NOTICE
12
+ Keywords: bandwidth,cli,internet,speedtest,speedtest.net
11
13
  Classifier: Development Status :: 4 - Beta
12
14
  Classifier: Environment :: Console
13
15
  Classifier: Intended Audience :: Developers
14
16
  Classifier: Intended Audience :: System Administrators
15
17
  Classifier: Operating System :: OS Independent
16
- Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Programming Language :: Python :: 3.15
17
20
  Classifier: Topic :: Internet :: WWW/HTTP
18
21
  Classifier: Topic :: System :: Networking
19
22
  Classifier: Topic :: Terminals
20
23
  Classifier: Topic :: Utilities
21
- Requires-Python: >=3.12
24
+ Requires-Python: >=3.14
25
+ Requires-Dist: httpx2<3,>=2.5.0
26
+ Requires-Dist: rich<16,>=15.0.0
22
27
  Description-Content-Type: text/markdown
23
- License-File: LICENSE
24
- License-File: NOTICE
25
- Dynamic: license-file
26
28
 
27
29
  # speedtest-cli
28
30
 
@@ -32,9 +34,11 @@ Dynamic: license-file
32
34
  [![CI](https://github.com/Omikorin/speedtest-cli/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/Omikorin/speedtest-cli/actions/workflows/ci.yml)
33
35
  [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=Omikorin_speedtest-cli&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=Omikorin_speedtest-cli)
34
36
 
37
+ ---
38
+
35
39
  Next generation CLI for testing internet bandwidth using speedtest.net
36
40
 
37
- Rebuilt for modern Python 3.12+
41
+ Rebuilt for modern Python 3.14+
38
42
 
39
43
  ## Getting started
40
44
 
@@ -110,7 +114,7 @@ Miscellaneous Options:
110
114
 
111
115
  ### Requirements
112
116
 
113
- - Python 3.12+
117
+ - Python 3.14+
114
118
  - uv 0.11+
115
119
 
116
120
  ### Setup project
@@ -131,7 +135,7 @@ uv run ruff format --check .
131
135
  uv run ruff check .
132
136
 
133
137
  # Run typecheck
134
- uv run pyright
138
+ uv run ty check
135
139
 
136
140
  # Run tests
137
141
  uv run pytest
@@ -153,7 +157,7 @@ This project is a heavily refactored and modernized fork of the original [speedt
153
157
 
154
158
  ## License
155
159
 
156
- This project is licensed undeer the [Apache License 2.0](https://github.com/Omikorin/speedtest-cli/blob/main/LICENSE).
160
+ This project is licensed under the terms of the [Apache License 2.0](https://github.com/Omikorin/speedtest-cli/blob/main/LICENSE).
157
161
 
158
162
  ---
159
163
 
@@ -6,9 +6,11 @@
6
6
  [![CI](https://github.com/Omikorin/speedtest-cli/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/Omikorin/speedtest-cli/actions/workflows/ci.yml)
7
7
  [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=Omikorin_speedtest-cli&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=Omikorin_speedtest-cli)
8
8
 
9
+ ---
10
+
9
11
  Next generation CLI for testing internet bandwidth using speedtest.net
10
12
 
11
- Rebuilt for modern Python 3.12+
13
+ Rebuilt for modern Python 3.14+
12
14
 
13
15
  ## Getting started
14
16
 
@@ -84,7 +86,7 @@ Miscellaneous Options:
84
86
 
85
87
  ### Requirements
86
88
 
87
- - Python 3.12+
89
+ - Python 3.14+
88
90
  - uv 0.11+
89
91
 
90
92
  ### Setup project
@@ -105,7 +107,7 @@ uv run ruff format --check .
105
107
  uv run ruff check .
106
108
 
107
109
  # Run typecheck
108
- uv run pyright
110
+ uv run ty check
109
111
 
110
112
  # Run tests
111
113
  uv run pytest
@@ -127,7 +129,7 @@ This project is a heavily refactored and modernized fork of the original [speedt
127
129
 
128
130
  ## License
129
131
 
130
- This project is licensed undeer the [Apache License 2.0](https://github.com/Omikorin/speedtest-cli/blob/main/LICENSE).
132
+ This project is licensed under the terms of the [Apache License 2.0](https://github.com/Omikorin/speedtest-cli/blob/main/LICENSE).
131
133
 
132
134
  ---
133
135
 
@@ -1,33 +1,44 @@
1
1
  [build-system]
2
- requires = ["setuptools>=80.0.0", "wheel"]
3
- build-backend = "setuptools.build_meta"
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "speedtest-cli-ng"
7
- version = "0.2.0"
8
- authors = [
9
- {name = "Michał Korczak", email = "git@omikor.in"}
10
- # TODO: I'm not sure whether I should put the old author because of possible spam for him unknowingly etc
11
- # {name = "Matt Martz", email = "matt@sivel.net"}
12
- ]
7
+ description = "Next generation CLI for testing internet bandwidth using speedtest.net"
8
+ keywords = ["speedtest", "speedtest.net", "bandwidth", "internet", "cli"]
13
9
  license = "Apache-2.0"
10
+ version = "0.3.0"
11
+ requires-python = ">=3.14"
12
+ authors = [{ name = "Michał Korczak", email = "git@omikor.in" }]
14
13
  license-files = ["LICENSE", "NOTICE"]
15
- description = "Next generation CLI for testing internet bandwidth using speedtest.net"
16
14
  readme = "README.md"
17
- requires-python = ">=3.12"
18
- keywords = ["speedtest", "speedtest.net", "bandwidth", "internet", "cli"]
19
15
  classifiers = [
20
16
  "Development Status :: 4 - Beta",
21
17
  "Environment :: Console",
22
18
  "Intended Audience :: Developers",
23
19
  "Intended Audience :: System Administrators",
24
20
  "Operating System :: OS Independent",
25
- "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.14",
22
+ "Programming Language :: Python :: 3.15",
26
23
  "Topic :: Internet :: WWW/HTTP",
27
24
  "Topic :: System :: Networking",
28
25
  "Topic :: Terminals",
29
26
  "Topic :: Utilities",
30
27
  ]
28
+ dependencies = [
29
+ "httpx2>=2.5.0,<3",
30
+ "rich>=15.0.0,<16",
31
+ ]
32
+
33
+ [dependency-groups]
34
+ dev = [
35
+ # Tests
36
+ "pytest>=9.0.0,<10",
37
+ "pytest-cov>=7.0.0,<8",
38
+ # Linting
39
+ "ruff>=0.15.0,<1",
40
+ "ty>=0.0.55,<1",
41
+ ]
31
42
 
32
43
  [project.urls]
33
44
  Homepage = "https://github.com/Omikorin/speedtest-cli"
@@ -37,16 +48,21 @@ Issues = "https://github.com/Omikorin/speedtest-cli/issues"
37
48
  [project.scripts]
38
49
  speedtest = "speedtest.__main__:main"
39
50
  speedtest-cli = "speedtest.__main__:main"
51
+ speedtest-cli-ng = "speedtest.__main__:main"
40
52
 
41
- # Tool-specific configurations
42
- [tool.setuptools.packages.find]
43
- include = ["speedtest", "speedtest.*"]
53
+ [tool.hatch.build.targets.sdist]
54
+ exclude = [
55
+ ".github",
56
+ ".python-version",
57
+ "renovate.json5",
58
+ ]
44
59
 
45
- [tool.setuptools.data-files]
46
- "share/man/man1" = ["extras/man/speedtest-cli.1"]
60
+ [tool.hatch.build.targets.wheel]
61
+ packages = ["src/speedtest"]
47
62
 
48
63
  [tool.uv]
49
64
  required-version = ">=0.11.0"
65
+ exclude-newer = "7 days"
50
66
 
51
67
  [[tool.uv.index]]
52
68
  name = "testpypi"
@@ -55,42 +71,37 @@ publish-url = "https://test.pypi.org/legacy/"
55
71
  explicit = true
56
72
 
57
73
  [tool.ruff]
58
- target-version = "py312"
59
- line-length = 100
74
+ line-length = 120
60
75
  src = ["speedtest", "tests"]
61
76
 
62
77
  [tool.ruff.lint]
63
78
  select = [
79
+ "ANN", # flake8 annotations
80
+ "B", # flake8 bugbear
81
+ "PYI", # flake8 pyi
64
82
  "E", # pycodestyle errors
65
83
  "W", # pycodestyle warnings
66
84
  "F", # pyflakes
67
- "I", # isort (automatically sorts imports)
68
- "UP", # pyupgrade (automatically upgrades syntax to modern Python 3.12 standards)
85
+ "I", # isort
86
+ "N", # pep8 naming
87
+ "UP", # pyupgrade
69
88
  "RUF", # ruff-specific rules
70
89
  ]
90
+ preview = true
71
91
  ignore = []
92
+ unfixable = ["B"]
72
93
 
73
94
  [tool.ruff.format]
74
95
  quote-style = "double"
75
96
  indent-style = "space"
76
97
 
77
- [tool.pyright]
78
- include = ["speedtest", "tests"]
79
- pythonVersion = "3.12"
80
- typeCheckingMode = "standard" # Can be bumped to "strict" once codebase is fully annotated
81
- useLibraryCodeForTypes = true
82
- reportMissingImports = true
98
+ [tool.ty.rules]
99
+ missing-type-argument = "error"
100
+ possibly-unresolved-reference = "warn"
83
101
 
84
102
  [tool.pytest]
85
103
  minversion = "9.0"
86
104
  # Automatically show extra test info, fail if coverage drops, and output a terminal coverage report
87
- addopts = "-v --cov=speedtest --cov-report=term-missing"
105
+ addopts = ["-v", "--cov=speedtest", "--cov-report=term-missing"]
88
106
  testpaths = ["tests"]
89
107
 
90
- [dependency-groups]
91
- dev = [
92
- "pyright>=1.1.400",
93
- "pytest>=9.0.0",
94
- "pytest-cov>=7.0.0",
95
- "ruff>=0.15.0",
96
- ]
@@ -0,0 +1,3 @@
1
+ from .__version__ import __description__, __title__, __version__
2
+
3
+ __all__ = ["__description__", "__title__", "__version__"]
@@ -4,25 +4,22 @@ The main entry point. Invoke as `speedtest-cli` or `python -m speedtest`.
4
4
 
5
5
  import sys
6
6
 
7
- from speedtest.cli.main import shell
8
- from speedtest.exceptions import SpeedtestException
9
- from speedtest.utils.logger import logger
10
- from speedtest.utils.status import ExitStatus
7
+ from speedtest.cli import shell
8
+ from speedtest.exceptions import SpeedtestError
9
+ from speedtest.utils import ExitStatus, logger
11
10
 
12
11
 
13
12
  def main() -> int:
14
13
  """Execute the CLI and return an integer exit status."""
15
14
 
16
15
  try:
17
- exit_status = shell()
18
-
19
- return int(exit_status)
16
+ return shell()
20
17
 
21
18
  except KeyboardInterrupt:
22
19
  logger.error("Stopped by user")
23
20
  return ExitStatus.ERROR_CTRL_C.value
24
21
 
25
- except SpeedtestException as e:
22
+ except SpeedtestError as e:
26
23
  code = getattr(e, "code", ExitStatus.ERROR.value)
27
24
 
28
25
  if code not in (ExitStatus.SUCCESS.value, ExitStatus.ERROR_CTRL_C.value):
@@ -31,10 +28,10 @@ def main() -> int:
31
28
 
32
29
  return int(code)
33
30
 
34
- except Exception as e:
35
- logger.exception("An unexpected error occurred.", e)
31
+ except Exception:
32
+ logger.exception("An unexpected error occurred.")
36
33
  return ExitStatus.ERROR.value
37
34
 
38
35
 
39
- if __name__ == "__main__":
36
+ if __name__ == "__main__": # pragma: no cover
40
37
  sys.exit(main())
@@ -0,0 +1,5 @@
1
+ from importlib.metadata import version
2
+
3
+ __title__ = "speedtest-cli-ng"
4
+ __description__ = "Next generation CLI for testing internet bandwidth using speedtest.net"
5
+ __version__ = version("speedtest-cli-ng")
@@ -0,0 +1,7 @@
1
+ """
2
+ Command-line interface module.
3
+ """
4
+
5
+ from .runner import shell
6
+
7
+ __all__ = ["shell"]
@@ -0,0 +1,69 @@
1
+ """
2
+ Handles terminal output and presentation logic for the CLI.
3
+ """
4
+
5
+ import dataclasses
6
+ import json
7
+ import sys
8
+ from datetime import UTC, datetime
9
+
10
+ from rich.table import Table
11
+
12
+ from speedtest.models import ApiConfig, Server, SpeedtestResult
13
+ from speedtest.utils import ExitStatus, console
14
+
15
+ __all__ = ["print_json", "print_server_list"]
16
+
17
+
18
+ def print_server_list(servers: list[Server]) -> int:
19
+ """Print nearby servers in a table."""
20
+
21
+ table = Table(title="Available Target Servers")
22
+
23
+ table.add_column("ID", justify="right", style="cyan", no_wrap=True)
24
+ table.add_column("Distance", justify="right", style="green", no_wrap=True)
25
+ table.add_column("Name", style="magenta")
26
+ table.add_column("Country", style="blue")
27
+ table.add_column("Sponsor", style="yellow")
28
+
29
+ for server in servers:
30
+ table.add_row(
31
+ str(server.id),
32
+ f"{server.distance} km",
33
+ server.name,
34
+ server.country,
35
+ server.sponsor,
36
+ )
37
+
38
+ try:
39
+ console.print(table)
40
+ except BrokenPipeError:
41
+ pass
42
+
43
+ return ExitStatus.SUCCESS.value
44
+
45
+
46
+ def print_json(results: SpeedtestResult, client_config: ApiConfig | None = None) -> None:
47
+ """Print the machine-readable JSON representation."""
48
+
49
+ json_data = {
50
+ "timestamp": datetime.now(UTC).isoformat(),
51
+ "ping": results.ping_ms,
52
+ "download": results.download_bps,
53
+ "upload": results.upload_bps,
54
+ "bytes_received": results.download_bytes,
55
+ "bytes_sent": results.upload_bytes,
56
+ "share": results.share_url,
57
+ "server": dataclasses.asdict(results.server) if results.server else None,
58
+ }
59
+
60
+ if client_config:
61
+ json_data["client"] = {
62
+ "ip": client_config.ip_address,
63
+ "isp": client_config.isp_name,
64
+ "isp_id": client_config.isp_id,
65
+ "location": dataclasses.asdict(client_config.location),
66
+ }
67
+
68
+ indent = 2 if sys.stdout.isatty() else None
69
+ print(json.dumps(json_data, indent=indent))
@@ -75,20 +75,14 @@ def parse_args() -> argparse.Namespace:
75
75
  action="store_const",
76
76
  const=("B", 8),
77
77
  default=("b", 1),
78
- help=(
79
- "Display values in bytes instead of bits. "
80
- "Does not affect image generation or JSON output."
81
- ),
78
+ help=("Display values in bytes instead of bits. Does not affect image generation or JSON output."),
82
79
  )
83
80
 
84
81
  format_group = output_group.add_mutually_exclusive_group()
85
82
  format_group.add_argument(
86
83
  "--json",
87
84
  action="store_true",
88
- help=(
89
- "Suppress verbose output, only show basic information "
90
- "in JSON format. Speeds listed in bit/s."
91
- ),
85
+ help=("Suppress verbose output, only show basic information in JSON format. Speeds listed in bit/s."),
92
86
  )
93
87
 
94
88
  misc_group = parser.add_argument_group("Miscellaneous Options")