Compare commits
No commits in common. "master" and "v2.1.0" have entirely different histories.
19
.travis.yml
19
.travis.yml
|
|
@ -1,14 +1,12 @@
|
|||
language: python
|
||||
sudo: required
|
||||
dist: xenial
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- deadsnakes
|
||||
packages:
|
||||
- python2.4
|
||||
- python2.5
|
||||
# - python2.4
|
||||
# - python2.5
|
||||
- python2.6
|
||||
- python3.2
|
||||
- python3.3
|
||||
|
|
@ -17,10 +15,10 @@ 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=py24
|
||||
# - python: 2.7
|
||||
# env: TOXENV=py25
|
||||
- python: 2.7
|
||||
env: TOXENV=py26
|
||||
- python: 2.7
|
||||
|
|
@ -35,13 +33,8 @@ matrix:
|
|||
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 "py35") != 0 ]]; then pyenv global system 3.5; fi;
|
||||
|
|
|
|||
3
setup.py
3
setup.py
|
|
@ -92,8 +92,5 @@ 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',
|
||||
]
|
||||
)
|
||||
|
|
|
|||
168
speedtest.py
168
speedtest.py
|
|
@ -15,18 +15,18 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import csv
|
||||
import datetime
|
||||
import errno
|
||||
import math
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import csv
|
||||
import sys
|
||||
import math
|
||||
import errno
|
||||
import signal
|
||||
import socket
|
||||
import sys
|
||||
import threading
|
||||
import timeit
|
||||
import datetime
|
||||
import platform
|
||||
import threading
|
||||
import xml.parsers.expat
|
||||
|
||||
try:
|
||||
|
|
@ -36,7 +36,7 @@ except ImportError:
|
|||
gzip = None
|
||||
GZIP_BASE = object
|
||||
|
||||
__version__ = '2.1.4b1'
|
||||
__version__ = '2.1.0'
|
||||
|
||||
|
||||
class FakeShutdownEvent(object):
|
||||
|
|
@ -49,16 +49,10 @@ class FakeShutdownEvent(object):
|
|||
"Dummy method to always return false"""
|
||||
return False
|
||||
|
||||
is_set = isSet
|
||||
|
||||
|
||||
# Some global variables we use
|
||||
DEBUG = False
|
||||
_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
|
||||
try:
|
||||
|
|
@ -70,15 +64,14 @@ except ImportError:
|
|||
json = None
|
||||
|
||||
try:
|
||||
import xml.etree.ElementTree as ET
|
||||
try:
|
||||
from xml.etree.ElementTree import _Element as ET_Element
|
||||
except ImportError:
|
||||
pass
|
||||
import xml.etree.cElementTree as ET
|
||||
except ImportError:
|
||||
from xml.dom import minidom as DOM
|
||||
from xml.parsers.expat import ExpatError
|
||||
ET = None
|
||||
try:
|
||||
import xml.etree.ElementTree as ET
|
||||
except ImportError:
|
||||
from xml.dom import minidom as DOM
|
||||
from xml.parsers.expat import ExpatError
|
||||
ET = None
|
||||
|
||||
try:
|
||||
from urllib2 import (urlopen, Request, HTTPError, URLError,
|
||||
|
|
@ -104,11 +97,6 @@ except ImportError:
|
|||
except ImportError:
|
||||
HTTPSConnection = None
|
||||
|
||||
try:
|
||||
from httplib import FakeSocket
|
||||
except ImportError:
|
||||
FakeSocket = None
|
||||
|
||||
try:
|
||||
from Queue import Queue
|
||||
except ImportError:
|
||||
|
|
@ -269,6 +257,7 @@ else:
|
|||
write(arg)
|
||||
write(end)
|
||||
|
||||
|
||||
# Exception "constants" to support Python 2 through Python 3
|
||||
try:
|
||||
import ssl
|
||||
|
|
@ -285,23 +274,6 @@ 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"""
|
||||
|
|
@ -422,8 +394,6 @@ 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
|
||||
|
|
@ -444,23 +414,17 @@ class SpeedtestHTTPConnection(HTTPConnection):
|
|||
self.source_address
|
||||
)
|
||||
|
||||
if self._tunnel_host:
|
||||
self._tunnel()
|
||||
|
||||
|
||||
if HTTPSConnection:
|
||||
class SpeedtestHTTPSConnection(HTTPSConnection):
|
||||
class SpeedtestHTTPSConnection(HTTPSConnection,
|
||||
SpeedtestHTTPConnection):
|
||||
"""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
|
||||
|
|
@ -468,30 +432,14 @@ 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
|
||||
)
|
||||
|
||||
if self._tunnel_host:
|
||||
self._tunnel()
|
||||
SpeedtestHTTPConnection.connect(self)
|
||||
|
||||
if ssl:
|
||||
try:
|
||||
kwargs = {}
|
||||
if hasattr(ssl, 'SSLContext'):
|
||||
if self._tunnel_host:
|
||||
kwargs['server_hostname'] = self._tunnel_host
|
||||
else:
|
||||
kwargs['server_hostname'] = self.host
|
||||
kwargs['server_hostname'] = self.host
|
||||
self.sock = self._context.wrap_socket(self.sock, **kwargs)
|
||||
except AttributeError:
|
||||
self.sock = ssl.wrap_socket(self.sock)
|
||||
|
|
@ -499,20 +447,6 @@ if HTTPSConnection:
|
|||
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):
|
||||
|
|
@ -778,7 +712,7 @@ def print_dots(shutdown_event):
|
|||
status
|
||||
"""
|
||||
def inner(current, total, start=False, end=False):
|
||||
if event_is_set(shutdown_event):
|
||||
if shutdown_event.isSet():
|
||||
return
|
||||
|
||||
sys.stdout.write('.')
|
||||
|
|
@ -817,7 +751,7 @@ class HTTPDownloader(threading.Thread):
|
|||
try:
|
||||
if (timeit.default_timer() - self.starttime) <= self.timeout:
|
||||
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) <=
|
||||
self.timeout):
|
||||
self.result.append(len(f.read(10240)))
|
||||
|
|
@ -826,8 +760,6 @@ class HTTPDownloader(threading.Thread):
|
|||
f.close()
|
||||
except IOError:
|
||||
pass
|
||||
except HTTP_ERRORS:
|
||||
pass
|
||||
|
||||
|
||||
class HTTPUploaderData(object):
|
||||
|
|
@ -873,7 +805,7 @@ class HTTPUploaderData(object):
|
|||
|
||||
def read(self, n=10240):
|
||||
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)
|
||||
self.total.append(len(chunk))
|
||||
return chunk
|
||||
|
|
@ -893,7 +825,7 @@ class HTTPUploader(threading.Thread):
|
|||
self.request = request
|
||||
self.request.data.start = self.starttime = start
|
||||
self.size = size
|
||||
self.result = 0
|
||||
self.result = None
|
||||
self.timeout = timeout
|
||||
self.i = i
|
||||
|
||||
|
|
@ -911,7 +843,7 @@ class HTTPUploader(threading.Thread):
|
|||
request = self.request
|
||||
try:
|
||||
if ((timeit.default_timer() - self.starttime) <= self.timeout and
|
||||
not event_is_set(self._shutdown_event)):
|
||||
not self._shutdown_event.isSet()):
|
||||
try:
|
||||
f = self._opener(request)
|
||||
except TypeError:
|
||||
|
|
@ -928,8 +860,6 @@ 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):
|
||||
|
|
@ -1183,9 +1113,9 @@ class Speedtest(object):
|
|||
# times = get_attributes_by_tag_name(root, 'times')
|
||||
client = get_attributes_by_tag_name(root, 'client')
|
||||
|
||||
ignore_servers = [
|
||||
int(i) for i in server_config['ignoreids'].split(',') if i
|
||||
]
|
||||
ignore_servers = list(
|
||||
map(int, server_config['ignoreids'].split(','))
|
||||
)
|
||||
|
||||
ratio = int(upload['ratio'])
|
||||
upload_max = int(upload['maxchunkcount'])
|
||||
|
|
@ -1313,7 +1243,7 @@ class Speedtest(object):
|
|||
raise SpeedtestServersError(
|
||||
'Malformed speedtest.net server list: %s' % e
|
||||
)
|
||||
elements = etree_iter(root, 'server')
|
||||
elements = root.getiterator('server')
|
||||
except AttributeError:
|
||||
try:
|
||||
root = DOM.parseString(serversxml)
|
||||
|
|
@ -1533,9 +1463,6 @@ class Speedtest(object):
|
|||
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):
|
||||
for i, request in enumerate(requests):
|
||||
thread = HTTPDownloader(
|
||||
|
|
@ -1546,26 +1473,21 @@ class Speedtest(object):
|
|||
opener=self._opener,
|
||||
shutdown_event=self._shutdown_event
|
||||
)
|
||||
while in_flight['threads'] >= max_threads:
|
||||
timeit.time.sleep(0.001)
|
||||
thread.start()
|
||||
q.put(thread, True)
|
||||
in_flight['threads'] += 1
|
||||
callback(i, request_count, start=True)
|
||||
|
||||
finished = []
|
||||
|
||||
def consumer(q, request_count):
|
||||
_is_alive = thread_is_alive
|
||||
while len(finished) < request_count:
|
||||
thread = q.get(True)
|
||||
while _is_alive(thread):
|
||||
thread.join(timeout=0.001)
|
||||
in_flight['threads'] -= 1
|
||||
while thread.isAlive():
|
||||
thread.join(timeout=0.1)
|
||||
finished.append(sum(thread.result))
|
||||
callback(thread.i, request_count, end=True)
|
||||
|
||||
q = Queue(max_threads)
|
||||
q = Queue(threads or self.config['threads']['download'])
|
||||
prod_thread = threading.Thread(target=producer,
|
||||
args=(q, requests, request_count))
|
||||
cons_thread = threading.Thread(target=consumer,
|
||||
|
|
@ -1573,11 +1495,10 @@ class Speedtest(object):
|
|||
start = timeit.default_timer()
|
||||
prod_thread.start()
|
||||
cons_thread.start()
|
||||
_is_alive = thread_is_alive
|
||||
while _is_alive(prod_thread):
|
||||
prod_thread.join(timeout=0.001)
|
||||
while _is_alive(cons_thread):
|
||||
cons_thread.join(timeout=0.001)
|
||||
while prod_thread.isAlive():
|
||||
prod_thread.join(timeout=0.1)
|
||||
while cons_thread.isAlive():
|
||||
cons_thread.join(timeout=0.1)
|
||||
|
||||
stop = timeit.default_timer()
|
||||
self.results.bytes_received = sum(finished)
|
||||
|
|
@ -1626,9 +1547,6 @@ class Speedtest(object):
|
|||
)
|
||||
)
|
||||
|
||||
max_threads = threads or self.config['threads']['upload']
|
||||
in_flight = {'threads': 0}
|
||||
|
||||
def producer(q, requests, request_count):
|
||||
for i, request in enumerate(requests[:request_count]):
|
||||
thread = HTTPUploader(
|
||||
|
|
@ -1640,22 +1558,17 @@ class Speedtest(object):
|
|||
opener=self._opener,
|
||||
shutdown_event=self._shutdown_event
|
||||
)
|
||||
while in_flight['threads'] >= max_threads:
|
||||
timeit.time.sleep(0.001)
|
||||
thread.start()
|
||||
q.put(thread, True)
|
||||
in_flight['threads'] += 1
|
||||
callback(i, request_count, start=True)
|
||||
|
||||
finished = []
|
||||
|
||||
def consumer(q, request_count):
|
||||
_is_alive = thread_is_alive
|
||||
while len(finished) < request_count:
|
||||
thread = q.get(True)
|
||||
while _is_alive(thread):
|
||||
thread.join(timeout=0.001)
|
||||
in_flight['threads'] -= 1
|
||||
while thread.isAlive():
|
||||
thread.join(timeout=0.1)
|
||||
finished.append(thread.result)
|
||||
callback(thread.i, request_count, end=True)
|
||||
|
||||
|
|
@ -1667,10 +1580,9 @@ class Speedtest(object):
|
|||
start = timeit.default_timer()
|
||||
prod_thread.start()
|
||||
cons_thread.start()
|
||||
_is_alive = thread_is_alive
|
||||
while _is_alive(prod_thread):
|
||||
while prod_thread.isAlive():
|
||||
prod_thread.join(timeout=0.1)
|
||||
while _is_alive(cons_thread):
|
||||
while cons_thread.isAlive():
|
||||
cons_thread.join(timeout=0.1)
|
||||
|
||||
stop = timeit.default_timer()
|
||||
|
|
|
|||
Loading…
Reference in New Issue