Compare commits

...

10 Commits

Author SHA1 Message Date
VisualPlugin
fae3918bfc Merge 12d868f8d9 into 22210ca352 2024-04-12 22:20:03 +00:00
Windows81
12d868f8d9 2024-04-12T22:19Z 2024-04-12 22:19:18 +00:00
Windows81
f317af3bf4 Fixed cases where --bytes and --units are both not supplied. 2021-09-19 16:47:03 +00:00
Windows81
8fcd4a019e Added additional unit flexibility for download/upload speeds. 2021-07-28 22:34:52 +00:00
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
2 changed files with 108 additions and 42 deletions

View File

@@ -92,5 +92,8 @@ setup(
'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

@@ -15,18 +15,18 @@
# License for the specific language governing permissions and limitations
# under the License.
import os
import re
import csv
import sys
import math
import datetime
import errno
import math
import os
import platform
import re
import signal
import socket
import timeit
import datetime
import platform
import sys
import threading
import timeit
import xml.parsers.expat
try:
@@ -36,7 +36,7 @@ except ImportError:
gzip = None
GZIP_BASE = object
__version__ = '2.1.2a'
__version__ = '2.1.4b1'
class FakeShutdownEvent(object):
@@ -49,6 +49,8 @@ class FakeShutdownEvent(object):
"Dummy method to always return false"""
return False
is_set = isSet
# Some global variables we use
DEBUG = False
@@ -56,6 +58,19 @@ _GLOBAL_DEFAULT_TIMEOUT = object()
PY25PLUS = sys.version_info[:2] >= (2, 5)
PY26PLUS = sys.version_info[:2] >= (2, 6)
PY32PLUS = sys.version_info[:2] >= (3, 2)
PY310PLUS = sys.version_info[:2] >= (3, 10)
SIZEMAP = {
"Mibyte": 8. * 1024 * 1024,
"Mbyte": 8. * 1000 * 1000,
"Mibit": 1. * 1024 * 1024,
"Mbit": 1. * 1000 * 1000,
"Kibyte": 8. * 1024,
"Kbyte": 8. * 1000,
"Kibit": 1. * 1024,
"Kbit": 1. * 1000,
"byte": 8.,
"bit": 1.,
}
# Begin import game to handle Python 2 and Python 3
try:
@@ -266,17 +281,6 @@ else:
write(arg)
write(end)
if PY32PLUS:
etree_iter = ET.Element.iter
elif PY25PLUS:
etree_iter = ET_Element.getiterator
if PY26PLUS:
thread_is_alive = threading.Thread.is_alive
else:
thread_is_alive = threading.Thread.isAlive
# Exception "constants" to support Python 2 through Python 3
try:
import ssl
@@ -293,6 +297,23 @@ except ImportError:
ssl = None
HTTP_ERRORS = (HTTPError, URLError, socket.error, BadStatusLine)
if PY32PLUS:
etree_iter = ET.Element.iter
elif PY25PLUS:
etree_iter = ET_Element.getiterator
if PY26PLUS:
thread_is_alive = threading.Thread.is_alive
else:
thread_is_alive = threading.Thread.isAlive
def event_is_set(event):
try:
return event.is_set()
except AttributeError:
return event.isSet()
class SpeedtestException(Exception):
"""Base exception for this module"""
@@ -413,6 +434,8 @@ class SpeedtestHTTPConnection(HTTPConnection):
source_address = kwargs.pop('source_address', None)
timeout = kwargs.pop('timeout', 10)
self._tunnel_host = None
HTTPConnection.__init__(self, *args, **kwargs)
self.source_address = source_address
@@ -433,17 +456,23 @@ class SpeedtestHTTPConnection(HTTPConnection):
self.source_address
)
if self._tunnel_host:
self._tunnel()
if HTTPSConnection:
class SpeedtestHTTPSConnection(HTTPSConnection,
SpeedtestHTTPConnection):
class SpeedtestHTTPSConnection(HTTPSConnection):
"""Custom HTTPSConnection to support source_address across
Python 2.4 - Python 3
"""
default_port = 443
def __init__(self, *args, **kwargs):
source_address = kwargs.pop('source_address', None)
timeout = kwargs.pop('timeout', 10)
self._tunnel_host = None
HTTPSConnection.__init__(self, *args, **kwargs)
self.timeout = timeout
@@ -451,14 +480,30 @@ if HTTPSConnection:
def connect(self):
"Connect to a host on a given (SSL) port."
try:
self.sock = socket.create_connection(
(self.host, self.port),
self.timeout,
self.source_address
)
except (AttributeError, TypeError):
self.sock = create_connection(
(self.host, self.port),
self.timeout,
self.source_address
)
SpeedtestHTTPConnection.connect(self)
if self._tunnel_host:
self._tunnel()
if ssl:
try:
kwargs = {}
if hasattr(ssl, 'SSLContext'):
kwargs['server_hostname'] = self.host
if self._tunnel_host:
kwargs['server_hostname'] = self._tunnel_host
else:
kwargs['server_hostname'] = self.host
self.sock = self._context.wrap_socket(self.sock, **kwargs)
except AttributeError:
self.sock = ssl.wrap_socket(self.sock)
@@ -745,7 +790,7 @@ def print_dots(shutdown_event):
status
"""
def inner(current, total, start=False, end=False):
if shutdown_event.isSet():
if event_is_set(shutdown_event):
return
sys.stdout.write('.')
@@ -784,7 +829,7 @@ class HTTPDownloader(threading.Thread):
try:
if (timeit.default_timer() - self.starttime) <= self.timeout:
f = self._opener(self.request)
while (not self._shutdown_event.isSet() and
while (not event_is_set(self._shutdown_event) and
(timeit.default_timer() - self.starttime) <=
self.timeout):
self.result.append(len(f.read(10240)))
@@ -793,6 +838,8 @@ class HTTPDownloader(threading.Thread):
f.close()
except IOError:
pass
except HTTP_ERRORS:
pass
class HTTPUploaderData(object):
@@ -838,7 +885,7 @@ class HTTPUploaderData(object):
def read(self, n=10240):
if ((timeit.default_timer() - self.start) <= self.timeout and
not self._shutdown_event.isSet()):
not event_is_set(self._shutdown_event)):
chunk = self.data.read(n)
self.total.append(len(chunk))
return chunk
@@ -858,7 +905,7 @@ class HTTPUploader(threading.Thread):
self.request = request
self.request.data.start = self.starttime = start
self.size = size
self.result = None
self.result = 0
self.timeout = timeout
self.i = i
@@ -876,7 +923,7 @@ class HTTPUploader(threading.Thread):
request = self.request
try:
if ((timeit.default_timer() - self.starttime) <= self.timeout and
not self._shutdown_event.isSet()):
not event_is_set(self._shutdown_event)):
try:
f = self._opener(request)
except TypeError:
@@ -893,6 +940,8 @@ class HTTPUploader(threading.Thread):
self.result = 0
except (IOError, SpeedtestUploadTimeout):
self.result = sum(self.request.data.total)
except HTTP_ERRORS:
self.result = 0
class SpeedtestResults(object):
@@ -920,7 +969,7 @@ class SpeedtestResults(object):
self.client = client or {}
self._share = None
self.timestamp = '%sZ' % datetime.datetime.utcnow().isoformat()
self.timestamp = '%sZ' % datetime.datetime.now(datetime.UTC).isoformat()
self.bytes_received = 0
self.bytes_sent = 0
@@ -1146,9 +1195,9 @@ class Speedtest(object):
# times = get_attributes_by_tag_name(root, 'times')
client = get_attributes_by_tag_name(root, 'client')
ignore_servers = list(
map(int, server_config['ignoreids'].split(','))
)
ignore_servers = [
int(i) for i in server_config['ignoreids'].split(',') if i
]
ratio = int(upload['ratio'])
upload_max = int(upload['maxchunkcount'])
@@ -1696,11 +1745,14 @@ def parse_args():
help='Only use a single connection instead of '
'multiple. This simulates a typical file '
'transfer.')
parser.add_argument('--bytes', dest='units', action='store_const',
const=('byte', 8), default=('bit', 1),
parser.add_argument('--bytes', default=False, action='store_true',
help='Display values in bytes instead of bits. Does '
'not affect the image generated by --share, nor '
'output from --json or --csv')
parser.add_argument('--units', choices=tuple(SIZEMAP),
help='Determines which units to display values in. Does '
'not affect the image generated by --share, nor '
'output from --json or --csv')
parser.add_argument('--share', action='store_true',
help='Generate and provide a URL to the speedtest.net '
'share results image, not displayed with --csv')
@@ -1710,7 +1762,7 @@ def parse_args():
parser.add_argument('--csv', action='store_true', default=False,
help='Suppress verbose output, only show basic '
'information in CSV format. Speeds listed in '
'bit/s and not affected by --bytes')
'bit/s and not affected by --bytes or --units')
parser.add_argument('--csv-delimiter', default=',', type=PARSER_TYPE_STR,
help='Single character delimiter to use in CSV '
'output. Default ","')
@@ -1719,7 +1771,7 @@ def parse_args():
parser.add_argument('--json', action='store_true', default=False,
help='Suppress verbose output, only show basic '
'information in JSON format. Speeds listed in '
'bit/s and not affected by --bytes')
'bit/s and not affected by --bytes or --units')
parser.add_argument('--list', action='store_true',
help='Display a list of speedtest.net servers '
'sorted by distance')
@@ -1813,6 +1865,10 @@ def shell():
raise SpeedtestCLIError('Cannot supply both --no-download and '
'--no-upload')
if args.bytes and args.units:
raise SpeedtestCLIError('Cannot supply both --bytes and '
'--units')
if len(args.csv_delimiter) != 1:
raise SpeedtestCLIError('--csv-delimiter must be a single character')
@@ -1827,6 +1883,13 @@ def shell():
if debug:
DEBUG = True
if args.bytes:
units = 'Mbyte'
elif args.units:
units = args.units
else:
units = 'Mbit'
if args.simple or args.csv or args.json:
quiet = True
else:
@@ -1914,9 +1977,9 @@ def shell():
callback=callback,
threads=(None, 1)[args.single]
)
printer('Download: %0.2f M%s/s' %
((results.download / 1000.0 / 1000.0) / args.units[1],
args.units[0]),
printer('Download: %0.2f %s/s' %
(results.download / SIZEMAP[units],
units),
quiet)
else:
printer('Skipping download test', quiet)
@@ -1929,9 +1992,9 @@ def shell():
pre_allocate=args.pre_allocate,
threads=(None, 1)[args.single]
)
printer('Upload: %0.2f M%s/s' %
((results.upload / 1000.0 / 1000.0) / args.units[1],
args.units[0]),
printer('Upload: %0.2f %s/s' %
(results.upload / SIZEMAP[units],
units),
quiet)
else:
printer('Skipping upload test', quiet)