Compare commits
No commits in common. "master" and "v2.0.2" have entirely different histories.
|
|
@ -1,6 +1,4 @@
|
||||||
language: python
|
language: python
|
||||||
sudo: required
|
|
||||||
dist: xenial
|
|
||||||
|
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
|
|
@ -35,13 +33,8 @@ matrix:
|
||||||
env: TOXENV=py35
|
env: TOXENV=py35
|
||||||
- python: 3.6
|
- python: 3.6
|
||||||
env: TOXENV=py36
|
env: TOXENV=py36
|
||||||
- python: 3.7
|
|
||||||
env: TOXENV=py37
|
|
||||||
- python: 3.8-dev
|
|
||||||
env: TOXENV=py38
|
|
||||||
- python: pypy
|
- python: pypy
|
||||||
env: TOXENV=pypy
|
env: TOXENV=pypy
|
||||||
dist: trusty
|
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- if [[ $(echo "$TOXENV" | egrep -c "py35") != 0 ]]; then pyenv global system 3.5; fi;
|
- if [[ $(echo "$TOXENV" | egrep -c "py35") != 0 ]]; then pyenv global system 3.5; fi;
|
||||||
|
|
|
||||||
21
README.rst
21
README.rst
|
|
@ -51,8 +51,7 @@ or
|
||||||
::
|
::
|
||||||
|
|
||||||
git clone https://github.com/sivel/speedtest-cli.git
|
git clone https://github.com/sivel/speedtest-cli.git
|
||||||
cd speedtest-cli
|
python speedtest-cli/setup.py install
|
||||||
python setup.py install
|
|
||||||
|
|
||||||
Just download (Like the way it used to be)
|
Just download (Like the way it used to be)
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
@ -75,23 +74,21 @@ Usage
|
||||||
::
|
::
|
||||||
|
|
||||||
$ speedtest-cli -h
|
$ speedtest-cli -h
|
||||||
usage: speedtest-cli [-h] [--no-download] [--no-upload] [--single] [--bytes]
|
usage: speedtest-cli [-h] [--no-download] [--no-upload] [--bytes] [--share]
|
||||||
[--share] [--simple] [--csv]
|
[--simple] [--csv] [--csv-delimiter CSV_DELIMITER]
|
||||||
[--csv-delimiter CSV_DELIMITER] [--csv-header] [--json]
|
[--csv-header] [--json] [--list] [--server SERVER]
|
||||||
[--list] [--server SERVER] [--exclude EXCLUDE]
|
[--exclude EXCLUDE] [--mini MINI] [--source SOURCE]
|
||||||
[--mini MINI] [--source SOURCE] [--timeout TIMEOUT]
|
[--timeout TIMEOUT] [--secure] [--no-pre-allocate]
|
||||||
[--secure] [--no-pre-allocate] [--version]
|
[--version]
|
||||||
|
|
||||||
Command line interface for testing internet bandwidth using speedtest.net.
|
Command line interface for testing internet bandwidth using speedtest.net.
|
||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
https://github.com/sivel/speedtest-cli
|
https://github.com/sivel/speedtest-cli
|
||||||
|
|
||||||
optional arguments:
|
optional arguments:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
--no-download Do not perform download test
|
--no-download Do not perform download test
|
||||||
--no-upload Do not perform upload 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
|
--bytes Display values in bytes instead of bits. Does not
|
||||||
affect the image generated by --share, nor output from
|
affect the image generated by --share, nor output from
|
||||||
--json or --csv
|
--json or --csv
|
||||||
|
|
|
||||||
5
setup.py
5
setup.py
|
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright 2012 Matt Martz
|
# Copyright 2012-2018 Matt Martz
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
|
@ -92,8 +92,5 @@ setup(
|
||||||
'Programming Language :: Python :: 3.5',
|
'Programming Language :: Python :: 3.5',
|
||||||
'Programming Language :: Python :: 3.6',
|
'Programming Language :: Python :: 3.6',
|
||||||
'Programming Language :: Python :: 3.7',
|
'Programming Language :: Python :: 3.7',
|
||||||
'Programming Language :: Python :: 3.8',
|
|
||||||
'Programming Language :: Python :: 3.9',
|
|
||||||
'Programming Language :: Python :: 3.10',
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -33,11 +33,6 @@ Do not perform download test
|
||||||
Do not perform upload test
|
Do not perform upload test
|
||||||
.RE
|
.RE
|
||||||
|
|
||||||
\fB\-\-single\fR
|
|
||||||
.RS
|
|
||||||
Only use a single connection instead of multiple. This simulates a typical file transfer.
|
|
||||||
.RE
|
|
||||||
|
|
||||||
\fB\-\-bytes\fR
|
\fB\-\-bytes\fR
|
||||||
.RS
|
.RS
|
||||||
Display values in bytes instead of bits. Does not affect the image generated by \-\-share, nor output from \-\-json or \-\-csv
|
Display values in bytes instead of bits. Does not affect the image generated by \-\-share, nor output from \-\-json or \-\-csv
|
||||||
|
|
|
||||||
236
speedtest.py
236
speedtest.py
|
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright 2012 Matt Martz
|
# Copyright 2012-2018 Matt Martz
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
|
@ -15,18 +15,18 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import csv
|
|
||||||
import datetime
|
|
||||||
import errno
|
|
||||||
import math
|
|
||||||
import os
|
import os
|
||||||
import platform
|
|
||||||
import re
|
import re
|
||||||
|
import csv
|
||||||
|
import sys
|
||||||
|
import math
|
||||||
|
import errno
|
||||||
import signal
|
import signal
|
||||||
import socket
|
import socket
|
||||||
import sys
|
|
||||||
import threading
|
|
||||||
import timeit
|
import timeit
|
||||||
|
import datetime
|
||||||
|
import platform
|
||||||
|
import threading
|
||||||
import xml.parsers.expat
|
import xml.parsers.expat
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -36,7 +36,7 @@ except ImportError:
|
||||||
gzip = None
|
gzip = None
|
||||||
GZIP_BASE = object
|
GZIP_BASE = object
|
||||||
|
|
||||||
__version__ = '2.1.4b1'
|
__version__ = '2.0.2'
|
||||||
|
|
||||||
|
|
||||||
class FakeShutdownEvent(object):
|
class FakeShutdownEvent(object):
|
||||||
|
|
@ -49,16 +49,10 @@ class FakeShutdownEvent(object):
|
||||||
"Dummy method to always return false"""
|
"Dummy method to always return false"""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
is_set = isSet
|
|
||||||
|
|
||||||
|
|
||||||
# Some global variables we use
|
# Some global variables we use
|
||||||
DEBUG = False
|
DEBUG = False
|
||||||
_GLOBAL_DEFAULT_TIMEOUT = object()
|
_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)
|
|
||||||
|
|
||||||
# Begin import game to handle Python 2 and Python 3
|
# Begin import game to handle Python 2 and Python 3
|
||||||
try:
|
try:
|
||||||
|
|
@ -70,15 +64,14 @@ except ImportError:
|
||||||
json = None
|
json = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.cElementTree as ET
|
||||||
try:
|
|
||||||
from xml.etree.ElementTree import _Element as ET_Element
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from xml.dom import minidom as DOM
|
try:
|
||||||
from xml.parsers.expat import ExpatError
|
import xml.etree.ElementTree as ET
|
||||||
ET = None
|
except ImportError:
|
||||||
|
from xml.dom import minidom as DOM
|
||||||
|
from xml.parsers.expat import ExpatError
|
||||||
|
ET = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from urllib2 import (urlopen, Request, HTTPError, URLError,
|
from urllib2 import (urlopen, Request, HTTPError, URLError,
|
||||||
|
|
@ -104,11 +97,6 @@ except ImportError:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
HTTPSConnection = None
|
HTTPSConnection = None
|
||||||
|
|
||||||
try:
|
|
||||||
from httplib import FakeSocket
|
|
||||||
except ImportError:
|
|
||||||
FakeSocket = None
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from Queue import Queue
|
from Queue import Queue
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
@ -269,6 +257,7 @@ else:
|
||||||
write(arg)
|
write(arg)
|
||||||
write(end)
|
write(end)
|
||||||
|
|
||||||
|
|
||||||
# Exception "constants" to support Python 2 through Python 3
|
# Exception "constants" to support Python 2 through Python 3
|
||||||
try:
|
try:
|
||||||
import ssl
|
import ssl
|
||||||
|
|
@ -285,23 +274,6 @@ except ImportError:
|
||||||
ssl = None
|
ssl = None
|
||||||
HTTP_ERRORS = (HTTPError, URLError, socket.error, BadStatusLine)
|
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):
|
class SpeedtestException(Exception):
|
||||||
"""Base exception for this module"""
|
"""Base exception for this module"""
|
||||||
|
|
@ -422,8 +394,6 @@ class SpeedtestHTTPConnection(HTTPConnection):
|
||||||
source_address = kwargs.pop('source_address', None)
|
source_address = kwargs.pop('source_address', None)
|
||||||
timeout = kwargs.pop('timeout', 10)
|
timeout = kwargs.pop('timeout', 10)
|
||||||
|
|
||||||
self._tunnel_host = None
|
|
||||||
|
|
||||||
HTTPConnection.__init__(self, *args, **kwargs)
|
HTTPConnection.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
self.source_address = source_address
|
self.source_address = source_address
|
||||||
|
|
@ -444,23 +414,17 @@ class SpeedtestHTTPConnection(HTTPConnection):
|
||||||
self.source_address
|
self.source_address
|
||||||
)
|
)
|
||||||
|
|
||||||
if self._tunnel_host:
|
|
||||||
self._tunnel()
|
|
||||||
|
|
||||||
|
|
||||||
if HTTPSConnection:
|
if HTTPSConnection:
|
||||||
class SpeedtestHTTPSConnection(HTTPSConnection):
|
class SpeedtestHTTPSConnection(HTTPSConnection,
|
||||||
|
SpeedtestHTTPConnection):
|
||||||
"""Custom HTTPSConnection to support source_address across
|
"""Custom HTTPSConnection to support source_address across
|
||||||
Python 2.4 - Python 3
|
Python 2.4 - Python 3
|
||||||
"""
|
"""
|
||||||
default_port = 443
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
source_address = kwargs.pop('source_address', None)
|
source_address = kwargs.pop('source_address', None)
|
||||||
timeout = kwargs.pop('timeout', 10)
|
timeout = kwargs.pop('timeout', 10)
|
||||||
|
|
||||||
self._tunnel_host = None
|
|
||||||
|
|
||||||
HTTPSConnection.__init__(self, *args, **kwargs)
|
HTTPSConnection.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
|
|
@ -468,51 +432,17 @@ if HTTPSConnection:
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
"Connect to a host on a given (SSL) port."
|
"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
|
|
||||||
)
|
|
||||||
|
|
||||||
if self._tunnel_host:
|
SpeedtestHTTPConnection.connect(self)
|
||||||
self._tunnel()
|
|
||||||
|
|
||||||
|
kwargs = {}
|
||||||
if ssl:
|
if ssl:
|
||||||
|
if hasattr(ssl, 'SSLContext'):
|
||||||
|
kwargs['server_hostname'] = self.host
|
||||||
try:
|
try:
|
||||||
kwargs = {}
|
|
||||||
if hasattr(ssl, 'SSLContext'):
|
|
||||||
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)
|
self.sock = self._context.wrap_socket(self.sock, **kwargs)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
self.sock = ssl.wrap_socket(self.sock)
|
self.sock = ssl.wrap_socket(self.sock, **kwargs)
|
||||||
try:
|
|
||||||
self.sock.server_hostname = self.host
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
elif FakeSocket:
|
|
||||||
# Python 2.4/2.5 support
|
|
||||||
try:
|
|
||||||
self.sock = FakeSocket(self.sock, socket.ssl(self.sock))
|
|
||||||
except AttributeError:
|
|
||||||
raise SpeedtestException(
|
|
||||||
'This version of Python does not support HTTPS/SSL '
|
|
||||||
'functionality'
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
raise SpeedtestException(
|
|
||||||
'This version of Python does not support HTTPS/SSL '
|
|
||||||
'functionality'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _build_connection(connection, source_address, timeout, context=None):
|
def _build_connection(connection, source_address, timeout, context=None):
|
||||||
|
|
@ -677,8 +607,7 @@ def build_user_agent():
|
||||||
|
|
||||||
ua_tuple = (
|
ua_tuple = (
|
||||||
'Mozilla/5.0',
|
'Mozilla/5.0',
|
||||||
'(%s; U; %s; en-us)' % (platform.platform(),
|
'(%s; U; %s; en-us)' % (platform.system(), platform.architecture()[0]),
|
||||||
platform.architecture()[0]),
|
|
||||||
'Python/%s' % platform.python_version(),
|
'Python/%s' % platform.python_version(),
|
||||||
'(KHTML, like Gecko)',
|
'(KHTML, like Gecko)',
|
||||||
'speedtest-cli/%s' % __version__
|
'speedtest-cli/%s' % __version__
|
||||||
|
|
@ -737,8 +666,6 @@ def catch_request(request, opener=None):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
uh = _open(request)
|
uh = _open(request)
|
||||||
if request.get_full_url() != uh.geturl():
|
|
||||||
printer('Redirected to %s' % uh.geturl(), debug=True)
|
|
||||||
return uh, False
|
return uh, False
|
||||||
except HTTP_ERRORS:
|
except HTTP_ERRORS:
|
||||||
e = get_exception()
|
e = get_exception()
|
||||||
|
|
@ -778,7 +705,7 @@ def print_dots(shutdown_event):
|
||||||
status
|
status
|
||||||
"""
|
"""
|
||||||
def inner(current, total, start=False, end=False):
|
def inner(current, total, start=False, end=False):
|
||||||
if event_is_set(shutdown_event):
|
if shutdown_event.isSet():
|
||||||
return
|
return
|
||||||
|
|
||||||
sys.stdout.write('.')
|
sys.stdout.write('.')
|
||||||
|
|
@ -817,7 +744,7 @@ class HTTPDownloader(threading.Thread):
|
||||||
try:
|
try:
|
||||||
if (timeit.default_timer() - self.starttime) <= self.timeout:
|
if (timeit.default_timer() - self.starttime) <= self.timeout:
|
||||||
f = self._opener(self.request)
|
f = self._opener(self.request)
|
||||||
while (not event_is_set(self._shutdown_event) and
|
while (not self._shutdown_event.isSet() and
|
||||||
(timeit.default_timer() - self.starttime) <=
|
(timeit.default_timer() - self.starttime) <=
|
||||||
self.timeout):
|
self.timeout):
|
||||||
self.result.append(len(f.read(10240)))
|
self.result.append(len(f.read(10240)))
|
||||||
|
|
@ -826,8 +753,6 @@ class HTTPDownloader(threading.Thread):
|
||||||
f.close()
|
f.close()
|
||||||
except IOError:
|
except IOError:
|
||||||
pass
|
pass
|
||||||
except HTTP_ERRORS:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class HTTPUploaderData(object):
|
class HTTPUploaderData(object):
|
||||||
|
|
@ -873,7 +798,7 @@ class HTTPUploaderData(object):
|
||||||
|
|
||||||
def read(self, n=10240):
|
def read(self, n=10240):
|
||||||
if ((timeit.default_timer() - self.start) <= self.timeout and
|
if ((timeit.default_timer() - self.start) <= self.timeout and
|
||||||
not event_is_set(self._shutdown_event)):
|
not self._shutdown_event.isSet()):
|
||||||
chunk = self.data.read(n)
|
chunk = self.data.read(n)
|
||||||
self.total.append(len(chunk))
|
self.total.append(len(chunk))
|
||||||
return chunk
|
return chunk
|
||||||
|
|
@ -893,7 +818,7 @@ class HTTPUploader(threading.Thread):
|
||||||
self.request = request
|
self.request = request
|
||||||
self.request.data.start = self.starttime = start
|
self.request.data.start = self.starttime = start
|
||||||
self.size = size
|
self.size = size
|
||||||
self.result = 0
|
self.result = None
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
self.i = i
|
self.i = i
|
||||||
|
|
||||||
|
|
@ -911,7 +836,7 @@ class HTTPUploader(threading.Thread):
|
||||||
request = self.request
|
request = self.request
|
||||||
try:
|
try:
|
||||||
if ((timeit.default_timer() - self.starttime) <= self.timeout and
|
if ((timeit.default_timer() - self.starttime) <= self.timeout and
|
||||||
not event_is_set(self._shutdown_event)):
|
not self._shutdown_event.isSet()):
|
||||||
try:
|
try:
|
||||||
f = self._opener(request)
|
f = self._opener(request)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
|
|
@ -928,8 +853,6 @@ class HTTPUploader(threading.Thread):
|
||||||
self.result = 0
|
self.result = 0
|
||||||
except (IOError, SpeedtestUploadTimeout):
|
except (IOError, SpeedtestUploadTimeout):
|
||||||
self.result = sum(self.request.data.total)
|
self.result = sum(self.request.data.total)
|
||||||
except HTTP_ERRORS:
|
|
||||||
self.result = 0
|
|
||||||
|
|
||||||
|
|
||||||
class SpeedtestResults(object):
|
class SpeedtestResults(object):
|
||||||
|
|
@ -1118,7 +1041,10 @@ class Speedtest(object):
|
||||||
@property
|
@property
|
||||||
def best(self):
|
def best(self):
|
||||||
if not self._best:
|
if not self._best:
|
||||||
self.get_best_server()
|
raise SpeedtestMissingBestServer(
|
||||||
|
'get_best_server not called or not able to determine best '
|
||||||
|
'server'
|
||||||
|
)
|
||||||
return self._best
|
return self._best
|
||||||
|
|
||||||
def get_config(self):
|
def get_config(self):
|
||||||
|
|
@ -1183,9 +1109,9 @@ class Speedtest(object):
|
||||||
# times = get_attributes_by_tag_name(root, 'times')
|
# times = get_attributes_by_tag_name(root, 'times')
|
||||||
client = get_attributes_by_tag_name(root, 'client')
|
client = get_attributes_by_tag_name(root, 'client')
|
||||||
|
|
||||||
ignore_servers = [
|
ignore_servers = list(
|
||||||
int(i) for i in server_config['ignoreids'].split(',') if i
|
map(int, server_config['ignoreids'].split(','))
|
||||||
]
|
)
|
||||||
|
|
||||||
ratio = int(upload['ratio'])
|
ratio = int(upload['ratio'])
|
||||||
upload_max = int(upload['maxchunkcount'])
|
upload_max = int(upload['maxchunkcount'])
|
||||||
|
|
@ -1313,7 +1239,7 @@ class Speedtest(object):
|
||||||
raise SpeedtestServersError(
|
raise SpeedtestServersError(
|
||||||
'Malformed speedtest.net server list: %s' % e
|
'Malformed speedtest.net server list: %s' % e
|
||||||
)
|
)
|
||||||
elements = etree_iter(root, 'server')
|
elements = root.getiterator('server')
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
try:
|
try:
|
||||||
root = DOM.parseString(serversxml)
|
root = DOM.parseString(serversxml)
|
||||||
|
|
@ -1513,12 +1439,8 @@ class Speedtest(object):
|
||||||
printer('Best Server:\n%r' % best, debug=True)
|
printer('Best Server:\n%r' % best, debug=True)
|
||||||
return best
|
return best
|
||||||
|
|
||||||
def download(self, callback=do_nothing, threads=None):
|
def download(self, callback=do_nothing):
|
||||||
"""Test download speed against speedtest.net
|
"""Test download speed against speedtest.net"""
|
||||||
|
|
||||||
A ``threads`` value of ``None`` will fall back to those dictated
|
|
||||||
by the speedtest.net configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
urls = []
|
urls = []
|
||||||
for size in self.config['sizes']['download']:
|
for size in self.config['sizes']['download']:
|
||||||
|
|
@ -1533,9 +1455,6 @@ class Speedtest(object):
|
||||||
build_request(url, bump=i, secure=self._secure)
|
build_request(url, bump=i, secure=self._secure)
|
||||||
)
|
)
|
||||||
|
|
||||||
max_threads = threads or self.config['threads']['download']
|
|
||||||
in_flight = {'threads': 0}
|
|
||||||
|
|
||||||
def producer(q, requests, request_count):
|
def producer(q, requests, request_count):
|
||||||
for i, request in enumerate(requests):
|
for i, request in enumerate(requests):
|
||||||
thread = HTTPDownloader(
|
thread = HTTPDownloader(
|
||||||
|
|
@ -1546,26 +1465,21 @@ class Speedtest(object):
|
||||||
opener=self._opener,
|
opener=self._opener,
|
||||||
shutdown_event=self._shutdown_event
|
shutdown_event=self._shutdown_event
|
||||||
)
|
)
|
||||||
while in_flight['threads'] >= max_threads:
|
|
||||||
timeit.time.sleep(0.001)
|
|
||||||
thread.start()
|
thread.start()
|
||||||
q.put(thread, True)
|
q.put(thread, True)
|
||||||
in_flight['threads'] += 1
|
|
||||||
callback(i, request_count, start=True)
|
callback(i, request_count, start=True)
|
||||||
|
|
||||||
finished = []
|
finished = []
|
||||||
|
|
||||||
def consumer(q, request_count):
|
def consumer(q, request_count):
|
||||||
_is_alive = thread_is_alive
|
|
||||||
while len(finished) < request_count:
|
while len(finished) < request_count:
|
||||||
thread = q.get(True)
|
thread = q.get(True)
|
||||||
while _is_alive(thread):
|
while thread.isAlive():
|
||||||
thread.join(timeout=0.001)
|
thread.join(timeout=0.1)
|
||||||
in_flight['threads'] -= 1
|
|
||||||
finished.append(sum(thread.result))
|
finished.append(sum(thread.result))
|
||||||
callback(thread.i, request_count, end=True)
|
callback(thread.i, request_count, end=True)
|
||||||
|
|
||||||
q = Queue(max_threads)
|
q = Queue(self.config['threads']['download'])
|
||||||
prod_thread = threading.Thread(target=producer,
|
prod_thread = threading.Thread(target=producer,
|
||||||
args=(q, requests, request_count))
|
args=(q, requests, request_count))
|
||||||
cons_thread = threading.Thread(target=consumer,
|
cons_thread = threading.Thread(target=consumer,
|
||||||
|
|
@ -1573,11 +1487,10 @@ class Speedtest(object):
|
||||||
start = timeit.default_timer()
|
start = timeit.default_timer()
|
||||||
prod_thread.start()
|
prod_thread.start()
|
||||||
cons_thread.start()
|
cons_thread.start()
|
||||||
_is_alive = thread_is_alive
|
while prod_thread.isAlive():
|
||||||
while _is_alive(prod_thread):
|
prod_thread.join(timeout=0.1)
|
||||||
prod_thread.join(timeout=0.001)
|
while cons_thread.isAlive():
|
||||||
while _is_alive(cons_thread):
|
cons_thread.join(timeout=0.1)
|
||||||
cons_thread.join(timeout=0.001)
|
|
||||||
|
|
||||||
stop = timeit.default_timer()
|
stop = timeit.default_timer()
|
||||||
self.results.bytes_received = sum(finished)
|
self.results.bytes_received = sum(finished)
|
||||||
|
|
@ -1588,12 +1501,8 @@ class Speedtest(object):
|
||||||
self.config['threads']['upload'] = 8
|
self.config['threads']['upload'] = 8
|
||||||
return self.results.download
|
return self.results.download
|
||||||
|
|
||||||
def upload(self, callback=do_nothing, pre_allocate=True, threads=None):
|
def upload(self, callback=do_nothing, pre_allocate=True):
|
||||||
"""Test upload speed against speedtest.net
|
"""Test upload speed against speedtest.net"""
|
||||||
|
|
||||||
A ``threads`` value of ``None`` will fall back to those dictated
|
|
||||||
by the speedtest.net configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
sizes = []
|
sizes = []
|
||||||
|
|
||||||
|
|
@ -1616,19 +1525,13 @@ class Speedtest(object):
|
||||||
)
|
)
|
||||||
if pre_allocate:
|
if pre_allocate:
|
||||||
data.pre_allocate()
|
data.pre_allocate()
|
||||||
|
|
||||||
headers = {'Content-length': size}
|
|
||||||
requests.append(
|
requests.append(
|
||||||
(
|
(
|
||||||
build_request(self.best['url'], data, secure=self._secure,
|
build_request(self.best['url'], data, secure=self._secure),
|
||||||
headers=headers),
|
|
||||||
size
|
size
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
max_threads = threads or self.config['threads']['upload']
|
|
||||||
in_flight = {'threads': 0}
|
|
||||||
|
|
||||||
def producer(q, requests, request_count):
|
def producer(q, requests, request_count):
|
||||||
for i, request in enumerate(requests[:request_count]):
|
for i, request in enumerate(requests[:request_count]):
|
||||||
thread = HTTPUploader(
|
thread = HTTPUploader(
|
||||||
|
|
@ -1640,26 +1543,21 @@ class Speedtest(object):
|
||||||
opener=self._opener,
|
opener=self._opener,
|
||||||
shutdown_event=self._shutdown_event
|
shutdown_event=self._shutdown_event
|
||||||
)
|
)
|
||||||
while in_flight['threads'] >= max_threads:
|
|
||||||
timeit.time.sleep(0.001)
|
|
||||||
thread.start()
|
thread.start()
|
||||||
q.put(thread, True)
|
q.put(thread, True)
|
||||||
in_flight['threads'] += 1
|
|
||||||
callback(i, request_count, start=True)
|
callback(i, request_count, start=True)
|
||||||
|
|
||||||
finished = []
|
finished = []
|
||||||
|
|
||||||
def consumer(q, request_count):
|
def consumer(q, request_count):
|
||||||
_is_alive = thread_is_alive
|
|
||||||
while len(finished) < request_count:
|
while len(finished) < request_count:
|
||||||
thread = q.get(True)
|
thread = q.get(True)
|
||||||
while _is_alive(thread):
|
while thread.isAlive():
|
||||||
thread.join(timeout=0.001)
|
thread.join(timeout=0.1)
|
||||||
in_flight['threads'] -= 1
|
|
||||||
finished.append(thread.result)
|
finished.append(thread.result)
|
||||||
callback(thread.i, request_count, end=True)
|
callback(thread.i, request_count, end=True)
|
||||||
|
|
||||||
q = Queue(threads or self.config['threads']['upload'])
|
q = Queue(self.config['threads']['upload'])
|
||||||
prod_thread = threading.Thread(target=producer,
|
prod_thread = threading.Thread(target=producer,
|
||||||
args=(q, requests, request_count))
|
args=(q, requests, request_count))
|
||||||
cons_thread = threading.Thread(target=consumer,
|
cons_thread = threading.Thread(target=consumer,
|
||||||
|
|
@ -1667,10 +1565,9 @@ class Speedtest(object):
|
||||||
start = timeit.default_timer()
|
start = timeit.default_timer()
|
||||||
prod_thread.start()
|
prod_thread.start()
|
||||||
cons_thread.start()
|
cons_thread.start()
|
||||||
_is_alive = thread_is_alive
|
while prod_thread.isAlive():
|
||||||
while _is_alive(prod_thread):
|
|
||||||
prod_thread.join(timeout=0.1)
|
prod_thread.join(timeout=0.1)
|
||||||
while _is_alive(cons_thread):
|
while cons_thread.isAlive():
|
||||||
cons_thread.join(timeout=0.1)
|
cons_thread.join(timeout=0.1)
|
||||||
|
|
||||||
stop = timeit.default_timer()
|
stop = timeit.default_timer()
|
||||||
|
|
@ -1695,8 +1592,7 @@ def ctrl_c(shutdown_event):
|
||||||
def version():
|
def version():
|
||||||
"""Print the version"""
|
"""Print the version"""
|
||||||
|
|
||||||
printer('speedtest-cli %s' % __version__)
|
printer(__version__)
|
||||||
printer('Python %s' % sys.version.replace('\n', ''))
|
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1729,10 +1625,6 @@ def parse_args():
|
||||||
parser.add_argument('--no-upload', dest='upload', default=True,
|
parser.add_argument('--no-upload', dest='upload', default=True,
|
||||||
action='store_const', const=False,
|
action='store_const', const=False,
|
||||||
help='Do not perform upload test')
|
help='Do not perform upload test')
|
||||||
parser.add_argument('--single', default=False, action='store_true',
|
|
||||||
help='Only use a single connection instead of '
|
|
||||||
'multiple. This simulates a typical file '
|
|
||||||
'transfer.')
|
|
||||||
parser.add_argument('--bytes', dest='units', action='store_const',
|
parser.add_argument('--bytes', dest='units', action='store_const',
|
||||||
const=('byte', 8), default=('bit', 1),
|
const=('byte', 8), default=('bit', 1),
|
||||||
help='Display values in bytes instead of bits. Does '
|
help='Display values in bytes instead of bits. Does '
|
||||||
|
|
@ -1947,10 +1839,7 @@ def shell():
|
||||||
if args.download:
|
if args.download:
|
||||||
printer('Testing download speed', quiet,
|
printer('Testing download speed', quiet,
|
||||||
end=('', '\n')[bool(debug)])
|
end=('', '\n')[bool(debug)])
|
||||||
speedtest.download(
|
speedtest.download(callback=callback)
|
||||||
callback=callback,
|
|
||||||
threads=(None, 1)[args.single]
|
|
||||||
)
|
|
||||||
printer('Download: %0.2f M%s/s' %
|
printer('Download: %0.2f M%s/s' %
|
||||||
((results.download / 1000.0 / 1000.0) / args.units[1],
|
((results.download / 1000.0 / 1000.0) / args.units[1],
|
||||||
args.units[0]),
|
args.units[0]),
|
||||||
|
|
@ -1961,11 +1850,7 @@ def shell():
|
||||||
if args.upload:
|
if args.upload:
|
||||||
printer('Testing upload speed', quiet,
|
printer('Testing upload speed', quiet,
|
||||||
end=('', '\n')[bool(debug)])
|
end=('', '\n')[bool(debug)])
|
||||||
speedtest.upload(
|
speedtest.upload(callback=callback, pre_allocate=args.pre_allocate)
|
||||||
callback=callback,
|
|
||||||
pre_allocate=args.pre_allocate,
|
|
||||||
threads=(None, 1)[args.single]
|
|
||||||
)
|
|
||||||
printer('Upload: %0.2f M%s/s' %
|
printer('Upload: %0.2f M%s/s' %
|
||||||
((results.upload / 1000.0 / 1000.0) / args.units[1],
|
((results.upload / 1000.0 / 1000.0) / args.units[1],
|
||||||
args.units[0]),
|
args.units[0]),
|
||||||
|
|
@ -2003,10 +1888,7 @@ def main():
|
||||||
e = get_exception()
|
e = get_exception()
|
||||||
# Ignore a successful exit, or argparse exit
|
# Ignore a successful exit, or argparse exit
|
||||||
if getattr(e, 'code', 1) not in (0, 2):
|
if getattr(e, 'code', 1) not in (0, 2):
|
||||||
msg = '%s' % e
|
raise SystemExit('ERROR: %s' % e)
|
||||||
if not msg:
|
|
||||||
msg = '%r' % e
|
|
||||||
raise SystemExit('ERROR: %s' % msg)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue