Python speedtest-cli库源码分析(第二篇:导入模块)

(备注:本文代码基于:speedtest-cli 2.1.2)

     在代码中使用speedtest-cli库,只需要在模块文件中导入speedtest模块即可

import speedtest

    1、导入speedtest模块后,变量名speedtest将指向一个模块对象

    2、导入sppedtest模块后,通过变量名speedtest即可访问此speedtest模块对象所有公开的属性

    3、导入sppedtest模块后,speedtest模块顶层的语句会执行

    接下来我们学习,在speedtest模块的顶层执行了哪些代码?

speedtest模块总览(不是总揽)

    speedtest.py为一个模块文件,作者没有使用Python包,这个模块文件有2001行,我们分析一下在sppedtest模块下创建了哪些成员……截图太长,我先把speedtest模块的统计结果放在前面……

全局变量:6个

函数(顶层):22个

类(顶层):14个

PS:顶层表示在Python模块中没有缩进的代码,不包含那些在内部定义的类、类方法、静态方法、函数

 

 

speedtest模块顶层执行代码分析

    我们一起分析speedtest模块顶层执行的代码,前辈提出分析代码时,要挑树干去分析,可我总想对着树叶分析,别急,慢慢会分析每个功能点是怎么开发出来的,这篇只是先大概了解一下speedtest模块中的顶层代码的设计,我们开始吧……

1、speedtest模块中直接导入的模块

import os
import re
import csv
import sys
import math
import errno
import signal
import socket
import timeit
import datetime
import platform
import threading
import xml.parsers.expat

import语句会导入模块对象

os 这个模块与操作系统相关,标准库

re 正则表达式模块,标准库

csv 处于处理csv文件的模块,标准库

sys 与Python解释器操作相关的模块,标准库

math 与数学计算相关的模块,标准库

errno 目前不知道干啥的

……剩下的导入语句暂且不表……

 

2、尝试导入gzip

try:
    import gzip
    GZIP_BASE = gzip.GzipFile
except ImportError:
    gzip = None
    GZIP_BASE = object

gzip 该模块用于压缩,这对导入失败产生ImportError的情况,这里做了容错处理

 

3、尝试导入json

# Begin import game to handle Python 2 and Python 3
try:
    import json
except ImportError:
    try:
        import simplejson as json
    except ImportError:
        json = None

为了兼容Python2和Python3,作者下了功夫,厉害

 

4、尝试导入xml

try:
    import xml.etree.ElementTree as ET
    try:
        from xml.etree.ElementTree import _Element as ET_Element
    except ImportError:
        pass
except ImportError:
    from xml.dom import minidom as DOM
    from xml.parsers.expat import ExpatError
    ET = None

同样为了兼容性

 

5、尝试导入urllib2

try:
    from urllib2 import (urlopen, Request, HTTPError, URLError,
                         AbstractHTTPHandler, ProxyHandler,
                         HTTPDefaultErrorHandler, HTTPRedirectHandler,
                         HTTPErrorProcessor, OpenerDirector)
except ImportError:
    from urllib.request import (urlopen, Request, HTTPError, URLError,
                                AbstractHTTPHandler, ProxyHandler,
                                HTTPDefaultErrorHandler, HTTPRedirectHandler,
                                HTTPErrorProcessor, OpenerDirector)

同样为了兼容性

 

6、尝试导入httplib中的http

try:
    from httplib import HTTPConnection, BadStatusLine
except ImportError:
    from http.client import HTTPConnection, BadStatusLine

 

7、尝试导入httplib中的https

try:
    from httplib import HTTPSConnection
except ImportError:
    try:
        from http.client import HTTPSConnection
    except ImportError:
        HTTPSConnection = None

 

8、尝试导入httplib中的FakeSocket

try:
    from httplib import FakeSocket
except ImportError:
    FakeSocket = None

 

9、尝试导入队列

try:
    from Queue import Queue
except ImportError:
    from queue import Queue

 

10、尝试导入urlparse

try:
    from urlparse import urlparse
except ImportError:
    from urllib.parse import urlparse

 

11、尝试导入urlparse中的parse_qs

try:
    from urlparse import parse_qs
except ImportError:
    try:
        from urllib.parse import parse_qs
    except ImportError:
        from cgi import parse_qs

 

12、尝试导入hashlib模块的md5属性

try:
    from hashlib import md5
except ImportError:
    from md5 import md5

 

13、尝试导入

try:
    from argparse import ArgumentParser as ArgParser
    from argparse import SUPPRESS as ARG_SUPPRESS
    PARSER_TYPE_INT = int
    PARSER_TYPE_STR = str
    PARSER_TYPE_FLOAT = float
except ImportError:
    from optparse import OptionParser as ArgParser
    from optparse import SUPPRESS_HELP as ARG_SUPPRESS
    PARSER_TYPE_INT = 'int'
    PARSER_TYPE_STR = 'string'
    PARSER_TYPE_FLOAT = 'float'

 

14、尝试导入StringIO

try:
    from cStringIO import StringIO
    BytesIO = None
except ImportError:
    try:
        from StringIO import StringIO
        BytesIO = None
    except ImportError:
        from io import StringIO, BytesIO

 

15、尝试导入__builtin__

try:
    import __builtin__
except ImportError:
    import builtins
    from io import TextIOWrapper, FileIO

    class _Py3Utf8Output(TextIOWrapper):
        """UTF-8 encoded wrapper around stdout for py3, to override
        ASCII stdout
        """
        def __init__(self, f, **kwargs):
            buf = FileIO(f.fileno(), 'w')
            super(_Py3Utf8Output, self).__init__(
                buf,
                encoding='utf8',
                errors='strict'
            )

        def write(self, s):
            super(_Py3Utf8Output, self).write(s)
            self.flush()

    _py3_print = getattr(builtins, 'print')
    try:
        _py3_utf8_stdout = _Py3Utf8Output(sys.stdout)
        _py3_utf8_stderr = _Py3Utf8Output(sys.stderr)
    except OSError:
        # sys.stdout/sys.stderr is not a compatible stdout/stderr object
        # just use it and hope things go ok
        _py3_utf8_stdout = sys.stdout
        _py3_utf8_stderr = sys.stderr

    def to_utf8(v):
        """No-op encode to utf-8 for py3"""
        return v

    def print_(*args, **kwargs):
        """Wrapper function for py3 to print, with a utf-8 encoded stdout"""
        if kwargs.get('file') == sys.stderr:
            kwargs['file'] = _py3_utf8_stderr
        else:
            kwargs['file'] = kwargs.get('file', _py3_utf8_stdout)
        _py3_print(*args, **kwargs)
else:
    del __builtin__

    def to_utf8(v):
        """Encode value to utf-8 if possible for py2"""
        try:
            return v.encode('utf8', 'strict')
        except AttributeError:
            return v

    def print_(*args, **kwargs):
        """The new-style print function for Python 2.4 and 2.5.

        Taken from https://pypi.python.org/pypi/six/

        Modified to set encoding to UTF-8 always, and to flush after write
        """
        fp = kwargs.pop("file", sys.stdout)
        if fp is None:
            return

        def write(data):
            if not isinstance(data, basestring):
                data = str(data)
            # If the file has an encoding, encode unicode with it.
            encoding = 'utf8'  # Always trust UTF-8 for output
            if (isinstance(fp, file) and
                    isinstance(data, unicode) and
                    encoding is not None):
                errors = getattr(fp, "errors", None)
                if errors is None:
                    errors = "strict"
                data = data.encode(encoding, errors)
            fp.write(data)
            fp.flush()
        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)

这一段真牛逼……

 

16、尝试导入ssl

# Exception "constants" to support Python 2 through Python 3
try:
    import ssl
    try:
        CERT_ERROR = (ssl.CertificateError,)
    except AttributeError:
        CERT_ERROR = tuple()

    HTTP_ERRORS = (
        (HTTPError, URLError, socket.error, ssl.SSLError, BadStatusLine) +
        CERT_ERROR
    )
except ImportError:
    ssl = None
    HTTP_ERRORS = (HTTPError, URLError, socket.error, BadStatusLine)

 

17、全局变量分支

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

 

18、各种自定义异常类对象的创建

class SpeedtestException(Exception):
    """Base exception for this module"""

class SpeedtestCLIError(SpeedtestException):
    """Generic exception for raising errors during CLI operation"""

class SpeedtestHTTPError(SpeedtestException):
    """Base HTTP exception for this module"""

class SpeedtestConfigError(SpeedtestException):
    """Configuration XML is invalid"""

class SpeedtestServersError(SpeedtestException):
    """Servers XML is invalid"""

class ConfigRetrievalError(SpeedtestHTTPError):
    """Could not retrieve config.php"""

class ServersRetrievalError(SpeedtestHTTPError):
    """Could not retrieve speedtest-servers.php"""

class InvalidServerIDType(SpeedtestException):
    """Server ID used for filtering was not an integer"""

class NoMatchedServers(SpeedtestException):
    """No servers matched when filtering"""

class SpeedtestMiniConnectFailure(SpeedtestException):
    """Could not connect to the provided speedtest mini server"""

class InvalidSpeedtestMiniServer(SpeedtestException):
    """Server provided as a speedtest mini server does not actually appear
    to be a speedtest mini server
    """

class ShareResultsConnectFailure(SpeedtestException):
    """Could not connect to speedtest.net API to POST results"""

class ShareResultsSubmitFailure(SpeedtestException):
    """Unable to successfully POST results to speedtest.net API after
    connection
    """

class SpeedtestUploadTimeout(SpeedtestException):
    """testlength configuration reached during upload
    Used to ensure the upload halts when no additional data should be sent
    """

class SpeedtestBestServerFailure(SpeedtestException):
    """Unable to determine best server"""

class SpeedtestMissingBestServer(SpeedtestException):
    """get_best_server not called or not able to determine best server"""

作者为了排查错误,自定义异常类写的真好

 

19、各种函数对象的创建

def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
                      source_address=None): 
def _build_connection(connection, source_address, timeout, context=None): 
def build_opener(source_address=None, timeout=10): 
def get_exception(): 
def distance(origin, destination):
def build_user_agent():
def build_request(url, data=None, headers=None, bump='0', secure=False):
def catch_request(request, opener=None):
def get_response_stream(response):
def get_attributes_by_tag_name(dom, tag_name):
def print_dots(shutdown_event):
def do_nothing(*args, **kwargs):
def ctrl_c(shutdown_event):
def version():
def csv_header(delimiter=','):
def parse_args():
def validate_optional_args(args):
def printer(string, quiet=False, debug=False, error=False, **kwargs):
def shell():
def main():

创建了这么多函数,作者真厉害!每个函数

 

20、各种类对象的创建

class SpeedtestHTTPConnection(HTTPConnection): 
if HTTPSConnection:
    class SpeedtestHTTPSConnection(HTTPSConnection): 
class SpeedtestHTTPHandler(AbstractHTTPHandler): 
class SpeedtestHTTPSHandler(AbstractHTTPHandler): 
class GzipDecodedResponse(GZIP_BASE):
class HTTPDownloader(threading.Thread):
class HTTPUploaderData(object):
class HTTPUploader(threading.Thread):
class SpeedtestResults(object):
class Speedtest(object):

又是一堆类的创建,创建类是为了使用类产生的对象!

 

21、各种全局变量的初始化

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)
__version__ = '2.1.2' #覆盖的模块内置属性,表示模块的版本

清楚看到__version__表示的版本号

 

22、程序入口

if __name__ == '__main__':
    main()

作为脚本使用时的入口

 

 

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值