Compare commits

...

201 Commits

Author SHA1 Message Date
Matt Martz 22210ca352
Python 3.10 support 2021-07-07 14:50:15 -05:00
Matt Martz 42e96b13dd
Bump to 2.1.3 2021-04-08 08:45:29 -05:00
Matt Martz cadc68b5ae
Handle case where ignoreids is empty or contains empty ids 2021-04-08 08:44:32 -05:00
Matt Martz db46af8bcd
Ensure we catch HTTP errors on upload/download. Fixes #752 2021-01-19 17:04:47 -06:00
Matt Martz c58ad3367b Bump release 2019-08-22 09:48:18 -05:00
Matt Martz 266e53c256 Fix proxy support. Fixes #610 2019-08-22 09:45:01 -05:00
Matt Martz 7ebb9965dd Ensure threads don't start before a position in the queue is available.
Fixes #628
2019-08-22 09:43:19 -05:00
Matt Martz 2658bd50b4 Bump devel version 2019-03-13 15:57:05 -05:00
Matt Martz 81bba6070c
Add support for py38 without deprecation warnings (#585)
* Add support for py38 without deprecation warnings

* Address Py2.5 issue

* Add py3.7 and 3.8

* xenial

* pypy trusty
2019-03-13 15:56:00 -05:00
Matt Martz 681cdf20a5 Re-enable python 2.4 and 2.5 testing 2019-03-12 11:01:31 -05:00
Matt Martz cdf6002865 Bump to 2.1.1 2019-03-12 10:58:17 -05:00
Matt Martz 9af203652b Python2.4/2.5 SSL support 2019-03-12 10:55:23 -05:00
Matt Martz 2d5a9ef364 Switch copyright from range, to date started 2019-03-11 10:03:12 -05:00
Matt Martz 3109fcf407 Update usage 2019-03-11 09:57:19 -05:00
Matt Martz 69ddff1a11 Disable py2.4/2.5 tests for now 2019-03-05 11:44:19 -06:00
Matt Martz fb0569946d Bump to 2.1.0 for upcoming release 2019-03-05 10:55:57 -06:00
Matt Martz f356c7b02d ensure ERROR doesn't print an empty string 2019-02-19 17:17:25 -06:00
Matt Martz 6cf43b2ff7 linting fix 2019-02-19 16:58:50 -06:00
Matt Martz 217ce8eff1 ssl.wrap_socket doesn't support server_hostname. See #572 2019-02-19 16:56:26 -06:00
Matt Martz b43334f1ec Switch from platform.system to platform.platform. Fixes #574 2019-02-19 16:38:15 -06:00
Matt Martz b0b826c870 Add the python version to the version output 2019-01-23 11:34:23 -06:00
Matt Martz 9ac1091eae Add debug support to show if a URL request resulted in a redirect 2019-01-23 11:34:00 -06:00
Matt Martz ca2250f700 Add functionality for single threaded testing. Fixes #571 2019-01-23 11:33:30 -06:00
Matt Martz ddb8db0c94 Fix install instructions with git clone. Fixes #566 2019-01-02 09:18:21 -06:00
Matt Martz 72bf53affa Fix linting error 2018-12-03 10:44:49 -06:00
liuxu a8a3265001 Fix python3 upload problem
In python3, if Content-length is not set,urllib.request.AbstractHTTPHandler::do_request_() will use "Transfer-encoding:chunked", which will cause HTTPUploader not to exit until timeout
2018-12-03 10:26:09 -06:00
Alex Ward b2654de410 Automatically resolve .best property (#514)
* automatically call get_best_server

* add back SpeedtestBestServerFailer exception
2018-12-03 10:20:28 -06:00
Matt Martz 72ed585c6f Bump to v2.0.2 2018-05-24 11:06:29 -05:00
Matt Martz 41e599f9c3 Ensure we are utilizing the context created by HTTPSConnection, or falling back to ssl. Fixes #517 2018-05-24 09:37:39 -05:00
Matt Martz c7530bb143 Bump to 2.0.1 for release 2018-05-23 15:26:20 -05:00
Matt Martz 4ceae77401 Handle virtualenv and tox versions for py2.6/3.3 2018-05-22 16:38:10 -05:00
Matt Martz 9e185e8f88 Properly handle older HTTPSConnection. Fixes #513 2018-05-22 15:28:00 -05:00
Matt Martz 9c2977acfc Gracefully handle XML parsing errors. Fixes #490 #491 2018-03-09 09:46:10 -06:00
Matt Martz f8aa20ecdf Move results.share() to ensure csv and json have access to it. Fixes #483 2018-02-20 14:59:08 -06:00
Matt Martz 8ff923b0fb Bump to 2.0.1a 2018-02-13 16:22:23 -06:00
Matt Martz 35c3ee20ed Exit with nicer error if lat/lon is not valid 2018-02-13 16:21:57 -06:00
Matt Martz 0a7823db7a Tested through 3.7 2018-02-05 16:33:07 -06:00
Matt Martz 27a8301192 Replace downloads badge with travis 2018-02-05 16:33:01 -06:00
Matt Martz ee2e647b9b Remove deprecated speedtest_cli.py 2018-02-05 16:25:59 -06:00
Matt Martz 831c079113 Bump for release 2018-02-05 16:17:03 -06:00
Matt Martz 4f4c1dd8d1 Update man page 2018-02-05 16:16:51 -06:00
Matt Martz 2c847a1849 Add some guard code for places where sys.stdout and stderr are replaced with some other incompatible object 2018-01-26 15:52:06 -06:00
Matt Martz e1bab1ab55 Only add terminal colors with DEBUG if stdout is a tty 2018-01-08 16:57:26 -06:00
Matt Martz 48a3d33ae4 Bump to beta 2018-01-03 09:16:51 -06:00
Matt Martz c16ffd4ae7 Catch OSError and EOFError while reading from gzip stream 2018-01-02 18:32:03 -06:00
Matt Martz 9848481d06 Use the printer everywhere, leaving print_ to only be used within printer 2018-01-02 18:22:16 -06:00
Matt Martz 4737a69f10 Add a few additional tests, specifically around --source 2018-01-02 17:16:52 -06:00
Matt Martz 6381ba3742 Eliminate SHUTDOWN_EVENT global 2018-01-02 16:07:46 -06:00
Matt Martz fa2e15ee08 Skipping test should be quiet 2017-12-11 09:36:03 -06:00
Matt Martz eab354603f Don't display ERROR: 2 when argparse exits with non-0 2017-12-11 09:35:41 -06:00
Matt Martz e80ccc4647 update README for usage changes 2017-11-23 10:30:31 -06:00
Matt Martz 5fbe593fc8 Get travis working properly again 2017-11-23 10:18:16 -06:00
Matt Martz f70cc86222 No bare except 2017-11-23 10:16:23 -06:00
Matt Martz 5c061da8e0 Move the majority of the csv_header functionality to SpeedtestResults 2017-11-23 10:16:23 -06:00
Matt Martz 4457fe9fb8 Support csv-delimiter for csv-header 2017-11-23 10:16:17 -06:00
Matt Martz b27f69d1ad Output a different message when only 1 server is provided 2017-11-23 10:15:48 -06:00
Matt Martz 5a9f82a20a Add additional information to machine parsable outputs 2017-11-23 10:15:46 -06:00
Matt Martz 3cb44f5630 Attempt to catch MemoryError if possible 2017-11-23 10:15:09 -06:00
Matt Martz 16054cc3bc Print errors to stderr 2017-11-23 10:15:09 -06:00
Matt Martz d9642b2047 Always flush in py2 print_ 2017-11-23 10:15:09 -06:00
Matt Martz f3a607feb2 Allow timeout to be a float 2017-11-23 10:15:09 -06:00
Matt Martz 6bfa5922c3 Add option to exclude servers, and allow --server and --exclude to be specified multiple times 2017-11-23 10:15:09 -06:00
Matt Martz ca72d40033 Create a getter for Speedtest.best to raise an exception is get_best_server has not found a best server 2017-11-23 10:15:09 -06:00
Matt Martz 3ebb9734a2 Indicate speedtest-cli supports python 3.6, and ensure py3.2 has an appropriate setuptools version 2017-11-23 10:15:09 -06:00
Matt Martz 8854d82049 More and better debugging 2017-11-23 10:15:09 -06:00
Matt Martz f2a97baf1e Revert "Test failing --source"
This reverts commit be7d7f6a1c2448cc60342142a405945817817115.
2017-11-23 10:15:09 -06:00
Matt Martz 6531677346 Test failing --source 2017-11-23 10:15:09 -06:00
Matt Martz 6556be190a Switch to using matrix for travis 2017-11-23 10:15:09 -06:00
Matt Martz 2fe34ecf4e Remove debug print 2017-11-23 10:15:09 -06:00
Matt Martz 0e585cbf64 Docstrings and version bump 2017-11-23 10:15:07 -06:00
Matt Martz 2fe369fdf8 Remove SCHEME global 2017-11-23 10:14:35 -06:00
Matt Martz b33c7533df flake8 fixes 2017-11-23 10:14:35 -06:00
Matt Martz fe864f6dce Use vendored create_connection when socket doesn't have it, or socket.create_connection is too old 2017-11-23 10:14:35 -06:00
Matt Martz 10b3b09f02 Don't override socket.socket for binding, eliminiate globals SOURCE and USER_AGENT 2017-11-23 10:14:35 -06:00
Matt Martz 20e5d12a5c Support csv-delimiter for csv-header 2017-10-16 09:28:35 -05:00
Matt Martz 6603954e45 Bump to v1.0.6 2017-04-25 11:49:26 -05:00
Matt Martz e982830350 Revert "Be consistent, use the shorter var"
This reverts commit 3c1c9d3179.
2017-04-25 10:52:40 -05:00
Matt Martz 2c89c53a79 Switch upload pre-allocation to true, but allow disabling 2017-04-25 10:34:20 -05:00
Matt Martz 401c469991 Do better calculations of the number of uploads per size to perform 2017-04-25 10:08:14 -05:00
Matt Martz 3c1c9d3179 Be consistent, use the shorter var 2017-04-25 10:07:34 -05:00
Matt Martz e2f815618b Be consistent, explicitly instantiate all exceptions 2017-04-25 10:07:05 -05:00
Matt Martz 955a756c96 Ensure to specify Z for UTC in iso8601 date. Fixes #388 2017-04-24 13:32:35 -05:00
Matt Martz ceef55488c Bump to v1.0.5 2017-04-21 14:38:57 -05:00
Matt Martz 20eeadcb0c Reorder StringIO imports again, add to_utf8 function to ensure we encode csv data properly. Fixes #385 2017-04-19 10:33:01 -05:00
Matt Martz 4aebe01c3e Bump version to 1.0.4 2017-04-12 12:19:39 -05:00
Matt Martz 1871b26b9a Flake8 fix 2017-04-12 12:18:54 -05:00
Matt Martz 824c584658 Invert logic for py3 print detection, to avoid confusion created by the future package 2017-04-12 12:18:54 -05:00
Matt Martz 9806e401e0 Prefer io over cStringIO and StringIO 2017-04-12 12:18:54 -05:00
Matt Martz 1642d0669f Handle utf-8 output encoding in py3 also. Fixes #382 2017-04-12 12:18:46 -05:00
Matt Martz 2e79fbf1dc Bump to 1.0.3 2017-03-30 19:29:40 -05:00
Matt Martz f20e8808a8 Add link to python api docs 2017-03-24 15:31:02 -05:00
Matt Martz 3feb38d9d4 Allow skipping download or upload tests. Fixes #377 2017-03-24 15:30:52 -05:00
Matt Martz d712f947d9 Always print using utf-8. Fixes #362 2017-03-17 16:21:01 -05:00
Matt Martz 55b3cf14a3 Allow --share to work with --simple and --json. Fixes #375 2017-03-17 16:06:48 -05:00
Matt Martz 33e498beb3 Add bytes_sent and bytes_received to the json output. Addresses #358 2017-01-18 15:03:32 -06:00
Matt Martz 068d71597b Bump to 1.0.2 2017-01-18 14:10:02 -06:00
Matt Martz 1863c35f6b alpha version bump 2017-01-09 19:49:19 -06:00
Matt Martz 823d7dc2b7 Better handling of retrieving config and server list 2017-01-09 19:48:35 -06:00
Matt Martz 411f1609e8 Don't sys.exit(1), instead either raise the exception, or a new SpeedtestCLIError. Fixes #352 2017-01-06 13:20:46 -06:00
Matt Martz 7b38e264bc Bump to 1.0.1 2016-12-22 11:35:06 -06:00
Matt Martz 2acba6ecd7 Revert "Pre create upload data, to not impact timing"
This reverts commit 6685d91729.
2016-12-12 14:08:20 -06:00
Matt Martz 53ef57bd5e Ensure we install speedtest_cli.py 2016-11-17 11:20:19 -06:00
Matt Martz c512684ffa Don't use mutable defaults as args for methods 2016-11-11 10:00:24 -06:00
Matt Martz 6685d91729 Pre create upload data, to not impact timing 2016-11-04 16:05:57 -05:00
Matt Martz 91270dbc67 Update README with changes to help 2016-11-04 10:04:17 -05:00
Matt Martz b75ecc291c Test on py35 also 2016-11-02 20:56:40 -05:00
Matt Martz 1d6717e714 Fix debug in py<=2.5 2016-11-02 19:47:55 -05:00
Matt Martz d41cfc0cb1 Use BytesIO on py3 in HTTPUploaderData 2016-11-02 19:47:31 -05:00
Matt Martz 59880107a7 Support gzip encoding if available 2016-11-02 19:47:07 -05:00
Matt Martz 4280c448cf Debug print XML from servers 2016-09-27 13:45:32 -05:00
Matt Martz 9f44a72fdb Add debug output to latency testing 2016-09-27 11:23:25 -05:00
Matt Martz b075152e3e Catch scenario where no servers could be connected to for latency tests 2016-09-26 11:07:02 -05:00
Matt Martz 2be4d0a5e7 Make sure to enumerate requests to support cache busting properly 2016-09-19 16:40:32 -05:00
Matt Martz 9299e0860c Add additional note that --bytes doesn't affect json or csv 2016-09-14 10:34:38 -05:00
Matt Martz 292e250990 Update API post data to match current UI post data 2016-09-14 10:34:20 -05:00
Matt Martz fd8b8cfa92 Fix SpeedtestResults attributes for upload and download 2016-09-14 10:33:32 -05:00
Matt Martz 9e3a5b3a59 update usage information 2016-09-13 09:02:29 -05:00
Matt Martz 53b760dfba Adjust copyright in setup.py 2016-09-13 08:54:02 -05:00
Matt Martz d0c927e8ae Don't concern SpeedtestResults with units 2016-09-13 08:50:15 -05:00
Matt Martz 6fffcd5b63 Remove unneeded commented out code 2016-09-12 09:51:45 -05:00
Matt Martz f88c41f97f Don't use the crazy units data descriptor, store as bits and convert as needed 2016-09-12 09:51:33 -05:00
Matt Martz 64b03777da Divide by float to avoid incorrect 0 values 2016-08-30 13:43:46 -05:00
Matt Martz 01abb3ae71 Add ability to print out the CSV headers 2016-08-29 09:42:14 -05:00
Matt Martz 884c7fed87 Also list py3.5 2016-05-27 11:36:58 -05:00
Matt Martz 38870b69ea Update readme for deprecation 2016-05-27 11:32:36 -05:00
Matt Martz 4bd4b7dfec Bump copyright date range 2016-05-27 11:31:53 -05:00
Matt Martz 5e0bd05c81 Deprecate speedtest_cli.py 2016-05-27 11:31:43 -05:00
Matt Martz b3f9a48cbb Handle py3 compatibility for speedtest mini servers 2016-05-26 15:20:58 -05:00
Matt Martz 478f9affdd Better detection of file extension for mini server 2016-05-26 15:20:15 -05:00
Matt Martz 9ccce5d861 Remove unneeded constants 2016-05-16 16:57:59 -05:00
Matt Martz 2a4990c96c request may get overwritten, use self.request 2016-05-16 16:34:13 -05:00
Matt Martz 07c38d7194 Pre build requests, lazy build upload data 2016-05-16 16:10:51 -05:00
Matt Martz c5f75f783e More accurate timing by setting stop stamp earlier 2016-05-16 11:35:31 -05:00
Matt Martz 050da542b3 Remove unneeded var, clear servers on get_servers and break if we were successful collecting servers 2016-05-16 11:34:38 -05:00
Matt Martz e14f7ed108 Try an old virtualenv version with py32 2016-03-10 10:55:04 -06:00
Matt Martz a7e9bc695e Install py32 compatible pip 2016-03-10 10:44:21 -06:00
Matt Martz 77db2ea8f4 Move xml.parsers.expat import for flake8 2016-03-10 10:15:00 -06:00
Matt Martz e4218c7612 ssl.CertificateError not always available 2016-03-10 09:54:04 -06:00
Matt Martz 69bae532c5 Ensure we don't accidentally set debug in python versions using optparse 2016-03-07 17:07:48 -06:00
Matt Martz 4f7f367391 Handle SSL errors, and specify scheme for CDN URLs 2016-03-07 17:06:22 -06:00
Matt Martz 08e87f4c54 make sure ignore_servers is a list 2016-03-07 17:05:28 -06:00
Matt Martz aa52e550bf Threading and callback improvements 2016-03-07 17:05:05 -06:00
Matt Martz 537c5aeda0 Remove unneeded imports 2016-03-07 17:03:39 -06:00
Matt Martz 95fe038752 Handle HTTPSConnection imports differently, don't bail unless we need it 2016-03-07 17:02:11 -06:00
Matt Martz 1c0a029ca6 Handle broken pipe exceptions more effectively 2016-03-07 17:00:46 -06:00
Matt Martz 9913b9915f Update print_ from six, so that encoding can be handled automatically 2016-03-07 16:59:41 -06:00
Matt Martz a4cb217522 download and upload measurements should be stored in SpeedtestResults in the units specified, defaulting to bits 2016-01-05 16:06:18 -06:00
Matt Martz d09ec27cb2 Address performance degredation by incerasing read default from 1500 back to 10240 2016-01-05 16:05:12 -06:00
Matt Martz 446e6eb27e Update tox.ini to reflect filename change 2015-12-28 14:34:07 -06:00
Matt Martz 81182c1c94 Rename from speedtest_cli.py to speedtest.py, but maintain backwards compat with a symlink 2015-12-28 14:32:47 -06:00
Matt Martz 51014d5a70 flake8 fixes 2015-12-23 12:44:14 -06:00
Matt Martz 65145d9aae Also debug the user-agent 2015-12-23 12:29:40 -06:00
Matt Martz 308c530f07 Add hidden debug option, which prints debug information 2015-12-23 12:25:50 -06:00
Matt Martz abe85d85ff Implement cache busting 2015-12-23 12:23:42 -06:00
Matt Martz d1b1185bfc Change how we build out info from config data 2015-12-23 12:22:36 -06:00
Matt Martz 713860a4b4 Build user agent, if not built by the time build_request happens 2015-12-22 15:53:53 -06:00
Matt Martz ff606d0ec1 Update README 2015-12-22 15:53:26 -06:00
Matt Martz 3f22a9d815 Move representation methods, and add __repr__ 2015-12-22 15:20:30 -06:00
Matt Martz cb6dee8a77 1st pass on 2nd attempt at modularizing the code base 2015-12-22 15:02:07 -06:00
Matt Martz 7b09d8759f Bumping to 0.3.4 2015-09-21 11:37:50 -05:00
Matt Martz 25d845362c 2015 2015-09-21 11:37:32 -05:00
Matt Martz 4b9662e0b3 Bump to 0.3.3 2015-09-21 11:26:54 -05:00
Matt Martz 93951f1154 Migrate from pypip.in to shields.io. Fixes #164 2015-08-19 11:51:24 -05:00
Matt Martz 0e6b85d4d5 printf requires a tuple 2015-08-19 11:36:44 -05:00
Matt Martz 6ab5f27300 Add server list URLs without -static also. Fixes #167 2015-08-19 11:35:13 -05:00
Matt Martz 2ee26bbf54 Update README with new help info 2015-08-19 11:34:36 -05:00
Matt Martz 514b310484 Fix missing space 2015-05-21 14:31:05 -05:00
Matt Martz 918e70e66d Remove py31 2015-05-20 18:00:19 -05:00
Matt Martz bae642ccde Meant to add an additional line for compileall 2015-05-20 14:52:35 -05:00
Matt Martz 1df3e76b19 Switch to travis addons for installing deadsnakes 2015-05-20 14:49:45 -05:00
Matt Martz 1e44e9e2f1 Always encode server info 2015-05-17 19:50:05 -05:00
Matt Martz 51d0d88b96 Bump to 0.3.3b 2015-05-15 10:03:10 -05:00
Matt Martz 47c17d4a49 Just use the global instead of calling the function again 2015-05-13 11:35:01 -05:00
Matt Martz d1be67be48 Attempt a Mozilla/5.0 compatible user-agent string 2015-04-20 09:45:02 -05:00
Matt Martz 075cfda9cf Default to http, add --secure to specify use of https 2015-04-15 16:51:08 -05:00
Matt Martz 3c04dfefd3 Switch back to http 2015-04-15 08:48:22 -05:00
Matt Martz ffd2c7f963 Add some debugging for failed http requests using catch_request 2015-03-25 09:57:00 -05:00
Matt Martz aef4a78831 missing httplib/http.client classes 2015-03-25 09:45:01 -05:00
Matt Martz 72da41e4fc fix ups for pep8 1.6 changes 2015-02-26 11:06:42 -06:00
Matt Martz cb77da3d37 Support building wheels. Fixes #126 2015-02-26 10:51:32 -06:00
Matt Martz 790720b33a Bump to 0.3.2 2015-02-26 10:35:55 -06:00
Matt Martz 3a31df31c1 README.md updates 2015-02-26 10:35:36 -06:00
Matt Martz 7383ad97af Use HTTPS URLs where possible 2015-02-26 10:25:33 -06:00
Matt Martz 3cc06168f5 Don't continu elooping if the request is successful 2015-02-26 10:25:11 -06:00
Matt Martz 3ee45cace8 Better handling of HTTP exceptions, and loop through server list URLs. See #86 2015-01-16 16:06:27 -06:00
Matt Martz b0e1e58a0b s/bytes/byte/ and s/bits/bit/. Fixes #89 2014-09-02 15:06:14 -05:00
Matt Martz 60c3ec2a5e Send custom User-Agent with all http requests. See #86 2014-08-20 13:12:30 -05:00
Matt Martz 65c85a9b15 Add timeout argument 2014-08-20 13:11:30 -05:00
Matt Martz 795bc51da4 Bump version to 0.3.1 2014-08-05 12:56:05 -05:00
Matt Martz 6c8dd05872 Make sure to also catch socket.error. Fixes #87 2014-07-29 17:14:41 -05:00
Matt Martz 759ef15636 Seems some people get parser errors from c.speedtest.net, use www instead. Fixes #86 2014-07-25 08:56:23 -05:00
Matt Martz f907418e6e Bumping version to 0.3.0 2014-06-27 19:37:02 -05:00
Matt Martz fe93e9ed75 Reorder some imports 2014-06-27 19:30:22 -05:00
Matt Martz cea45762ca Some people may be relying on matching Ping, don't change this yet 2014-06-27 19:27:21 -05:00
Matt Martz 328b851a07 Merge pull request #79 from McBochi/devel
Use timeit instead of time to prevent inconsistencies in the time module with various operating systems.
2014-06-25 12:18:48 -05:00
McBochi ec21971a10 Fixes sivel/speedtest-cli#76 2014-06-25 18:26:14 +02:00
Matt Martz 3558b22de1 If we got bogus XML that can't be parsed, provide a better error message instead of a trace. Fixes #77 2014-06-25 11:06:42 -05:00
Matt Martz c0cd0d1666 Prevent a redirect and use the final URL 2014-06-25 11:06:05 -05:00
Matt Martz 3655a31ac1 Add section about inconsistency in README.rst 2014-05-27 09:31:04 -05:00
Matt Martz c1b9a0db0a Switch to httplib/http.client to allow for better timing of latency, results may be slightly higher than previous, but more reliable 2014-05-27 09:30:39 -05:00
Matt Martz b14e104ad1 Some additional coding guidelines 2014-05-20 16:34:17 -05:00
10 changed files with 2276 additions and 738 deletions

View File

@ -1,35 +1,61 @@
language: python
sudo: required
dist: xenial
python:
- 2.7
addons:
apt:
sources:
- deadsnakes
packages:
- python2.4
- python2.5
- python2.6
- python3.2
- python3.3
env:
- TOXENV=py24
- TOXENV=py25
- TOXENV=py26
- TOXENV=py27
- TOXENV=py31
- TOXENV=py32
- TOXENV=py33
- TOXENV=py34
- TOXENV=pypy
- TOXENV=flake8
matrix:
include:
- python: 2.7
env: TOXENV=flake8
- python: 2.7
env: TOXENV=py24
- python: 2.7
env: TOXENV=py25
- python: 2.7
env: TOXENV=py26
- python: 2.7
env: TOXENV=py27
- python: 2.7
env: TOXENV=py32
- python: 2.7
env: TOXENV=py33
- python: 3.4
env: TOXENV=py34
- python: 3.5
env: TOXENV=py35
- python: 3.6
env: TOXENV=py36
- python: 3.7
env: TOXENV=py37
- python: 3.8-dev
env: TOXENV=py38
- python: pypy
env: TOXENV=pypy
dist: trusty
before_install:
- if [[ $(echo "$TOXENV" | egrep -c "(py2[45]|py3[14])") != 0 ]]; then sudo add-apt-repository -y ppa:fkrull/deadsnakes; fi;
- if [[ $(echo "$TOXENV" | egrep -c "(py2[45]|py3[14])") != 0 ]]; then sudo apt-get update -qq; fi;
- if [[ "$TOXENV" == "py24" ]]; then sudo apt-get install -y python2.4; fi;
- if [[ "$TOXENV" == "py25" ]]; then sudo apt-get install -y python2.5; fi;
- if [[ "$TOXENV" == "py31" ]]; then sudo apt-get install -y python3.1; fi;
- if [[ "$TOXENV" == "py34" ]]; then sudo apt-get install -y python3.4; fi;
- if [[ "$TOXENV" == "pypy" ]]; then sudo apt-get install -y pypy; fi;
- if [[ $(echo "$TOXENV" | egrep -c "py35") != 0 ]]; then pyenv global system 3.5; fi;
install:
- if [[ $(echo "$TOXENV" | egrep -c "(py2[45]|py31)") != 0 ]]; then pip install virtualenv==1.7.2 tox==1.3; fi;
- if [[ $(echo "$TOXENV" | egrep -c "(py2[45]|py31)") == 0 ]]; then pip install tox; fi;
- if [[ $(echo "$TOXENV" | egrep -c "py32") != 0 ]]; then pip install setuptools==17.1.1; fi;
- if [[ $(echo "$TOXENV" | egrep -c "(py2[45]|py3[12])") != 0 ]]; then pip install virtualenv==1.7.2 tox==1.3; fi;
- if [[ $(echo "$TOXENV" | egrep -c "(py26|py33)") != 0 ]]; then pip install virtualenv==15.2.0 tox==2.9.1; fi;
- if [[ $(echo "$TOXENV" | egrep -c "(py2[456]|py3[123])") == 0 ]]; then pip install tox; fi;
script:
- tox
- tox
notifications:
email:

View File

@ -18,6 +18,14 @@
In general, I follow strict pep8 and pyflakes. All code must pass these tests. Since we support python 2.4-3.4 and pypy, pyflakes reports unknown names in python 3. pyflakes is run in python 2.7 only in my tests.
## Some other points
1. Do not use `\` for line continuations, long strings should be wrapped in `()`. Imports should start a brand new line in the form of `from foo import...`
1. String quoting should be done with single quotes `'`, except for situations where you would otherwise have to escape an internal single quote
1. Docstrings should use three double quotes `"""`
1. All functions, classes and modules should have docstrings following both the PEP257 and PEP8 standards
1. Inline comments should only be used on code where it is not immediately obvious what the code achieves
# Supported Python Versions
All code needs to support Python 2.4-3.4 and pypy.

View File

@ -4,20 +4,24 @@ speedtest-cli
Command line interface for testing internet bandwidth using
speedtest.net
.. image:: https://pypip.in/v/speedtest-cli/badge.png
.. image:: https://img.shields.io/pypi/v/speedtest-cli.svg
:target: https://pypi.python.org/pypi/speedtest-cli/
:alt: Latest Version
.. image:: https://pypip.in/d/speedtest-cli/badge.png
.. image:: https://img.shields.io/travis/sivel/speedtest-cli.svg
:target: https://pypi.python.org/pypi/speedtest-cli/
:alt: Downloads
.. image:: https://pypip.in/license/speedtest-cli/badge.png
:alt: Travis
.. image:: https://img.shields.io/pypi/l/speedtest-cli.svg
:target: https://pypi.python.org/pypi/speedtest-cli/
:alt: License
Versions
--------
speedtest-cli works with Python 2.4-3.4
speedtest-cli works with Python 2.4-3.7
.. image:: https://img.shields.io/pypi/pyversions/speedtest-cli.svg
:target: https://pypi.python.org/pypi/speedtest-cli/
:alt: Versions
Installation
------------
@ -47,21 +51,22 @@ or
::
git clone https://github.com/sivel/speedtest-cli.git
python speedtest-cli/setup.py install
cd speedtest-cli
python setup.py install
Just download (Like the way it used to be)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
::
wget -O speedtest-cli https://raw.github.com/sivel/speedtest-cli/master/speedtest_cli.py
wget -O speedtest-cli https://raw.githubusercontent.com/sivel/speedtest-cli/master/speedtest.py
chmod +x speedtest-cli
or
::
curl -o speedtest-cli https://raw.github.com/sivel/speedtest-cli/master/speedtest_cli.py
curl -Lo speedtest-cli https://raw.githubusercontent.com/sivel/speedtest-cli/master/speedtest.py
chmod +x speedtest-cli
Usage
@ -70,24 +75,80 @@ Usage
::
$ speedtest-cli -h
usage: speedtest-cli [-h] [--bytes] [--share] [--simple] [--list]
[--server SERVER] [--mini MINI] [--source SOURCE]
[--version]
usage: speedtest-cli [-h] [--no-download] [--no-upload] [--single] [--bytes]
[--share] [--simple] [--csv]
[--csv-delimiter CSV_DELIMITER] [--csv-header] [--json]
[--list] [--server SERVER] [--exclude EXCLUDE]
[--mini MINI] [--source SOURCE] [--timeout TIMEOUT]
[--secure] [--no-pre-allocate] [--version]
Command line interface for testing internet bandwidth using speedtest.net.
--------------------------------------------------------------------------
https://github.com/sivel/speedtest-cli
optional arguments:
-h, --help show this help message and exit
--bytes Display values in bytes instead of bits. Does not affect
the image generated by --share
--share Generate and provide a URL to the speedtest.net share
results image
--simple Suppress verbose output, only show basic information
--list Display a list of speedtest.net servers sorted by distance
--server SERVER Specify a server ID to test against
--mini MINI URL of the Speedtest Mini server
--source SOURCE Source IP address to bind to
--version Show the version number and exit
optional arguments:
-h, --help show this help message and exit
--no-download Do not perform download test
--no-upload Do not perform upload test
--single Only use a single connection instead of multiple. This
simulates a typical file transfer.
--bytes Display values in bytes instead of bits. Does not
affect the image generated by --share, nor output from
--json or --csv
--share Generate and provide a URL to the speedtest.net share
results image, not displayed with --csv
--simple Suppress verbose output, only show basic information
--csv Suppress verbose output, only show basic information
in CSV format. Speeds listed in bit/s and not affected
by --bytes
--csv-delimiter CSV_DELIMITER
Single character delimiter to use in CSV output.
Default ","
--csv-header Print CSV headers
--json Suppress verbose output, only show basic information
in JSON format. Speeds listed in bit/s and not
affected by --bytes
--list Display a list of speedtest.net servers sorted by
distance
--server SERVER Specify a server ID to test against. Can be supplied
multiple times
--exclude EXCLUDE Exclude a server from selection. Can be supplied
multiple times
--mini MINI URL of the Speedtest Mini server
--source SOURCE Source IP address to bind to
--timeout TIMEOUT HTTP timeout in seconds. Default 10
--secure Use HTTPS instead of HTTP when communicating with
speedtest.net operated servers
--no-pre-allocate Do not pre allocate upload data. Pre allocation is
enabled by default to improve upload performance. To
support systems with insufficient memory, use this
option to avoid a MemoryError
--version Show the version number and exit
Python API
----------
See the `wiki <https://github.com/sivel/speedtest-cli/wiki>`_.
Inconsistency
-------------
It is not a goal of this application to be a reliable latency reporting tool.
Latency reported by this tool should not be relied on as a value indicative of ICMP
style latency. It is a relative value used for determining the lowest latency server
for performing the actual speed test against.
There is the potential for this tool to report results inconsistent with Speedtest.net.
There are several concepts to be aware of that factor into the potential inconsistency:
1. Speedtest.net has migrated to using pure socket tests instead of HTTP based tests
2. This application is written in Python
3. Different versions of Python will execute certain parts of the code faster than others
4. CPU and Memory capacity and speed will play a large part in inconsistency between
Speedtest.net and even other machines on the same network
Issues relating to inconsistencies will be closed as wontfix and without
additional reason or context.

2
setup.cfg Normal file
View File

@ -0,0 +1,2 @@
[wheel]
universal=1

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright 2012-2014 Matt Martz
# Copyright 2012 Matt Martz
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -57,7 +57,7 @@ except:
setup(
name='speedtest-cli',
version=find_version('speedtest_cli.py'),
version=find_version('speedtest.py'),
description=('Command line interface for testing internet bandwidth using '
'speedtest.net'),
long_description=long_description,
@ -66,11 +66,11 @@ setup(
author_email='matt@sivel.net',
url='https://github.com/sivel/speedtest-cli',
license='Apache License, Version 2.0',
py_modules=['speedtest_cli'],
py_modules=['speedtest'],
entry_points={
'console_scripts': [
'speedtest=speedtest_cli:main',
'speedtest-cli=speedtest_cli:main'
'speedtest=speedtest:main',
'speedtest-cli=speedtest:main'
]
},
classifiers=[
@ -89,5 +89,11 @@ setup(
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
]
)

View File

@ -1,9 +1,9 @@
.TH "speedtest-cli" 1 "2014-04-23" "speedtest-cli"
.TH "speedtest-cli" 1 "2018-01-05" "speedtest-cli"
.SH NAME
speedtest\-cli \- Test your bandwidth througput using speedtest.net
speedtest\-cli \- Command line interface for testing internet bandwidth using speedtest.net
.SH SYNOPSIS
.B speedtest\-cli
[OPTION]...
[OPTION...]
.SH DESCRIPTION
Speedtest.net is a web service for testing your broadband connection by downloading a file
from a nearby speedtest.net server on the web. This tool allows you to access the service
@ -23,14 +23,29 @@ Displays usage for the tool.
.B Options
\fB\-\-no\-download\fR
.RS
Do not perform download test
.RE
\fB\-\-no\-upload\fR
.RS
Do not perform upload test
.RE
\fB\-\-single\fR
.RS
Only use a single connection instead of multiple. This simulates a typical file transfer.
.RE
\fB\-\-bytes\fR
.RS
Display values in bytes instead of bits. Does not affect the image generated by \-\-share
Display values in bytes instead of bits. Does not affect the image generated by \-\-share, nor output from \-\-json or \-\-csv
.RE
\fB\-\-share\fR
.RS
Generate and provide a URL to the speedtest.net share results image
Generate and provide a URL to the speedtest.net share results image, not displayed with \-\-csv
.RE
\fB\-\-simple\fR
@ -38,6 +53,26 @@ Generate and provide a URL to the speedtest.net share results image
Suppress verbose output, only show basic information
.RE
\fB\-\-csv\fR
.RS
Suppress verbose output, only show basic information in CSV format. Speeds listed in bit/s and not affected by \-\-bytes
.RE
\fB\-\-csv\-delimiter CSV_DELIMITER\fR
.RS
Single character delimiter to use in CSV output. Default ","
.RE
\fB\-\-csv\-header\fR
.RS
Print CSV headers
.RE
\fB\-\-json\fR
.RS
Suppress verbose output, only show basic information in JSON format. Speeds listed in bit/s and not affected by \-\-bytes
.RE
\fB\-\-list\fR
.RS
Display a list of speedtest.net servers sorted by distance
@ -45,7 +80,12 @@ Display a list of speedtest.net servers sorted by distance
\fB\-\-server SERVER\fR
.RS
Specify a server ID to test against
Specify a server ID to test against. Can be supplied multiple times
.RE
\fB\-\-exclude EXCLUDE\fR
.RS
Exclude a server from selection. Can be supplied multiple times
.RE
\fB\-\-mini MINI\fR
@ -58,6 +98,21 @@ URL of the Speedtest Mini server
Source IP address to bind to
.RE
\fB\-\-timeout TIMEOUT\fR
.RS
HTTP timeout in seconds. Default 10
.RE
\fB\-\-secure\fR
.RS
Use HTTPS instead of HTTP when communicating with speedtest.net operated servers
.RE
\fB\-\-no\-pre\-allocate\fR
.RS
Do not pre allocate upload data. Pre allocation is enabled by default to improve upload performance. To support systems with insufficient memory, use this option to avoid a MemoryError
.RE
\fB\-\-version\fR
.RS
Show the version number and exit

2013
speedtest.py Executable file

File diff suppressed because it is too large Load Diff

View File

@ -1,676 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright 2012-2014 Matt Martz
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
__version__ = '0.2.7'
# Some global variables we use
source = None
shutdown_event = None
import math
import time
import os
import sys
import threading
import re
import signal
import socket
# Used for bound_interface
socket_socket = socket.socket
try:
import xml.etree.cElementTree as ET
except ImportError:
try:
import xml.etree.ElementTree as ET
except ImportError:
from xml.dom import minidom as DOM
ET = None
# Begin import game to handle Python 2 and Python 3
try:
from urllib2 import urlopen, Request, HTTPError, URLError
except ImportError:
from urllib.request import urlopen, Request, HTTPError, URLError
try:
from Queue import Queue
except ImportError:
from queue import Queue
try:
from urlparse import urlparse
except ImportError:
from urllib.parse import urlparse
try:
from urlparse import parse_qs
except ImportError:
try:
from urllib.parse import parse_qs
except ImportError:
from cgi import parse_qs
try:
from hashlib import md5
except ImportError:
from md5 import md5
try:
from argparse import ArgumentParser as ArgParser
except ImportError:
from optparse import OptionParser as ArgParser
try:
import builtins
except ImportError:
def print_(*args, **kwargs):
"""The new-style print function taken from
https://pypi.python.org/pypi/six/
"""
fp = kwargs.pop("file", sys.stdout)
if fp is None:
return
def write(data):
if not isinstance(data, basestring):
data = str(data)
fp.write(data)
want_unicode = False
sep = kwargs.pop("sep", None)
if sep is not None:
if isinstance(sep, unicode):
want_unicode = True
elif not isinstance(sep, str):
raise TypeError("sep must be None or a string")
end = kwargs.pop("end", None)
if end is not None:
if isinstance(end, unicode):
want_unicode = True
elif not isinstance(end, str):
raise TypeError("end must be None or a string")
if kwargs:
raise TypeError("invalid keyword arguments to print()")
if not want_unicode:
for arg in args:
if isinstance(arg, unicode):
want_unicode = True
break
if want_unicode:
newline = unicode("\n")
space = unicode(" ")
else:
newline = "\n"
space = " "
if sep is None:
sep = space
if end is None:
end = newline
for i, arg in enumerate(args):
if i:
write(sep)
write(arg)
write(end)
else:
print_ = getattr(builtins, 'print')
del builtins
def bound_socket(*args, **kwargs):
"""Bind socket to a specified source IP address"""
global source
sock = socket_socket(*args, **kwargs)
sock.bind((source, 0))
return sock
def distance(origin, destination):
"""Determine distance between 2 sets of [lat,lon] in km"""
lat1, lon1 = origin
lat2, lon2 = destination
radius = 6371 # km
dlat = math.radians(lat2 - lat1)
dlon = math.radians(lon2 - lon1)
a = (math.sin(dlat / 2) * math.sin(dlat / 2) + math.cos(math.radians(lat1))
* math.cos(math.radians(lat2)) * math.sin(dlon / 2)
* math.sin(dlon / 2))
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
d = radius * c
return d
class FileGetter(threading.Thread):
"""Thread class for retrieving a URL"""
def __init__(self, url, start):
self.url = url
self.result = None
self.starttime = start
threading.Thread.__init__(self)
def run(self):
self.result = [0]
try:
if (time.time() - self.starttime) <= 10:
f = urlopen(self.url)
while 1 and not shutdown_event.isSet():
self.result.append(len(f.read(10240)))
if self.result[-1] == 0:
break
f.close()
except IOError:
pass
def downloadSpeed(files, quiet=False):
"""Function to launch FileGetter threads and calculate download speeds"""
start = time.time()
def producer(q, files):
for file in files:
thread = FileGetter(file, start)
thread.start()
q.put(thread, True)
if not quiet and not shutdown_event.isSet():
sys.stdout.write('.')
sys.stdout.flush()
finished = []
def consumer(q, total_files):
while len(finished) < total_files:
thread = q.get(True)
while thread.isAlive():
thread.join(timeout=0.1)
finished.append(sum(thread.result))
del thread
q = Queue(6)
prod_thread = threading.Thread(target=producer, args=(q, files))
cons_thread = threading.Thread(target=consumer, args=(q, len(files)))
start = time.time()
prod_thread.start()
cons_thread.start()
while prod_thread.isAlive():
prod_thread.join(timeout=0.1)
while cons_thread.isAlive():
cons_thread.join(timeout=0.1)
return (sum(finished) / (time.time() - start))
class FilePutter(threading.Thread):
"""Thread class for putting a URL"""
def __init__(self, url, start, size):
self.url = url
chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
data = chars * (int(round(int(size) / 36.0)))
self.data = ('content1=%s' % data[0:int(size) - 9]).encode()
del data
self.result = None
self.starttime = start
threading.Thread.__init__(self)
def run(self):
try:
if ((time.time() - self.starttime) <= 10 and
not shutdown_event.isSet()):
f = urlopen(self.url, self.data)
f.read(11)
f.close()
self.result = len(self.data)
else:
self.result = 0
except IOError:
self.result = 0
def uploadSpeed(url, sizes, quiet=False):
"""Function to launch FilePutter threads and calculate upload speeds"""
start = time.time()
def producer(q, sizes):
for size in sizes:
thread = FilePutter(url, start, size)
thread.start()
q.put(thread, True)
if not quiet and not shutdown_event.isSet():
sys.stdout.write('.')
sys.stdout.flush()
finished = []
def consumer(q, total_sizes):
while len(finished) < total_sizes:
thread = q.get(True)
while thread.isAlive():
thread.join(timeout=0.1)
finished.append(thread.result)
del thread
q = Queue(6)
prod_thread = threading.Thread(target=producer, args=(q, sizes))
cons_thread = threading.Thread(target=consumer, args=(q, len(sizes)))
start = time.time()
prod_thread.start()
cons_thread.start()
while prod_thread.isAlive():
prod_thread.join(timeout=0.1)
while cons_thread.isAlive():
cons_thread.join(timeout=0.1)
return (sum(finished) / (time.time() - start))
def getAttributesByTagName(dom, tagName):
"""Retrieve an attribute from an XML document and return it in a
consistent format
Only used with xml.dom.minidom, which is likely only to be used
with python versions older than 2.5
"""
elem = dom.getElementsByTagName(tagName)[0]
return dict(list(elem.attributes.items()))
def getConfig():
"""Download the speedtest.net configuration and return only the data
we are interested in
"""
uh = urlopen('http://www.speedtest.net/speedtest-config.php')
configxml = []
while 1:
configxml.append(uh.read(10240))
if len(configxml[-1]) == 0:
break
if int(uh.code) != 200:
return None
uh.close()
try:
root = ET.fromstring(''.encode().join(configxml))
config = {
'client': root.find('client').attrib,
'times': root.find('times').attrib,
'download': root.find('download').attrib,
'upload': root.find('upload').attrib}
except AttributeError:
root = DOM.parseString(''.join(configxml))
config = {
'client': getAttributesByTagName(root, 'client'),
'times': getAttributesByTagName(root, 'times'),
'download': getAttributesByTagName(root, 'download'),
'upload': getAttributesByTagName(root, 'upload')}
del root
del configxml
return config
def closestServers(client, all=False):
"""Determine the 5 closest speedtest.net servers based on geographic
distance
"""
uh = urlopen('http://www.speedtest.net/speedtest-servers.php')
serversxml = []
while 1:
serversxml.append(uh.read(10240))
if len(serversxml[-1]) == 0:
break
if int(uh.code) != 200:
return None
uh.close()
try:
root = ET.fromstring(''.encode().join(serversxml))
elements = root.getiterator('server')
except AttributeError:
root = DOM.parseString(''.join(serversxml))
elements = root.getElementsByTagName('server')
servers = {}
for server in elements:
try:
attrib = server.attrib
except AttributeError:
attrib = dict(list(server.attributes.items()))
d = distance([float(client['lat']), float(client['lon'])],
[float(attrib.get('lat')), float(attrib.get('lon'))])
attrib['d'] = d
if d not in servers:
servers[d] = [attrib]
else:
servers[d].append(attrib)
del root
del serversxml
del elements
closest = []
for d in sorted(servers.keys()):
for s in servers[d]:
closest.append(s)
if len(closest) == 5 and not all:
break
else:
continue
break
del servers
return closest
def getBestServer(servers):
"""Perform a speedtest.net "ping" to determine which speedtest.net
server has the lowest latency
"""
results = {}
for server in servers:
cum = []
url = os.path.dirname(server['url'])
for i in range(0, 3):
try:
uh = urlopen('%s/latency.txt' % url)
except (HTTPError, URLError):
cum.append(3600)
continue
start = time.time()
text = uh.read(9)
total = time.time() - start
if int(uh.code) == 200 and text == 'test=test'.encode():
cum.append(total)
else:
cum.append(3600)
uh.close()
avg = round((sum(cum) / 3) * 1000000, 3)
results[avg] = server
fastest = sorted(results.keys())[0]
best = results[fastest]
best['latency'] = fastest
return best
def ctrl_c(signum, frame):
"""Catch Ctrl-C key sequence and set a shutdown_event for our threaded
operations
"""
global shutdown_event
shutdown_event.set()
raise SystemExit('\nCancelling...')
def version():
"""Print the version"""
raise SystemExit(__version__)
def speedtest():
"""Run the full speedtest.net test"""
global shutdown_event, source
shutdown_event = threading.Event()
signal.signal(signal.SIGINT, ctrl_c)
description = (
'Command line interface for testing internet bandwidth using '
'speedtest.net.\n'
'------------------------------------------------------------'
'--------------\n'
'https://github.com/sivel/speedtest-cli')
parser = ArgParser(description=description)
# Give optparse.OptionParser an `add_argument` method for
# compatibility with argparse.ArgumentParser
try:
parser.add_argument = parser.add_option
except AttributeError:
pass
parser.add_argument('--bytes', dest='units', action='store_const',
const=('bytes', 1), default=('bits', 8),
help='Display values in bytes instead of bits. Does '
'not affect the image generated by --share')
parser.add_argument('--share', action='store_true',
help='Generate and provide a URL to the speedtest.net '
'share results image')
parser.add_argument('--simple', action='store_true',
help='Suppress verbose output, only show basic '
'information')
parser.add_argument('--list', action='store_true',
help='Display a list of speedtest.net servers '
'sorted by distance')
parser.add_argument('--server', help='Specify a server ID to test against')
parser.add_argument('--mini', help='URL of the Speedtest Mini server')
parser.add_argument('--source', help='Source IP address to bind to')
parser.add_argument('--version', action='store_true',
help='Show the version number and exit')
options = parser.parse_args()
if isinstance(options, tuple):
args = options[0]
else:
args = options
del options
# Print the version and exit
if args.version:
version()
# If specified bind to a specific IP address
if args.source:
source = args.source
socket.socket = bound_socket
if not args.simple:
print_('Retrieving speedtest.net configuration...')
try:
config = getConfig()
except URLError:
print_('Cannot retrieve speedtest configuration')
sys.exit(1)
if not args.simple:
print_('Retrieving speedtest.net server list...')
if args.list or args.server:
servers = closestServers(config['client'], True)
if args.list:
serverList = []
for server in servers:
line = ('%(id)4s) %(sponsor)s (%(name)s, %(country)s) '
'[%(d)0.2f km]' % server)
serverList.append(line)
# Python 2.7 and newer seem to be ok with the resultant encoding
# from parsing the XML, but older versions have some issues.
# This block should detect whether we need to encode or not
try:
unicode()
print_('\n'.join(serverList).encode('utf-8', 'ignore'))
except NameError:
print_('\n'.join(serverList))
except IOError:
pass
sys.exit(0)
else:
servers = closestServers(config['client'])
if not args.simple:
print_('Testing from %(isp)s (%(ip)s)...' % config['client'])
if args.server:
try:
best = getBestServer(filter(lambda x: x['id'] == args.server,
servers))
except IndexError:
print_('Invalid server ID')
sys.exit(1)
elif args.mini:
name, ext = os.path.splitext(args.mini)
if ext:
url = os.path.dirname(args.mini)
else:
url = args.mini
urlparts = urlparse(url)
try:
f = urlopen(args.mini)
except:
print_('Invalid Speedtest Mini URL')
sys.exit(1)
else:
text = f.read()
f.close()
extension = re.findall('upload_extension: "([^"]+)"', text.decode())
if not extension:
for ext in ['php', 'asp', 'aspx', 'jsp']:
try:
f = urlopen('%s/speedtest/upload.%s' % (args.mini, ext))
except:
pass
else:
data = f.read().strip()
if (f.code == 200 and
len(data.splitlines()) == 1 and
re.match('size=[0-9]', data)):
extension = [ext]
break
if not urlparts or not extension:
print_('Please provide the full URL of your Speedtest Mini server')
sys.exit(1)
servers = [{
'sponsor': 'Speedtest Mini',
'name': urlparts[1],
'd': 0,
'url': '%s/speedtest/upload.%s' % (url.rstrip('/'), extension[0]),
'latency': 0,
'id': 0
}]
try:
best = getBestServer(servers)
except:
best = servers[0]
else:
if not args.simple:
print_('Selecting best server based on ping...')
best = getBestServer(servers)
if not args.simple:
# Python 2.7 and newer seem to be ok with the resultant encoding
# from parsing the XML, but older versions have some issues.
# This block should detect whether we need to encode or not
try:
unicode()
print_(('Hosted by %(sponsor)s (%(name)s) [%(d)0.2f km]: '
'%(latency)s ms' % best).encode('utf-8', 'ignore'))
except NameError:
print_('Hosted by %(sponsor)s (%(name)s) [%(d)0.2f km]: '
'%(latency)s ms' % best)
else:
print_('Ping: %(latency)s ms' % best)
sizes = [350, 500, 750, 1000, 1500, 2000, 2500, 3000, 3500, 4000]
urls = []
for size in sizes:
for i in range(0, 4):
urls.append('%s/random%sx%s.jpg' %
(os.path.dirname(best['url']), size, size))
if not args.simple:
print_('Testing download speed', end='')
dlspeed = downloadSpeed(urls, args.simple)
if not args.simple:
print_()
print_('Download: %0.2f M%s/s' %
((dlspeed / 1000 / 1000) * args.units[1], args.units[0]))
sizesizes = [int(.25 * 1000 * 1000), int(.5 * 1000 * 1000)]
sizes = []
for size in sizesizes:
for i in range(0, 25):
sizes.append(size)
if not args.simple:
print_('Testing upload speed', end='')
ulspeed = uploadSpeed(best['url'], sizes, args.simple)
if not args.simple:
print_()
print_('Upload: %0.2f M%s/s' %
((ulspeed / 1000 / 1000) * args.units[1], args.units[0]))
if args.share and args.mini:
print_('Cannot generate a speedtest.net share results image while '
'testing against a Speedtest Mini server')
elif args.share:
dlspeedk = int(round((dlspeed / 1000) * 8, 0))
ping = int(round(best['latency'], 0))
ulspeedk = int(round((ulspeed / 1000) * 8, 0))
# Build the request to send results back to speedtest.net
# We use a list instead of a dict because the API expects parameters
# in a certain order
apiData = [
'download=%s' % dlspeedk,
'ping=%s' % ping,
'upload=%s' % ulspeedk,
'promo=',
'startmode=%s' % 'pingselect',
'recommendedserverid=%s' % best['id'],
'accuracy=%s' % 1,
'serverid=%s' % best['id'],
'hash=%s' % md5(('%s-%s-%s-%s' %
(ping, ulspeedk, dlspeedk, '297aae72'))
.encode()).hexdigest()]
req = Request('http://www.speedtest.net/api/api.php',
data='&'.join(apiData).encode())
req.add_header('Referer', 'http://c.speedtest.net/flash/speedtest.swf')
f = urlopen(req)
response = f.read()
code = f.code
f.close()
if int(code) != 200:
print_('Could not submit results to speedtest.net')
sys.exit(1)
qsargs = parse_qs(response.decode())
resultid = qsargs.get('resultid')
if not resultid or len(resultid) != 1:
print_('Could not submit results to speedtest.net')
sys.exit(1)
print_('Share results: http://www.speedtest.net/result/%s.png' %
resultid[0])
def main():
try:
speedtest()
except KeyboardInterrupt:
print_('\nCancelling...')
if __name__ == '__main__':
main()
# vim:ts=4:sw=4:expandtab

37
tests/scripts/source.py Normal file
View File

@ -0,0 +1,37 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright 2018 Matt Martz
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import sys
import subprocess
cmd = [sys.executable, 'speedtest.py', '--source', '127.0.0.1']
p = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
stdout, stderr = p.communicate()
if p.returncode != 1:
raise SystemExit('%s did not fail with exit code 1' % ' '.join(cmd))
if 'Invalid argument'.encode() not in stderr:
raise SystemExit(
'"Invalid argument" not found in stderr:\n%s' % stderr.decode()
)

12
tox.ini
View File

@ -4,16 +4,22 @@ skipsdist=true
[testenv]
commands =
{envpython} -V
{envpython} speedtest_cli.py
{envpython} -m compileall speedtest.py
{envpython} speedtest.py
{envpython} speedtest.py --source 172.17.0.1
{envpython} tests/scripts/source.py
[testenv:flake8]
basepython=python
deps=flake8
commands =
{envpython} -V
flake8 speedtest_cli.py
flake8 speedtest.py
[testenv:pypy]
commands =
pypy -V
pypy speedtest_cli.py
pypy -m compileall speedtest.py
pypy speedtest.py
pypy speedtest.py --source 172.17.0.1
pypy tests/scripts/source.py