Compare commits
	
		
			No commits in common. "master" and "v0.3.3" have entirely different histories.
		
	
	
		
							
								
								
									
										58
									
								
								.travis.yml
								
								
								
								
							
							
						
						
									
										58
									
								
								.travis.yml
								
								
								
								
							| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
language: python
 | 
					language: python
 | 
				
			||||||
sudo: required
 | 
					
 | 
				
			||||||
dist: xenial
 | 
					python:
 | 
				
			||||||
 | 
					 - 2.7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
addons:
 | 
					addons:
 | 
				
			||||||
  apt:
 | 
					  apt:
 | 
				
			||||||
| 
						 | 
					@ -10,49 +11,22 @@ addons:
 | 
				
			||||||
      - python2.4
 | 
					      - python2.4
 | 
				
			||||||
      - python2.5
 | 
					      - python2.5
 | 
				
			||||||
      - python2.6
 | 
					      - python2.6
 | 
				
			||||||
      - python3.2
 | 
					      - pypy
 | 
				
			||||||
      - python3.3
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
matrix:
 | 
					env:
 | 
				
			||||||
  include:
 | 
					 - TOXENV=py24
 | 
				
			||||||
    - python: 2.7
 | 
					 - TOXENV=py25
 | 
				
			||||||
      env: TOXENV=flake8
 | 
					 - TOXENV=py26
 | 
				
			||||||
    - python: 2.7
 | 
					 - TOXENV=py27
 | 
				
			||||||
      env: TOXENV=py24
 | 
					 - TOXENV=py32
 | 
				
			||||||
    - python: 2.7
 | 
					 - TOXENV=py33
 | 
				
			||||||
      env: TOXENV=py25
 | 
					 - TOXENV=py34
 | 
				
			||||||
    - python: 2.7
 | 
					 - TOXENV=pypy
 | 
				
			||||||
      env: TOXENV=py26
 | 
					 - TOXENV=flake8
 | 
				
			||||||
    - 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 "py35") != 0 ]]; then pyenv global system 3.5; fi;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
install:
 | 
					install:
 | 
				
			||||||
  - if [[ $(echo "$TOXENV" | egrep -c "py32") != 0 ]]; then pip install setuptools==17.1.1; fi;
 | 
					 - 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]|py3[12])") != 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 "(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:
 | 
					script:
 | 
				
			||||||
 - tox
 | 
					 - tox
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										59
									
								
								README.rst
								
								
								
								
							
							
						
						
									
										59
									
								
								README.rst
								
								
								
								
							| 
						 | 
					@ -7,9 +7,9 @@ speedtest.net
 | 
				
			||||||
.. image:: https://img.shields.io/pypi/v/speedtest-cli.svg
 | 
					.. image:: https://img.shields.io/pypi/v/speedtest-cli.svg
 | 
				
			||||||
        :target: https://pypi.python.org/pypi/speedtest-cli/
 | 
					        :target: https://pypi.python.org/pypi/speedtest-cli/
 | 
				
			||||||
        :alt: Latest Version
 | 
					        :alt: Latest Version
 | 
				
			||||||
.. image:: https://img.shields.io/travis/sivel/speedtest-cli.svg
 | 
					.. image:: https://img.shields.io/pypi/dm/speedtest-cli.svg
 | 
				
			||||||
        :target: https://pypi.python.org/pypi/speedtest-cli/
 | 
					        :target: https://pypi.python.org/pypi/speedtest-cli/
 | 
				
			||||||
        :alt: Travis
 | 
					        :alt: Downloads
 | 
				
			||||||
.. image:: https://img.shields.io/pypi/l/speedtest-cli.svg
 | 
					.. image:: https://img.shields.io/pypi/l/speedtest-cli.svg
 | 
				
			||||||
        :target: https://pypi.python.org/pypi/speedtest-cli/
 | 
					        :target: https://pypi.python.org/pypi/speedtest-cli/
 | 
				
			||||||
        :alt: License
 | 
					        :alt: License
 | 
				
			||||||
| 
						 | 
					@ -17,7 +17,7 @@ speedtest.net
 | 
				
			||||||
Versions
 | 
					Versions
 | 
				
			||||||
--------
 | 
					--------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
speedtest-cli works with Python 2.4-3.7
 | 
					speedtest-cli works with Python 2.4-3.4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. image:: https://img.shields.io/pypi/pyversions/speedtest-cli.svg
 | 
					.. image:: https://img.shields.io/pypi/pyversions/speedtest-cli.svg
 | 
				
			||||||
        :target: https://pypi.python.org/pypi/speedtest-cli/
 | 
					        :target: https://pypi.python.org/pypi/speedtest-cli/
 | 
				
			||||||
| 
						 | 
					@ -51,22 +51,21 @@ 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)
 | 
				
			||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
					~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
				
			||||||
 | 
					
 | 
				
			||||||
::
 | 
					::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    wget -O speedtest-cli https://raw.githubusercontent.com/sivel/speedtest-cli/master/speedtest.py
 | 
					    wget -O speedtest-cli https://raw.githubusercontent.com/sivel/speedtest-cli/master/speedtest_cli.py
 | 
				
			||||||
    chmod +x speedtest-cli
 | 
					    chmod +x speedtest-cli
 | 
				
			||||||
 | 
					
 | 
				
			||||||
or
 | 
					or
 | 
				
			||||||
 | 
					
 | 
				
			||||||
::
 | 
					::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    curl -Lo speedtest-cli https://raw.githubusercontent.com/sivel/speedtest-cli/master/speedtest.py
 | 
					    curl -Lo speedtest-cli https://raw.githubusercontent.com/sivel/speedtest-cli/master/speedtest_cli.py
 | 
				
			||||||
    chmod +x speedtest-cli
 | 
					    chmod +x speedtest-cli
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Usage
 | 
					Usage
 | 
				
			||||||
| 
						 | 
					@ -75,12 +74,9 @@ Usage
 | 
				
			||||||
::
 | 
					::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    $ speedtest-cli -h
 | 
					    $ speedtest-cli -h
 | 
				
			||||||
    usage: speedtest-cli [-h] [--no-download] [--no-upload] [--single] [--bytes]
 | 
					    usage: speedtest-cli [-h] [--bytes] [--share] [--simple] [--list]
 | 
				
			||||||
                         [--share] [--simple] [--csv]
 | 
					                         [--server SERVER] [--mini MINI] [--source SOURCE]
 | 
				
			||||||
                         [--csv-delimiter CSV_DELIMITER] [--csv-header] [--json]
 | 
					                         [--timeout TIMEOUT] [--secure] [--version]
 | 
				
			||||||
                         [--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.
 | 
					    Command line interface for testing internet bandwidth using speedtest.net.
 | 
				
			||||||
    --------------------------------------------------------------------------
 | 
					    --------------------------------------------------------------------------
 | 
				
			||||||
| 
						 | 
					@ -88,50 +84,21 @@ Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    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
 | 
					      --bytes            Display values in bytes instead of bits. Does not affect
 | 
				
			||||||
      --no-upload           Do not perform upload test
 | 
					                         the image generated by --share
 | 
				
			||||||
      --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
 | 
					      --share            Generate and provide a URL to the speedtest.net share
 | 
				
			||||||
                            results image, not displayed with --csv
 | 
					                         results image
 | 
				
			||||||
      --simple           Suppress verbose output, only show basic information
 | 
					      --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
 | 
					      --list             Display a list of speedtest.net servers sorted by
 | 
				
			||||||
                         distance
 | 
					                         distance
 | 
				
			||||||
      --server SERVER       Specify a server ID to test against. Can be supplied
 | 
					      --server SERVER    Specify a server ID to test against
 | 
				
			||||||
                            multiple times
 | 
					 | 
				
			||||||
      --exclude EXCLUDE     Exclude a server from selection. Can be supplied
 | 
					 | 
				
			||||||
                            multiple times
 | 
					 | 
				
			||||||
      --mini MINI        URL of the Speedtest Mini server
 | 
					      --mini MINI        URL of the Speedtest Mini server
 | 
				
			||||||
      --source SOURCE    Source IP address to bind to
 | 
					      --source SOURCE    Source IP address to bind to
 | 
				
			||||||
      --timeout TIMEOUT  HTTP timeout in seconds. Default 10
 | 
					      --timeout TIMEOUT  HTTP timeout in seconds. Default 10
 | 
				
			||||||
      --secure           Use HTTPS instead of HTTP when communicating with
 | 
					      --secure           Use HTTPS instead of HTTP when communicating with
 | 
				
			||||||
                         speedtest.net operated servers
 | 
					                         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
 | 
					      --version          Show the version number and exit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
Python API
 | 
					 | 
				
			||||||
----------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
See the `wiki <https://github.com/sivel/speedtest-cli/wiki>`_.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Inconsistency
 | 
					Inconsistency
 | 
				
			||||||
-------------
 | 
					-------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										16
									
								
								setup.py
								
								
								
								
							
							
						
						
									
										16
									
								
								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-2014 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
 | 
				
			||||||
| 
						 | 
					@ -57,7 +57,7 @@ except:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
setup(
 | 
					setup(
 | 
				
			||||||
    name='speedtest-cli',
 | 
					    name='speedtest-cli',
 | 
				
			||||||
    version=find_version('speedtest.py'),
 | 
					    version=find_version('speedtest_cli.py'),
 | 
				
			||||||
    description=('Command line interface for testing internet bandwidth using '
 | 
					    description=('Command line interface for testing internet bandwidth using '
 | 
				
			||||||
                 'speedtest.net'),
 | 
					                 'speedtest.net'),
 | 
				
			||||||
    long_description=long_description,
 | 
					    long_description=long_description,
 | 
				
			||||||
| 
						 | 
					@ -66,11 +66,11 @@ setup(
 | 
				
			||||||
    author_email='matt@sivel.net',
 | 
					    author_email='matt@sivel.net',
 | 
				
			||||||
    url='https://github.com/sivel/speedtest-cli',
 | 
					    url='https://github.com/sivel/speedtest-cli',
 | 
				
			||||||
    license='Apache License, Version 2.0',
 | 
					    license='Apache License, Version 2.0',
 | 
				
			||||||
    py_modules=['speedtest'],
 | 
					    py_modules=['speedtest_cli'],
 | 
				
			||||||
    entry_points={
 | 
					    entry_points={
 | 
				
			||||||
        'console_scripts': [
 | 
					        'console_scripts': [
 | 
				
			||||||
            'speedtest=speedtest:main',
 | 
					            'speedtest=speedtest_cli:main',
 | 
				
			||||||
            'speedtest-cli=speedtest:main'
 | 
					            'speedtest-cli=speedtest_cli:main'
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    classifiers=[
 | 
					    classifiers=[
 | 
				
			||||||
| 
						 | 
					@ -89,11 +89,5 @@ setup(
 | 
				
			||||||
        'Programming Language :: Python :: 3.2',
 | 
					        'Programming Language :: Python :: 3.2',
 | 
				
			||||||
        'Programming Language :: Python :: 3.3',
 | 
					        'Programming Language :: Python :: 3.3',
 | 
				
			||||||
        'Programming Language :: Python :: 3.4',
 | 
					        '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',
 | 
					 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,9 @@
 | 
				
			||||||
.TH "speedtest-cli" 1 "2018-01-05" "speedtest-cli"
 | 
					.TH "speedtest-cli" 1 "2014-04-23" "speedtest-cli"
 | 
				
			||||||
.SH NAME
 | 
					.SH NAME
 | 
				
			||||||
speedtest\-cli \- Command line interface for testing internet bandwidth using speedtest.net
 | 
					speedtest\-cli \- Test your bandwidth througput using speedtest.net
 | 
				
			||||||
.SH SYNOPSIS
 | 
					.SH SYNOPSIS
 | 
				
			||||||
.B speedtest\-cli
 | 
					.B speedtest\-cli
 | 
				
			||||||
[OPTION...]
 | 
					[OPTION]...
 | 
				
			||||||
.SH DESCRIPTION
 | 
					.SH DESCRIPTION
 | 
				
			||||||
Speedtest.net is a web service for testing your broadband connection by downloading a file
 | 
					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
 | 
					from a nearby speedtest.net server on the web. This tool allows you to access the service
 | 
				
			||||||
| 
						 | 
					@ -23,29 +23,14 @@ Displays usage for the tool.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.B Options
 | 
					.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
 | 
					\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
 | 
				
			||||||
.RE
 | 
					.RE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
\fB\-\-share\fR
 | 
					\fB\-\-share\fR
 | 
				
			||||||
.RS
 | 
					.RS
 | 
				
			||||||
Generate and provide a URL to the speedtest.net share results image, not displayed with \-\-csv
 | 
					Generate and provide a URL to the speedtest.net share results image
 | 
				
			||||||
.RE
 | 
					.RE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
\fB\-\-simple\fR
 | 
					\fB\-\-simple\fR
 | 
				
			||||||
| 
						 | 
					@ -53,26 +38,6 @@ Generate and provide a URL to the speedtest.net share results image, not display
 | 
				
			||||||
Suppress verbose output, only show basic information
 | 
					Suppress verbose output, only show basic information
 | 
				
			||||||
.RE
 | 
					.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
 | 
					\fB\-\-list\fR
 | 
				
			||||||
.RS
 | 
					.RS
 | 
				
			||||||
Display a list of speedtest.net servers sorted by distance
 | 
					Display a list of speedtest.net servers sorted by distance
 | 
				
			||||||
| 
						 | 
					@ -80,12 +45,7 @@ Display a list of speedtest.net servers sorted by distance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
\fB\-\-server SERVER\fR
 | 
					\fB\-\-server SERVER\fR
 | 
				
			||||||
.RS
 | 
					.RS
 | 
				
			||||||
Specify a server ID to test against. Can be supplied multiple times
 | 
					Specify a server ID to test against
 | 
				
			||||||
.RE
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
\fB\-\-exclude EXCLUDE\fR
 | 
					 | 
				
			||||||
.RS
 | 
					 | 
				
			||||||
Exclude a server from selection. Can be supplied multiple times
 | 
					 | 
				
			||||||
.RE
 | 
					.RE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
\fB\-\-mini MINI\fR
 | 
					\fB\-\-mini MINI\fR
 | 
				
			||||||
| 
						 | 
					@ -98,21 +58,6 @@ URL of the Speedtest Mini server
 | 
				
			||||||
Source IP address to bind to
 | 
					Source IP address to bind to
 | 
				
			||||||
.RE
 | 
					.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
 | 
					\fB\-\-version\fR
 | 
				
			||||||
.RS
 | 
					.RS
 | 
				
			||||||
Show the version number and exit
 | 
					Show the version number and exit
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										2013
									
								
								speedtest.py
								
								
								
								
							
							
						
						
									
										2013
									
								
								speedtest.py
								
								
								
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
					@ -0,0 +1,798 @@
 | 
				
			||||||
 | 
					#!/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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import re
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					import math
 | 
				
			||||||
 | 
					import signal
 | 
				
			||||||
 | 
					import socket
 | 
				
			||||||
 | 
					import timeit
 | 
				
			||||||
 | 
					import platform
 | 
				
			||||||
 | 
					import threading
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__version__ = '0.3.3'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Some global variables we use
 | 
				
			||||||
 | 
					user_agent = None
 | 
				
			||||||
 | 
					source = None
 | 
				
			||||||
 | 
					shutdown_event = None
 | 
				
			||||||
 | 
					scheme = 'http'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 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 httplib import HTTPConnection, HTTPSConnection
 | 
				
			||||||
 | 
					except ImportError:
 | 
				
			||||||
 | 
					    e_http_py2 = sys.exc_info()
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        from http.client import HTTPConnection, HTTPSConnection
 | 
				
			||||||
 | 
					    except ImportError:
 | 
				
			||||||
 | 
					        e_http_py3 = sys.exc_info()
 | 
				
			||||||
 | 
					        raise SystemExit('Your python installation is missing required HTTP '
 | 
				
			||||||
 | 
					                         'client classes:\n\n'
 | 
				
			||||||
 | 
					                         'Python 2: %s\n'
 | 
				
			||||||
 | 
					                         'Python 3: %s' % (e_http_py2[1], e_http_py3[1]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SpeedtestCliServerListError(Exception):
 | 
				
			||||||
 | 
					    """Internal Exception class used to indicate to move on to the next
 | 
				
			||||||
 | 
					    URL for retrieving speedtest.net server details
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def build_user_agent():
 | 
				
			||||||
 | 
					    """Build a Mozilla/5.0 compatible User-Agent string"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    global user_agent
 | 
				
			||||||
 | 
					    if user_agent:
 | 
				
			||||||
 | 
					        return user_agent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ua_tuple = (
 | 
				
			||||||
 | 
					        'Mozilla/5.0',
 | 
				
			||||||
 | 
					        '(%s; U; %s; en-us)' % (platform.system(), platform.architecture()[0]),
 | 
				
			||||||
 | 
					        'Python/%s' % platform.python_version(),
 | 
				
			||||||
 | 
					        '(KHTML, like Gecko)',
 | 
				
			||||||
 | 
					        'speedtest-cli/%s' % __version__
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    user_agent = ' '.join(ua_tuple)
 | 
				
			||||||
 | 
					    return user_agent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def build_request(url, data=None, headers={}):
 | 
				
			||||||
 | 
					    """Build a urllib2 request object
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This function automatically adds a User-Agent header to all requests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if url[0] == ':':
 | 
				
			||||||
 | 
					        schemed_url = '%s%s' % (scheme, url)
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        schemed_url = url
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    headers['User-Agent'] = user_agent
 | 
				
			||||||
 | 
					    return Request(schemed_url, data=data, headers=headers)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def catch_request(request):
 | 
				
			||||||
 | 
					    """Helper function to catch common exceptions encountered when
 | 
				
			||||||
 | 
					    establishing a connection with a HTTP/HTTPS request
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        uh = urlopen(request)
 | 
				
			||||||
 | 
					        return uh, False
 | 
				
			||||||
 | 
					    except (HTTPError, URLError, socket.error):
 | 
				
			||||||
 | 
					        e = sys.exc_info()[1]
 | 
				
			||||||
 | 
					        return None, e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 (timeit.default_timer() - self.starttime) <= 10:
 | 
				
			||||||
 | 
					                request = build_request(self.url)
 | 
				
			||||||
 | 
					                f = urlopen(request)
 | 
				
			||||||
 | 
					                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 = timeit.default_timer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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 = timeit.default_timer()
 | 
				
			||||||
 | 
					    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) / (timeit.default_timer() - 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 ((timeit.default_timer() - self.starttime) <= 10 and
 | 
				
			||||||
 | 
					                    not shutdown_event.isSet()):
 | 
				
			||||||
 | 
					                request = build_request(self.url, data=self.data)
 | 
				
			||||||
 | 
					                f = urlopen(request)
 | 
				
			||||||
 | 
					                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 = timeit.default_timer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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 = timeit.default_timer()
 | 
				
			||||||
 | 
					    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) / (timeit.default_timer() - 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
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    request = build_request('://www.speedtest.net/speedtest-config.php')
 | 
				
			||||||
 | 
					    uh, e = catch_request(request)
 | 
				
			||||||
 | 
					    if e:
 | 
				
			||||||
 | 
					        print_('Could not retrieve speedtest.net configuration: %s' % e)
 | 
				
			||||||
 | 
					        sys.exit(1)
 | 
				
			||||||
 | 
					    configxml = []
 | 
				
			||||||
 | 
					    while 1:
 | 
				
			||||||
 | 
					        configxml.append(uh.read(10240))
 | 
				
			||||||
 | 
					        if len(configxml[-1]) == 0:
 | 
				
			||||||
 | 
					            break
 | 
				
			||||||
 | 
					    if int(uh.code) != 200:
 | 
				
			||||||
 | 
					        return None
 | 
				
			||||||
 | 
					    uh.close()
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        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:  # Python3 branch
 | 
				
			||||||
 | 
					            root = DOM.parseString(''.join(configxml))
 | 
				
			||||||
 | 
					            config = {
 | 
				
			||||||
 | 
					                'client': getAttributesByTagName(root, 'client'),
 | 
				
			||||||
 | 
					                'times': getAttributesByTagName(root, 'times'),
 | 
				
			||||||
 | 
					                'download': getAttributesByTagName(root, 'download'),
 | 
				
			||||||
 | 
					                'upload': getAttributesByTagName(root, 'upload')}
 | 
				
			||||||
 | 
					    except SyntaxError:
 | 
				
			||||||
 | 
					        print_('Failed to parse speedtest.net configuration')
 | 
				
			||||||
 | 
					        sys.exit(1)
 | 
				
			||||||
 | 
					    del root
 | 
				
			||||||
 | 
					    del configxml
 | 
				
			||||||
 | 
					    return config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def closestServers(client, all=False):
 | 
				
			||||||
 | 
					    """Determine the 5 closest speedtest.net servers based on geographic
 | 
				
			||||||
 | 
					    distance
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    urls = [
 | 
				
			||||||
 | 
					        '://www.speedtest.net/speedtest-servers-static.php',
 | 
				
			||||||
 | 
					        '://c.speedtest.net/speedtest-servers-static.php',
 | 
				
			||||||
 | 
					        '://www.speedtest.net/speedtest-servers.php',
 | 
				
			||||||
 | 
					        '://c.speedtest.net/speedtest-servers.php',
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					    errors = []
 | 
				
			||||||
 | 
					    servers = {}
 | 
				
			||||||
 | 
					    for url in urls:
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            request = build_request(url)
 | 
				
			||||||
 | 
					            uh, e = catch_request(request)
 | 
				
			||||||
 | 
					            if e:
 | 
				
			||||||
 | 
					                errors.append('%s' % e)
 | 
				
			||||||
 | 
					                raise SpeedtestCliServerListError
 | 
				
			||||||
 | 
					            serversxml = []
 | 
				
			||||||
 | 
					            while 1:
 | 
				
			||||||
 | 
					                serversxml.append(uh.read(10240))
 | 
				
			||||||
 | 
					                if len(serversxml[-1]) == 0:
 | 
				
			||||||
 | 
					                    break
 | 
				
			||||||
 | 
					            if int(uh.code) != 200:
 | 
				
			||||||
 | 
					                uh.close()
 | 
				
			||||||
 | 
					                raise SpeedtestCliServerListError
 | 
				
			||||||
 | 
					            uh.close()
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                try:
 | 
				
			||||||
 | 
					                    root = ET.fromstring(''.encode().join(serversxml))
 | 
				
			||||||
 | 
					                    elements = root.getiterator('server')
 | 
				
			||||||
 | 
					                except AttributeError:  # Python3 branch
 | 
				
			||||||
 | 
					                    root = DOM.parseString(''.join(serversxml))
 | 
				
			||||||
 | 
					                    elements = root.getElementsByTagName('server')
 | 
				
			||||||
 | 
					            except SyntaxError:
 | 
				
			||||||
 | 
					                raise SpeedtestCliServerListError
 | 
				
			||||||
 | 
					            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
 | 
				
			||||||
 | 
					        except SpeedtestCliServerListError:
 | 
				
			||||||
 | 
					            continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # We were able to fetch and parse the list of speedtest.net servers
 | 
				
			||||||
 | 
					        if servers:
 | 
				
			||||||
 | 
					            break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if not servers:
 | 
				
			||||||
 | 
					        print_('Failed to retrieve list of speedtest.net servers:\n\n %s' %
 | 
				
			||||||
 | 
					               '\n'.join(errors))
 | 
				
			||||||
 | 
					        sys.exit(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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 latency request to determine which
 | 
				
			||||||
 | 
					    speedtest.net server has the lowest latency
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    results = {}
 | 
				
			||||||
 | 
					    for server in servers:
 | 
				
			||||||
 | 
					        cum = []
 | 
				
			||||||
 | 
					        url = '%s/latency.txt' % os.path.dirname(server['url'])
 | 
				
			||||||
 | 
					        urlparts = urlparse(url)
 | 
				
			||||||
 | 
					        for i in range(0, 3):
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                if urlparts[0] == 'https':
 | 
				
			||||||
 | 
					                    h = HTTPSConnection(urlparts[1])
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    h = HTTPConnection(urlparts[1])
 | 
				
			||||||
 | 
					                headers = {'User-Agent': user_agent}
 | 
				
			||||||
 | 
					                start = timeit.default_timer()
 | 
				
			||||||
 | 
					                h.request("GET", urlparts[2], headers=headers)
 | 
				
			||||||
 | 
					                r = h.getresponse()
 | 
				
			||||||
 | 
					                total = (timeit.default_timer() - start)
 | 
				
			||||||
 | 
					            except (HTTPError, URLError, socket.error):
 | 
				
			||||||
 | 
					                cum.append(3600)
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					            text = r.read(9)
 | 
				
			||||||
 | 
					            if int(r.status) == 200 and text == 'test=test'.encode():
 | 
				
			||||||
 | 
					                cum.append(total)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                cum.append(3600)
 | 
				
			||||||
 | 
					            h.close()
 | 
				
			||||||
 | 
					        avg = round((sum(cum) / 6) * 1000, 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, scheme
 | 
				
			||||||
 | 
					    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=('byte', 1), default=('bit', 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('--timeout', default=10, type=int,
 | 
				
			||||||
 | 
					                        help='HTTP timeout in seconds. Default 10')
 | 
				
			||||||
 | 
					    parser.add_argument('--secure', action='store_true',
 | 
				
			||||||
 | 
					                        help='Use HTTPS instead of HTTP when communicating '
 | 
				
			||||||
 | 
					                             'with speedtest.net operated servers')
 | 
				
			||||||
 | 
					    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()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    socket.setdefaulttimeout(args.timeout)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Pre-cache the user agent string
 | 
				
			||||||
 | 
					    build_user_agent()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # If specified bind to a specific IP address
 | 
				
			||||||
 | 
					    if args.source:
 | 
				
			||||||
 | 
					        source = args.source
 | 
				
			||||||
 | 
					        socket.socket = bound_socket
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if args.secure:
 | 
				
			||||||
 | 
					        scheme = 'https'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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)
 | 
				
			||||||
 | 
					            print_('\n'.join(serverList).encode('utf-8', 'ignore'))
 | 
				
			||||||
 | 
					            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:
 | 
				
			||||||
 | 
					            request = build_request(args.mini)
 | 
				
			||||||
 | 
					            f = urlopen(request)
 | 
				
			||||||
 | 
					        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:
 | 
				
			||||||
 | 
					                    request = build_request('%s/speedtest/upload.%s' %
 | 
				
			||||||
 | 
					                                            (args.mini, ext))
 | 
				
			||||||
 | 
					                    f = urlopen(request)
 | 
				
			||||||
 | 
					                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 latency...')
 | 
				
			||||||
 | 
					        best = getBestServer(servers)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if not args.simple:
 | 
				
			||||||
 | 
					        print_(('Hosted by %(sponsor)s (%(name)s) [%(d)0.2f km]: '
 | 
				
			||||||
 | 
					               '%(latency)s ms' % best).encode('utf-8', 'ignore'))
 | 
				
			||||||
 | 
					    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()]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        headers = {'Referer': 'http://c.speedtest.net/flash/speedtest.swf'}
 | 
				
			||||||
 | 
					        request = build_request('://www.speedtest.net/api/api.php',
 | 
				
			||||||
 | 
					                                data='&'.join(apiData).encode(),
 | 
				
			||||||
 | 
					                                headers=headers)
 | 
				
			||||||
 | 
					        f, e = catch_request(request)
 | 
				
			||||||
 | 
					        if e:
 | 
				
			||||||
 | 
					            print_('Could not submit results to speedtest.net: %s' % e)
 | 
				
			||||||
 | 
					            sys.exit(1)
 | 
				
			||||||
 | 
					        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: %s://www.speedtest.net/result/%s.png' %
 | 
				
			||||||
 | 
					               (scheme, resultid[0]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def main():
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        speedtest()
 | 
				
			||||||
 | 
					    except KeyboardInterrupt:
 | 
				
			||||||
 | 
					        print_('\nCancelling...')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == '__main__':
 | 
				
			||||||
 | 
					    main()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# vim:ts=4:sw=4:expandtab
 | 
				
			||||||
| 
						 | 
					@ -1,37 +0,0 @@
 | 
				
			||||||
#!/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()
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
							
								
								
									
										14
									
								
								tox.ini
								
								
								
								
							
							
						
						
									
										14
									
								
								tox.ini
								
								
								
								
							| 
						 | 
					@ -4,22 +4,18 @@ skipsdist=true
 | 
				
			||||||
[testenv]
 | 
					[testenv]
 | 
				
			||||||
commands =
 | 
					commands =
 | 
				
			||||||
    {envpython} -V
 | 
					    {envpython} -V
 | 
				
			||||||
    {envpython} -m compileall speedtest.py
 | 
					    {envpython} -m compileall speedtest_cli.py
 | 
				
			||||||
    {envpython} speedtest.py
 | 
					    {envpython} speedtest_cli.py
 | 
				
			||||||
    {envpython} speedtest.py --source 172.17.0.1
 | 
					 | 
				
			||||||
    {envpython} tests/scripts/source.py
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
[testenv:flake8]
 | 
					[testenv:flake8]
 | 
				
			||||||
basepython=python
 | 
					basepython=python
 | 
				
			||||||
deps=flake8
 | 
					deps=flake8
 | 
				
			||||||
commands =
 | 
					commands =
 | 
				
			||||||
    {envpython} -V
 | 
					    {envpython} -V
 | 
				
			||||||
    flake8 speedtest.py
 | 
					    flake8 speedtest_cli.py
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[testenv:pypy]
 | 
					[testenv:pypy]
 | 
				
			||||||
commands =
 | 
					commands =
 | 
				
			||||||
    pypy -V
 | 
					    pypy -V
 | 
				
			||||||
    pypy -m compileall speedtest.py
 | 
					    pypy -m compileall speedtest_cli.py
 | 
				
			||||||
    pypy speedtest.py
 | 
					    pypy speedtest_cli.py
 | 
				
			||||||
    pypy speedtest.py --source 172.17.0.1
 | 
					 | 
				
			||||||
    pypy tests/scripts/source.py
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue