Compare commits

..

No commits in common. "master" and "v2.1.0" have entirely different histories.

3 changed files with 46 additions and 144 deletions

View File

@ -1,14 +1,12 @@
language: python language: python
sudo: required
dist: xenial
addons: addons:
apt: apt:
sources: sources:
- deadsnakes - deadsnakes
packages: packages:
- python2.4 # - python2.4
- python2.5 # - python2.5
- python2.6 - python2.6
- python3.2 - python3.2
- python3.3 - python3.3
@ -17,10 +15,10 @@ matrix:
include: include:
- python: 2.7 - python: 2.7
env: TOXENV=flake8 env: TOXENV=flake8
- python: 2.7 # - python: 2.7
env: TOXENV=py24 # env: TOXENV=py24
- python: 2.7 # - python: 2.7
env: TOXENV=py25 # env: TOXENV=py25
- python: 2.7 - python: 2.7
env: TOXENV=py26 env: TOXENV=py26
- python: 2.7 - python: 2.7
@ -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;

View File

@ -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',
] ]
) )

View File

@ -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.1.0'
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,11 +64,10 @@ 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: except ImportError:
pass try:
import xml.etree.ElementTree as ET
except ImportError: except ImportError:
from xml.dom import minidom as DOM from xml.dom import minidom as DOM
from xml.parsers.expat import ExpatError from xml.parsers.expat import ExpatError
@ -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,29 +432,13 @@ 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()
if ssl: if ssl:
try: try:
kwargs = {} kwargs = {}
if hasattr(ssl, 'SSLContext'): 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) self.sock = self._context.wrap_socket(self.sock, **kwargs)
except AttributeError: except AttributeError:
@ -499,20 +447,6 @@ if HTTPSConnection:
self.sock.server_hostname = self.host self.sock.server_hostname = self.host
except AttributeError: except AttributeError:
pass 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):
@ -778,7 +712,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 +751,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 +760,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 +805,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 +825,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 +843,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 +860,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):
@ -1183,9 +1113,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 +1243,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)
@ -1533,9 +1463,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 +1473,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(threads or 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 +1495,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)
@ -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): 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,22 +1558,17 @@ 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)
@ -1667,10 +1580,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()