Python-Standard Library


Python 标准库

  • 并发执行
    • threading —- 基于线程的并行
    • multiprocessing —- 基于进程的并行
    • multiprocessing.shared_memory —- 可从进程直接访问的共享内存
    • concurrent
    • concurrent.futures —- 启动并行任务
    • subprocess —- 子进程管理
    • sched —- 事件调度器
    • queue —- 一个同步的队列类
    • contextvars —- 上下文变量
    • _thread —- 底层多线程 API
  • 网络和进程间通信
    • asyncio —- 异步 I/O
    • socket —- 底层网络接口
    • ssl —- 套接字对象的 TLS/SSL 包装器
    • select —- 等待 I/O 完成
    • selectors —- 高级 I/O 复用库
    • asyncore —- 异步套接字处理器
    • asynchat —- 异步套接字指令/响应处理程序
    • signal —- 设置异步事件处理程序
    • mmap —- 内存映射文件支持
  • 互联网数据处理
    • email —- 电子邮件与 MIME 处理包
    • json —- JSON 编码和解码器
    • mailcap —- Mailcap 文件处理
    • mailbox —- 操作多种格式的邮箱
    • mimetypes —- 映射文件名到 MIME 类型
    • base64 —- Base16, Base32, Base64, Base85 数据编码
    • binhex —- 对binhex4文件进行编码和解码
    • binascii —- 二进制和 ASCII 码互转
    • quopri —- 编码与解码经过 MIME 转码的可打印数据
    • uu —- 对 uuencode 文件进行编码与解码
  • 结构化标记处理工具
    • html —- 超文本标记语言支持
    • html.parser —- 简单的 HTML 和 XHTML 解析器
    • html.entities —- HTML 一般实体的定义
    • XML处理模块
    • xml.etree.ElementTree —- ElementTree XML API
    • xml.dom —- 文档对象模型 API
    • xml.dom.minidom —- 最小化的 DOM 实现
    • xml.dom.pulldom —- 支持构建部分 DOM 树
    • xml.sax —- 支持 SAX2 解析器
    • xml.sax.handler —- SAX 处理句柄的基类
    • xml.sax.saxutils —- SAX 工具集
    • xml.sax.xmlreader —- 用于 XML 解析器的接口
    • xml.parsers.expat —- 使用 Expat 的快速 XML 解析
  • 互联网协议和支持
    • webbrowser —- 方便的 Web 浏览器控制工具
    • cgi —- 通用网关接口支持
    • cgitb —- 用于 CGI 脚本的回溯管理器
    • wsgiref —- WSGI 工具和参考实现
    • urllib —- URL 处理模块
    • urllib.request —- 用于打开 URL 的可扩展库
    • urllib.response —- urllib 使用的 Response 类
    • urllib.parse 用于解析 URL
    • urllib.error —- urllib.request 引发的异常类
    • urllib.robotparser —- robots.txt 语法分析程序
    • http —- HTTP 模块
    • http.client —- HTTP 协议客户端
    • ftplib —- FTP 协议客户端
    • poplib —- POP3 协议客户端
    • imaplib —- IMAP4 协议客户端
    • nntplib —- NNTP protocol client
    • smtplib —-SMTP协议客户端
    • smtpd —- SMTP 服务器
    • telnetlib — Telnet 客户端
    • uuid —- UUID objects according to RFC 4122
    • socketserver —- A framework for network servers
    • http.server —- HTTP 服务器
    • http.cookies —- HTTP状态管理
    • http.cookiejar —— HTTP 客户端的 Cookie 处理
    • xmlrpc —- XMLRPC 服务端与客户端模块
    • xmlrpc.client —- XML-RPC 客户端访问
    • xmlrpc.server —- 基本 XML-RPC 服务器
    • ipaddress —- IPv4/IPv6 操作库

并发执行

本章中描述的模块支持并发执行代码。 适当的工具选择取决于要执行的任务(CPU密集型或IO密集型)和偏好的开发风格(事件驱动的协作式多任务或抢占式多任务处理)。 这是一个概述:

  • threading —- 基于线程的并行
    • 线程本地数据
    • 线程对象
    • 锁对象
    • 递归锁对象
    • 条件对象
    • 信号量对象
      • Semaphore 例子
    • 事件对象
    • 定时器对象
    • 栅栏对象
    • with 语句中使用锁、条件和信号量
  • multiprocessing —- 基于进程的并行
    • 概述
      • Process
      • 上下文和启动方法
      • 在进程之间交换对象
      • 进程间同步
      • 进程间共享状态
      • 使用工作进程
    • 参考
      • Process 和异常
      • 管道和队列
      • 杂项
      • 连接对象(Connection)
      • 同步原语
      • 共享 ctypes 对象
        • multiprocessing.sharedctypes 模块
      • 管理器
        • 自定义管理器
        • 使用远程管理器
      • 代理对象
        • 清理
      • 进程池
      • 监听器及客户端
        • 地址格式
      • 认证密码
      • 日志记录
      • multiprocessing.dummy 模块
    • 编程指导
      • 所有start方法
      • spawnforkserver 启动方式
    • 例子
  • multiprocessing.shared_memory —- 可从进程直接访问的共享内存
  • concurrent
  • concurrent.futures —- 启动并行任务
    • Executor 对象
    • ThreadPoolExecutor
      • ThreadPoolExecutor 例子
    • ProcessPoolExecutor
      • ProcessPoolExecutor 例子
    • Future 对象
    • 模块函数
    • Exception 类
  • subprocess —- 子进程管理
    • 使用 subprocess 模块
      • 常用参数
      • Popen 构造函数
      • 异常
    • 安全考量
    • Popen 对象
    • Windows Popen 助手
      • Windows 常数
    • 较旧的高阶 API
    • 使用 subprocess 模块替换旧函数
      • 替代 /bin/sh shell 命令替换
      • 替代 shell 管道
      • 替代 os.system()
      • 替代 os.spawn 函数族
      • 替代 os.popen(), os.popen2(), os.popen3()
      • 来自 popen2 模块的替代函数
    • 旧式的 Shell 发起函数
    • 备注
      • 在 Windows 上将参数列表转换为一个字符串
  • sched —- 事件调度器
    • 调度器对象
  • queue —- 一个同步的队列类
    • Queue对象
    • SimpleQueue 对象
  • contextvars —- 上下文变量
    • 上下文变量
    • 手动上下文管理
    • asyncio 支持

以下是上述某些服务的支持模块:

  • _thread —- 底层多线程 API

threading —- 基于线程的并行

源代码:Lib/threading.py


这个模块在较低级的模块 _thread 基础上建立较高级的线程接口。

在 3.7 版更改: 这个模块曾经为可选项,但现在总是可用。

注解

在 Python 2.x 系列中,此模块包含有某些方法和函数 camelCase 形式的名称。 它们在 Python 3.10 中已弃用,但为了与 Python 2.5 及更旧版本的兼容性而仍受到支持。

CPython implementation detail: 在 CPython 中,由于存在 全局解释器锁,同一时刻只有一个线程可以执行 Python 代码(虽然某些性能导向的库可能会去除此限制)。 如果你想让你的应用更好地利用多核心计算机的计算资源,推荐你使用 multiprocessingconcurrent.futures.ProcessPoolExecutor。 但是,如果你想要同时运行多个 I/O 密集型任务,则多线程仍然是一个合适的模型。

这个模块定义了以下函数:

threading.active_count()

返回当前存活的 Thread 对象的数量。 返回值与 enumerate() 所返回的列表长度一致。

函数 activeCount 是此函数的已弃用别名。

threading.current_thread()

返回当前对应调用者的控制线程的 Thread 对象。如果调用者的控制线程不是利用 threading 创建,会返回一个功能受限的虚拟线程对象。

函数 currentThread 是此函数的已弃用别名。

threading.excepthook(args, /)

处理由 Thread.run() 引发的未捕获异常。

args 参数具有以下属性:

  • exc_type: 异常类型
  • exc_value: 异常值,可以是 None.
  • exc_traceback: 异常回溯,可以是 None.
  • thread: 引发异常的线程,可以为 None

如果 exc_typeSystemExit,则异常会被静默地忽略。 在其他情况下,异常将被打印到 sys.stderr

如果此函数引发了异常,则会调用 sys.excepthook() 来处理它。

threading.excepthook() 可以被重载以控制由 Thread.run() 引发的未捕获异常的处理方式。

使用定制钩子存放 exc_value 可能会创建引用循环。 它应当在不再需要异常时被显式地清空以打破引用循环。

如果一个对象正在被销毁,那么使用自定义的钩子储存 thread 可能会将其复活。请在自定义钩子生效后避免储存 thread,以避免对象的复活。

参见

sys.excepthook() 处理未捕获的异常。

3.8 新版功能.

threading.__excepthook__

保存 threading.excepthook() 的原始值。 它被保存以便在原始值碰巧被已损坏或替代对象所替换的情况下可被恢复。

3.10 新版功能.

threading.get_ident()

返回当前线程的 “线程标识符”。它是一个非零的整数。它的值没有直接含义,主要是用作 magic cookie,比如作为含有线程相关数据的字典的索引。线程标识符可能会在线程退出,新线程创建时被复用。

3.3 新版功能.

threading.get_native_id()

返回内核分配给当前线程的原生集成线程 ID。 这是一个非负整数。 它的值可被用来在整个系统中唯一地标识这个特定线程(直到线程终结,在那之后该值可能会被 OS 回收再利用)。

可用性: Windows, FreeBSD, Linux, macOS, OpenBSD, NetBSD, AIX。

3.8 新版功能.

threading.enumerate()

返回当前所有存活的 Thread 对象的列表。 该列表包括守护线程以及 current_thread() 创建的空线程。 它不包括已终结的和尚未开始的线程。 但是,主线程将总是结果的一部分,即使是在已终结的时候。

threading.main_thread()

返回主 Thread 对象。一般情况下,主线程是Python解释器开始时创建的线程。

3.4 新版功能.

threading.settrace(func)

为所有 threading 模块开始的线程设置追踪函数。在每个线程的 run() 方法被调用前,func 会被传递给 sys.settrace()

threading.gettrace()

返回由 settrace() 设置的跟踪函数。

3.10 新版功能.

threading.setprofile(func)

为所有 threading 模块开始的线程设置性能测试函数。在每个线程的 run() 方法被调用前,func 会被传递给 sys.setprofile()

threading.getprofile()

返回由 setprofile() 设置的性能分析函数。

3.10 新版功能.

threading.stack_size([size])

返回创建线程时使用的堆栈大小。可选参数 size 指定之后新建的线程的堆栈大小,而且一定要是0(根据平台或者默认配置)或者最小是32,768(32KiB)的一个正整数。如果 size 没有指定,默认是0。如果不支持改变线程堆栈大小,会抛出 RuntimeError 错误。如果指定的堆栈大小不合法,会抛出 ValueError 错误并且不会修改堆栈大小。32KiB是当前最小的能保证解释器有足够堆栈空间的堆栈大小。需要注意的是部分平台对于堆栈大小会有特定的限制,例如要求大于32KiB的堆栈大小或者需要根据系统内存页面的整数倍进行分配 - 应当查阅平台文档有关详细信息(4KiB页面比较普遍,在没有更具体信息的情况下,建议的方法是使用4096的倍数作为堆栈大小)。

适用于: Windows,具有 POSIX 线程的系统。

这个模块同时定义了以下常量:

threading.TIMEOUT_MAX

阻塞函数( Lock.acquire(), RLock.acquire(), Condition.wait(), …)中形参 timeout 允许的最大值。传入超过这个值的 timeout 会抛出 OverflowError 异常。

3.2 新版功能.

这个模块定义了许多类,详见以下部分。

该模块的设计基于 Java的线程模型。 但是,在Java里面,锁和条件变量是每个对象的基础特性,而在Python里面,这些被独立成了单独的对象。 Python 的 Thread 类只是 Java 的 Thread 类的一个子集;目前还没有优先级,没有线程组,线程还不能被销毁、停止、暂停、恢复或中断。 Java 的 Thread 类的静态方法在实现时会映射为模块级函数。

下述方法的执行都是原子性的。

线程本地数据

线程本地数据是特定线程的数据。管理线程本地数据,只需要创建一个 local (或者一个子类型)的实例并在实例中储存属性:

mydata = threading.local()
mydata.x =1

在不同的线程中,实例的值会不同。

classthreading.local

一个代表线程本地数据的类。

更多相关细节和大量示例,参见 _threading_local 模块的文档。

线程对象

The Thread class represents an activity that is run in a separate thread of control. There are two ways to specify the activity: by passing a callable object to the constructor, or by overriding the run() method in a subclass. No other methods (except for the constructor) should be overridden in a subclass. In other words, only override the __init__() and run() methods of this class.

当线程对象一但被创建,其活动一定会因调用线程的 start() 方法开始。这会在独立的控制线程调用 run() 方法。

一旦线程活动开始,该线程会被认为是 ‘存活的’ 。当它的 run() 方法终结了(不管是正常的还是抛出未被处理的异常),就不是’存活的’。 is_alive() 方法用于检查线程是否存活。

其他线程可以调用一个线程的 join() 方法。这会阻塞调用该方法的线程,直到被调用 join() 方法的线程终结。

线程有名字。名字可以传递给构造函数,也可以通过 name 属性读取或者修改。

如果 run() 方法引发了异常,则会调用 threading.excepthook() 来处理它。 在默认情况下,threading.excepthook() 会静默地忽略 SystemExit

一个线程可以被标记成一个“守护线程”。 这个标识的意义是,当剩下的线程都是守护线程时,整个 Python 程序将会退出。 初始值继承于创建线程。 这个标识可以通过 daemon 特征属性或者 daemon 构造器参数来设置。

注解

守护线程在程序关闭时会突然关闭。他们的资源(例如已经打开的文档,数据库事务等等)可能没有被正确释放。如果你想你的线程正常停止,设置他们成为非守护模式并且使用合适的信号机制,例如: Event

有个 “主线程” 对象;这对应Python程序里面初始的控制线程。它不是一个守护线程。

“虚拟线程对象” 是可以被创建的。这些是对应于“外部线程”的线程对象,它们是在线程模块外部启动的控制线程,例如直接来自C代码。虚拟线程对象功能受限;他们总是被认为是存活的和守护模式,不能被 join() 。因为无法检测外来线程的终结,它们永远不会被删除。

classthreading.Thread(group=None, target=None, name=None, args=(), kwargs={}, **, daemon=None*)

调用这个构造函数时,必需带有关键字参数。参数如下:

group 应该为 None;为了日后扩展 ThreadGroup 类实现而保留。

target 是用于 run() 方法调用的可调用对象。默认是 None,表示不需要调用任何方法。

name 是线程名称。 在默认情况下,会以 “Thread-N“ 的形式构造唯一名称,其中 N 为一个较小的十进制数值,或是 “Thread-N (target)” 的形式,其中 “target” 为 target.__name__,如果指定了 target 参数的话。

args 是用于调用目标函数的参数元组。默认是 ()

kwargs 是用于调用目标函数的关键字参数字典。默认是 {}

如果不是 Nonedaemon 参数将显式地设置该线程是否为守护模式。 如果是 None (默认值),线程将继承当前线程的守护模式属性。

如果子类型重载了构造函数,它一定要确保在做任何事前,先发起调用基类构造器(Thread.__init__())。

在 3.10 版更改: 使用 target 名称,如果 name 参数被省略的话。

在 3.3 版更改: 加入 daemon 参数。

  • start()

    开始线程活动。

    它在一个线程里最多只能被调用一次。它安排对象的 run() 方法在一个独立的控制进程中调用。

    如果同一个线程对象中调用这个方法的次数大于一次,会抛出 RuntimeError

  • run()

    代表线程活动的方法。

    你可以在子类型里重载这个方法。 标准的 run() 方法会对作为 target 参数传递给该对象构造器的可调用对象(如果存在)发起调用,并附带从 argskwargs 参数分别获取的位置和关键字参数。

  • join(timeout=None)

    等待,直到线程终结。这会阻塞调用这个方法的线程,直到被调用 join() 的线程终结 — 不管是正常终结还是抛出未处理异常 — 或者直到发生超时,超时选项是可选的。

    timeout 参数存在而且不是 None 时,它应该是一个用于指定操作超时的以秒为单位的浮点数或者分数。因为 join() 总是返回 None ,所以你一定要在 join() 后调用 is_alive() 才能判断是否发生超时 — 如果线程仍然存活,则 join() 超时。

    timeout 参数不存在或者是 None ,这个操作会阻塞直到线程终结。

    一个线程可以被 join() 很多次。

    如果尝试加入当前线程会导致死锁, join() 会引起 RuntimeError 异常。如果尝试 join() 一个尚未开始的线程,也会抛出相同的异常。

  • name

    只用于识别的字符串。它没有语义。多个线程可以赋予相同的名称。 初始名称由构造函数设置。

  • getName()

    setName()

    已被弃用的 name 的取值/设值 API;请改为直接以特征属性方式使用它。

    3.10 版后已移除.

  • ident

    这个线程的 ‘线程标识符’,如果线程尚未开始则为 None 。这是个非零整数。当一个线程退出而另外一个线程被创建,线程标识符会被复用。即使线程退出后,仍可得到标识符。

  • native_id

    此线程的线程 ID (TID),由 OS (内核) 分配。 这是一个非负整数,或者如果线程还未启动则为 None。 这个值可被用来在全系统范围内唯一地标识这个特定线程 (直到线程终结,在那之后该值可能会被 OS 回收再利用)。

    注解

    类似于进程 ID,线程 ID 的有效期(全系统范围内保证唯一)将从线程被创建开始直到线程被终结。

    可用性: 需要 get_native_id() 函数。

    3.8 新版功能.

  • is_alive()

    返回线程是否存活。

    run() 方法刚开始直到 run() 方法刚结束,这个方法返回 True 。模块函数 enumerate() 返回包含所有存活线程的列表。

  • daemon

    一个表示这个线程是(True)否(False)守护线程的布尔值。一定要在调用 start() 前设置好,不然会抛出 RuntimeError 。初始值继承于创建线程;主线程不是守护线程,因此主线程创建的所有线程默认都是 daemon = False

    当没有存活的非守护线程时,整个Python程序才会退出。

  • isDaemon()

    setDaemon()

    已被弃用的 daemon 的取值/设值 API;请改为直接以特征属性方式使用它。

    3.10 版后已移除.

锁对象

原始锁是一个在锁定时不属于特定线程的同步基元组件。在Python中,它是能用的最低级的同步基元组件,由 _thread 扩展模块直接实现。

原始锁处于 “锁定” 或者 “非锁定” 两种状态之一。它被创建时为非锁定状态。它有两个基本方法, acquire()release() 。当状态为非锁定时, acquire() 将状态改为 锁定 并立即返回。当状态是锁定时, acquire() 将阻塞至其他线程调用 release() 将其改为非锁定状态,然后 acquire() 调用重置其为锁定状态并返回。 release() 只在锁定状态下调用; 它将状态改为非锁定并立即返回。如果尝试释放一个非锁定的锁,则会引发 RuntimeError 异常。

锁同样支持 上下文管理协议。

当多个线程在 acquire() 等待状态转变为未锁定被阻塞,然后 release() 重置状态为未锁定时,只有一个线程能继续执行;至于哪个等待线程继续执行没有定义,并且会根据实现而不同。

所有方法的执行都是原子性的。

classthreading.Lock

实现原始锁对象的类。一旦一个线程获得一个锁,会阻塞随后尝试获得锁的线程,直到它被释放;任何线程都可以释放它。

需要注意的是 Lock 其实是一个工厂函数,返回平台支持的具体锁类中最有效的版本的实例。

  • acquire(blocking=True, timeout=- 1)

    可以阻塞或非阻塞地获得锁。

    当调用时参数 blocking 设置为 True (缺省值),阻塞直到锁被释放,然后将锁锁定并返回 True

    在参数 blocking 被设置为 False 的情况下调用,将不会发生阻塞。如果调用时 blocking 设为 True 会阻塞,并立即返回 False ;否则,将锁锁定并返回 True

    当浮点型 timeout 参数被设置为正值调用时,只要无法获得锁,将最多阻塞 timeout 设定的秒数。timeout 参数被设置为 -1 时将无限等待。当 blocking 为 false 时,timeout 指定的值将被忽略。

    如果成功获得锁,则返回 True,否则返回 False (例如发生 超时 的时候)。

    在 3.2 版更改: 新的 timeout 形参。

    在 3.2 版更改: 现在如果底层线程实现支持,则可以通过POSIX上的信号中断锁的获取。

  • release()

    释放一个锁。这个方法可以在任何线程中调用,不单指获得锁的线程。

    当锁被锁定,将它重置为未锁定,并返回。如果其他线程正在等待这个锁解锁而被阻塞,只允许其中一个允许。

    当在未锁定的锁上发起调用时,会引发 RuntimeError

    没有返回值。

  • locked()

    如果获得了锁则返回真值。

递归锁对象

重入锁是一个可以被同一个线程多次获取的同步基元组件。在内部,它在基元锁的锁定/非锁定状态上附加了 “所属线程” 和 “递归等级” 的概念。在锁定状态下,某些线程拥有锁 ; 在非锁定状态下, 没有线程拥有它。

若要锁定锁,线程调用其 acquire() 方法;一旦线程拥有了锁,方法将返回。若要解锁,线程调用 release() 方法。 acquire()/release() 对可以嵌套;只有最终 release() (最外面一对的 release() ) 将锁解开,才能让其他线程继续处理 acquire() 阻塞。

递归锁也支持 上下文管理协议。

classthreading.RLock

此类实现了重入锁对象。重入锁必须由获取它的线程释放。一旦线程获得了重入锁,同一个线程再次获取它将不阻塞;线程必须在每次获取它时释放一次。

需要注意的是 RLock 其实是一个工厂函数,返回平台支持的具体递归锁类中最有效的版本的实例。

  • acquire(blocking=True, timeout=- 1)

    可以阻塞或非阻塞地获得锁。

    当无参数调用时: 如果这个线程已经拥有锁,递归级别增加一,并立即返回。否则,如果其他线程拥有该锁,则阻塞至该锁解锁。一旦锁被解锁(不属于任何线程),则抢夺所有权,设置递归等级为一,并返回。如果多个线程被阻塞,等待锁被解锁,一次只有一个线程能抢到锁的所有权。在这种情况下,没有返回值。

    当发起调用时将 blocking 参数设为真值,则执行与无参数调用时一样的操作,然后返回 True

    当发起调用时将 blocking 参数设为假值,则不进行阻塞。 如果一个无参数调用将要阻塞,则立即返回 False;在其他情况下,执行与无参数调用时一样的操作,然后返回 True

    当发起调用时将浮点数的 timeout 参数设为正值时,只要无法获得锁,将最多阻塞 timeout 所指定的秒数。 如果已经获得锁则返回 True,如果超时则返回假值。

    在 3.2 版更改: 新的 timeout 形参。

  • release()

    释放锁,自减递归等级。如果减到零,则将锁重置为非锁定状态(不被任何线程拥有),并且,如果其他线程正被阻塞着等待锁被解锁,则仅允许其中一个线程继续。如果自减后,递归等级仍然不是零,则锁保持锁定,仍由调用线程拥有。

    只有当前线程拥有锁才能调用这个方法。如果锁被释放后调用这个方法,会引起 RuntimeError 异常。

    没有返回值。

条件对象

条件变量总是与某种类型的锁对象相关联,锁对象可以通过传入获得,或者在缺省的情况下自动创建。当多个条件变量需要共享同一个锁时,传入一个锁很有用。锁是条件对象的一部分,你不必单独地跟踪它。

条件变量遵循 上下文管理协议 :使用 with 语句会在它包围的代码块内获取关联的锁。 acquire()release() 方法也能调用关联锁的相关方法。

其它方法必须在持有关联的锁的情况下调用。 wait() 方法释放锁,然后阻塞直到其它线程调用 notify() 方法或 notify_all() 方法唤醒它。一旦被唤醒, wait() 方法重新获取锁并返回。它也可以指定超时时间。

The notify() method wakes up one of the threads waiting for the condition variable, if any are waiting. The notify_all() method wakes up all threads waiting for the condition variable.

注意: notify() 方法和 notify_all() 方法并不会释放锁,这意味着被唤醒的线程不会立即从它们的 wait() 方法调用中返回,而是会在调用了 notify() 方法或 notify_all() 方法的线程最终放弃了锁的所有权后返回。

使用条件变量的典型编程风格是将锁用于同步某些共享状态的权限,那些对状态的某些特定改变感兴趣的线程,它们重复调用 wait() 方法,直到看到所期望的改变发生;而对于修改状态的线程,它们将当前状态改变为可能是等待者所期待的新状态后,调用 notify() 方法或者 notify_all() 方法。例如,下面的代码是一个通用的无限缓冲区容量的生产者-消费者情形:

# Consume one item
with cv:
whilenot an_item_is_available():
        cv.wait()
    get_an_available_item()
# Produce one item
with cv:
    make_an_item_available()
    cv.notify()

使用 while 循环检查所要求的条件成立与否是有必要的,因为 wait() 方法可能要经过不确定长度的时间后才会返回,而此时导致 notify() 方法调用的那个条件可能已经不再成立。这是多线程编程所固有的问题。 wait_for() 方法可自动化条件检查,并简化超时计算。

# Consume an item
with cv:
    cv.wait_for(an_item_is_available)
    get_an_available_item()

选择 notify() 还是 notify_all() ,取决于一次状态改变是只能被一个还是能被多个等待线程所用。例如在一个典型的生产者-消费者情形中,添加一个项目到缓冲区只需唤醒一个消费者线程。

classthreading.Condition(lock=None)

实现条件变量对象的类。一个条件变量对象允许一个或多个线程在被其它线程所通知之前进行等待。

如果给出了非 Nonelock 参数,则它必须为 Lock 或者 RLock 对象,并且它将被用作底层锁。否则,将会创建新的 RLock 对象,并将其用作底层锁。

在 3.3 版更改: 从工厂函数变为类。

  • acquire(\args*)

    请求底层锁。此方法调用底层锁的相应方法,返回值是底层锁相应方法的返回值。

  • release()

    释放底层锁。此方法调用底层锁的相应方法。没有返回值。

  • wait(timeout=None)

    等待直到被通知或发生超时。如果线程在调用此方法时没有获得锁,将会引发 RuntimeError 异常。

    这个方法释放底层锁,然后阻塞,直到在另外一个线程中调用同一个条件变量的 notify()notify_all() 唤醒它,或者直到可选的超时发生。一旦被唤醒或者超时,它重新获得锁并返回。

    当提供了 timeout 参数且不是 None 时,它应该是一个浮点数,代表操作的超时时间,以秒为单位(可以为小数)。

    当底层锁是个 RLock ,不会使用它的 release() 方法释放锁,因为当它被递归多次获取时,实际上可能无法解锁。相反,使用了 RLock 类的内部接口,即使多次递归获取它也能解锁它。 然后,在重新获取锁时,使用另一个内部接口来恢复递归级别。

    返回 True ,除非提供的 timeout 过期,这种情况下返回 False

    在 3.2 版更改: 很明显,方法总是返回 None

  • wait_for(predicate, timeout=None)

    等待,直到条件计算为真。 predicate 应该是一个可调用对象而且它的返回值可被解释为一个布尔值。可以提供 timeout 参数给出最大等待时间。

    这个实用方法会重复地调用 wait() 直到满足判断式或者发生超时。返回值是判断式最后一个返回值,而且如果方法发生超时会返回 False

    忽略超时功能,调用此方法大致相当于编写:

    whilenot predicate():
        cv.wait()

    因此,规则同样适用于 wait() :锁必须在被调用时保持获取,并在返回时重新获取。 随着锁定执行判断式。

    3.2 新版功能.

  • notify(n=1)

    默认唤醒一个等待这个条件的线程。如果调用线程在没有获得锁的情况下调用这个方法,会引发 RuntimeError 异常。

    这个方法唤醒最多 n 个正在等待这个条件变量的线程;如果没有线程在等待,这是一个空操作。

    当前实现中,如果至少有 n 个线程正在等待,准确唤醒 n 个线程。但是依赖这个行为并不安全。未来,优化的实现有时会唤醒超过 n 个线程。

    注意:被唤醒的线程实际上不会返回它调用的 wait() ,直到它可以重新获得锁。因为 notify() 不会释放锁,只有它的调用者应该这样做。

  • notify_all()

    唤醒所有正在等待这个条件的线程。这个方法行为与 notify() 相似,但并不只唤醒单一线程,而是唤醒所有等待线程。如果调用线程在调用这个方法时没有获得锁,会引发 RuntimeError 异常。

    notifyAll 方法是此方法的已弃用别名。

信号量对象

这是计算机科学史上最古老的同步原语之一,早期的荷兰科学家 Edsger W. Dijkstra 发明了它。(他使用名称 P()V() 而不是 acquire()release() )。

一个信号量管理一个内部计数器,该计数器因 acquire() 方法的调用而递减,因 release() 方法的调用而递增。 计数器的值永远不会小于零;当 acquire() 方法发现计数器为零时,将会阻塞,直到其它线程调用 release() 方法。

信号量对象也支持 上下文管理协议 。

classthreading.Semaphore(value=1)

该类实现信号量对象。信号量对象管理一个原子性的计数器,代表 release() 方法的调用次数减去 acquire() 的调用次数再加上一个初始值。如果需要, acquire() 方法将会阻塞直到可以返回而不会使得计数器变成负数。在没有显式给出 value 的值时,默认为1。

可选参数 value 赋予内部计数器初始值,默认值为 1 。如果 value 被赋予小于0的值,将会引发 ValueError 异常。

在 3.3 版更改: 从工厂函数变为类。

  • acquire(blocking=True, timeout=None)

    获取一个信号量。

    在不带参数的情况下调用时:

    • 如果在进入时内部计数器的值大于零,则将其减一并立即返回 True
    • 如果在进入时内部计数器的值为零,则将会阻塞直到被对 release() 的调用唤醒。 一旦被唤醒(并且计数器的值大于 0),则将计数器减 1 并返回 True。 每次对 release() 的调用将只唤醒一个线程。 线程被唤醒的次序是不可确定的。

    当发起调用时将 blocking 设为假值,则不进行阻塞。 如果一个无参数调用将要阻塞,则立即返回 False;在其他情况下,执行与无参数调用时一样的操作,然后返回 True

    当发起调用时如果 timeout 不为 None,则它将阻塞最多 timeout 秒。 请求在此时段时未能成功完成获取则将返回 False。 在其他情况下返回 True

    在 3.2 版更改: 新的 timeout 形参。

  • release(n=1)

    释放一个信号量,将内部计数器的值增加 n。 当进入时值为零且有其他线程正在等待它再次变为大于零时,则唤醒那 n 个线程。

    在 3.9 版更改: 增加了 n 形参以一次性释放多个等待线程。

classthreading.BoundedSemaphore(value=1)

该类实现有界信号量。有界信号量通过检查以确保它当前的值不会超过初始值。如果超过了初始值,将会引发 ValueError 异常。在大多情况下,信号量用于保护数量有限的资源。如果信号量被释放的次数过多,则表明出现了错误。没有指定时, value 的值默认为1。

在 3.3 版更改: 从工厂函数变为类。

Semaphore 例子

信号量通常用于保护数量有限的资源,例如数据库服务器。在资源数量固定的任何情况下,都应该使用有界信号量。在生成任何工作线程前,应该在主线程中初始化信号量。

maxconnections =5
# ...
pool_sema =BoundedSemaphore(value=maxconnections)

工作线程生成后,当需要连接服务器时,这些线程将调用信号量的 acquire 和 release 方法:

with pool_sema:
    conn = connectdb()
try:
# ... use connection ...
finally:
        conn.close()

使用有界信号量能减少这种编程错误:信号量的释放次数多于其请求次数。

事件对象

这是线程之间通信的最简单机制之一:一个线程发出事件信号,而其他线程等待该信号。

一个事件对象管理一个内部标识,调用 set() 方法可将其设置为 true ,调用 clear() 方法可将其设置为 false ,调用 wait() 方法将进入阻塞直到标识为 true 。

classthreading.Event

实现事件对象的类。事件对象管理一个内部标识,调用 set() 方法可将其设置为true。调用 clear() 方法可将其设置为 false 。调用 wait() 方法将进入阻塞直到标识为true。这个标识初始时为 false 。

在 3.3 版更改: 从工厂函数变为类。

  • is_set()

    当且仅当内部标识为 true 时返回 True

    isSet 方法是此方法的已弃用别名。

  • set()

    将内部标识设置为 true 。所有正在等待这个事件的线程将被唤醒。当标识为 true 时,调用 wait() 方法的线程不会被被阻塞。

  • clear()

    将内部标识设置为 false 。之后调用 wait() 方法的线程将会被阻塞,直到调用 set() 方法将内部标识再次设置为 true 。

  • wait(timeout=None)

    阻塞线程直到内部变量为 true 。如果调用时内部标识为 true,将立即返回。否则将阻塞线程,直到调用 set() 方法将标识设置为 true 或者发生可选的超时。

    当提供了timeout参数且不是 None 时,它应该是一个浮点数,代表操作的超时时间,以秒为单位(可以为小数)。

    当且仅当内部旗标在等待调用之前或者等待开始之后被设为真值时此方法将返回 True,也就是说,它将总是返回 True 除非设定了超时且操作发生了超时。

    在 3.1 版更改: 很明显,方法总是返回 None

定时器对象

此类表示一个操作应该在等待一定的时间之后运行 —- 相当于一个定时器。 Timer 类是 Thread 类的子类,因此可以像一个自定义线程一样工作。

与线程一样,通过调用 start() 方法启动定时器。而 cancel() 方法可以停止计时器(在计时结束前), 定时器在执行其操作之前等待的时间间隔可能与用户指定的时间间隔不完全相同。

例如:

def hello():
print("hello, world")
t =Timer(30.0, hello)
t.start()# after 30 seconds, "hello, world" will be printed

classthreading.Timer(interval, function, args=None, kwargs=None)

创建一个定时器,在经过 interval 秒的间隔事件后,将会用参数 args 和关键字参数 kwargs 调用 function*。如果 *argsNone (默认值),则会使用一个空列表。如果 kwargsNone (默认值),则会使用一个空字典。

在 3.3 版更改: 从工厂函数变为类。

  • cancel()

    停止定时器并取消执行计时器将要执行的操作。仅当计时器仍处于等待状态时有效。

栅栏对象

3.2 新版功能.

栅栏类提供一个简单的同步原语,用于应对固定数量的线程需要彼此相互等待的情况。线程调用 wait() 方法后将阻塞,直到所有线程都调用了 wait() 方法。此时所有线程将被同时释放。

栅栏对象可以被多次使用,但进程的数量不能改变。

这是一个使用简便的方法实现客户端进程与服务端进程同步的例子:

b =Barrier(2, timeout=5)
def server():
    start_server()
    b.wait()
whileTrue:
        connection = accept_connection()
        process_server_connection(connection)
def client():
    b.wait()
whileTrue:
        connection = make_connection()
        process_client_connection(connection)

classthreading.Barrier(parties, action=None, timeout=None)

创建一个需要 parties 个线程的栅栏对象。如果提供了可调用的 action 参数,它会在所有线程被释放时在其中一个线程中自动调用。 timeout 是默认的超时时间,如果没有在 wait() 方法中指定超时时间的话。

  • wait(timeout=None)

    冲出栅栏。当栅栏中所有线程都已经调用了这个函数,它们将同时被释放。如果提供了 timeout 参数,这里的 timeout 参数优先于创建栅栏对象时提供的 timeout 参数。

    函数返回值是一个整数,取值范围在0到 parties — 1,在每个线程中的返回值不相同。可用于从所有线程中选择唯一的一个线程执行一些特别的工作。例如:

    i = barrier.wait()
    if i ==0:
    # Only one thread needs to print this
    print("passed the barrier")

    如果创建栅栏对象时在构造函数中提供了 action 参数,它将在其中一个线程释放前被调用。如果此调用引发了异常,栅栏对象将进入损坏态。

    如果发生了超时,栅栏对象将进入破损态。

    如果栅栏对象进入破损态,或重置栅栏时仍有线程等待释放,将会引发 BrokenBarrierError 异常。

  • reset()

    重置栅栏为默认的初始态。如果栅栏中仍有线程等待释放,这些线程将会收到 BrokenBarrierError 异常。

    请注意使用此函数时,如果存在状态未知的其他线程,则可能需要执行外部同步。 如果栅栏已损坏则最好将其废弃并新建一个。

  • abort()

    使栅栏处于损坏状态。 这将导致任何现有和未来对 wait() 的调用失败并引发 BrokenBarrierError。 例如可以在需要中止某个线程时使用此方法,以避免应用程序的死锁。

    更好的方式是:创建栅栏时提供一个合理的超时时间,来自动避免某个线程出错。

  • parties

    冲出栅栏所需要的线程数量。

  • n_waiting

    当前时刻正在栅栏中阻塞的线程数量。

  • broken

    一个布尔值,值为 True 表明栅栏为破损态。

exceptionthreading.BrokenBarrierError

异常类,是 RuntimeError 异常的子类,在 Barrier 对象重置时仍有线程阻塞时和对象进入破损态时被引发。

with 语句中使用锁、条件和信号量

这个模块提供的带有 acquire()release() 方法的对象,可以被用作 with 语句的上下文管理器。当进入语句块时 acquire() 方法会被调用,退出语句块时 release() 会被调用。因此,以下片段:

with some_lock:
# do something...

相当于:

some_lock.acquire()
try:
# do something...
finally:
    some_lock.release()

现在 LockRLockConditionSemaphoreBoundedSemaphore 对象可以用作 with 语句的上下文管理器。

multiprocessing —- 基于进程的并行

源代码 Lib/multiprocessing/


概述

multiprocessing 是一个支持使用与 threading 模块类似的 API 来产生进程的包。 multiprocessing 包同时提供了本地和远程并发操作,通过使用子进程而非线程有效地绕过了 全局解释器锁。 因此,multiprocessing 模块允许程序员充分利用给定机器上的多个处理器。 它在 Unix 和 Windows 上均可运行。

multiprocessing 模块还引入了在 threading 模块中没有的API。一个主要的例子就是 Pool 对象,它提供了一种快捷的方法,赋予函数并行化处理一系列输入值的能力,可以将输入数据分配给不同进程处理(数据并行)。下面的例子演示了在模块中定义此类函数的常见做法,以便子进程可以成功导入该模块。这个数据并行的基本例子使用了 Pool

from multiprocessing import Pool
def f(x):
    return x*x
if __name__ == '__main__':
    with Pool(5) as p:
        print(p.map(f, [1, 2, 3]))

将在标准输出中打印

[1, 4, 9]

Process

multiprocessing 中,通过创建一个 Process 对象然后调用它的 start() 方法来生成进程。 Processthreading.Thread API 相同。 一个简单的多进程程序示例是:

from multiprocessing import Process
def f(name):
    print('hello', name)
if __name__ == '__main__':
    p = Process(target=f, args=('bob',))
    p.start()
    p.join()

要显示所涉及的各个进程ID,这是一个扩展示例:

from multiprocessing import Process
import os
def info(title):
    print(title)
    print('module name:', __name__)
    print('parent process:', os.getppid())
    print('process id:', os.getpid())
def f(name):
    info('function f')
    print('hello', name)
if __name__ == '__main__':
    info('main line')
    p = Process(target=f, args=('bob',))
    p.start()
    p.join()

关于为什么 if __name__ == '__main__' 部分是必需的解释。

上下文和启动方法

根据不同的平台, multiprocessing 支持三种启动进程的方法。这些 启动方法

spawn

父进程会启动一个全新的 python 解释器进程。 子进程将只继承那些运行进程对象的 run() 方法所必需的资源。 特别地,来自父进程的非必需文件描述符和句柄将不会被继承。 使用此方法启动进程相比使用 forkforkserver 要慢上许多。

可在Unix和Windows上使用。 Windows上的默认设置。

fork

父进程使用 os.fork() 来产生 Python 解释器分叉。子进程在开始时实际上与父进程相同。父进程的所有资源都由子进程继承。请注意,安全分叉多线程进程是棘手的。

只存在于Unix。Unix中的默认值。

forkserver

程序启动并选择* forkserver * 启动方法时,将启动服务器进程。从那时起,每当需要一个新进程时,父进程就会连接到服务器并请求它分叉一个新进程。分叉服务器进程是单线程的,因此使用 os.fork() 是安全的。没有不必要的资源被继承。

可在Unix平台上使用,支持通过Unix管道传递文件描述符。

在 3.8 版更改: 对于 macOS,spawn 启动方式是默认方式。 因为 fork 可能导致subprocess崩溃,被认为是不安全的,查看 bpo-33725

在 3.4 版更改: 在所有unix平台上添加支持了 spawn ,并且为一些unix平台添加了 forkserver 。在Windows上子进程不再继承所有可继承的父进程句柄。

在 Unix 上通过 spawnforkserver 方式启动多进程会同时启动一个 资源追踪 进程,负责追踪当前程序的进程产生的、并且不再被使用的命名系统资源(如命名信号量以及 SharedMemory 对象)。当所有进程退出后,资源追踪会负责释放这些仍被追踪的的对象。通常情况下是不会有这种对象的,但是假如一个子进程被某个信号杀死,就可能存在这一类资源的“泄露”情况。(泄露的信号量以及共享内存不会被释放,直到下一次系统重启,对于这两类资源来说,这是一个比较大的问题,因为操作系统允许的命名信号量的数量是有限的,而共享内存也会占据主内存的一片空间)

要选择一个启动方法,你应该在主模块的 if __name__ == '__main__' 子句中调用 set_start_method() 。例如:

import multiprocessing as mp
def foo(q):
    q.put('hello')
if __name__ == '__main__':
    mp.set_start_method('spawn')
    q = mp.Queue()
    p = mp.Process(target=foo, args=(q,))
    p.start()
    print(q.get())
    p.join()

在程序中 set_start_method() 不应该被多次调用。

或者,你可以使用 get_context() 来获取上下文对象。上下文对象与 multiprocessing 模块具有相同的API,并允许在同一程序中使用多种启动方法。:

import multiprocessing as mp
def foo(q):
    q.put('hello')
if __name__ == '__main__':
    ctx = mp.get_context('spawn')
    q = ctx.Queue()
    p = ctx.Process(target=foo, args=(q,))
    p.start()
    print(q.get())
    p.join()

请注意,对象在不同上下文创建的进程间可能并不兼容。 特别是,使用 fork 上下文创建的锁不能传递给使用 spawnforkserver 启动方法启动的进程。

想要使用特定启动方法的库应该使用 get_context() 以避免干扰库用户的选择。

警告

'spawn''forkserver' 启动方法当前不能在Unix上和“冻结的”可执行内容一同使用(例如,有类似 PyInstallercx_Freeze 的包产生的二进制文件)。 'fork' 启动方法可以使用。

在进程之间交换对象

multiprocessing 支持进程之间的两种通信通道:

队列

Queue 类是一个近似 queue.Queue 的克隆。 例如:

from multiprocessing import Process, Queue
def f(q):
    q.put([42, None, 'hello'])
if __name__ == '__main__':
    q = Queue()
    p = Process(target=f, args=(q,))
    p.start()
    print(q.get())    # prints "[42, None, 'hello']"
    p.join()

队列是线程和进程安全的。

管道

Pipe() 函数返回一个由管道连接的连接对象,默认情况下是双工(双向)。例如:

from multiprocessing import Process, Pipe
def f(conn):
    conn.send([42, None, 'hello'])
    conn.close()
if __name__ == '__main__':
    parent_conn, child_conn = Pipe()
    p = Process(target=f, args=(child_conn,))
    p.start()
    print(parent_conn.recv())   # prints "[42, None, 'hello']"
    p.join()

返回的两个连接对象 Pipe() 表示管道的两端。每个连接对象都有 send()recv() 方法(相互之间的)。请注意,如果两个进程(或线程)同时尝试读取或写入管道的 同一 端,则管道中的数据可能会损坏。当然,在不同进程中同时使用管道的不同端的情况下不存在损坏的风险。

进程间同步

multiprocessing 包含来自 threading 的所有同步原语的等价物。例如,可以使用锁来确保一次只有一个进程打印到标准输出:

from multiprocessing import Process, Lock
def f(l, i):
    l.acquire()
    try:
        print('hello world', i)
    finally:
        l.release()
if __name__ == '__main__':
    lock = Lock()
    for num in range(10):
        Process(target=f, args=(lock, num)).start()

不使用锁的情况下,来自于多进程的输出很容易产生混淆。

进程间共享状态

如上所述,在进行并发编程时,通常最好尽量避免使用共享状态。使用多个进程时尤其如此。

但是,如果你真的需要使用一些共享数据,那么 multiprocessing 提供了两种方法。

共享内存

可以使用 ValueArray 将数据存储在共享内存映射中。例如,以下代码:

from multiprocessing import Process, Value, Array
def f(n, a):
    n.value = 3.1415927
    for i in range(len(a)):
        a[i] = -a[i]
if __name__ == '__main__':
    num = Value('d', 0.0)
    arr = Array('i', range(10))
    p = Process(target=f, args=(num, arr))
    p.start()
    p.join()
    print(num.value)
    print(arr[:])

将打印

3.1415927
[0, -1, -2, -3, -4, -5, -6, -7, -8, -9]

创建 numarr 时使用的 'd''i' 参数是 array 模块使用的类型的 typecode : 'd' 表示双精度浮点数, 'i' 表示有符号整数。这些共享对象将是进程和线程安全的。

为了更灵活地使用共享内存,可以使用 multiprocessing.sharedctypes 模块,该模块支持创建从共享内存分配的任意ctypes对象。

服务进程

Manager() 返回的管理器对象控制一个服务进程,该进程保存Python对象并允许其他进程使用代理操作它们。

Manager() 返回的管理器支持类型: listdictNamespaceLockRLockSemaphoreBoundedSemaphoreConditionEventBarrierQueueValueArray 。例如

from multiprocessing import Process, Manager
def f(d, l):
    d[1] = '1'
    d['2'] = 2
    d[0.25] = None
    l.reverse()
if __name__ == '__main__':
    with Manager() as manager:
        d = manager.dict()
        l = manager.list(range(10))
        p = Process(target=f, args=(d, l))
        p.start()
        p.join()
        print(d)
        print(l)

将打印

{0.25: None, 1: '1', '2': 2}
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

使用服务进程的管理器比使用共享内存对象更灵活,因为它们可以支持任意对象类型。此外,单个管理器可以通过网络由不同计算机上的进程共享。但是,它们比使用共享内存慢。

使用工作进程

Pool 类表示一个工作进程池。它具有允许以几种不同方式将任务分配到工作进程的方法。

例如:

from multiprocessing import Pool, TimeoutError
import time
import os
def f(x):
    return x*x
if __name__ == '__main__':
    # start 4 worker processes
    with Pool(processes=4) as pool:
        # print "[0, 1, 4,..., 81]"
        print(pool.map(f, range(10)))
        # print same numbers in arbitrary order
        for i in pool.imap_unordered(f, range(10)):
            print(i)
        # evaluate "f(20)" asynchronously
        res = pool.apply_async(f, (20,))      # runs in *only* one process
        print(res.get(timeout=1))             # prints "400"
        # evaluate "os.getpid()" asynchronously
        res = pool.apply_async(os.getpid, ()) # runs in *only* one process
        print(res.get(timeout=1))             # prints the PID of that process
        # launching multiple evaluations asynchronously *may* use more processes
        multiple_results = [pool.apply_async(os.getpid, ()) for i in range(4)]
        print([res.get(timeout=1) for res in multiple_results])
        # make a single worker sleep for 10 secs
        res = pool.apply_async(time.sleep, (10,))
        try:
            print(res.get(timeout=1))
        except TimeoutError:
            print("We lacked patience and got a multiprocessing.TimeoutError")
        print("For the moment, the pool remains available for more work")
    # exiting the 'with'-block has stopped the pool
    print("Now the pool is closed and no longer available")

请注意,进程池的方法只能由创建它的进程使用。

注解

这个包中的功能要求子进程可以导入 __main__ 模块。虽然这在 编程指导 中有描述,但还是需要提前说明一下。这意味着一些示例在交互式解释器中不起作用,比如 multiprocessing.pool.Pool 示例。例如:

>>> from multiprocessing import Pool
>>> p = Pool(5)
>>> def f(x):
...     return x*x
...
>>> with p:
...   p.map(f, [1,2,3])
Process PoolWorker-1:
Process PoolWorker-2:
Process PoolWorker-3:
Traceback (most recent call last):
AttributeError: 'module' object has no attribute 'f'
AttributeError: 'module' object has no attribute 'f'
AttributeError: 'module' object has no attribute 'f'

(如果尝试执行上面的代码,它会以一种半随机的方式将三个完整的堆栈内容交替输出,然后你只能以某种方式停止父进程。)

参考

multiprocessing 包主要复制了 threading 模块的API。

Process 和异常

class multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={}, **, daemon=None*)

进程对象表示在单独进程中运行的活动。 Process 类拥有和 threading.Thread 等价的大部分方法。

应始终使用关键字参数调用构造函数。 group 应该始终是 None ;它仅用于兼容 threading.Threadtarget 是由 run() 方法调用的可调用对象。它默认为 None ,意味着什么都没有被调用。 name 是进程名称。 args 是目标调用的参数元组。 kwargs 是目标调用的关键字参数字典。如果提供,则键参数 daemon 将进程 daemon 标志设置为 TrueFalse 。如果是 None (默认值),则该标志将从创建的进程继承。

默认情况下,不会将任何参数传递给 target

如果子类重写构造函数,它必须确保它在对进程执行任何其他操作之前调用基类构造函数( Process.__init__() )。

在 3.3 版更改: 加入 daemon 参数。

  • run()

    表示进程活动的方法。

    你可以在子类中重载此方法。标准 run() 方法调用传递给对象构造函数的可调用对象作为目标参数(如果有),分别从 argskwargs 参数中获取顺序和关键字参数。

  • start()

    启动进程活动。

    这个方法每个进程对象最多只能调用一次。它会将对象的 run() 方法安排在一个单独的进程中调用。

  • join([timeout])

    如果可选参数 timeoutNone (默认值),则该方法将阻塞,直到调用 join() 方法的进程终止。如果 timeout 是一个正数,它最多会阻塞 timeout 秒。请注意,如果进程终止或方法超时,则该方法返回 None 。检查进程的 exitcode 以确定它是否终止。

    一个进程可以被 join 多次。

    进程无法join自身,因为这会导致死锁。尝试在启动进程之前join进程是错误的。

  • name

    进程的名称。该名称是一个字符串,仅用于识别目的。它没有语义。可以为多个进程指定相同的名称。

    初始名称由构造器设定。 如果没有为构造器提供显式名称,则会构造一个形式为 ‘Process-N1:N2:…:Nk’ 的名称,其中每个 Nk 是其父亲的第 N 个孩子。

  • is_alive()

    返回进程是否还活着。

    粗略地说,从 start() 方法返回到子进程终止之前,进程对象仍处于活动状态。

  • daemon

    进程的守护标志,一个布尔值。这必须在 start() 被调用之前设置。

    初始值继承自创建进程。

    当进程退出时,它会尝试终止其所有守护进程子进程。

    请注意,不允许在守护进程中创建子进程。这是因为当守护进程由于父进程退出而中断时,其子进程会变成孤儿进程。 另外,这些 不是 Unix 守护进程或服务,它们是正常进程,如果非守护进程已经退出,它们将被终止(并且不被合并)。

除了 threading.Thread API ,Process 对象还支持以下属性和方法:

  • pid

    返回进程ID。在生成该进程之前,这将是 None

  • exitcode

    子进程的退出代码。如果进程尚未终止,这将是 None 。负值 -N 表示子进程被信号 N 终止。

  • authkey

    进程的身份验证密钥(字节字符串)。

    multiprocessing 初始化时,主进程使用 os.urandom() 分配一个随机字符串。

    当创建 Process 对象时,它将继承其父进程的身份验证密钥,尽管可以通过将 authkey 设置为另一个字节字符串来更改。

  • sentinel

    系统对象的数字句柄,当进程结束时将变为 “ready” 。

    如果要使用 multiprocessing.connection.wait() 一次等待多个事件,可以使用此值。否则调用 join() 更简单。

    在Windows上,这是一个操作系统句柄,可以与 WaitForSingleObjectWaitForMultipleObjects 系列API调用一起使用。在Unix上,这是一个文件描述符,可以使用来自 select 模块的原语。

    3.3 新版功能.

  • terminate()

    终止进程。 在Unix上,这是使用 SIGTERM 信号完成的;在Windows上使用 TerminateProcess() 。 请注意,不会执行退出处理程序和finally子句等。

    请注意,进程的后代进程将不会被终止 —— 它们将简单地变成孤立的。

    警告

    如果在关联进程使用管道或队列时使用此方法,则管道或队列可能会损坏,并可能无法被其他进程使用。类似地,如果进程已获得锁或信号量等,则终止它可能导致其他进程死锁。

  • kill()

    terminate() 相同,但在Unix上使用 SIGKILL 信号。

    3.7 新版功能.

  • close()

    关闭 Process 对象,释放与之关联的所有资源。如果底层进程仍在运行,则会引发 ValueError 。一旦 close() 成功返回, Process 对象的大多数其他方法和属性将引发 ValueError

    3.7 新版功能.

注意 start()join()is_alive()terminate()exitcode 方法只能由创建进程对象的进程调用。

Process 一些方法的示例用法:

 >>> import multiprocessing, time, signal
 >>> p = multiprocessing.Process(target=time.sleep, args=(1000,))
 >>> print(p, p.is_alive())
 <Process ... initial> False
 >>> p.start()
 >>> print(p, p.is_alive())
 <Process ... started> True
 >>> p.terminate()
 >>> time.sleep(0.1)
 >>> print(p, p.is_alive())
 <Process ... stopped exitcode=-SIGTERM> False
 >>> p.exitcode == -signal.SIGTERM
 True

exception multiprocessing.ProcessError

所有 multiprocessing 异常的基类。

exception multiprocessing.BufferTooShort

当提供的缓冲区对象太小而无法读取消息时, Connection.recv_bytes_into() 引发的异常。

如果 e 是一个 BufferTooShort 实例,那么 e.args[0] 将把消息作为字节字符串给出。

exception multiprocessing.AuthenticationError

出现身份验证错误时引发。

exception multiprocessing.TimeoutError

有超时的方法超时时引发。

管道和队列

使用多进程时,一般使用消息机制实现进程间通信,尽可能避免使用同步原语,例如锁。

消息机制包含: Pipe() (可以用于在两个进程间传递消息),以及队列(能够在多个生产者和消费者之间通信)。

Queue, SimpleQueue 以及 JoinableQueue 都是多生产者,多消费者,并且实现了 FIFO 的队列类型,其表现与标准库中的 queue.Queue 类相似。 不同之处在于 Queue 缺少标准库的 queue.Queue 从 Python 2.5 开始引入的 task_done()join() 方法。

如果你使用了 JoinableQueue ,那么你 必须 对每个已经移出队列的任务调用 JoinableQueue.task_done()。 不然的话用于统计未完成任务的信号量最终会溢出并抛出异常。

另外还可以通过使用一个管理器对象创建一个共享队列 。

注解

multiprocessing 使用了普通的 queue.Emptyqueue.Full 异常去表示超时。 你需要从 queue 中导入它们,因为它们并不在 multiprocessing 的命名空间中。

注解

当一个对象被放入一个队列中时,这个对象首先会被一个后台线程用 pickle 序列化,并将序列化后的数据通过一个底层管道的管道传递到队列中。 这种做法会有点让人惊讶,但一般不会出现什么问题。 如果它们确实妨碍了你,你可以使用一个由管理器 manager 创建的队列替换它。

  1. 将一个对象放入一个空队列后,可能需要极小的延迟,队列的方法 empty() 才会返回 False 。而 get_nowait() 可以不抛出 queue.Empty 直接返回。
  2. 如果有多个进程同时将对象放入队列,那么在队列的另一端接受到的对象可能是无序的。但是由同一个进程放入的多个对象的顺序在另一端输出时总是一样的。

警告

如果一个进程在尝试使用 Queue 期间被 Process.terminate()os.kill() 调用终止了,那么队列中的数据很可能被破坏。 这可能导致其他进程在尝试使用该队列时发生异常。

警告

正如刚才提到的,如果一个子进程将一些对象放进队列中 (并且它没有用 JoinableQueue.cancel_join_thread 方法),那么这个进程在所有缓冲区的对象被刷新进管道之前,是不会终止的。

这意味着,除非你确定所有放入队列中的对象都已经被消费了,否则如果你试图等待这个进程,你可能会陷入死锁中。相似地,如果该子进程不是后台进程,那么父进程可能在试图等待所有非后台进程退出时挂起。

注意用管理器创建的队列不存在这个问题。

multiprocessing.Pipe([duplex])

返回一对 Connection 对象 (conn1, conn2) , 分别表示管道的两端。

如果 duplex 被置为 True (默认值),那么该管道是双向的。如果 duplex 被置为 False ,那么该管道是单向的,即 conn1 只能用于接收消息,而 conn2 仅能用于发送消息。

class multiprocessing.Queue([maxsize])

返回一个使用一个管道和少量锁和信号量实现的共享队列实例。当一个进程将一个对象放进队列中时,一个写入线程会启动并将对象从缓冲区写入管道中。

一旦超时,将抛出标准库 queue 模块中常见的异常 queue.Emptyqueue.Full

除了 task_done()join() 之外,Queue 实现了标准库类 queue.Queue 中所有的方法。

  • qsize()

    返回队列的大致长度。由于多线程或者多进程的上下文,这个数字是不可靠的。

    Note that this may raise NotImplementedError on Unix platforms like macOS where sem_getvalue() is not implemented.

  • empty()

    如果队列是空的,返回 True ,反之返回 False 。 由于多线程或多进程的环境,该状态是不可靠的。

  • full()

    如果队列是满的,返回 True ,反之返回 False 。 由于多线程或多进程的环境,该状态是不可靠的。

  • put(obj[, block[, timeout]])

    将 obj 放入队列。如果可选参数 blockTrue (默认值) 而且 timeoutNone (默认值), 将会阻塞当前进程,直到有空的缓冲槽。如果 timeout 是正数,将会在阻塞了最多 timeout 秒之后还是没有可用的缓冲槽时抛出 queue.Full 异常。反之 (blockFalse 时),仅当有可用缓冲槽时才放入对象,否则抛出 queue.Full 异常 (在这种情形下 timeout 参数会被忽略)。

    在 3.8 版更改: 如果队列已经关闭,会抛出 ValueError 而不是 AssertionError

  • put_nowait(obj)

    相当于 put(obj, False)

  • get([block[, timeout]])

    从队列中取出并返回对象。如果可选参数 blockTrue (默认值) 而且 timeoutNone (默认值), 将会阻塞当前进程,直到队列中出现可用的对象。如果 timeout 是正数,将会在阻塞了最多 timeout 秒之后还是没有可用的对象时抛出 queue.Empty 异常。反之 (blockFalse 时),仅当有可用对象能够取出时返回,否则抛出 queue.Empty 异常 (在这种情形下 timeout 参数会被忽略)。

    在 3.8 版更改: 如果队列已经关闭,会抛出 ValueError 而不是 OSError

  • get_nowait()

    相当于 get(False)

multiprocessing.Queue 类有一些在 queue.Queue 类中没有出现的方法。这些方法在大多数情形下并不是必须的。

  • close()

    指示当前进程将不会再往队列中放入对象。一旦所有缓冲区中的数据被写入管道之后,后台的线程会退出。这个方法在队列被gc回收时会自动调用。

  • join_thread()

    等待后台线程。这个方法仅在调用了 close() 方法之后可用。这会阻塞当前进程,直到后台线程退出,确保所有缓冲区中的数据都被写入管道中。

    默认情况下,如果一个不是队列创建者的进程试图退出,它会尝试等待这个队列的后台线程。这个进程可以使用 cancel_join_thread()join_thread() 方法什么都不做直接跳过。

  • cancel_join_thread()

    防止 join_thread() 方法阻塞当前进程。具体而言,这防止进程退出时自动等待后台线程退出。

    这个方法更好的名字可能是 allow_exit_without_flush()。 这可能会导致已排入队列的数据丢失,几乎可以肯定你将不需要用到这个方法。 实际上它仅适用于当你需要当前进程立即退出而不必等待将已排入的队列更新到下层管道,并且你不担心丢失数据的时候。

注解

该类的功能依赖于宿主操作系统具有可用的共享信号量实现。否则该类将被禁用,任何试图实例化一个 Queue 对象的操作都会抛出 ImportError 异常 。后续说明的任何专用队列对象亦如此。

class multiprocessing.SimpleQueue

这是一个简化的 Queue 类的实现,很像带锁的 Pipe

  • close()

    关闭队列:释放内部资源。

    队列在被关闭后就不可再被使用。 例如不可再调用 get(), put()empty() 等方法。

    3.9 新版功能.

  • empty()

    如果队列为空返回 True ,否则返回 False

  • get()

    从队列中移出并返回一个对象。

  • put(item)

    item 放入队列。

class multiprocessing.JoinableQueue([maxsize])

JoinableQueue 类是 Queue 的子类,额外添加了 task_done()join() 方法。

  • task_done()

    指出之前进入队列的任务已经完成。由队列的消费者进程使用。对于每次调用 get() 获取的任务,执行完成后调用 task_done() 告诉队列该任务已经处理完成。

    如果 join() 方法正在阻塞之中,该方法会在所有对象都被处理完的时候返回 (即对之前使用 put() 放进队列中的所有对象都已经返回了对应的 task_done() ) 。

    如果被调用的次数多于放入队列中的项目数量,将引发 ValueError 异常 。

  • join()

    阻塞至队列中所有的元素都被接收和处理完毕。

    当条目添加到队列的时候,未完成任务的计数就会增加。每当消费者进程调用 task_done() 表示这个条目已经被回收,该条目所有工作已经完成,未完成计数就会减少。当未完成计数降到零的时候, join() 阻塞被解除。

杂项

multiprocessing.active_children()

返回当前进程存活的子进程的列表。

调用该方法有“等待”已经结束的进程的副作用。

multiprocessing.cpu_count()

返回系统的CPU数量。

该数量不同于当前进程可以使用的CPU数量。可用的CPU数量可以由 len(os.sched_getaffinity(0)) 方法获得。

When the number of CPUs cannot be determined a NotImplementedError is raised.

参见

os.cpu_count()

multiprocessing.current_process()

返回与当前进程相对应的 Process 对象。

threading.current_thread() 相同。

multiprocessing.parent_process()

返回父进程 Process 对象,和父进程调用 current_process() 返回的对象一样。如果一个进程已经是主进程, parent_process 会返回 None.

3.8 新版功能.

multiprocessing.freeze_support()

为使用了 multiprocessing 的程序,提供冻结以产生 Windows 可执行文件的支持。(在 py2exe, PyInstallercx_Freeze 上测试通过)

需要在 main 模块的 if __name__ == '__main__' 该行之后马上调用该函数。例如:

from multiprocessing import Process, freeze_support
def f():
    print('hello world!')
if __name__ == '__main__':
    freeze_support()
    Process(target=f).start()

如果没有调用 freeze_support() 在尝试运行被冻结的可执行文件时会抛出 RuntimeError 异常。

freeze_support() 的调用在非 Windows 平台上是无效的。如果该模块在 Windows 平台的 Python 解释器中正常运行 (该程序没有被冻结), 调用freeze_support() 也是无效的。

multiprocessing.get_all_start_methods()

返回支持的启动方法的列表,该列表的首项即为默认选项。可能的启动方法有 'fork''spawn'‘forkserver’ 。在 Windows 中,只有 'spawn' 是可用的。 Unix 平台总是支持 'fork''spawn' ,且 'fork' 是默认值。

3.4 新版功能.

multiprocessing.get_context(method=None)

返回一个 Context 对象。该对象具有和 multiprocessing 模块相同的API。

如果 method 设置成 None 那么将返回默认上下文对象。否则 method 应该是 'fork', 'spawn', 'forkserver' 。 如果指定的启动方法不存在,将抛出 ValueError 异常。

3.4 新版功能.

multiprocessing.get_start_method(allow_none=False)

返回启动进程时使用的启动方法名。

如果启动方法已经固定,并且 allow_none 被设置成 False ,那么启动方法将被固定为默认的启动方法,并且返回其方法名。如果启动方法没有设定,并且 allow_none 被设置成 True ,那么将返回 None

The return value can be 'fork', 'spawn', 'forkserver' or None. 'fork' is the default on Unix, while 'spawn' is the default on Windows and macOS.

在 3.8 版更改: 对于 macOS,spawn 启动方式是默认方式。 因为 fork 可能导致subprocess崩溃,被认为是不安全的,查看 bpo-33725

3.4 新版功能.

multiprocessing.set_executable()

设置在启动子进程时使用的 Python 解释器路径。 ( 默认使用 sys.executable ) 嵌入式编程人员可能需要这样做:

set_executable(os.path.join(sys.exec_prefix, 'pythonw.exe'))

以使他们可以创建子进程。

在 3.4 版更改: 现在在 Unix 平台上使用 'spawn' 启动方法时支持调用该方法。

multiprocessing.set_start_method(method)

设置启动子进程的方法。 method 可以是 'fork' , 'spawn' 或者 'forkserver'

注意这最多只能调用一次,并且需要藏在 main 模块中,由 if __name__ == '__main__' 保护着。

3.4 新版功能.

注解

multiprocessing 并没有包含类似 threading.active_count() , threading.enumerate() , threading.settrace() , threading.setprofile(), threading.Timer , 或者 threading.local 的方法和类。

连接对象(Connection)

Connection 对象允许收发可以序列化的对象或字符串。它们可以看作面向消息的连接套接字。

通常使用 Pipe 创建 Connection 对象。

class multiprocessing.connection.Connection

  • send(obj)

    将一个对象发送到连接的另一端,可以用 recv() 读取。

    发送的对象必须是可以序列化的,过大的对象 ( 接近 32MiB+ ,这个值取决于操作系统 ) 有可能引发 ValueError 异常。

  • recv()

    返回一个由另一端使用 send() 发送的对象。该方法会一直阻塞直到接收到对象。 如果对端关闭了连接或者没有东西可接收,将抛出 EOFError 异常。

  • fileno()

    返回由连接对象使用的描述符或者句柄。

  • close()

    关闭连接对象。

    当连接对象被垃圾回收时会自动调用。

  • poll([timeout])

    返回连接对象中是否有可以读取的数据。

    如果未指定 timeout ,此方法会马上返回。如果 timeout 是一个数字,则指定了最大阻塞的秒数。如果 timeoutNone,那么将一直等待,不会超时。

    注意通过使用 multiprocessing.connection.wait() 可以一次轮询多个连接对象。

  • send_bytes(buffer[, offset[, size]])

    从一个 bytes-like object 对象中取出字节数组并作为一条完整消息发送。

    如果由 offset 给定了在 buffer 中读取数据的位置。 如果给定了 size ,那么将会从缓冲区中读取多个字节。 过大的缓冲区 ( 接近 32MiB+ ,此值依赖于操作系统 ) 有可能引发 ValueError 异常。

  • recv_bytes([maxlength])

    以字符串形式返回一条从连接对象另一端发送过来的字节数据。此方法在接收到数据前将一直阻塞。 如果连接对象被对端关闭或者没有数据可读取,将抛出 EOFError 异常。

    如果给定了 maxlength 并且消息长于 maxlength 那么将抛出 OSError 并且该连接对象将不再可读。

    在 3.3 版更改: 曾经该函数抛出 IOError ,现在这是 OSError 的别名。

  • recv_bytes_into(buffer[, offset])

    将一条完整的字节数据消息读入 buffer 中并返回消息的字节数。 此方法在接收到数据前将一直阻塞。 如果连接对象被对端关闭或者没有数据可读取,将抛出 EOFError 异常。

    buffer must be a writable bytes-like object. If offset is given then the message will be written into the buffer from that position. Offset must be a non-negative integer less than the length of buffer (in bytes).

    如果缓冲区太小,则将引发 BufferTooShort 异常,并且完整的消息将会存放在异常实例 ee.args[0] 中。

在 3.3 版更改: 现在连接对象自身可以通过 Connection.send()Connection.recv() 在进程之间传递。

3.3 新版功能: 连接对象现已支持上下文管理协议 。 __enter__() 返回连接对象, __exit__() 会调用 close()

例如:

>>> from multiprocessing import Pipe
>>> a, b = Pipe()
>>> a.send([1, 'hello', None])
>>> b.recv()
[1, 'hello', None]
>>> b.send_bytes(b'thank you')
>>> a.recv_bytes()
b'thank you'
>>> import array
>>> arr1 = array.array('i', range(5))
>>> arr2 = array.array('i', [0] * 10)
>>> a.send_bytes(arr1)
>>> count = b.recv_bytes_into(arr2)
>>> assert count == len(arr1) * arr1.itemsize
>>> arr2
array('i', [0, 1, 2, 3, 4, 0, 0, 0, 0, 0])

警告

Connection.recv() 方法会自动解封它收到的数据,除非你能够信任发送消息的进程,否则此处可能有安全风险。

因此, 除非连接对象是由 Pipe() 产生的,否则你应该仅在使用了某种认证手段之后才使用 recv()send() 方法。

警告

如果一个进程在试图读写管道时被终止了,那么管道中的数据很可能是不完整的,因为此时可能无法确定消息的边界。

同步原语

通常来说同步原语在多进程环境中并不像它们在多线程环境中那么必要。

注意可以使用管理器对象创建同步原语。

class multiprocessing.Barrier(parties[, action[, timeout]])

类似 threading.Barrier 的栅栏对象。

3.3 新版功能.

class multiprocessing.BoundedSemaphore([value])

非常类似 threading.BoundedSemaphore 的有界信号量对象。

一个小小的不同在于,它的 acquire 方法的第一个参数名是和 Lock.acquire() 一样的 block

注解

On macOS, this is indistinguishable from Semaphore because sem_getvalue() is not implemented on that platform.

class multiprocessing.Condition([lock])

条件变量: threading.Condition 的别名。

指定的 lock 参数应该是 multiprocessing 模块中的 Lock 或者 RLock 对象。

在 3.3 版更改: 新增了 wait_for() 方法。

class multiprocessing.Event

A clone of threading.Event.

class multiprocessing.Lock

原始锁(非递归锁)对象,类似于 threading.Lock 。一旦一个进程或者线程拿到了锁,后续的任何其他进程或线程的其他请求都会被阻塞直到锁被释放。任何进程或线程都可以释放锁。除非另有说明,否则 multiprocessing.Lock 用于进程或者线程的概念和行为都和 threading.Lock 一致。

注意 Lock 实际上是一个工厂函数。它返回由默认上下文初始化的 multiprocessing.synchronize.Lock 对象。

Lock supports the context manager protocol and thus may be used in with statements.

  • acquire(block=True, timeout=None)

    可以阻塞或非阻塞地获得锁。

    如果 block 参数被设为 True ( 默认值 ) , 对该方法的调用在锁处于释放状态之前都会阻塞,然后将锁设置为锁住状态并返回 True 。需要注意的是第一个参数名与 threading.Lock.acquire() 的不同。

    如果 block 参数被设置成 False ,方法的调用将不会阻塞。 如果锁当前处于锁住状态,将返回 False ; 否则将锁设置成锁住状态,并返回 True

    timeout 是一个正浮点数时,会在等待锁的过程中最多阻塞等待 timeout 秒,当 timeout 是负数时,效果和 timeout 为0时一样,当 timeoutNone (默认值)时,等待时间是无限长。需要注意的是,对于 timeout 参数是负数和 None 的情况, 其行为与 threading.Lock.acquire() 是不一样的。当 block 参数 为 False 时, timeout 并没有实际用处,会直接忽略。否则,函数会在拿到锁后返回 True 或者 超时没拿到锁后返回 False

  • release()

    释放锁,可以在任何进程、线程使用,并不限于锁的拥有者。

    当尝试释放一个没有被持有的锁时,会抛出 ValueError 异常,除此之外其行为与 threading.Lock.release() 一样。

class multiprocessing.RLock

递归锁对象: 类似于 threading.RLock 。递归锁必须由持有线程、进程亲自释放。如果某个进程或者线程拿到了递归锁,这个进程或者线程可以再次拿到这个锁而不需要等待。但是这个进程或者线程的拿锁操作和释放锁操作的次数必须相同。

注意 RLock 是一个工厂函数,调用后返回一个使用默认 context 初始化的 multiprocessing.synchronize.RLock 实例。

RLock 支持 context manager 协议,因此可在 with 语句内使用。

  • acquire(block=True, timeout=None)

    可以阻塞或非阻塞地获得锁。

    block 参数设置为 True 时,会一直阻塞直到锁处于空闲状态(没有被任何进程、线程拥有),除非当前进程或线程已经拥有了这把锁。然后当前进程/线程会持有这把锁(在锁没有其他持有者的情况下),锁内的递归等级加一,并返回 True . 注意, 这个函数第一个参数的行为和 threading.RLock.acquire() 的实现有几个不同点,包括参数名本身。

    block 参数是 False , 将不会阻塞,如果此时锁被其他进程或者线程持有,当前进程、线程获取锁操作失败,锁的递归等级也不会改变,函数返回 False , 如果当前锁已经处于释放状态,则当前进程、线程则会拿到锁,并且锁内的递归等级加一,函数返回 True

    timeout 参数的使用方法及行为与 Lock.acquire() 一样。但是要注意 timeout 的其中一些行为和 threading.RLock.acquire() 中实现的行为是不同的。

  • release()

    释放锁,使锁内的递归等级减一。如果释放后锁内的递归等级降低为0,则会重置锁的状态为释放状态(即没有被任何进程、线程持有),重置后如果有有其他进程和线程在等待这把锁,他们中的一个会获得这个锁而继续运行。如果释放后锁内的递归等级还没到达0,则这个锁仍将保持未释放状态且当前进程和线程仍然是持有者。

    只有当前进程或线程是锁的持有者时,才允许调用这个方法。如果当前进程或线程不是这个锁的拥有者,或者这个锁处于已释放的状态(即没有任何拥有者),调用这个方法会抛出 AssertionError 异常。注意这里抛出的异常类型和 threading.RLock.release() 中实现的行为不一样。

class multiprocessing.Semaphore([value])

一种信号量对象: 类似于 threading.Semaphore.

一个小小的不同在于,它的 acquire 方法的第一个参数名是和 Lock.acquire() 一样的 block

注解

On macOS, sem_timedwait is unsupported, so calling acquire() with a timeout will emulate that function’s behavior using a sleeping loop.

注解

假如信号 SIGINT 是来自于 Ctrl-C ,并且主线程被 BoundedSemaphore.acquire(), Lock.acquire(), RLock.acquire(), Semaphore.acquire(), Condition.acquire()Condition.wait() 阻塞,则调用会立即中断同时抛出 KeyboardInterrupt 异常。

这和 threading 的行为不同,此模块中当执行对应的阻塞式调用时,SIGINT 会被忽略。

注解

这个包的某些功能依赖于宿主机系统的共享信号量的实现,如果系统没有这个特性, multiprocessing.synchronize 会被禁用,尝试导入这个模块会引发 ImportError 异常,详细信息请查看 bpo-3770

共享 ctypes 对象

在共享内存上创建可被子进程继承的共享对象时是可行的。

multiprocessing.Value(typecode_or_type, \args, lock=True*)

返回一个从共享内存上创建的 ctypes 对象。默认情况下返回的对象实际上是经过了同步器包装过的。可以通过 Valuevalue 属性访问这个对象本身。

typecode_or_type 指明了返回的对象类型: 它可能是一个 ctypes 类型或者 array 模块中每个类型对应的单字符长度的字符串。 \args* 会透传给这个类的构造函数。

如果 lock 参数是 True (默认值), 将会新建一个递归锁用于同步对于此值的访问操作。 如果 lockLock 或者 RLock 对象,那么这个传入的锁将会用于同步对这个值的访问操作,如果 lockFalse , 那么对这个对象的访问将没有锁保护,也就是说这个变量不是进程安全的。

诸如 += 这类的操作会引发独立的读操作和写操作,也就是说这类操作符并不具有原子性。所以,如果你想让递增共享变量的操作具有原子性,仅仅以这样的方式并不能达到要求:

counter.value += 1

共享对象内部关联的锁是递归锁(默认情况下就是)的情况下, 你可以采用这种方式

with counter.get_lock():    
    counter.value += 1

注意 lock 只能是命名参数。

multiprocessing.Array(typecode_or_type, size_or_initializer, **, lock=True*)

从共享内存中申请并返回一个具有ctypes类型的数组对象。默认情况下返回值实际上是被同步器包装过的数组对象。

typecode_or_type 指明了返回的数组中的元素类型: 它可能是一个 ctypes 类型或者 array 模块中每个类型对应的单字符长度的字符串。 如果 size_or_initializer 是一个整数,那就会当做数组的长度,并且整个数组的内存会初始化为0。否则,如果 size_or_initializer 会被当成一个序列用于初始化数组中的每一个元素,并且会根据元素个数自动判断数组的长度。

如果 lockTrue (默认值) 则将创建一个新的锁对象用于同步对值的访问。 如果 lock 为一个 LockRLock 对象则该对象将被用于同步对值的访问。 如果 lockFalse 则对返回对象的访问将不会自动得到锁的保护,也就是说它不是“进程安全的”。

请注意 lock 是一个仅限关键字参数。

请注意 ctypes.c_char 的数组具有 valueraw 属性,允许被用来保存和提取字符串。

multiprocessing.sharedctypes 模块

multiprocessing.sharedctypes 模块提供了一些函数,用于分配来自共享内存的、可被子进程继承的 ctypes 对象。

注解

虽然可以将指针存储在共享内存中,但请记住它所引用的是特定进程地址空间中的位置。 而且,指针很可能在第二个进程的上下文中无效,尝试从第二个进程对指针进行解引用可能会导致崩溃。

multiprocessing.sharedctypes.RawArray(typecode_or_type, size_or_initializer)

从共享内存中申请并返回一个 ctypes 数组。

typecode_or_type 指明了返回的数组中的元素类型: 它可能是一个 ctypes 类型或者 array 模块中使用的类型字符。 如果 size_or_initializer 是一个整数,那就会当做数组的长度,并且整个数组的内存会初始化为0。否则,如果 size_or_initializer 会被当成一个序列用于初始化数组中的每一个元素,并且会根据元素个数自动判断数组的长度。

注意对元素的访问、赋值操作可能是非原子操作 - 使用 Array() , 从而借助其中的锁保证操作的原子性。

multiprocessing.sharedctypes.RawValue(typecode_or_type, \args*)

从共享内存中申请并返回一个 ctypes 对象。

typecode_or_type 指明了返回的对象类型: 它可能是一个 ctypes 类型或者 array 模块中每个类型对应的单字符长度的字符串。 \args* 会透传给这个类的构造函数。

注意对 value 的访问、赋值操作可能是非原子操作 - 使用 Value() ,从而借助其中的锁保证操作的原子性。

请注意 ctypes.c_char 的数组具有 valueraw 属性,允许被用来保存和提取字符串 。

multiprocessing.sharedctypes.Array(typecode_or_type, size_or_initializer, **, lock=True*)

返回一个纯 ctypes 数组, 或者在此之上经过同步器包装过的进程安全的对象,这取决于 lock 参数的值,除此之外,和 RawArray() 一样。

如果 lockTrue (默认值) 则将创建一个新的锁对象用于同步对值的访问。 如果 lock 为一个 LockRLock 对象则该对象将被用于同步对值的访问。 如果 lockFalse 则对所返回对象的访问将不会自动得到锁的保护,也就是说它将不是“进程安全的”。

注意 lock 只能是命名参数。

multiprocessing.sharedctypes.Value(typecode_or_type, \args, lock=True*)

返回一个纯 ctypes 数组, 或者在此之上经过同步器包装过的进程安全的对象,这取决于 lock 参数的值,除此之外,和 RawArray() 一样。

如果 lockTrue (默认值) 则将创建一个新的锁对象用于同步对值的访问。 如果 lock 为一个 LockRLock 对象则该对象将被用于同步对值的访问。 如果 lockFalse 则对所返回对象的访问将不会自动得到锁的保护,也就是说它将不是“进程安全的”。

注意 lock 只能是命名参数。

multiprocessing.sharedctypes.copy(obj)

从共享内存中申请一片空间将 ctypes 对象 obj 过来,然后返回一个新的 ctypes 对象。

multiprocessing.sharedctypes.synchronized(obj[, lock])

将一个 ctypes 对象包装为进程安全的对象并返回,使用 lock 同步对于它的操作。如果 lockNone (默认值) ,则会自动创建一个 multiprocessing.RLock 对象。

同步器包装后的对象会在原有对象基础上额外增加两个方法: get_obj() 返回被包装的对象, get_lock() 返回内部用于同步的锁。

需要注意的是,访问包装后的ctypes对象会比直接访问原来的纯 ctypes 对象慢得多。

在 3.5 版更改: 同步器包装后的对象支持 context manager 协议。

下面的表格对比了创建普通ctypes对象和基于共享内存上创建共享ctypes对象的语法。(表格中的 MyStructctypes.Structure 的子类)

ctypes 使用类型的共享ctypes 使用 typecode 的共享 ctypes
c_double(2.4) RawValue(c_double, 2.4) RawValue(‘d’, 2.4)
MyStruct(4, 6) RawValue(MyStruct, 4, 6)
(c_short 7)() RawArray(c_short, 7) RawArray(‘h’, 7)
(c_int 3)(9, 2, 8) RawArray(c_int, (9, 2, 8)) RawArray(‘i’, (9, 2, 8))

下面是一个在子进程中修改多个ctypes对象的例子。

from multiprocessing import Process, Lock
from multiprocessing.sharedctypes import Value, Array
from ctypes import Structure, c_double
class Point(Structure):
    _fields_ = [('x', c_double), ('y', c_double)]
def modify(n, x, s, A):
    n.value **= 2
    x.value **= 2
    s.value = s.value.upper()
    for a in A:
        a.x **= 2
        a.y **= 2
if __name__ == '__main__':
    lock = Lock()
    n = Value('i', 7)
    x = Value(c_double, 1.0/3.0, lock=False)
    s = Array('c', b'hello world', lock=lock)
    A = Array(Point, [(1.875,-6.25), (-5.75,2.0), (2.375,9.5)], lock=lock)
    p = Process(target=modify, args=(n, x, s, A))
    p.start()
    p.join()
    print(n.value)
    print(x.value)
    print(s.value)
    print([(a.x, a.y) for a in A])

输出如下

49
0.1111111111111111
HELLO WORLD
[(3.515625, 39.0625), (33.0625, 4.0), (5.640625, 90.25)]

管理器

管理器提供了一种创建共享数据的方法,从而可以在不同进程中共享,甚至可以通过网络跨机器共享数据。管理器维护一个用于管理 共享对象 的服务。其他进程可以通过代理访问这些共享对象。

multiprocessing.Manager()

返回一个已启动的 SyncManager 管理器对象,这个对象可以用于在不同进程中共享数据。返回的管理器对象对应了一个已经启动的子进程,并且拥有一系列方法可以用于创建共享对象、返回对应的代理。

当管理器被垃圾回收或者父进程退出时,管理器进程会立即退出。管理器类定义在 multiprocessing.managers 模块:

class multiprocessing.managers.BaseManager([address[, authkey]])

创建一个 BaseManager 对象。

一旦创建,应该及时调用 start() 或者 get_server().serve_forever() 以确保管理器对象对应的管理进程已经启动。

address 是管理器服务进程监听的地址。如果 addressNone ,则允许和任意主机的请求建立连接。

authkey 是认证标识,用于检查连接服务进程的请求合法性。如果 authkeyNone, 则会使用 current_process().authkey , 否则,就使用 authkey , 需要保证它必须是 byte 类型的字符串。

  • start([initializer[, initargs]])

    为管理器开启一个子进程,如果 initializer 不是 None , 子进程在启动时将会调用 initializer(*initargs)

  • get_server()

    返回一个 Server 对象,它是管理器在后台控制的真实的服务。 Server 对象拥有 serve_forever() 方法。

    >>> from multiprocessing.managers import BaseManager
    >>> manager = BaseManager(address=('', 50000), authkey=b'abc')
    >>> server = manager.get_server()
    >>> server.serve_forever()

    Server 额外拥有一个 address 属性。

  • connect()

    将本地管理器对象连接到一个远程管理器进程:

    >>> from multiprocessing.managers import BaseManager
    >>> m = BaseManager(address=('127.0.0.1', 50000), authkey=b'abc')
    >>> m.connect()
  • shutdown()

    停止管理器的进程。这个方法只能用于已经使用 start() 启动的服务进程。

    它可以被多次调用。

  • register(typeid[, callable[, proxytype[, exposed[, method_to_typeid[, create_method]]]]])

    一个 classmethod,可以将一个类型或者可调用对象注册到管理器类。

    typeid 是一种 “类型标识符”,用于唯一表示某种共享对象类型,必须是一个字符串。

    callable 是一个用来为此类型标识符创建对象的可调用对象。如果一个管理器实例将使用 connect() 方法连接到服务器,或者 create_method 参数为 False,那么这里可留下 None

    proxytypeBaseProxy 的子类,可以根据 typeid 为共享对象创建一个代理,如果是 None , 则会自动创建一个代理类。

    exposed 是一个函数名组成的序列,用来指明只有这些方法可以使用 BaseProxy._callmethod() 代理。(如果 exposedNone, 则会在 proxytype._exposed_ 存在的情况下转而使用它) 当暴露的方法列表没有指定的时候,共享对象的所有 “公共方法” 都会被代理。(这里的“公共方法”是指所有拥有 __call__() 方法并且不是以 '_' 开头的属性)

    method_to_typeid 是一个映射,用来指定那些应该返回代理对象的暴露方法所返回的类型。(如果 method_to_typeidNone, 则 proxytype._method_to_typeid_ 会在存在的情况下被使用)如果方法名称不在这个映射中或者映射是 None ,则方法返回的对象会是一个值拷贝。

    create_method 指明,是否要创建一个以 typeid 命名并返回一个代理对象的方法,这个函数会被服务进程用于创建共享对象,默认为 True

BaseManager 实例也有一个只读属性。

  • address

    管理器所用的地址。

在 3.3 版更改: 管理器对象支持上下文管理协议 。__enter__() 启动服务进程(如果它还没有启动)并且返回管理器对象, __exit__() 会调用 shutdown()

在之前的版本中,如果管理器服务进程没有启动, __enter__() 不会负责启动它。

class multiprocessing.managers.SyncManager

BaseManager 的子类,可用于进程的同步。这个类型的对象使用 multiprocessing.Manager() 创建。

它拥有一系列方法,可以为大部分常用数据类型创建并返回 代理对象 代理,用于进程间同步。甚至包括共享列表和字典。

  • Barrier(parties[, action[, timeout]])

    创建一个共享的 threading.Barrier 对象并返回它的代理。

    3.3 新版功能.

  • BoundedSemaphore([value])

    创建一个共享的 threading.BoundedSemaphore 对象并返回它的代理。

  • Condition([lock])

    创建一个共享的 threading.Condition 对象并返回它的代理。

    如果提供了 lock 参数,那它必须是 threading.Lockthreading.RLock 的代理对象。

    在 3.3 版更改: 新增了 wait_for() 方法。

  • Event()

    创建一个共享的 threading.Event 对象并返回它的代理。

  • Lock()

    创建一个共享的 threading.Lock 对象并返回它的代理。

  • Namespace()

    创建一个共享的 Namespace 对象并返回它的代理。

  • Queue([maxsize])

    创建一个共享的 queue.Queue 对象并返回它的代理。

  • RLock()

    创建一个共享的 threading.RLock 对象并返回它的代理。

  • Semaphore([value])

    创建一个共享的 threading.Semaphore 对象并返回它的代理。

  • Array(typecode, sequence)

    创建一个数组并返回它的代理。

  • Value(typecode, value)

    创建一个具有可写 value 属性的对象并返回它的代理。

  • dict()

    dict(mapping)

    dict(sequence)

    创建一个共享的 dict 对象并返回它的代理。

  • list()

    list(sequence)

    创建一个共享的 list 对象并返回它的代理。

在 3.6 版更改: 共享对象能够嵌套。例如, 共享的容器对象如共享列表,可以包含另一个共享对象,他们全都会在 SyncManager 中进行管理和同步。

class multiprocessing.managers.Namespace

一个可以注册到 SyncManager 的类型。

命名空间对象没有公共方法,但是拥有可写的属性。直接print会显示所有属性的值。

值得一提的是,当对命名空间对象使用代理的时候,访问所有名称以 '_' 开头的属性都只是代理器上的属性,而不是命名空间对象的属性。

>>> manager = multiprocessing.Manager()
>>> Global = manager.Namespace()
>>> Global.x = 10
>>> Global.y = 'hello'
>>> Global._z = 12.3    # this is an attribute of the proxy
>>> print(Global)
Namespace(x=10, y='hello')
自定义管理器

要创建一个自定义的管理器,需要新建一个 BaseManager 的子类,然后使用这个管理器类上的 register() 类方法将新类型或者可调用方法注册上去。例如:

from multiprocessing.managers import BaseManager
class MathsClass:
    def add(self, x, y):
        return x + y
    def mul(self, x, y):
        return x * y
class MyManager(BaseManager):
    pass
MyManager.register('Maths', MathsClass)
if __name__ == '__main__':
    with MyManager() as manager:
        maths = manager.Maths()
        print(maths.add(4, 3))         # prints 7
        print(maths.mul(7, 8))         # prints 56
使用远程管理器

可以将管理器服务运行在一台机器上,然后使用客户端从其他机器上访问。(假设它们的防火墙允许)

运行下面的代码可以启动一个服务,此付包含了一个共享队列,允许远程客户端访问:

>>> from multiprocessing.managers import BaseManager
>>> from queue import Queue
>>> queue = Queue()
>>> class QueueManager(BaseManager): pass
>>> QueueManager.register('get_queue', callable=lambda:queue)
>>> m = QueueManager(address=('', 50000), authkey=b'abracadabra')
>>> s = m.get_server()
>>> s.serve_forever()

远程客户端可以通过下面的方式访问服务:

>>> from multiprocessing.managers import BaseManager
>>> class QueueManager(BaseManager): pass
>>> QueueManager.register('get_queue')
>>> m = QueueManager(address=('foo.bar.org', 50000), authkey=b'abracadabra')
>>> m.connect()
>>> queue = m.get_queue()
>>> queue.put('hello')

也可以通过下面的方式:

>>> from multiprocessing.managers import BaseManager
>>> class QueueManager(BaseManager): pass
>>> QueueManager.register('get_queue')
>>> m = QueueManager(address=('foo.bar.org', 50000), authkey=b'abracadabra')
>>> m.connect()
>>> queue = m.get_queue()
>>> queue.get()
'hello'

本地进程也可以访问这个队列,利用上面的客户端代码通过远程方式访问:

>>> from multiprocessing import Process, Queue
>>> from multiprocessing.managers import BaseManager
>>> class Worker(Process):
...     def __init__(self, q):
...         self.q = q
...         super().__init__()
...     def run(self):
...         self.q.put('local hello')
...
>>> queue = Queue()
>>> w = Worker(queue)
>>> w.start()
>>> class QueueManager(BaseManager): pass
...
>>> QueueManager.register('get_queue', callable=lambda: queue)
>>> m = QueueManager(address=('', 50000), authkey=b'abracadabra')
>>> s = m.get_server()
>>> s.serve_forever()

代理对象

代理是一个 指向 其他共享对象的对象,这个对象(很可能)在另外一个进程中。共享对象也可以说是代理 指涉 的对象。多个代理对象可能指向同一个指涉对象。

代理对象代理了指涉对象的一系列方法调用(虽然并不是指涉对象的每个方法都有必要被代理)。通过这种方式,代理的使用方法可以和它的指涉对象一样:

>>> from multiprocessing import Manager
>>> manager = Manager()
>>> l = manager.list([i*i for i in range(10)])
>>> print(l)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> print(repr(l))
<ListProxy object, typeid 'list' at 0x...>
>>> l[4]
16
>>> l[2:5]
[4, 9, 16]

注意,对代理使用 str() 函数会返回指涉对象的字符串表示,但是 repr() 却会返回代理本身的内部字符串表示。

被代理的对象很重要的一点是必须可以被序列化,这样才能允许他们在进程间传递。因此,指涉对象可以包含 代理对象 。这允许管理器中列表、字典或者其他 代理对象 对象之间的嵌套。

>>> a = manager.list()
>>> b = manager.list()
>>> a.append(b)         # referent of a now contains referent of b
>>> print(a, b)
[<ListProxy object, typeid 'list' at ...>] []
>>> b.append('hello')
>>> print(a[0], b)
['hello'] ['hello']

类似地,字典和列表代理也可以相互嵌套:

>>> l_outer = manager.list([ manager.dict() for i in range(2) ])
>>> d_first_inner = l_outer[0]
>>> d_first_inner['a'] = 1
>>> d_first_inner['b'] = 2
>>> l_outer[1]['c'] = 3
>>> l_outer[1]['z'] = 26
>>> print(l_outer[0])
{'a': 1, 'b': 2}
>>> print(l_outer[1])
{'c': 3, 'z': 26}

如果指涉对象包含了普通 listdict 对象,对这些内部可变对象的修改不会通过管理器传播,因为代理无法得知被包含的值什么时候被修改了。但是把存放在容器代理中的值本身是会通过管理器传播的(会触发代理对象中的 __setitem__ )从而有效修改这些对象,所以可以把修改过的值重新赋值给容器代理:

# create a list proxy and append a mutable object (a dictionary)
lproxy = manager.list()
lproxy.append({})
# now mutate the dictionary
d = lproxy[0]
d['a'] = 1
d['b'] = 2
# at this point, the changes to d are not yet synced, but by
# updating the dictionary, the proxy is notified of the change
lproxy[0] = d

在大多是使用情形下,这种实现方式并不比嵌套 代理对象 方便,但是依然演示了对于同步的一种控制级别。

注解

multiprocessing 中的代理类并没有提供任何对于代理值比较的支持。所以,我们会得到如下结果:

>>> manager.list([1,2,3]) == [1,2,3]
False

当需要比较值的时候,应该替换为使用指涉对象的拷贝。

class multiprocessing.managers.BaseProxy

代理对象是 BaseProxy 派生类的实例。

  • _callmethod(methodname[, args[, kwds]])

    调用指涉对象的方法并返回结果。

    如果 proxy 是一个代理且其指涉的是 obj , 那么下面的表达式:

    proxy._callmethod(methodname, args, kwds)

    相当于求取以下表达式的值:

    getattr(obj, methodname)(*args, **kwds)

    于管理器进程。

    返回结果会是一个值拷贝或者一个新的共享对象的代理 - 见函数 BaseManager.register() 中关于参数 method_to_typeid 的文档。

    如果这个调用熬出了异常,则这个异常会被 _callmethod() 透传出来。如果是管理器进程本身抛出的一些其他异常,则会被 _callmethod() 转换为 RemoteError 异常重新抛出。

    特别注意,如果 methodname 没有 暴露 出来,将会引发一个异常。

    _callmethod() 的一个使用示例:

    >>> l = manager.list(range(10))
    >>> l._callmethod('__len__')
    10
    >>> l._callmethod('__getitem__', (slice(2, 7),)) # equivalent to l[2:7]
    [2, 3, 4, 5, 6]
    >>> l._callmethod('__getitem__', (20,))          # equivalent to l[20]
    Traceback (most recent call last):
    ...
    IndexError: list index out of range
  • _getvalue()

    返回指涉对象的一份拷贝。

    如果指涉对象无法序列化,则会抛出一个异常。

  • __repr__()

    返回代理对象的内部字符串表示。

  • __str__()

    返回指涉对象的内部字符串表示。

清理

代理对象使用了一个弱引用回调函数,当它被垃圾回收时,会将自己从拥有此指涉对象的管理器上反注册,

当共享对象没有被任何代理器引用时,会被管理器进程删除。

进程池

可以创建一个进程池,它将使用 Pool 类执行提交给它的任务。

class multiprocessing.pool.Pool([processes[, initializer[, initargs[, maxtasksperchild[, context]]]]])

一个进程池对象,它控制可以提交作业的工作进程池。它支持带有超时和回调的异步结果,以及一个并行的 map 实现。

processes 是要使用的工作进程数目。如果 processesNone,则使用 os.cpu_count() 返回的值。

如果 initializer 不为 None,则每个工作进程将会在启动时调用 initializer(*initargs)

maxtasksperchild 是一个工作进程在它退出或被一个新的工作进程代替之前能完成的任务数量,为了释放未使用的资源。默认的 maxtasksperchildNone,意味着工作进程寿与池齐。

context 可被用于指定启动的工作进程的上下文。通常一个进程池是使用函数 multiprocessing.Pool() 或者一个上下文对象的 Pool() 方法创建的。在这两种情况下, context 都是适当设置的。

注意,进程池对象的方法只有创建它的进程能够调用。

警告

multiprocessing.pool 对象具有需要正确管理的内部资源 (像任何其他资源一样),具体方式是将进程池用作上下文管理器,或者手动调用 close()terminate()。 未做此类操作将导致进程在终结阶段挂起。

请注意依赖垃圾回收器来销毁进程池是 不正确的 做法,因为 CPython 并不保证进程池终结器会被调用

3.2 新版功能: maxtasksperchild

3.4 新版功能: context

注解

通常来说,Pool 中的 Worker 进程的生命周期和进程池的工作队列一样长。一些其他系统中(如 Apache, mod_wsgi 等)也可以发现另一种模式,他们会让工作进程在完成一些任务后退出,清理、释放资源,然后启动一个新的进程代替旧的工作进程。 Poolmaxtasksperchild 参数给用户提供了这种能力。

  • apply(func[, args[, kwds]])

    使用 args 参数以及 kwds 命名参数调用 func , 它会返回结果前阻塞。这种情况下,apply_async() 更适合并行化工作。另外 func 只会在一个进程池中的一个工作进程中执行。

  • apply_async(func[, args[, kwds[, callback[, error_callback]]]])

    apply() 方法的一个变种,返回一个 AsyncResult 对象。

    如果指定了 callback , 它必须是一个接受单个参数的可调用对象。当执行成功时, callback 会被用于处理执行后的返回结果,否则,调用 error_callback

    如果指定了 error_callback , 它必须是一个接受单个参数的可调用对象。当目标函数执行失败时, 会将抛出的异常对象作为参数传递给 error_callback 执行。

    回调函数应该立即执行完成,否则会阻塞负责处理结果的线程。

  • map(func, iterable[, chunksize])

    内置 map() 函数的并行版本 (但它只支持一个 iterable 参数,对于多个可迭代对象请参阅 starmap())。 它会保持阻塞直到获得结果。

    这个方法会将可迭代对象分割为许多块,然后提交给进程池。可以将 chunksize 设置为一个正整数从而(近似)指定每个块的大小可以。

    注意对于很长的迭代对象,可能消耗很多内存。可以考虑使用 imap()imap_unordered() 并且显示指定 chunksize 以提升效率。

  • map_async(func, iterable[, chunksize[, callback[, error_callback]]])

    map() 方法的一个变种,返回一个 AsyncResult 对象。

    如果指定了 callback , 它必须是一个接受单个参数的可调用对象。当执行成功时, callback 会被用于处理执行后的返回结果,否则,调用 error_callback

    如果指定了 error_callback , 它必须是一个接受单个参数的可调用对象。当目标函数执行失败时, 会将抛出的异常对象作为参数传递给 error_callback 执行。

    回调函数应该立即执行完成,否则会阻塞负责处理结果的线程。

  • imap(func, iterable[, chunksize])

    map() 的延迟执行版本。

    chunksize 参数的作用和 map() 方法的一样。对于很长的迭代器,给 chunksize 设置一个很大的值会比默认值 1 极大 地加快执行速度。

    同样,如果 chunksize1 , 那么 imap() 方法所返回的迭代器的 next() 方法拥有一个可选的 timeout 参数: 如果无法在 timeout 秒内执行得到结果,则next(timeout) 会抛出 multiprocessing.TimeoutError 异常。

  • imap_unordered(func, iterable[, chunksize])

    imap() 相同,只不过通过迭代器返回的结果是任意的。(当进程池中只有一个工作进程的时候,返回结果的顺序才能认为是”有序”的)

  • starmap(func, iterable[, chunksize])

    map() 类似,不过 iterable 中的每一项会被解包再作为函数参数。

    比如可迭代对象 [(1,2), (3, 4)] 会转化为等价于 [func(1,2), func(3,4)] 的调用。

    3.3 新版功能.

  • starmap_async(func, iterable[, chunksize[, callback[, error_callback]]])

    相当于 starmap()map_async() 的结合,迭代 iterable 的每一项,解包作为 func 的参数并执行,返回用于获取结果的对象。

    3.3 新版功能.

  • close()

    阻止后续任务提交到进程池,当所有任务执行完成后,工作进程会退出。

  • terminate()

    不必等待未完成的任务,立即停止工作进程。当进程池对象被垃圾回收时,会立即调用 terminate()

  • join()

    等待工作进程结束。调用 join() 前必须先调用 close() 或者 terminate()

3.3 新版功能: 进程池对象现在支持上下文管理器协议。__enter__() 返回进程池对象, __exit__() 会调用 terminate()

class multiprocessing.pool.AsyncResult

Pool.apply_async()Pool.map_async() 返回对象所属的类。

  • get([timeout])

    用于获取执行结果。如果 timeout 不是 None 并且在 timeout 秒内仍然没有执行完得到结果,则抛出 multiprocessing.TimeoutError 异常。如果远程调用发生异常,这个异常会通过 get() 重新抛出。

  • wait([timeout])

    阻塞,直到返回结果,或者 timeout 秒后超时。

  • ready()

    返回执行状态,是否已经完成。

  • successful()

    判断调用是否已经完成并且未引发异常。 如果还未获得结果则将引发 ValueError

    在 3.7 版更改: 如果没有执行完,会抛出 ValueError 异常而不是 AssertionError

下面的例子演示了进程池的用法:

from multiprocessing import Pool
import time
def f(x):
    return x*x
if __name__ == '__main__':
    with Pool(processes=4) as pool:         # start 4 worker processes
        result = pool.apply_async(f, (10,)) # evaluate "f(10)" asynchronously in a single process
        print(result.get(timeout=1))        # prints "100" unless your computer is *very* slow
        print(pool.map(f, range(10)))       # prints "[0, 1, 4,..., 81]"
        it = pool.imap(f, range(10))
        print(next(it))                     # prints "0"
        print(next(it))                     # prints "1"
        print(it.next(timeout=1))           # prints "4" unless your computer is *very* slow
        result = pool.apply_async(time.sleep, (10,))
        print(result.get(timeout=1))        # raises multiprocessing.TimeoutError

监听器及客户端

通常情况下,进程间通过队列或者 Pipe() 返回的 Connection 传递消息。

不过,multiprocessing.connection 模块其实提供了一些更灵活的特性。最基础的用法是通过它抽象出来的高级API来操作socket或者Windows命名管道。也提供一些高级用法,如通过 hmac 模块来支持 摘要认证,以及同时监听多个管道连接。

multiprocessing.connection.deliver_challenge(connection, authkey)

发送一个随机生成的消息到另一端,并等待回复。

如果收到的回复与使用 authkey 作为键生成的信息摘要匹配成功,就会发送一个欢迎信息给管道另一端。否则抛出 AuthenticationError 异常。

multiprocessing.connection.answer_challenge(connection, authkey)

接收一条信息,使用 authkey 作为键计算信息摘要,然后将摘要发送回去。

如果没有收到欢迎消息,就抛出 AuthenticationError 异常。

multiprocessing.connection.Client(address[, family[, authkey]])

尝试使用 address 地址上的监听器建立一个连接,返回 Connection

连接的类型取决于 family 参数,但是通常可以省略,因为可以通过 address 的格式推导出来。

如果提供了 authkey 参数并且不是 None,那它必须是一个字符串并且会被当做基于 HMAC 认证的密钥。如果 authkey 是None 则不会有认证行为。认证失败抛出 AuthenticationError 异常,请查看 See 认证密码 。

class multiprocessing.connection.Listener([address[, family[, backlog[, authkey]]]])

可以监听连接请求,是对于绑定套接字或者 Windows 命名管道的封装。

address 是监听器对象中的绑定套接字或命名管道使用的地址。

注解

如果使用 ‘0.0.0.0’ 作为监听地址,那么在Windows上这个地址无法建立连接。想要建立一个可连接的端点,应该使用 ‘127.0.0.1’ 。

family 是套接字(或者命名管道)使用的类型。它可以是以下一种: 'AF_INET' ( TCP 套接字类型), 'AF_UNIX' ( Unix 域套接字) 或者 'AF_PIPE' ( Windows 命名管道)。其中只有第一个保证各平台可用。如果 familyNone ,那么 family 会根据 address 的格式自动推导出来。如果 address 也是 None , 则取默认值。默认值为可用类型中速度最快的。注意,如果 family'AF_UNIX' 而address是None ,套接字会在一个 tempfile.mkstemp() 创建的私有临时目录中创建。

如果监听器对象使用了套接字,backlog (默认值为1) 会在套接字绑定后传递给它的 listen() 方法。

如果提供了 authkey 参数并且不是 None,那它必须是一个字符串并且会被当做基于 HMAC 认证的密钥。如果 authkey 是None 则不会有认证行为。认证失败抛出 AuthenticationError 异常。

  • accept()

    接受一个连接并返回一个 Connection 对象,其连接到的监听器对象已绑定套接字或者命名管道。如果已经尝试过认证并且失败了,则会抛出 AuthenticationError 异常。

  • close()

    关闭监听器对象上的绑定套接字或者命名管道。此函数会在监听器被垃圾回收后自动调用。不过仍然建议显式调用函数关闭。

监听器对象拥有下列只读属性:

  • address

    监听器对象使用的地址。

  • last_accepted

    最后一个连接所使用的地址。如果没有的话就是 None

3.3 新版功能: 监听器对象现在支持了上下文管理协议 。 __enter__() 返回一个监听器对象, __exit__() 会调用 close()

multiprocessing.connection.wait(object_list, timeout=None)

一直等待直到 object_list 中某个对象处于就绪状态。返回 object_list 中处于就绪状态的对象。如果 timeout 是一个浮点型,该方法会最多阻塞这么多秒。如果 timeoutNone ,则会允许阻塞的事件没有限制。timeout为负数的情况下和为0的情况相同。

对于 Unix 和 Windows ,满足下列条件的对象可以出现在 object_list

  • 可读的 Connection 对象;
  • 一个已连接并且可读的 socket.socket 对象;或者
  • Process 对象中的 sentinel 属性。

当一个连接或者套接字对象拥有有效的数据可被读取的时候,或者另一端关闭后,这个对象就处于就绪状态。

Unix: wait(object_list, timeout)select.select(object_list, [], [], timeout) 几乎相同。差别在于,如果 select.select() 被信号中断,它会抛出一个附带错误号为 EINTROSError 异常,而 wait() 不会。

Windows: object_list 中的元素必须是一个表示为整数的可等待的句柄(按照 Win32 函数 WaitForMultipleObjects() 的文档中所定义) 或者一个拥有 fileno() 方法的对象,这个对象返回一个套接字句柄或者管道句柄。(注意管道和套接字两种句柄 不是 可等待的句柄)

3.3 新版功能.

示例

下面的服务代码创建了一个使用 'secret password' 作为认证密码的监听器。它会等待连接然后发送一些数据给客户端:

from multiprocessing.connection import Listener
from array import array
address = ('localhost', 6000)     # family is deduced to be 'AF_INET'
with Listener(address, authkey=b'secret password') as listener:
    with listener.accept() as conn:
        print('connection accepted from', listener.last_accepted)
        conn.send([2.25, None, 'junk', float])
        conn.send_bytes(b'hello')
        conn.send_bytes(array('i', [42, 1729]))

下面的代码连接到服务然后从服务器上j接收一些数据:

from multiprocessing.connection import Client
from array import array
address = ('localhost', 6000)
with Client(address, authkey=b'secret password') as conn:
    print(conn.recv())                  # => [2.25, None, 'junk', float]
    print(conn.recv_bytes())            # => 'hello'
    arr = array('i', [0, 0, 0, 0, 0])
    print(conn.recv_bytes_into(arr))    # => 8
    print(arr)                          # => array('i', [42, 1729, 0, 0, 0])

下面的代码使用了 wait() ,以便在同时等待多个进程发来消息。

import time, random
from multiprocessing import Process, Pipe, current_process
from multiprocessing.connection import wait
def foo(w):
    for i in range(10):
        w.send((i, current_process().name))
    w.close()
if __name__ == '__main__':
    readers = []
    for i in range(4):
        r, w = Pipe(duplex=False)
        readers.append(r)
        p = Process(target=foo, args=(w,))
        p.start()
        # We close the writable end of the pipe now to be sure that
        # p is the only process which owns a handle for it.  This
        # ensures that when p closes its handle for the writable end,
        # wait() will promptly report the readable end as being ready.
        w.close()
    while readers:
        for r in wait(readers):
            try:
                msg = r.recv()
            except EOFError:
                readers.remove(r)
            else:
                print(msg)
地址格式
  • 'AF_INET' 地址是 (hostname, port) 形式的元组类型,其中 hostname 是一个字符串,port 是整数。
  • 'AF_UNIX' 地址是文件系统上文件名的字符串。
  • 'AF_PIPE' 地址是一个 r'\.\pipe{PipeName}' 形式的字符串。 要使用 Client() 来连接到远程计算机上一个名为 ServerName 的命名管道,则应当改用 r'\*ServerName*\pipe{PipeName}' 形式的地址。

注意,使用两个反斜线开头的字符串默认被当做 'AF_PIPE' 地址而不是 'AF_UNIX'

认证密码

当使用 Connection.recv 接收数据时,数据会自动被反序列化。不幸的是,对于一个不可信的数据源发来的数据,反序列化是存在安全风险的。所以 ListenerClient() 之间使用 hmac 模块进行摘要认证。

认证密钥是一个 byte 类型的字符串,可以认为是和密码一样的东西,连接建立好后,双方都会要求另一方证明知道认证密钥。(这个证明过程不会通过连接发送密钥)

如果要求认证但是没有指定认证密钥,则会使用 current_process().authkey 的返回值。 这个值将被当前进程所创建的任何 Process 对象自动继承。 这意味着 (在默认情况下) 一个包含多进程的程序中的所有进程会在相互间建立连接的时候共享单个认证密钥。

os.urandom() 也可以用来生成合适的认证密钥。

日志记录

当前模块也提供了一些对 logging 的支持。注意, logging 模块本身并没有使用进程间共享的锁,所以来自于多个进程的日志可能(具体取决于使用的日志 handler 类型)相互覆盖或者混杂。

multiprocessing.get_logger()

返回 multiprocessing 使用的 logger,必要的话会创建一个新的。

如果创建的首个 logger 日志级别为 logging.NOTSET 并且没有默认 handler。通过这个 logger 打印的消息不会传递到根 logger。

注意在 Windows 上,子进程只会继承父进程 logger 的日志级别 - 对于logger的其他自定义项不会继承。

multiprocessing.log_to_stderr()

此函数会调用 get_logger() 但是会在返回的 logger 上增加一个 handler,将所有输出都使用 '[%(levelname)s/%(processName)s] %(message)s' 的格式发送到 sys.stderr

下面是一个在交互式解释器中打开日志功能的例子:

>>> import multiprocessing, logging
>>> logger = multiprocessing.log_to_stderr()
>>> logger.setLevel(logging.INFO)
>>> logger.warning('doomed')
[WARNING/MainProcess] doomed
>>> m = multiprocessing.Manager()
[INFO/SyncManager-...] child process calling self.run()
[INFO/SyncManager-...] created temp directory /.../pymp-...
[INFO/SyncManager-...] manager serving at '/.../listener-...'
>>> del m
[INFO/MainProcess] sending shutdown message to manager
[INFO/SyncManager-...] manager exiting with exitcode 0

multiprocessing.dummy 模块

multiprocessing.dummy 复制了 multiprocessing 的 API,不过是在 threading 模块之上包装了一层。

特别地,multiprocessing.dummy 所提供的 Pool 函数会返回一个 ThreadPool 的实例,该类是 Pool 的子类,它支持所有相同的方法调用但会使用一个工作线程池而非工作进程池。

class multiprocessing.pool.ThreadPool([processes[, initializer[, initargs]]])

一个线程池对象,用来控制可向其提交任务的工作线程池。 ThreadPool 实例与 Pool 实例是完全接口兼容的,并且它们的资源也必须被正确地管理,或者是将线程池作为上下文管理器来使用,或者是通过手动调用 close()terminate()

processes 是要使用的工作线程数目。 如果 processesNone,则使用 os.cpu_count() 返回的值。

如果 initializer 不为 None,则每个工作进程将会在启动时调用 initializer(*initargs)

不同于 Poolmaxtasksperchildcontext 不可被提供。

注解

ThreadPool 具有与 Pool 相同的接口,它围绕一个进程池进行设计并且先于 concurrent.futures 模块的引入。 因此,它继承了一些对于基于线程的池来说没有意义的操作,并且它具有自己的用于表示异步任务状态的类型 AsyncResult,该类型不为任何其他库所知。

用户通常应该倾向于使用 concurrent.futures.ThreadPoolExecutor,它拥有从一开始就围绕线程进行设计的更简单接口,并且返回与许多其他库相兼容的 concurrent.futures.Future 实例,包括 asyncio 库。

编程指导

使用 multiprocessing 时,应遵循一些指导原则和习惯用法。

所有start方法

下面这些适用于所有start方法。

避免共享状态

应该尽可能避免在进程间传递大量数据,越少越好。

最好坚持使用队列或者管道进行进程间通信,而不是底层的同步原语。

可序列化

保证所代理的方法的参数是可以序列化的。

代理的线程安全性

不要在多线程中同时使用一个代理对象,除非你用锁保护它。

(而在不同进程中使用 相同 的代理对象却没有问题。)

使用 Join 避免僵尸进程

在 Unix 上,如果一个进程执行完成但是没有被 join,就会变成僵尸进程。一般来说,僵尸进程不会很多,因为每次新启动进程(或者 active_children() 被调用)时,所有已执行完成且没有被 join 的进程都会自动被 join,而且对一个执行完的进程调用 Process.is_alive 也会 join 这个进程。尽管如此,对自己启动的进程显式调用 join 依然是最佳实践。

继承优于序列化、反序列化

当使用 spawn 或者 forkserver 的启动方式时,multiprocessing 中的许多类型都必须是可序列化的,这样子进程才能使用它们。但是通常我们都应该避免使用管道和队列发送共享对象到另外一个进程,而是重新组织代码,对于其他进程创建出来的共享对象,让那些需要访问这些对象的子进程可以直接将这些对象从父进程继承过来。

避免杀死进程

听过 Process.terminate 停止一个进程很容易导致这个进程正在使用的共享资源(如锁、信号量、管道和队列)损坏或者变得不可用,无法在其他进程中继续使用。

所以,最好只对那些从来不使用共享资源的进程调用 Process.terminate

Join 使用队列的进程

记住,往队列放入数据的进程会一直等待直到队列中所有项被”feeder” 线程传给底层管道。(子进程可以调用队列的 Queue.cancel_join_thread 方法禁止这种行为)

这意味着,任何使用队列的时候,你都要确保在进程join之前,所有存放到队列中的项将会被其他进程、线程完全消费。否则不能保证这个写过队列的进程可以正常终止。记住非精灵进程会自动 join 。

下面是一个会导致死锁的例子:

from multiprocessing import Process, Queue
def f(q):
    q.put('X' * 1000000)
if __name__ == '__main__':
    queue = Queue()
    p = Process(target=f, args=(queue,))
    p.start()
    p.join()                    # this deadlocks
    obj = queue.get()

交换最后两行可以修复这个问题(或者直接删掉 p.join())。

显式传递资源给子进程

在Unix上,使用 fork 方式启动的子进程可以使用父进程中全局创建的共享资源。不过,最好是显式将资源对象通过参数的形式传递给子进程。

除了(部分原因)让代码兼容 Windows 以及其他的进程启动方式外,这种形式还保证了在子进程生命期这个对象是不会被父进程垃圾回收的。如果父进程中的某些对象被垃圾回收会导致资源释放,这就变得很重要。

所以对于实例:

from multiprocessing import Process, Lock
def f():
    ... do something using "lock" ...
if __name__ == '__main__':
    lock = Lock()
    for i in range(10):
        Process(target=f).start()

应当重写成这样:

from multiprocessing import Process, Lock
def f(l):
    ... do something using "l" ...
if __name__ == '__main__':
    lock = Lock()
    for i in range(10):
        Process(target=f, args=(lock,)).start()

谨防将 sys.stdin 数据替换为 “类似文件的对象”

multiprocessing 原本会无条件地这样调用:

os.close(sys.stdin.fileno())

multiprocessing.Process._bootstrap() 方法中 —— 这会导致与”进程中的进程”相关的一些问题。这已经被修改成了:

sys.stdin.close()
sys.stdin = open(os.open(os.devnull, os.O_RDONLY), closefd=False)

它解决了进程相互冲突导致文件描述符错误的根本问题,但是对使用带缓冲的“文件类对象”替换 sys.stdin() 作为输出的应用程序造成了潜在的危险。如果多个进程调用了此文件类对象的 close() 方法,会导致相同的数据多次刷写到此对象,损坏数据。

如果你写入文件类对象并实现了自己的缓存,可以在每次追加缓存数据时记录当前进程id,从而将其变成 fork 安全的,当发现进程id变化后舍弃之前的缓存,例如:

@property
def cache(self):
    pid = os.getpid()
    if pid != self._pid:
        self._pid = pid
        self._cache = []
    return self._cache

需要更多信息,请查看 bpo-5155, bpo-5313 以及 bpo-5331

spawnforkserver 启动方式

相对于 fork 启动方式,有一些额外的限制。

更依赖序列化

Process.__init__() 的所有参数都必须可序列化。同样的,当你继承 Process 时,需要保证当调用 Process.start 方法时,实例可以被序列化。

全局变量

记住,如果子进程中的代码尝试访问一个全局变量,它所看到的值(如果有)可能和父进程中执行 Process.start 那一刻的值不一样。

当全局变量知识模块级别的常量时,是不会有问题的。

安全导入主模块

确保主模块可以被新启动的Python解释器安全导入而不会引发什么副作用(比如又启动了一个子进程)

例如,使用 spawnforkserver 启动方式执行下面的模块,会引发 RuntimeError 异常而失败。

from multiprocessing import Process
def foo():
    print('hello')
p = Process(target=foo)
p.start()

应该通过下面的方法使用 if __name__ == '__main__': ,从而保护程序”入口点”:

from multiprocessing import Process, freeze_support, set_start_method
def foo():
    print('hello')
if __name__ == '__main__':
    freeze_support()
    set_start_method('spawn')
    p = Process(target=foo)
    p.start()

(如果程序将正常运行而不是冻结,则可以省略 freeze_support() 行)

这允许新启动的 Python 解释器安全导入模块然后运行模块中的 foo() 函数。

如果主模块中创建了进程池或者管理器,这个规则也适用。

例子

创建和使用自定义管理器、代理的示例:

from multiprocessing import freeze_support
from multiprocessing.managers import BaseManager, BaseProxy
import operator
##
class Foo:
    def f(self):
        print('you called Foo.f()')
    def g(self):
        print('you called Foo.g()')
    def _h(self):
        print('you called Foo._h()')
# A simple generator function
def baz():
    for i in range(10):
        yield i*i
# Proxy type for generator objects
class GeneratorProxy(BaseProxy):
    _exposed_ = ['__next__']
    def __iter__(self):
        return self
    def __next__(self):
        return self._callmethod('__next__')
# Function to return the operator module
def get_operator_module():
    return operator
##
class MyManager(BaseManager):
    pass
# register the Foo class; make `f()` and `g()` accessible via proxy
MyManager.register('Foo1', Foo)
# register the Foo class; make `g()` and `_h()` accessible via proxy
MyManager.register('Foo2', Foo, exposed=('g', '_h'))
# register the generator function baz; use `GeneratorProxy` to make proxies
MyManager.register('baz', baz, proxytype=GeneratorProxy)
# register get_operator_module(); make public functions accessible via proxy
MyManager.register('operator', get_operator_module)
##
def test():
    manager = MyManager()
    manager.start()
    print('-' * 20)
    f1 = manager.Foo1()
    f1.f()
    f1.g()
    assert not hasattr(f1, '_h')
    assert sorted(f1._exposed_) == sorted(['f', 'g'])
    print('-' * 20)
    f2 = manager.Foo2()
    f2.g()
    f2._h()
    assert not hasattr(f2, 'f')
    assert sorted(f2._exposed_) == sorted(['g', '_h'])
    print('-' * 20)
    it = manager.baz()
    for i in it:
        print('<%d>' % i, end=' ')
    print()
    print('-' * 20)
    op = manager.operator()
    print('op.add(23, 45) =', op.add(23, 45))
    print('op.pow(2, 94) =', op.pow(2, 94))
    print('op._exposed_ =', op._exposed_)
##
if __name__ == '__main__':
    freeze_support()
    test()

使用 Pool:

import multiprocessing
import time
import random
import sys
#
# Functions used by test code
#
def calculate(func, args):
    result = func(*args)
    return '%s says that %s%s = %s' % (
        multiprocessing.current_process().name,
        func.__name__, args, result
        )
def calculatestar(args):
    return calculate(*args)
def mul(a, b):
    time.sleep(0.5 * random.random())
    return a * b
def plus(a, b):
    time.sleep(0.5 * random.random())
    return a + b
def f(x):
    return 1.0 / (x - 5.0)
def pow3(x):
    return x ** 3
def noop(x):
    pass
#
# Test code
#
def test():
    PROCESSES = 4
    print('Creating pool with %d processes\n' % PROCESSES)
    with multiprocessing.Pool(PROCESSES) as pool:
        #
        # Tests
        #
        TASKS = [(mul, (i, 7)) for i in range(10)] + \
                [(plus, (i, 8)) for i in range(10)]
        results = [pool.apply_async(calculate, t) for t in TASKS]
        imap_it = pool.imap(calculatestar, TASKS)
        imap_unordered_it = pool.imap_unordered(calculatestar, TASKS)
        print('Ordered results using pool.apply_async():')
        for r in results:
            print('\t', r.get())
        print()
        print('Ordered results using pool.imap():')
        for x in imap_it:
            print('\t', x)
        print()
        print('Unordered results using pool.imap_unordered():')
        for x in imap_unordered_it:
            print('\t', x)
        print()
        print('Ordered results using pool.map() --- will block till complete:')
        for x in pool.map(calculatestar, TASKS):
            print('\t', x)
        print()
        #
        # Test error handling
        #
        print('Testing error handling:')
        try:
            print(pool.apply(f, (5,)))
        except ZeroDivisionError:
            print('\tGot ZeroDivisionError as expected from pool.apply()')
        else:
            raise AssertionError('expected ZeroDivisionError')
        try:
            print(pool.map(f, list(range(10))))
        except ZeroDivisionError:
            print('\tGot ZeroDivisionError as expected from pool.map()')
        else:
            raise AssertionError('expected ZeroDivisionError')
        try:
            print(list(pool.imap(f, list(range(10)))))
        except ZeroDivisionError:
            print('\tGot ZeroDivisionError as expected from list(pool.imap())')
        else:
            raise AssertionError('expected ZeroDivisionError')
        it = pool.imap(f, list(range(10)))
        for i in range(10):
            try:
                x = next(it)
            except ZeroDivisionError:
                if i == 5:
                    pass
            except StopIteration:
                break
            else:
                if i == 5:
                    raise AssertionError('expected ZeroDivisionError')
        assert i == 9
        print('\tGot ZeroDivisionError as expected from IMapIterator.next()')
        print()
        #
        # Testing timeouts
        #
        print('Testing ApplyResult.get() with timeout:', end=' ')
        res = pool.apply_async(calculate, TASKS[0])
        while 1:
            sys.stdout.flush()
            try:
                sys.stdout.write('\n\t%s' % res.get(0.02))
                break
            except multiprocessing.TimeoutError:
                sys.stdout.write('.')
        print()
        print()
        print('Testing IMapIterator.next() with timeout:', end=' ')
        it = pool.imap(calculatestar, TASKS)
        while 1:
            sys.stdout.flush()
            try:
                sys.stdout.write('\n\t%s' % it.next(0.02))
            except StopIteration:
                break
            except multiprocessing.TimeoutError:
                sys.stdout.write('.')
        print()
        print()
if __name__ == '__main__':
    multiprocessing.freeze_support()
    test()

一个演示如何使用队列来向一组工作进程提供任务并收集结果的例子:

import time
import random
from multiprocessing import Process, Queue, current_process, freeze_support
#
# Function run by worker processes
#
def worker(input, output):
    for func, args in iter(input.get, 'STOP'):
        result = calculate(func, args)
        output.put(result)
#
# Function used to calculate result
#
def calculate(func, args):
    result = func(*args)
    return '%s says that %s%s = %s' % \
        (current_process().name, func.__name__, args, result)
#
# Functions referenced by tasks
#
def mul(a, b):
    time.sleep(0.5*random.random())
    return a * b
def plus(a, b):
    time.sleep(0.5*random.random())
    return a + b
#
#
#
def test():
    NUMBER_OF_PROCESSES = 4
    TASKS1 = [(mul, (i, 7)) for i in range(20)]
    TASKS2 = [(plus, (i, 8)) for i in range(10)]
    # Create queues
    task_queue = Queue()
    done_queue = Queue()
    # Submit tasks
    for task in TASKS1:
        task_queue.put(task)
    # Start worker processes
    for i in range(NUMBER_OF_PROCESSES):
        Process(target=worker, args=(task_queue, done_queue)).start()
    # Get and print results
    print('Unordered results:')
    for i in range(len(TASKS1)):
        print('\t', done_queue.get())
    # Add more tasks using `put()`
    for task in TASKS2:
        task_queue.put(task)
    # Get and print some more results
    for i in range(len(TASKS2)):
        print('\t', done_queue.get())
    # Tell child processes to stop
    for i in range(NUMBER_OF_PROCESSES):
        task_queue.put('STOP')
if __name__ == '__main__':
    freeze_support()
    test()

multiprocessing.shared_memory —- 可从进程直接访问的共享内存

源代码: Lib/multiprocessing/shared_memory.py

3.8 新版功能.


该模块提供了一个 SharedMemory 类,用于分配和管理多核或对称多处理器(SMP)机器上进程间的共享内存。为了协助管理不同进程间的共享内存生命周期,multiprocessing.managers 模块也提供了一个 BaseManager 的子类: SharedMemoryManager

本模块中,共享内存是指 “System V 类型” 的共享内存块(虽然可能和它实现方式不完全一致)而不是 “分布式共享内存”。这种类型的的共享内存允许不同进程读写一片公共(或者共享)的易失性存储区域。一般来说,进程被限制只能访问属于自己进程空间的内存,但是共享内存允许跨进程共享数据,从而避免通过进程间发送消息的形式传递数据。相比通过磁盘、套接字或者其他要求序列化、反序列化和复制数据的共享形式,直接通过内存共享数据拥有更出色性能。

class multiprocessing.shared_memory.SharedMemory(name=None, create=False, size=0)

创建一个新的共享内存块或者连接到一片已经存在的共享内存块。每个共享内存块都被指定了一个全局唯一的名称。通过这种方式,进程可以使用一个特定的名字创建共享内存区块,然后其他进程使用同样的名字连接到这个共享内存块。

作为一种跨进程共享数据的方式,共享内存块的寿命可能超过创建它的原始进程。一个共享内存块可能同时被多个进程使用,当一个进程不再需要访问这个共享内存块的时候,应该调用 close() 方法。当一个共享内存块不被任何进程使用的时候,应该调用 unlink() 方法以执行必要的清理。

name 是共享内存的唯一名称,字符串类型。如果创建一个新共享内存块的时候,名称指定为 None (默认值),将会随机产生一个新名称。

create 指定创建一个新的共享内存块 (True) 还是连接到已存在的共享内存块 (False) 。

如果是新创建共享内存块则 size 用于指定块的大小为多少字节。由于某些平台是以内存页大小为最小单位来分配内存的,最终得到的内存块大小可能大于或等于要求的大小。如果是连接到已经存在的共享内存块, size 参数会被忽略。

  • close()

    关闭实例对于共享内存的访问连接。所有实例确认自己不再需要使用共享内存的时候都应该调用 close() ,以保证必要的资源清理。调用 close() 并不会销毁共享内存区域。

  • unlink()

    请求销毁底层的共享内存块。为了执行必要的资源清理, 在所有使用这个共享内存块的进程中, unlink() 应该调用一次(且只能调用一次) 。发出此销毁请求后,共享内存块可能会、也可能不会立即销毁,且此行为在不同操作系统之间可能不同。调用 unlink() 后再尝试方位其中的数据可能导致内存错误。注意: 最后一个关闭共享内存访问权限的进程可以以任意顺序调用 unlink()close()

  • buf

    共享内存块内容的 memoryview 。

  • name

    共享内存块的唯一标识,只读属性。

  • size

    共享内存块的字节大小,只读属性。

以下示例展示了 SharedMemory 底层的用法:

>>> from multiprocessing import shared_memory
>>> shm_a = shared_memory.SharedMemory(create=True, size=10)
>>> type(shm_a.buf)
<class 'memoryview'>
>>> buffer = shm_a.buf
>>> len(buffer)
10
>>> buffer[:4] = bytearray([22, 33, 44, 55])  # Modify multiple at once
>>> buffer[4] = 100                           # Modify single byte at a time
>>> # Attach to an existing shared memory block
>>> shm_b = shared_memory.SharedMemory(shm_a.name)
>>> import array
>>> array.array('b', shm_b.buf[:5])  # Copy the data into a new array.array
array('b', [22, 33, 44, 55, 100])
>>> shm_b.buf[:5] = b'howdy'  # Modify via shm_b using bytes
>>> bytes(shm_a.buf[:5])      # Access via shm_a
b'howdy'
>>> shm_b.close()   # Close each SharedMemory instance
>>> shm_a.close()
>>> shm_a.unlink()  # Call unlink only once to release the shared memory

以下示例展示了一个现实中的例子,使用 SharedMemory 类和 NumPy arrays 结合, 从两个 Python shell 中访问同一个 numpy.ndarray :

>>> # In the first Python interactive shell
>>> import numpy as np
>>> a = np.array([1, 1, 2, 3, 5, 8])  # Start with an existing NumPy array
>>> from multiprocessing import shared_memory
>>> shm = shared_memory.SharedMemory(create=True, size=a.nbytes)
>>> # Now create a NumPy array backed by shared memory
>>> b = np.ndarray(a.shape, dtype=a.dtype, buffer=shm.buf)
>>> b[:] = a[:]  # Copy the original data into shared memory
>>> b
array([1, 1, 2, 3, 5, 8])
>>> type(b)
<class 'numpy.ndarray'>
>>> type(a)
<class 'numpy.ndarray'>
>>> shm.name  # We did not specify a name so one was chosen for us
'psm_21467_46075'
>>> # In either the same shell or a new Python shell on the same machine
>>> import numpy as np
>>> from multiprocessing import shared_memory
>>> # Attach to the existing shared memory block
>>> existing_shm = shared_memory.SharedMemory(name='psm_21467_46075')
>>> # Note that a.shape is (6,) and a.dtype is np.int64 in this example
>>> c = np.ndarray((6,), dtype=np.int64, buffer=existing_shm.buf)
>>> c
array([1, 1, 2, 3, 5, 8])
>>> c[-1] = 888
>>> c
array([  1,   1,   2,   3,   5, 888])
>>> # Back in the first Python interactive shell, b reflects this change
>>> b
array([  1,   1,   2,   3,   5, 888])
>>> # Clean up from within the second Python shell
>>> del c  # Unnecessary; merely emphasizing the array is no longer used
>>> existing_shm.close()
>>> # Clean up from within the first Python shell
>>> del b  # Unnecessary; merely emphasizing the array is no longer used
>>> shm.close()
>>> shm.unlink()  # Free and release the shared memory block at the very end

class multiprocessing.managers.SharedMemoryManager([address[, authkey]])

BaseManager 的子类,可用于管理跨进程的共享内存块。

调用 SharedMemoryManager 实例上的 start() 方法会启动一个新进程。这个新进程的唯一目的就是管理所有由它创建的共享内存块的生命周期。想要释放此进程管理的所有共享内存块,可以调用实例的 shutdown() 方法。这会触发执行它管理的所有 SharedMemory 对象的 SharedMemory.unlink() 方法,然后停止这个进程。通过 SharedMemoryManager 创建 SharedMemory 实例,我们可以避免手动跟踪和释放共享内存资源。

这个类提供了创建和返回 SharedMemory 实例的方法,以及以共享内存为基础创建一个列表类对象 (ShareableList) 的方法。

有关继承的可选输入参数 addressauthkey 以及他们如何用于从进程连接已经存在的 SharedMemoryManager 服务,参见 multiprocessing.managers.BaseManager

  • SharedMemory(size)

    使用 size 参数,创建一个新的指定字节大小的 SharedMemory 对象并返回。

  • ShareableList(sequence)

    创建并返回一个新的 ShareableList 对象,通过输入参数 sequence 初始化。

下面的案例展示了 SharedMemoryManager 的基本机制:

>>> from multiprocessing.managers import SharedMemoryManager
>>> smm = SharedMemoryManager()
>>> smm.start()  # Start the process that manages the shared memory blocks
>>> sl = smm.ShareableList(range(4))
>>> sl
ShareableList([0, 1, 2, 3], name='psm_6572_7512')
>>> raw_shm = smm.SharedMemory(size=128)
>>> another_sl = smm.ShareableList('alpha')
>>> another_sl
ShareableList(['a', 'l', 'p', 'h', 'a'], name='psm_6572_12221')
>>> smm.shutdown()  # Calls unlink() on sl, raw_shm, and another_sl

以下案例展示了 SharedMemoryManager 对象的一种可能更方便的使用方式,通过 with 语句来保证所有共享内存块在使用完后被释放。

>>> with SharedMemoryManager() as smm:
...     sl = smm.ShareableList(range(2000))
...     # Divide the work among two processes, storing partial results in sl
...     p1 = Process(target=do_work, args=(sl, 0, 1000))
...     p2 = Process(target=do_work, args=(sl, 1000, 2000))
...     p1.start()
...     p2.start()  # A multiprocessing.Pool might be more efficient
...     p1.join()
...     p2.join()   # Wait for all work to complete in both processes
...     total_result = sum(sl)  # Consolidate the partial results now in sl

with 语句中使用 SharedMemoryManager 对象的时候,使用这个管理器创建的共享内存块会在 with 语句代码块结束后被释放。

class multiprocessing.shared_memory.ShareableList(sequence=None, **, name=None*)

提供一个可修改的类 list 对象,其中所有值都存放在共享内存块中。这限制了可被存储在其中的值只能是 int, float, bool, str (每条数据小于10M), bytes (每条数据小于10M)以及 None 这些内置类型。它另一个显著区别于内置 list 类型的地方在于它的长度无法修改(比如,没有 append, insert 等操作)且不支持通过切片操作动态创建新的 ShareableList 实例。

sequence 会被用来为一个新的 ShareableList 填充值。 设为 None 则会基于唯一的共享内存名称关联到已经存在的 ShareableList

name 是所请求的共享内存的唯一名称,与 SharedMemory 的定义中所描述的一致。 当关联到现有的 ShareableList 时,则指明其共享内存块的唯一名称并将 sequence 设为 None

  • count(value)

    返回 value 出现的次数。

  • index(value)

    返回 value 首次出现的位置,如果 value 不存在, 则抛出 ValueError 异常。

  • format

    包含由所有当前存储值所使用的 struct 打包格式的只读属性。

  • shm

    存储了值的 SharedMemory 实例。

下面的例子演示了 ShareableList 实例的基本用法:

>>> from multiprocessing import shared_memory
>>> a = shared_memory.ShareableList(['howdy', b'HoWdY', -273.154, 100, None, True, 42])
>>> [ type(entry) for entry in a ]
[<class 'str'>, <class 'bytes'>, <class 'float'>, <class 'int'>, <class 'NoneType'>, <class 'bool'>, <class 'int'>]
>>> a[2]
-273.154
>>> a[2] = -78.5
>>> a[2]
-78.5
>>> a[2] = 'dry ice'  # Changing data types is supported as well
>>> a[2]
'dry ice'
>>> a[2] = 'larger than previously allocated storage space'
Traceback (most recent call last):
  ...
ValueError: exceeds available storage for existing str
>>> a[2]
'dry ice'
>>> len(a)
7
>>> a.index(42)
6
>>> a.count(b'howdy')
0
>>> a.count(b'HoWdY')
1
>>> a.shm.close()
>>> a.shm.unlink()
>>> del a  # Use of a ShareableList after call to unlink() is unsupported

下面的例子演示了一个、两个或多个进程如何通过提供下层的共享内存块名称来访问同一个 ShareableList:

>>> b = shared_memory.ShareableList(range(5))         # In a first process
>>> c = shared_memory.ShareableList(name=b.shm.name)  # In a second process
>>> c
ShareableList([0, 1, 2, 3, 4], name='...')
>>> c[-1] = -999
>>> b[-1]
-999
>>> b.shm.close()
>>> c.shm.close()
>>> c.shm.unlink()

The following examples demonstrates that ShareableList (and underlying SharedMemory) objects can be pickled and unpickled if needed. Note, that it will still be the same shared object. This happens, because the deserialized object has the same unique name and is just attached to an existing object with the same name (if the object is still alive):

>>> import pickle
>>> from multiprocessing import shared_memory
>>> sl = shared_memory.ShareableList(range(10))
>>> list(sl)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> deserialized_sl = pickle.loads(pickle.dumps(sl))
>>> list(deserialized_sl)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> sl[0] = -1
>>> deserialized_sl[1] = -2
>>> list(sl)
[-1, -2, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(deserialized_sl)
[-1, -2, 2, 3, 4, 5, 6, 7, 8, 9]

>>> sl.shm.close()
>>> sl.shm.unlink()

concurrent

目前,此包中只有一个模块:

  • concurrent.futures —— 启动并行任务

concurrent.futures —- 启动并行任务

3.2 新版功能.

源码: Lib/concurrent/futures/thread.pyLib/concurrent/futures/process.py


concurrent.futures 模块提供异步执行可调用对象高层接口。

异步执行可以由 ThreadPoolExecutor 使用线程或由 ProcessPoolExecutor 使用单独的进程来实现。 两者都是实现抽像类 Executor 定义的接口。

Executor 对象

class concurrent.futures.Executor

抽象类提供异步执行调用方法。要通过它的子类调用,而不是直接调用。

  • submit(fn, /, \args, *kwargs)

    调度可调用对象 fn,以 fn(*args **kwargs) 方式执行并返回 Future 对象代表可调用对象的执行。:

    with ThreadPoolExecutor(max_workers=1) as executor:
        future = executor.submit(pow, 323, 1235)
        print(future.result())
  • map(func, \iterables, timeout=None, chunksize=1*)

    类似于 map(func, *iterables) 函数,除了以下两点:

    • iterables 是立即执行而不是延迟执行的;

    • func 是异步执行的,对 func 的多个调用可以并发执行。

      如果从原始调用到 Executor.map() 经过 timeout 秒后, __next__() 已被调用且返回的结果还不可用,那么已返回的迭代器将触发 concurrent.futures.TimeoutErrortimeout 可以是整数或浮点数。如果 timeout 没有指定或为 None ,则没有超时限制。
      如果 func 调用引发一个异常,当从迭代器中取回它的值时这个异常将被引发。
      使用 ProcessPoolExecutor 时,这个方法会将 iterables 分割任务块并作为独立的任务并提交到执行池中。这些块的大概数量可以由 chunksize 指定正整数设置。 对很长的迭代器来说,使用大的 chunksize 值比默认值 1 能显著地提高性能。 chunksizeThreadPoolExecutor 没有效果。
      在 3.5 版更改: 加入 chunksize 参数。

  • shutdown(wait=True, **, cancel_futures=False*)

    当待执行的 future 对象完成执行后向执行者发送信号,它就会释放正在使用的任何资源。 在关闭后调用 Executor.submit()Executor.map() 将会引发 RuntimeError

    如果 waitTrue 则此方法只有在所有待执行的 future 对象完成执行且释放已分配的资源后才会返回。 如果 waitFalse,方法立即返回,所有待执行的 future 对象完成执行后会释放已分配的资源。 不管 wait 的值是什么,整个 Python 程序将等到所有待执行的 future 对象完成执行后才退出。

    如果 cancel_futuresTrue,此方法将取消所有执行器还未开始运行的挂起的 Future。 任何已完成或正在运行的 Future 将不会被取消,无论 cancel_futures 的值是什么?

    如果 cancel_futureswait 均为 True,则执行器已开始运行的所有 Future 将在此方法返回之前完成。 其余的 Future 会被取消。

    如果使用 with 语句,你就可以避免显式调用这个方法,它将会停止 Executor (就好像 Executor.shutdown() 调用时 wait 设为 True 一样等待):

    import shutil
    with ThreadPoolExecutor(max_workers=4) as e:
        e.submit(shutil.copy, 'src1.txt', 'dest1.txt')
        e.submit(shutil.copy, 'src2.txt', 'dest2.txt')
        e.submit(shutil.copy, 'src3.txt', 'dest3.txt')
        e.submit(shutil.copy, 'src4.txt', 'dest4.txt')

    在 3.9 版更改: 增加了 cancel_futures

ThreadPoolExecutor

ThreadPoolExecutorExecutor 的子类,它使用线程池来异步执行调用。

当回调已关联了一个 Future 然后再等待另一个 Future 的结果时就会发产死锁情况。例如:

import time
def wait_on_b():
    time.sleep(5)
    print(b.result())  # b will never complete because it is waiting on a.
    return 5
def wait_on_a():
    time.sleep(5)
    print(a.result())  # a will never complete because it is waiting on b.
    return 6
executor = ThreadPoolExecutor(max_workers=2)
a = executor.submit(wait_on_b)
b = executor.submit(wait_on_a)

与:

def wait_on_future():
    f = executor.submit(pow, 5, 2)
    # This will never complete because there is only one worker thread and
    # it is executing this function.
    print(f.result())
executor = ThreadPoolExecutor(max_workers=1)
executor.submit(wait_on_future)

class concurrent.futures.ThreadPoolExecutor(max_workers=None, thread_name_prefix=’’, initializer=None, initargs=())

Executor 子类使用最多 max_workers 个线程的线程池来异步执行调用。

initializer 是在每个工作者线程开始处调用的一个可选可调用对象。 initargs 是传递给初始化器的元组参数。任何向池提交更多工作的尝试, initializer 都将引发一个异常,当前所有等待的工作都会引发一个 BrokenThreadPool

在 3.5 版更改: 如果 max_workersNone 或没有指定,将默认为机器处理器的个数,假如 ThreadPoolExecutor 则重于I/O操作而不是CPU运算,那么可以乘以 5 ,同时工作线程的数量可以比 ProcessPoolExecutor 的数量高。

3.6 新版功能: 添加 thread_name_prefix 参数允许用户控制由线程池创建的 threading.Thread 工作线程名称以方便调试。

在 3.7 版更改: 加入 initializerinitargs 参数。

在 3.8 版更改: max_workers 的默认值已改为 min(32, os.cpu_count() + 4)。 这个默认值会保留至少 5 个工作线程用于 I/O 密集型任务。 对于那些释放了 GIL 的 CPU 密集型任务,它最多会使用 32 个 CPU 核心。这样能够避免在多核机器上不知不觉地使用大量资源。

现在 ThreadPoolExecutor 在启动 max_workers 个工作线程之前也会重用空闲的工作线程。

ThreadPoolExecutor 例子

import concurrent.futures
import urllib.request
URLS = ['http://www.foxnews.com/',
        'http://www.cnn.com/',
        'http://europe.wsj.com/',
        'http://www.bbc.co.uk/',
        'http://some-made-up-domain.com/']
# Retrieve a single page and report the URL and contents
def load_url(url, timeout):
    with urllib.request.urlopen(url, timeout=timeout) as conn:
        return conn.read()
# We can use a with statement to ensure threads are cleaned up promptly
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    # Start the load operations and mark each future with its URL
    future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}
    for future in concurrent.futures.as_completed(future_to_url):
        url = future_to_url[future]
        try:
            data = future.result()
        except Exception as exc:
            print('%r generated an exception: %s' % (url, exc))
        else:
            print('%r page is %d bytes' % (url, len(data)))

ProcessPoolExecutor

ProcessPoolExecutor 类是 Executor 的子类,它使用进程池来异步地执行调用。 ProcessPoolExecutor 会使用 multiprocessing 模块,这允许它绕过 全局解释器锁 但也意味着只可以处理和返回可封存的对象。

__main__ 模块必须可以被工作者子进程导入。这意味着 ProcessPoolExecutor 不可以工作在交互式解释器中。

从可调用对象中调用 ExecutorFuture 的方法提交给 ProcessPoolExecutor 会导致死锁。

class concurrent.futures.ProcessPoolExecutor(max_workers=None, mp_context=None, initializer=None, initargs=())

异步地执行调用的 Executor 子类使用最多具有 max_workers 个进程的进程池。 如果 max_workersNone 或未给出,它将默认为机器的处理器个数。 如果 max_workers 小于等于 0,则将引发 ValueError。 在 Windows 上,max_workers 必须小于等于 61,否则将引发 ValueError。 如果 max_workersNone,则所选择的默认值最多为 61,即使存在更多的处理器。 mp_context 可以是一个多进程上下文或是 None。 它将被用来启动工作进程。 如果 mp_contextNone 或未给出,则将使用默认的多进程上下文。

initializer 是一个可选的可调用对象,它会在每个工作进程启动时被调用;initargs 是传给 initializer 的参数元组。 如果 initializer 引发了异常,则所有当前在等待的任务以及任何向进程池提交更多任务的尝试都将引发 BrokenProcessPool

在 3.3 版更改: 如果其中一个工作进程被突然终止,BrokenProcessPool 就会马上触发。 可预计的行为没有定义,但执行器上的操作或它的 future 对象会被冻结或死锁。

在 3.7 版更改: 添加 mp_context 参数允许用户控制由进程池创建给工作者进程的开始方法 。

加入 initializerinitargs 参数。

ProcessPoolExecutor 例子

import concurrent.futures
import math
PRIMES = [
    112272535095293,
    112582705942171,
    112272535095293,
    115280095190773,
    115797848077099,
    1099726899285419]
def is_prime(n):
    if n < 2:
        return False
    if n == 2:
        return True
    if n % 2 == 0:
        return False
    sqrt_n = int(math.floor(math.sqrt(n)))
    for i in range(3, sqrt_n + 1, 2):
        if n % i == 0:
            return False
    return True
def main():
    with concurrent.futures.ProcessPoolExecutor() as executor:
        for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)):
            print('%d is prime: %s' % (number, prime))
if __name__ == '__main__':
    main()

Future 对象

Future 类将可调用对象封装为异步执行。Future 实例由 Executor.submit() 创建。

class concurrent.futures.Future

将可调用对象封装为异步执行。Future 实例由 Executor.submit() 创建,除非测试,不应直接创建。

  • cancel()

    尝试取消调用。 如果调用正在执行或已结束运行不能被取消则该方法将返回 False,否则调用会被取消并且该方法将返回 True

  • cancelled()

    如果调用成功取消返回 True

  • running()

    如果调用正在执行而且不能被取消那么返回 True

  • done()

    如果调用已被取消或正常结束那么返回 True

  • result(timeout=None)

    返回调用返回的值。如果调用还没完成那么这个方法将等待 timeout 秒。如果在 timeout 秒内没有执行完成,concurrent.futures.TimeoutError 将会被触发。timeout 可以是整数或浮点数。如果 timeout 没有指定或为 None,那么等待时间就没有限制。

    如果 futrue 在完成前被取消则 CancelledError 将被触发。

    如果调用引发了一个异常,这个方法也会引发同样的异常。

  • exception(timeout=None)

    返回由调用引发的异常。如果调用还没完成那么这个方法将等待 timeout 秒。如果在 timeout 秒内没有执行完成,concurrent.futures.TimeoutError 将会被触发。timeout 可以是整数或浮点数。如果 timeout 没有指定或为 None,那么等待时间就没有限制。

    如果 futrue 在完成前被取消则 CancelledError 将被触发。

    如果调用正常完成那么返回 None

  • add_done_callback(fn)

    附加可调用 fn 到 future 对象。当 future 对象被取消或完成运行时,将会调用 fn,而这个 future 对象将作为它唯一的参数。

    加入的可调用对象总被属于添加它们的进程中的线程按加入的顺序调用。如果可调用对象引发一个 Exception 子类,它会被记录下来并被忽略掉。如果可调用对象引发一个 BaseException 子类,这个行为没有定义。

    如果 future 对象已经完成或已取消,fn 会被立即调用。

下面这些 Future 方法用于单元测试和 Executor 实现。

  • set_running_or_notify_cancel()

    这个方法只可以在执行关联 Future 工作之前由 Executor 实现调用或由单测试调用。

    如果这个方法返回 False 那么 Future 已被取消,即 Future.cancel() 已被调用并返回 True 。等待 Future 完成 (即通过 as_completed()wait()) 的线程将被唤醒。

    如果这个方法返回 True 那么 Future 不会被取消并已将它变为正在运行状态,也就是说调用 Future.running() 时将返回 True。

    这个方法只可以被调用一次并且不能在调用 Future.set_result()Future.set_exception() 之后再调用。

  • set_result(result)

    设置将 Future 关联工作的结果给 result

    这个方法只可以由 Executor 实现和单元测试使用。

    在 3.8 版更改: 如果 Future 已经完成则此方法会引发 concurrent.futures.InvalidStateError

  • set_exception(exception)

    设置 Future 关联工作的结果给 Exception exception

    这个方法只可以由 Executor 实现和单元测试使用。

    在 3.8 版更改: 如果 Future 已经完成则此方法会引发 concurrent.futures.InvalidStateError

模块函数

concurrent.futures.wait(fs, timeout=None, return_when=ALL_COMPLETED)

等待 fs 指定的 Future 实例(可能由不同的 Executor 实例创建)完成。 返回一个由集合构成的具名 2 元组。 第一个集合名称为 done,包含在等待完成之前已完成的期程(包括正常结束或被取消的 future 对象)。 第二个集合名称为 not_done,包含未完成的 future 对象(包括挂起的或正在运行的 future 对象)。

timeout 可以用来控制返回前最大的等待秒数。 timeout 可以为 int 或 float 类型。 如果 timeout 未指定或为 None ,则不限制等待时间。

return_when 指定此函数应在何时返回。它必须为以下常数之一:

常量 描述
FIRST_COMPLETED 函数将在任意可等待对象结束或取消时返回。
FIRST_EXCEPTION 函数将在任意可等待对象因引发异常而结束时返回。当没有引发任何异常时它就相当于 ALL_COMPLETED
ALL_COMPLETED 函数将在所有可等待对象结束或取消时返回。

concurrent.futures.as_completed(fs, timeout=None)

返回一个包含 fs 所指定的 Future 实例(可能由不同的 Executor 实例创建)的迭代器,这些实例会在完成时生成 future 对象(包括正常结束或被取消的 future 对象)。 任何由 fs 所指定的重复 future 对象将只被返回一次。 任何在 as_completed() 被调用之前完成的 future 对象将优先被生成。 如果 __next__() 被调用并且在对 as_completed() 的原始调用 timeout 秒之后结果仍不可用,则返回的迭代器将引发 concurrent.futures.TimeoutErrortimeout 可以为整数或浮点数。 如果 timeout 未指定或为 None,则不限制等待时间。

参见

PEP 3148 — future 对象 - 异步执行指令。

该提案描述了Python标准库中包含的这个特性。

Exception 类

exception concurrent.futures.CancelledError

future 对象被取消时会触发。

exception concurrent.futures.TimeoutError

future 对象执行超出给定的超时数值时引发。

exception concurrent.futures.BrokenExecutor

当执行器被某些原因中断而且不能用来提交或执行新任务时就会被引发派生于 RuntimeError 的异常类。

3.7 新版功能.

exception concurrent.futures.InvalidStateError

当某个操作在一个当前状态所不允许的 future 上执行时将被引发。

3.8 新版功能.

exception concurrent.futures.thread.BrokenThreadPool

ThreadPoolExecutor 中的其中一个工作者初始化失败时会引发派生于 BrokenExecutor 的异常类。

3.7 新版功能.

exception concurrent.futures.process.BrokenProcessPool

ThreadPoolExecutor 中的其中一个工作者不完整终止时(比如,被外部杀死)会引发派生于 BrokenExecutor ( 原名 RuntimeError ) 的异常类。

3.3 新版功能.

sched —- 事件调度器

源码: Lib/sched.py


sched 模块定义了一个实现通用事件调度程序的类:

class sched.scheduler(timefunc=time.monotonic, delayfunc=time.sleep)

scheduler 类定义了一个调度事件的通用接口。 它需要两个函数来实际处理“外部世界” —— timefunc 应当不带参数地调用,并返回一个数字(“时间”,可以为任意单位)。 delayfunc 函数应当带一个参数调用,与 timefunc 的输出相兼容,并且应当延迟其所指定的时间单位。 每个事件运行后还将调用 delayfunc 并传入参数 0 以允许其他线程有机会在多线程应用中运行。

在 3.3 版更改: timefuncdelayfunc 参数是可选的。

在 3.3 版更改: scheduler 类可以安全的在多线程环境中使用。

示例:

>>> import sched, time
>>> s = sched.scheduler(time.time, time.sleep)
>>> def print_time(a='default'):
...     print("From print_time", time.time(), a)
...
>>> def print_some_times():
...     print(time.time())
...     s.enter(10, 1, print_time)
...     s.enter(5, 2, print_time, argument=('positional',))
...     s.enter(5, 1, print_time, kwargs={'a': 'keyword'})
...     s.run()
...     print(time.time())
...
>>> print_some_times()
930343690.257
From print_time 930343695.274 positional
From print_time 930343695.275 keyword
From print_time 930343700.273 default
930343700.276

调度器对象

scheduler 实例拥有以下方法和属性:

scheduler.enterabs(time, priority, action, argument=(), kwargs={})

安排一个新事件。 time 参数应该有一个数字类型兼容的返回值,与传递给构造函数的 timefunc 函数的返回值兼容。 计划在相同 time 的事件将按其 priority 的顺序执行。 数字越小表示优先级越高。

执行事件意为执行 action(*argument, **kwargs)argument 是包含有 action 的位置参数的序列。 kwargs 是包含 action 的关键字参数的字典。

返回值是一个事件,可用于以后取消事件。

在 3.3 版更改: argument 参数是可选的。

在 3.3 版更改: 添加了 kwargs 形参。

scheduler.enter(delay, priority, action, argument=(), kwargs={})

安排延后 delay 时间单位的事件。 除了相对时间,其他参数、效果和返回值与 enterabs() 的相同。

在 3.3 版更改: argument 参数是可选的。

在 3.3 版更改: 添加了 kwargs 形参。

scheduler.cancel(event)

从队列中删除事件。 如果 event 不是当前队列中的事件,则此方法将引发 ValueError

scheduler.empty()

如果事件队列为空则返回 True

scheduler.run(blocking=True)

运行所有预定事件。 此方法将等待(使用传递给构造函数的 delayfunc() 函数)进行下一个事件,然后执行它,依此类推,直到没有更多的计划事件。

如果 blocking 为false,则执行由于最快到期(如果有)的预定事件,然后在调度程序中返回下一个预定调用的截止时间(如果有)。

actiondelayfunc 都可以引发异常。 在任何一种情况下,调度程序都将保持一致状态并传播异常。 如果 action 引发异常,则在将来调用 run() 时不会尝试该事件。

如果一系列事件的运行时间比下一个事件之前的可用时间长,那么调度程序将完全落后。 不会发生任何事件;调用代码负责取消不再相关的事件。

在 3.3 版更改: 添加了 blocking 形参。

scheduler.queue

只读属性按照将要运行的顺序返回即将发生的事件列表。 每个事件都显示为 named tuple ,包含以下字段:time、priority、action、argument、kwargs。

queue —- 一个同步的队列类

源代码: Lib/queue.py


queue 模块实现了多生产者、多消费者队列。这特别适用于消息必须安全地在多线程间交换的线程编程。模块中的 Queue 类实现了所有所需的锁定语义。

模块实现了三种类型的队列,它们的区别仅仅是条目取回的顺序。在 FIFO 队列中,先添加的任务先取回。在 LIFO 队列中,最近被添加的条目先取回(操作类似一个堆栈)。优先级队列中,条目将保持排序( 使用 heapq 模块 ) 并且最小值的条目第一个返回。

在内部,这三个类型的队列使用锁来临时阻塞竞争线程;然而,它们并未被设计用于线程的重入性处理。

此外,模块实现了一个 “简单的” FIFO 队列类型, SimpleQueue ,这个特殊实现为小功能在交换中提供额外的保障。

queue 模块定义了下列类和异常:

class queue.Queue(maxsize=0)

Constructor for a FIFO queue. maxsize is an integer that sets the upperbound limit on the number of items that can be placed in the queue. Insertion will block once this size has been reached, until queue items are consumed. If maxsize is less than or equal to zero, the queue size is infinite.

class queue.LifoQueue(maxsize=0)

LIFO 队列构造函数。 maxsize 是个整数,用于设置可以放入队列中的项目数的上限。当达到这个大小的时候,插入操作将阻塞至队列中的项目被消费掉。如果 maxsize 小于等于零,队列尺寸为无限大。

class queue.PriorityQueue(maxsize=0)

优先级队列构造函数。 maxsize 是个整数,用于设置可以放入队列中的项目数的上限。当达到这个大小的时候,插入操作将阻塞至队列中的项目被消费掉。如果 maxsize 小于等于零,队列尺寸为无限大。

最小值先被取出( 最小值条目是由 sorted(list(entries))[0] 返回的条目)。条目的典型模式是一个以下形式的元组: (priority_number, data)

如果 data 元素没有可比性,数据将被包装在一个类中,忽略数据值,仅仅比较优先级数字 :

from dataclasses import dataclass, field
from typing import Any
@dataclass(order=True)
class PrioritizedItem:
    priority: int
    item: Any=field(compare=False)

class queue.SimpleQueue

无界的 FIFO 队列构造函数。简单的队列,缺少任务跟踪等高级功能。

3.7 新版功能.

exception queue.Empty

对空的 Queue 对象,调用非阻塞的 get() (or get_nowait()) 时,引发的异常。

exception queue.Full

对满的 Queue 对象,调用非阻塞的 put() (or put_nowait()) 时,引发的异常。

Queue对象

队列对象 (Queue, LifoQueue, 或者 PriorityQueue) 提供下列描述的公共方法。

Queue.qsize()

返回队列的大致大小。注意,qsize() > 0 不保证后续的 get() 不被阻塞,qsize() < maxsize 也不保证 put() 不被阻塞。

Queue.empty()

如果队列为空,返回 True ,否则返回 False 。如果 empty() 返回 True ,不保证后续调用的 put() 不被阻塞。类似的,如果 empty() 返回 False ,也不保证后续调用的 get() 不被阻塞。

Queue.full()

如果队列是满的返回 True ,否则返回 False 。如果 full() 返回 True 不保证后续调用的 get() 不被阻塞。类似的,如果 full() 返回 False 也不保证后续调用的 put() 不被阻塞。

Queue.put(item, block=True, timeout=None)

item 放入队列。如果可选参数 block 是 true 并且 timeoutNone (默认),则在必要时阻塞至有空闲插槽可用。如果 timeout 是个正数,将最多阻塞 timeout 秒,如果在这段时间没有可用的空闲插槽,将引发 Full 异常。反之 (block 是 false),如果空闲插槽立即可用,则把 item 放入队列,否则引发 Full 异常 ( 在这种情况下,timeout 将被忽略)。

Queue.put_nowait(item)

相当于 put(item, False)

Queue.get(block=True, timeout=None)

从队列中移除并返回一个项目。如果可选参数 block 是 true 并且 timeoutNone (默认值),则在必要时阻塞至项目可得到。如果 timeout 是个正数,将最多阻塞 timeout 秒,如果在这段时间内项目不能得到,将引发 Empty 异常。反之 (block 是 false) , 如果一个项目立即可得到,则返回一个项目,否则引发 Empty 异常 (这种情况下,timeout 将被忽略)。

POSIX系统3.0之前,以及所有版本的Windows系统中,如果 block 是 true 并且 timeoutNone , 这个操作将进入基础锁的不间断等待。这意味着,没有异常能发生,尤其是 SIGINT 将不会触发 KeyboardInterrupt 异常。

Queue.get_nowait()

相当于 get(False)

提供了两个方法,用于支持跟踪 排队的任务 是否 被守护的消费者线程 完整的处理。

Queue.task_done()

表示前面排队的任务已经被完成。被队列的消费者线程使用。每个 get() 被用于获取一个任务, 后续调用 task_done() 告诉队列,该任务的处理已经完成。

如果 join() 当前正在阻塞,在所有条目都被处理后,将解除阻塞(意味着每个 put() 进队列的条目的 task_done() 都被收到)。

如果被调用的次数多于放入队列中的项目数量,将引发 ValueError 异常 。

Queue.join()

阻塞至队列中所有的元素都被接收和处理完毕。

当条目添加到队列的时候,未完成任务的计数就会增加。每当消费者线程调用 task_done() 表示这个条目已经被回收,该条目所有工作已经完成,未完成计数就会减少。当未完成计数降到零的时候, join() 阻塞被解除。

如何等待排队的任务被完成的示例:

import threading, queue
q = queue.Queue()
def worker():
    while True:
        item = q.get()
        print(f'Working on {item}')
        print(f'Finished {item}')
        q.task_done()
# turn-on the worker thread
threading.Thread(target=worker, daemon=True).start()
# send thirty task requests to the worker
for item in range(30):
    q.put(item)
print('All task requests sent\n', end='')
# block until all tasks are done
q.join()
print('All work completed')

SimpleQueue 对象

SimpleQueue 对象提供下列描述的公共方法。

SimpleQueue.qsize()

返回队列的大致大小。注意,qsize() > 0 不保证后续的 get() 不被阻塞。

SimpleQueue.empty()

如果队列为空,返回 True ,否则返回 False 。如果 empty() 返回 False ,不保证后续调用的 get() 不被阻塞。

SimpleQueue.put(item, block=True, timeout=None)

item 放入队列。此方法永不阻塞,始终成功(除了潜在的低级错误,例如内存分配失败)。可选参数 blocktimeout 仅仅是为了保持 Queue.put() 的兼容性而提供,其值被忽略。

CPython implementation detail: This method has a C implementation which is reentrant. That is, a put() or get() call can be interrupted by another put() call in the same thread without deadlocking or corrupting internal state inside the queue. This makes it appropriate for use in destructors such as __del__ methods or weakref callbacks.

SimpleQueue.put_nowait(item)

相当于 put(item) ,仅为保持 Queue.put_nowait() 兼容性而提供。

SimpleQueue.get(block=True, timeout=None)

从队列中移除并返回一个项目。如果可选参数 block 是 true 并且 timeoutNone (默认值),则在必要时阻塞至项目可得到。如果 timeout 是个正数,将最多阻塞 timeout 秒,如果在这段时间内项目不能得到,将引发 Empty 异常。反之 (block 是 false) , 如果一个项目立即可得到,则返回一个项目,否则引发 Empty 异常 (这种情况下,timeout 将被忽略)。

SimpleQueue.get_nowait()

相当于 get(False)

contextvars —- 上下文变量

本模块提供了相关API用于管理、存储和访问上下文相关的状态。 ContextVar 类用于声明 上下文变量 并与其一起使用。函数 copy_context() 和类 Context 用于管理当前上下文和异步框架中。

在多并发环境中,有状态上下文管理器应该使用上下文变量,而不是 threading.local() 来防止他们的状态意外泄露到其他代码。

更多信息参见 PEP 567

3.7 新版功能.

上下文变量

class contextvars.ContextVar(name[, **, default*])

此类用于声明一个新的上下文变量,如:

var: ContextVar[int] = ContextVar('var', default=42)

name 参数用于内省和调试,必需。

调用 ContextVar.get() 时,如果上下文中没有找到此变量的值,则返回可选的仅命名参数 default

重要: 上下文变量应该在顶级模块中创建,且永远不要在闭包中创建。 Context 对象拥有对上下文变量的强引用,这可以让上下文变量被垃圾收集器正确回收。

  • name

    上下文变量的名称,只读属性。

    3.7.1 新版功能.

  • get([default])

    返回当前上下文中此上下文变量的值。

    如果当前上下文中此变量没有值,则此方法会:

    • 如果提供了得 话,返回传入的 default 值;或者
    • 返回上下文变量本身的默认值, 如果创建此上下文变量时提供了默认值;或者
    • 抛出 LookupError 异常。
  • set(value)

    调用此方法设置上下文变量在当前上下文中的值。

    必选参数 value 是上下文变量的新值。

    返回一个 Token 对象,可通过 ContextVar.reset() 方法将上下文变量还原为之前某个状态。

  • reset(token)

    将上下文变量重置为调用 ContextVar.set() 之前、创建 token 时候的状态。

    例如:

    var = ContextVar('var')
    token = var.set('new value')
    # code that uses 'var'; var.get() returns 'new value'.
    var.reset(token)
    # After the reset call the var has no value again, so
    # var.get() would raise a LookupError.

class contextvars.Token

ContextVar.set() 方法返回 Token 对象。此对象可以传递给 ContextVar.reset() 方法用于将上下文变量还原为调用 set 前的状态。

  • Token.var

    只读属性。指向创建此 token 的 ContextVar 对象。

  • Token.old_value

    一个只读属性。 会被设为在创建此令牌的 ContextVar.set() 方法调用之前该变量所具有的值。 如果调用之前变量没有设置值,则它指向 Token.MISSING

  • Token.MISSING

    Token.old_value 会用到的一个标记对象。

手动上下文管理

contextvars.copy_context()

返回当前上下文中 Context 对象的拷贝。

以下代码片段会获取当前上下文的拷贝并打印设置到其中的所有变量及其值:

ctx: Context = copy_context()
print(list(ctx.items()))

此函数复杂度为 O(1) ,也就是说对于只包含几个上下文变量和很多上下文变量的情况,他们是一样快的。

class contextvars.Context

ContextVars 中所有值的映射。

Context() 创建一个不包含任何值的空上下文。如果要获取当前上下文的拷贝,使用 copy_context() 函数。

Context 实现了 collections.abc.Mapping 接口。

  • run(callable, \args, *kwargs)

    按照 run 方法中的参数在上下文对象中执行 callable(*args, **kwargs) 代码。返回执行结果,如果发生异常,则将异常透传出来。

    callable 中队上下文变量做出的任何修改会保留在上下文对象中:

    var = ContextVar('var')
    var.set('spam')
    def main():
        # 'var' was set to 'spam' before
        # calling 'copy_context()' and 'ctx.run(main)', so:
        # var.get() == ctx[var] == 'spam'
        var.set('ham')
        # Now, after setting 'var' to 'ham':
        # var.get() == ctx[var] == 'ham'
    ctx = copy_context()
    # Any changes that the 'main' function makes to 'var'
    # will be contained in 'ctx'.
    ctx.run(main)
    # The 'main()' function was run in the 'ctx' context,
    # so changes to 'var' are contained in it:
    # ctx[var] == 'ham'
    # However, outside of 'ctx', 'var' is still set to 'spam':
    # var.get() == 'spam'

    当在多个系统线程或者递归调用同一个上下文对象的此方法,抛出 RuntimeError 异常。

  • copy()

    返回此上下文对象的浅拷贝。

  • var in context

    如果context 中含有名称为 var 的变量,返回 True , 否则返回 False

  • context[var]

    返回名称为 varContextVar 变量。如果上下文对象中不包含这个变量,则抛出 KeyError 异常。

  • get(var[, default])

    如果 var 在上下文对象中具有值则返回 var 的值。 在其他情况下返回 default*。 如果未给出 *default 则返回 None

  • iter(context)

    返回一个存储在上下文对象中的变量的迭代器。

  • len(proxy)

    返回上下文对象中所设的变量的数量。

  • keys()

    返回上下文对象中的所有变量的列表。

  • values()

    返回上下文对象中所有变量值的列表。

  • items()

    返回包含上下文对象中所有变量及其值的 2 元组的列表。

asyncio 支持

上下文变量在 asyncio 中有原生的支持并且无需任何额外配置即可被使用。 例如,以下是一个简单的回显服务器,它使用上下文变量来让远程客户端的地址在处理该客户端的 Task 中可用:

import asyncio
import contextvars
client_addr_var = contextvars.ContextVar('client_addr')
def render_goodbye():
    # The address of the currently handled client can be accessed
    # without passing it explicitly to this function.
    client_addr = client_addr_var.get()
    return f'Good bye, client @ {client_addr}\n'.encode()
async def handle_request(reader, writer):
    addr = writer.transport.get_extra_info('socket').getpeername()
    client_addr_var.set(addr)
    # In any code that we call is now possible to get
    # client's address by calling 'client_addr_var.get()'.
    while True:
        line = await reader.readline()
        print(line)
        if not line.strip():
            break
        writer.write(line)
    writer.write(render_goodbye())
    writer.close()
async def main():
    srv = await asyncio.start_server(
        handle_request, '127.0.0.1', 8081)
    async with srv:
        await srv.serve_forever()
asyncio.run(main())
# To test it you can use telnet:
#     telnet 127.0.0.1 8081

_thread —- 底层多线程 API

该模块提供了操作多个线程(也被称为 轻量级进程任务*)的底层原语 —— 多个控制线程共享全局数据空间。为了处理同步问题,也提供了简单的锁机制(也称为 *互斥锁二进制信号)。threading 模块基于该模块提供了更易用的高级多线程 API。

在 3.7 版更改: 这个模块曾经为可选项,但现在总是可用。

这个模块定义了以下常量和函数:

exception _thread.error

发生线程相关错误时抛出。

在 3.3 版更改: 现在是内建异常 RuntimeError 的别名。

_thread.LockType

锁对象的类型。

_thread.start_new_thread(function, args[, kwargs])

开启一个新线程并返回其标识。 线程执行函数 function 并附带参数列表 args (必须是元组)。 可选的 kwargs 参数指定一个关键字参数字典。

当函数返回时,线程会静默地退出。

当函数因某个未处理异常而终结时,sys.unraisablehook() 会被调用以处理异常。 钩子参数的 object 属性为 function。 在默认情况下,会打印堆栈回溯然后该线程将退出(但其他线程会继续运行)。

当函数引发 SystemExit 异常时,它会被静默地忽略。

在 3.8 版更改: 现在会使用 sys.unraisablehook() 来处理未处理的异常。

_thread.interrupt_main(signum=signal.SIGINT, /)

模拟一个信号到达主线程的效果。 线程可使用此函数来打断主线程,虽然并不保证打断将立即发生。

如果给出 signum,则表示要模拟的信号的编号。 如果未给出 signum,则将模拟 signal.SIGINT

如果 Python 没有处理给定的信号 (它被设为 signal.SIG_DFLsignal.SIG_IGN),此函数将不做任何操作。

在 3.10 版更改: 添加了 signum 参数来定制信号的编号。

注解

这并不会发出对应的信号而是将一个调用排入关联处理句柄的计划任务(如果句柄存在的话)。 如果你想要真的发出信号,请使用 signal.raise_signal()

_thread.exit()

抛出 SystemExit 异常。如果没有捕获的话,这个异常会使线程退出。

_thread.allocate_lock()

返回一个新的锁对象。锁中的方法在后面描述。初始情况下锁处于解锁状态。

_thread.get_ident()

返回当前线程的 “线程标识符”。它是一个非零的整数。它的值没有直接含义,主要是用作 magic cookie,比如作为含有线程相关数据的字典的索引。线程标识符可能会在线程退出,新线程创建时被复用。

_thread.get_native_id()

返回内核分配给当前线程的原生集成线程 ID。 这是一个非负整数。 它的值可被用来在整个系统中唯一地标识这个特定线程(直到线程终结,在那之后该值可能会被 OS 回收再利用)。

可用性: Windows, FreeBSD, Linux, macOS, OpenBSD, NetBSD, AIX。

3.8 新版功能.

_thread.stack_size([size])

返回创建线程时使用的堆栈大小。可选参数 size 指定之后新建的线程的堆栈大小,而且一定要是0(根据平台或者默认配置)或者最小是32,768(32KiB)的一个正整数。如果 size 没有指定,默认是0。如果不支持改变线程堆栈大小,会抛出 RuntimeError 错误。如果指定的堆栈大小不合法,会抛出 ValueError 错误并且不会修改堆栈大小。32KiB是当前最小的能保证解释器有足够堆栈空间的堆栈大小。需要注意的是部分平台对于堆栈大小会有特定的限制,例如要求大于32KiB的堆栈大小或者需要根据系统内存页面的整数倍进行分配 - 应当查阅平台文档有关详细信息(4KiB页面比较普遍,在没有更具体信息的情况下,建议的方法是使用4096的倍数作为堆栈大小)。

适用于: Windows,具有 POSIX 线程的系统。

_thread.TIMEOUT_MAX

Lock.acquire() 方法中 timeout 参数允许的最大值。传入超过这个值的 timeout 会抛出 OverflowError 异常。

3.2 新版功能.

锁对象有以下方法:

lock.acquire(waitflag=1, timeout=- 1)

没有任何可选参数时,该方法无条件申请获得锁,有必要的话会等待其他线程释放锁(同时只有一个线程能获得锁 —— 这正是锁存在的原因)。

如果传入了整型参数 waitflag,具体的行为取决于传入的值:如果是 0 的话,只会在能够立刻获取到锁时才获取,不会等待,如果是非零的话,会像之前提到的一样,无条件获取锁。

如果传入正浮点数参数 timeout,相当于指定了返回之前等待得最大秒数。如果传入负的 *timeout,相当于无限期等待。如果 *waitflag 是 0 的话,不能指定 timeout

如果成功获取到所会返回 True,否则返回 False

在 3.2 版更改: 新的 timeout 形参。

在 3.2 版更改: 现在获取锁的操作可以被 POSIX 信号中断。

lock.release()

释放锁。锁必须已经被获取过,但不一定是同一个线程获取的。

lock.locked()

返回锁的状态:如果已被某个线程获取,返回 True,否则返回 False

除了这些方法之外,锁对象也可以通过 with 语句使用,例如:

import _thread
a_lock = _thread.allocate_lock()
with a_lock:
    print("a_lock is locked while this executes")

注意事项:

  • 线程与中断奇怪地交互:KeyboardInterrupt 异常可能会被任意一个线程捕获。(如果 signal 模块可用的话,中断总是会进入主线程。)
  • 调用 sys.exit() 或是抛出 SystemExit 异常等效于调用 _thread.exit()
  • 不可能中断锁的 acquire() 方法 —— KeyboardInterrupt 一场会在锁获取到之后发生。
  • 当主线程退出时,由系统决定其他线程是否存活。在大多数系统中,这些线程会直接被杀掉,不会执行 tryfinally 语句,也不会执行对象析构函数。
  • 当主线程退出时,不会进行正常的清理工作(除非使用了 tryfinally 语句),标准 I/O 文件也不会刷新。

subprocess —- 子进程管理

源代码:Lib/subprocess.py


subprocess 模块允许你生成新的进程,连接它们的输入、输出、错误管道,并且获取它们的返回码。此模块打算代替一些老旧的模块与功能:

os.system
os.spawn*

在下面的段落中,你可以找到关于 subprocess 模块如何代替这些模块和功能的相关信息。

参见

PEP 324 — 提出 subprocess 模块的 PEP

使用 subprocess 模块

推荐的调用子进程的方式是在任何它支持的用例中使用 run() 函数。对于更进阶的用例,也可以使用底层的 Popen 接口。

run() 函数是在 Python 3.5 被添加的。

subprocess.run(args, **, stdin=None, input=None, stdout=None, stderr=None, capture_output=False, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None, text=None, env=None, universal_newlines=None, *other_popen_kwargs)

运行被 arg 描述的指令. 等待指令完成, 然后返回一个 CompletedProcess 实例.

以上显示的参数仅仅是最简单的一些,下面 常用参数 描述(因此在缩写签名中使用仅关键字标示)。完整的函数头和 Popen 的构造函数一样,此函数接受的大多数参数都被传递给该接口。(timeout, input, checkcapture_output 除外)。

如果 capture_output 设为 true,stdout 和 stderr 将会被捕获。在使用时,内置的 Popen 对象将自动用 stdout=PIPEstderr=PIPE 创建。stdoutstderr 参数不应当与 capture_output 同时提供。如果你希望捕获并将两个流合并在一起,使用 stdout=PIPEstderr=STDOUT 来代替 capture_output

timeout 参数将被传递给 Popen.communicate()。如果发生超时,子进程将被杀死并等待。 TimeoutExpired 异常将在子进程中断后被抛出。

input 参数将被传递给 Popen.communicate() 以及子进程的 stdin。 如果使用此参数,它必须是一个字节序列。 如果指定了 encodingerrors 或者将 text 设置为 True,那么也可以是一个字符串。 当使用此参数时,在创建内部 Popen 对象时将自动带上 stdin=PIPE,并且不能再手动指定 stdin 参数。

如果 check 设为 True, 并且进程以非零状态码退出, 一个 CalledProcessError 异常将被抛出. 这个异常的属性将设置为参数, 退出码, 以及标准输出和标准错误, 如果被捕获到.

如果 encoding 或者 error 被指定, 或者 text 被设为 True, 标准输入, 标准输出和标准错误的文件对象将通过指定的 encodingerrors 以文本模式打开, 否则以默认的 io.TextIOWrapper 打开. universal_newline 参数等同于 text 并且提供了向后兼容性. 默认情况下, 文件对象是以二进制模式打开的.

如果 env 不是 None, 它必须是一个字典, 为新的进程设置环境变量; 它用于替换继承的当前进程的环境的默认行为. 它将直接被传递给 Popen.

示例:

>>> subprocess.run(["ls","-l"])# doesn't capture output
CompletedProcess(args=['ls','-l'], returncode=0)
>>> subprocess.run("exit 1", shell=True, check=True)
Traceback(most recent call last):
...
subprocess.CalledProcessError:Command'exit 1' returned non-zero exit status 1
>>> subprocess.run(["ls","-l","/dev/null"], capture_output=True)
CompletedProcess(args=['ls','-l','/dev/null'], returncode=0,
stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n', stderr=b'')

3.5 新版功能.

在 3.6 版更改: 添加了 encodingerrors 形参.

在 3.7 版更改: 添加了 text 形参, 作为 universal_newlines 的一个更好理解的别名. 添加了 capture_output 形参.

classsubprocess.CompletedProcess

run() 的返回值, 代表一个进程已经结束.

  • args

    被用作启动进程的参数. 可能是一个列表或字符串.

  • returncode

    子进程的退出状态码. 通常来说, 一个为 0 的退出码表示进程运行正常.

    一个负值 -N 表示子进程被信号 N 中断 (仅 POSIX).

  • stdout

    从子进程捕获到的标准输出. 一个字节序列, 或一个字符串, 如果 run() 是设置了 encoding, errors 或者 text=True 来运行的. 如果未有捕获, 则为 None.

    如果你通过 stderr=subprocess.STDOUT 运行进程,标准输入和标准错误将被组合在这个属性中,并且 stderr 将为 None

  • stderr

    捕获到的子进程的标准错误. 一个字节序列, 或者一个字符串, 如果 run() 是设置了参数 encoding, errors 或者 text=True 运行的. 如果未有捕获, 则为 None.

  • check_returncode()

    如果 returncode 非零, 抛出 CalledProcessError.

3.5 新版功能.

subprocess.DEVNULL

可被 Popenstdin, stdout 或者 stderr 参数使用的特殊值, 表示使用特殊文件 os.devnull.

3.3 新版功能.

subprocess.PIPE

可被 Popenstdin, stdout 或者 stderr 参数使用的特殊值, 表示打开标准流的管道. 常用于 Popen.communicate().

subprocess.STDOUT

可被 Popenstdinstdout 或者 stderr 参数使用的特殊值, 表示标准错误与标准输出使用同一句柄。

exceptionsubprocess.SubprocessError

此模块的其他异常的基类。

3.3 新版功能.

exceptionsubprocess.TimeoutExpired

SubprocessError 的子类,等待子进程的过程中发生超时时被抛出。

  • cmd

    用于创建子进程的指令。

  • timeout

    超时秒数。

  • output

    子进程的输出, 如果被 run()check_output() 捕获。否则为 None

  • stdout

    对 output 的别名,对应的有 stderr

  • stderr

    子进程的标准错误输出,如果被 run() 捕获。 否则为 None

3.3 新版功能.

在 3.5 版更改: 添加了 stdoutstderr 属性。

exceptionsubprocess.CalledProcessError

SubprocessError 的子类,当一个被 check_call()check_output() 函数运行的子进程返回了非零退出码时被抛出。

  • returncode

    子进程的退出状态。如果程序由一个信号终止,这将会被设为一个负的信号码。

  • cmd

    用于创建子进程的指令。

  • output

    子进程的输出, 如果被 run()check_output() 捕获。否则为 None

  • stdout

    对 output 的别名,对应的有 stderr

  • stderr

    子进程的标准错误输出,如果被 run() 捕获。 否则为 None

在 3.5 版更改: 添加了 stdoutstderr 属性。

常用参数

为了支持丰富的使用案例, Popen 的构造函数(以及方便的函数)接受大量可选的参数。对于大多数典型的用例,许多参数可以被安全地留以它们的默认值。通常需要的参数有:

args 被所有调用需要,应当为一个字符串,或者一个程序参数序列。提供一个参数序列通常更好,它可以更小心地使用参数中的转义字符以及引用(例如允许文件名中的空格)。如果传递一个简单的字符串,则 shell 参数必须为 True (见下文)或者该字符串中将被运行的程序名必须用简单的命名而不指定任何参数。

stdin*, *stdoutstderr 分别指定了执行的程序的标准输入、输出和标准错误文件句柄。合法的值有 PIPEDEVNULL 、 一个现存的文件描述符(一个正整数)、一个现存的文件对象以及 NonePIPE 表示应该新建一个对子进程的管道。 DEVNULL 表示使用特殊的文件 os.devnull。当使用默认设置 None 时,将不会进行重定向,子进程的文件流将继承自父进程。另外, stderr 可能为 STDOUT,表示来自于子进程的标准错误数据应该被 stdout 相同的句柄捕获。

如果 encodingerrors 被指定,或者 text (也名为 universal_newlines*)为真,则文件对象 *stdinstdoutstderr 将会使用在此次调用中指定的 encodingerrors 以文本模式打开或者为默认的 io.TextIOWrapper

当构造函数的 newline 参数为 None 时。对于 stdin*, 输入的换行符 '\n' 将被转换为默认的换行符 os.linesep。对于 *stdoutstderr, 所有输出的换行符都被转换为 '\n'。更多信息。

如果文本模式未被使用, stdin*, *stdoutstderr 将会以二进制流模式打开。没有编码与换行符转换发生。

3.6 新版功能: 添加了 encodingerrors 形参。

3.7 新版功能: 添加了 text 形参作为 universal_newlines 的别名。

注解

文件对象 Popen.stdinPopen.stdoutPopen.stderr 的换行符属性不会被 Popen.communicate() 方法更新。

如果 shell 设为 True,,则使用 shell 执行指定的指令。如果您主要使用 Python 增强的控制流(它比大多数系统 shell 提供的强大),并且仍然希望方便地使用其他 shell 功能,如 shell 管道、文件通配符、环境变量展开以及 ~ 展开到用户家目录,这将非常有用。但是,注意 Python 自己也实现了许多类似 shell 的特性(例如 glob, fnmatch, os.walk(), os.path.expandvars(), os.path.expanduser()shutil)。

在 3.3 版更改: 当 universal_newline 被设为 True,则类使用 locale.getpreferredencoding(False) 编码来代替 locale.getpreferredencoding()

这些选项以及所有其他选项在 Popen 构造函数文档中有更详细的描述。

Popen 构造函数

此模块的底层的进程创建与管理由 Popen 类处理。它提供了很大的灵活性,因此开发者能够处理未被便利函数覆盖的不常见用例。

classsubprocess.Popen(args, bufsize=- 1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=None, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False, pass_fds=(), **, group=None, extra_groups=None, user=None, umask=- 1, encoding=None, errors=None, text=None, pipesize=- 1*)

在一个新的进程中执行子程序。 在 POSIX 上,该类会使用类似于 os.execvpe() 的行为来执行子程序。 在 Windows 上,该类会使用 Windows CreateProcess() 函数。 Popen 的参数如下。

args 应当是一个程序参数的序列或者是一个单独的字符串或 path-like object。 默认情况下,如果 args 是序列则要运行的程序为 args 中的第一项。 如果 args 是字符串,则其解读依赖于具体平台,如下所述。 请查看 shellexecutable 参数了解其与默认行为的其他差异。 除非另有说明,否则推荐以序列形式传入 args

警告

为了最大化可靠性,请使用可执行文件的完整限定路径。 要在 PATH 中搜索一个未限定名称,请使用 shutil.which()。 在所有平台上,传入 sys.executable 是再次启动当前 Python 解释器的推荐方式,并请使用 -m 命令行格式来启动已安装的模块。

executable (或 args 的第一项) 路径的解析方式依赖于具体平台。 并要注意当解析或搜索可执行文件路径时,cwd 会覆盖当前工作目录而 env 可以覆盖 PATH 环境变量。 对于 Windows,请参阅 lpApplicationName 的文档以及 lpCommandLine 形参 (传给 WinAPI CreateProcess),并要注意当解析或搜索可执行文件路径时如果传入 shell=False,则 cwd 不会覆盖当前工作目录而 env 无法覆盖 PATH 环境变量。 使用完整路径可避免所有这些变化情况。

向外部函数传入序列形式参数的一个例子如下:

Popen(["/usr/bin/git","commit","-m","Fixes a bug."])

在 POSIX,如果 args 是一个字符串,此字符串被作为将被执行的程序的命名或路径解释。但是,只有在不传递任何参数给程序的情况下才能这么做。

注解

将 shell 命令拆分为参数序列的方式可能并不很直观,特别是在复杂的情况下。 shlex.split() 可以演示如何确定 args 适当的拆分形式:

>>>import shlex, subprocess
>>> command_line = input()
/bin/vikings -input eggs.txt -output "spam spam.txt"-cmd "echo '$MONEY'"
>>> args = shlex.split(command_line)
>>>print(args)
['/bin/vikings','-input','eggs.txt','-output','spam spam.txt','-cmd',"echo '$MONEY'"]
>>> p = subprocess.Popen(args)# Success!

特别注意,由 shell 中的空格分隔的选项(例如 -input*)和参数(例如 *eggs.txt )位于分开的列表元素中,而在需要时使用引号或反斜杠转义的参数在 shell (例如包含空格的文件名或上面显示的 echo 命令)是单独的列表元素。

在 Windows,如果 args 是一个序列,他将通过一个在 在 Windows 上将参数列表转换为一个字符串 描述的方式被转换为一个字符串。这是因为底层的 CreateProcess() 只处理字符串。

在 3.6 版更改: 在 POSIX 上如果 shellFalse 并且序列包含路径类对象则 args 形参可以接受一个 path-like object。

在 3.8 版更改: 如果在Windows 上 shellFalse 并且序列包含字节串和路径类对象则 args 形参可以接受一个 path-like object。

参数 shell (默认为 False)指定是否使用 shell 执行程序。如果 shellTrue,更推荐将 args 作为字符串传递而非序列。

在 POSIX,当 shell=True, shell 默认为 /bin/sh。如果 args 是一个字符串,此字符串指定将通过 shell 执行的命令。这意味着字符串的格式必须和在命令提示符中所输入的完全相同。这包括,例如,引号和反斜杠转义包含空格的文件名。如果 args 是一个序列,第一项指定了命令,另外的项目将作为传递给 shell (而非命令) 的参数对待。也就是说, Popen 等同于:

Popen(['/bin/sh','-c', args[0], args[1],...])

在 Windows,使用 shell=True,环境变量 COMSPEC 指定了默认 shell。在 Windows 你唯一需要指定 shell=True 的情况是你想要执行内置在 shell 中的命令(例如 dir 或者 copy)。在运行一个批处理文件或者基于控制台的可执行文件时,不需要 shell=True

bufsize 将在 open() 函数创建了 stdin/stdout/stderr 管道文件对象时作为对应的参数供应:

  • 0 表示不使用缓冲区 (读取与写入是一个系统调用并且可以返回短内容)
  • 1 表示行缓冲(只有 universal_newlines=True 时才有用,例如,在文本模式中)
  • 任何其他正值表示使用一个约为对应大小的缓冲区
  • 负的 bufsize (默认)表示使用系统默认的 io.DEFAULT_BUFFER_SIZE。

在 3.3.1 版更改: bufsize 现在默认为 -1 来启用缓冲,以符合大多数代码所期望的行为。在 Python 3.2.4 和 3.3.1 之前的版本中,它错误地将默认值设为了为 0,这是无缓冲的并且允许短读取。这是无意的,并且与大多数代码所期望的 Python 2 的行为不一致。

executable 参数指定一个要执行的替换程序。这很少需要。当 shell=Trueexecutable 替换 args 指定运行的程序。但是,原始的 args 仍然被传递给程序。大多数程序将被 args 指定的程序作为命令名对待,这可以与实际运行的程序不同。在 POSIX, args 名作为实际调用程序中可执行文件的显示名称,例如 ps。如果 shell=True,在 POSIX, executable 参数指定用于替换默认 shell /bin/sh 的 shell。

在 3.6 版更改: 在POSIX 上 executable 形参可以接受一个 path-like object。

在 3.8 版更改: 在Windows 上 executable 形参可以接受一个字节串和 path-like object。

stdin, stdoutstderr 分别指定被运行的程序的标准输入、输出和标准错误的文件句柄。合法的值有 PIPEDEVNULL , 一个存在的文件描述符(一个正整数),一个存在的 文件对象 以及 NonePIPE 表示应创建一个新的对子进程的管道。 DEVNULL 表示使用特殊的 os.devnull 文件。使用默认的 None,则不进行成定向;子进程的文件流将继承自父进程。另外, stderr 可设为 STDOUT,表示应用程序的标准错误数据应和标准输出一同捕获。

如果 preexec_fn 被设为一个可调用对象,此对象将在子进程刚创建时被调用。(仅 POSIX)

警告

preexec_fn 形参在应用程序中存在多线程时是不安全的。子进程在调用前可能死锁。如果你必须使用它,保持警惕!最小化你调用的库的数量。

注解

如果你需要修改子进程环境,使用 env 形参而非在 preexec_fn 中进行。 start_new_session 形参可以代替之前常用的 preexec_fn 来在子进程中调用 os.setsid()。

在 3.8 版更改: preexec_fn 形参在子解释器中已不再受支持。 在子解释器中使用此形参将引发 RuntimeError。 这个新限制可能会影响部署在 mod_wsgi, uWSGI 和其他嵌入式环境中的应用。

如果 close_fds 为真,所有文件描述符除了 0, 1, 2 之外都会在子进程执行前关闭。而当 close_fds 为假时,文件描述符遵守它们继承的标志,如 文件描述符的继承 所述。

在 Windows,如果 close_fds 为真, 则子进程不会继承任何句柄,除非在 STARTUPINFO.IpAttributeListhandle_list 的键中显式传递,或者通过标准句柄重定向传递。

在 3.2 版更改: close_fds 的默认值已经从 False 修改为上述值。

在 3.7 版更改: 在 Windows,当重定向标准句柄时 close_fds 的默认值从 False 变为 True。现在重定向标准句柄时有可能设置 close_fdsTrue。(标准句柄指三个 stdio 的句柄)

pass_fds 是一个可选的在父子进程间保持打开的文件描述符序列。提供任何 pass_fds 将强制 close_fdsTrue。(仅 POSIX)

在 3.2 版更改: 加入了 pass_fds 形参。

如果 cwd 不为 None,此函数在执行子进程前会将当前工作目录改为 cwd*。 *cwd 可以是一个字符串、字节串或 路径类对象。 在 POSIX 上,如果可执行文件路径为相对路径则此函数会相对于 cwd 来查找 executable (或 args 的第一项)。

在 3.6 版更改: 在 POSIX 上 cwd 形参接受一个 path-like object。

在 3.7 版更改: 在 Windows 上 cwd 形参接受一个 path-like object。

在 3.8 版更改: 在 Windows 上 cwd 形参接受一个字节串对象。

如果 restore_signals 为 true(默认值),则 Python 设置为 SIG_IGN 的所有信号将在 exec 之前的子进程中恢复为 SIG_DFL。目前,这包括 SIGPIPE ,SIGXFZ 和 SIGXFSZ 信号。 (仅 POSIX)

在 3.2 版更改: restore_signals 被加入。

如果 start_new_session 为 true,则 setsid() 系统调用将在子进程执行之前被执行。(仅 POSIX)

在 3.2 版更改: start_new_session 被添加。

如果 group 不为 None,则 setregid() 系统调用将于子进程执行之前在下级进程中进行。 如果所提供的值为一个字符串,将通过 grp.getgrnam() 来查找它,并将使用 gr_gid 中的值。 如果该值为一个整数,它将被原样传递。 (POSIX 专属)

可用性: POSIX

3.9 新版功能.

如果 extra_groups 不为 None,则 setgroups() 系统调用将于子进程之前在下级进程中进行。 在 extra_groups 中提供的字符串将通过 grp.getgrnam() 来查找,并将使用 gr_gid 中的值。 整数值将被原样传递。 (POSIX 专属)

可用性: POSIX

3.9 新版功能.

如果 user 不为 None,则 setreuid() 系统调用将于子进程执行之前在下级进程中进行。 如果所提供的值为一个字符串,将通过 pwd.getpwnam() 来查找它,并将使用 pw_uid 中的值。 如果该值为一个整数,它将被原样传递。 (POSIX 专属)

可用性: POSIX

3.9 新版功能.

如果 umask 不为负值,则 umask() 系统调用将在子进程执行之前在下级进程中进行。

可用性: POSIX

3.9 新版功能.

如果 env 不为 None,则必须为一个为新进程定义了环境变量的字典;这些用于替换继承的当前进程环境的默认行为。

注解

如果指定, env 必须提供所有被子进程需求的变量。在 Windows,为了运行一个 side-by-side assembly ,指定的 env**必须** 包含一个有效的 SystemRoot

如果 encodingerrors 被指定,或者 text 为 true,则文件对象 stdin, stdoutstderr 将会以指定的编码和 errors 以文本模式打开,如同 常用参数 所述。 universal_newlines 参数等同于 text 并且提供向后兼容性。默认情况下,文件对象都以二进制模式打开。

3.6 新版功能: encodingerrors 被添加。

3.7 新版功能: text 作为 universal_newlines 的一个更具可读性的别名被添加。

如果给出, startupinfo 将是一个将被传递给底层的 CreateProcess 函数的 STARTUPINFO 对象。 creationflags,如果给出,可以是一个或多个以下标志之一:

  • CREATE_NEW_CONSOLE
  • CREATE_NEW_PROCESS_GROUP
  • ABOVE_NORMAL_PRIORITY_CLASS
  • BELOW_NORMAL_PRIORITY_CLASS
  • HIGH_PRIORITY_CLASS
  • IDLE_PRIORITY_CLASS
  • NORMAL_PRIORITY_CLASS
  • REALTIME_PRIORITY_CLASS
  • CREATE_NO_WINDOW
  • DETACHED_PROCESS
  • CREATE_DEFAULT_ERROR_MODE
  • CREATE_BREAKAWAY_FROM_JOB

PIPE 被用作 stdin, stdoutstderrpipesize 可被用于改变管道的大小。 管道的大小仅会在受支持的平台上被改变(当撰写本文档时只有 Linux 支持)。 其他平台将忽略此形参。

3.10 新版功能: 增加了 pipesize 形参。

Popen 对象支持通过 with 语句作为上下文管理器,在退出时关闭文件描述符并等待进程:

withPopen(["ifconfig"], stdout=PIPE)as proc:
    log.write(proc.stdout.read())

引发一个 审计事件subprocess.Popen,附带参数 executable, args, cwd, env

在 3.2 版更改: 添加了上下文管理器支持。

在 3.6 版更改: 现在,如果 Popen 析构时子进程仍然在运行,则析构器会发送一个 ResourceWarning 警告。

在 3.8 版更改: 在某些情况下 Popen 可以使用 os.posix_spawn() 以获得更好的性能。在适用于 Linux 的 Windows 子系统和 QEMU 用户模拟器上,使用 os.posix_spawn() 的 Popen 构造器不再会因找不到程序等错误而引发异常,而是上下级进程失败并返回一个非零的 returncode

异常

在子进程中抛出的异常,在新的进程开始执行前,将会被再次在父进程中抛出。

被引发的最一般异常是 OSError。 例如这会在尝试执行一个不存在的文件时发生。 应用程序应当为 OSError 异常做好准备。 请注意,如果 shell=True,则 OSError 仅会在未找到选定的 shell 本身时被引发。 要确定 shell 是否未找到所请求的应用程序,必须检查来自子进程的返回码或输出。

如果 Popen 调用时有无效的参数,则一个 ValueError 将被抛出。

check_call()check_output() 在调用的进程返回非零退出码时将抛出 CalledProcessError

所有接受 timeout 形参的函数与方法,例如 call()Popen.communicate() 将会在进程退出前超时到期时抛出 TimeoutExpired

此模块中定义的异常都继承自 SubprocessError

3.3 新版功能: 基类 SubprocessError 被添加。

安全考量

不同于某些其他的 popen 函数,这个实现绝不会隐式地调用系统 shell。 这意味着所有字符,包括 shell 元字符,都可以安全地被传递给子进程。 如果 shell 通过 shell=True 被显式地发起调用,则应用程序有责任确保所有空白符和元字符被适当地转义以避免 shell 注入 漏洞。 在 某些平台 上,可以使用 shlex.quote() 来执行这样的转义。

Popen 对象

Popen 类的实例拥有以下方法:

Popen.poll()

检查子进程是否已被终止。设置并返回 returncode 属性。否则返回 None

Popen.wait(timeout=None)

等待子进程被终止。设置并返回 returncode 属性。

如果进程在 timeout 秒后未中断,抛出一个 TimeoutExpired 异常,可以安全地捕获此异常并重新等待。

注解

stdout=PIPE 或者 stderr=PIPE 并且子进程产生了足以阻塞 OS 管道缓冲区接收更多数据的输出到管道时,将会发生死锁。当使用管道时用 Popen.communicate() 来规避它。

注解

此函数使用了一个 busy loop (非阻塞调用以及短睡眠) 实现。使用 asyncio 模块进行异步等待: 参阅 asyncio.create_subprocess_exec

在 3.3 版更改: timeout 被添加

Popen.communicate(input=None, timeout=None)

与进程交互:将数据发送到 stdin。 从 stdout 和 stderr 读取数据,直到抵达文件结尾。 等待进程终止并设置 returncode 属性。 可选的 input 参数应为要发送到下级进程的数据,或者如果没有要发送到下级进程的数据则为 None。 如果流是以文本模式打开的,则 input 必须为字符串。 在其他情况下,它必须为字节串。

communicate() 返回一个 (stdout_data, stderr_data) 元组。如果文件以文本模式打开则为字符串;否则字节。

注意如果你想要向进程的 stdin 传输数据,你需要通过 stdin=PIPE 创建此 Popen 对象。类似的,要从结果元组获取任何非 None 值,你同样需要设置 stdout=PIPE 或者 stderr=PIPE

如果进程在 timeout 秒后未终止,一个 TimeoutExpired 异常将被抛出。捕获此异常并重新等待将不会丢失任何输出。

如果超时到期,子进程不会被杀死,所以为了正确清理一个行为良好的应用程序应该杀死子进程并完成通讯。

proc = subprocess.Popen(...)
try:
    outs, errs = proc.communicate(timeout=15)
exceptTimeoutExpired:
    proc.kill()
    outs, errs = proc.communicate()

注解

内存里数据读取是缓冲的,所以如果数据尺寸过大或无限,不要使用此方法。

在 3.3 版更改: timeout 被添加

Popen.send_signal(signal)

将信号 signal 发送给子进程。

如果进程已完成则不做任何操作。

注解

在 Windows, SIGTERM 是一个 terminate() 的别名。 CTRL_C_EVENT 和 CTRL_BREAK_EVENT 可以被发送给以包含 CREATE_NEW_PROCESScreationflags 形参启动的进程。

Popen.terminate()

停止子进程。 在 POSIX 操作系统上,此方法会发送 SIGTERM 给子进程。 在 Windows 上则会调用 Win32 API 函数 TerminateProcess() 来停止子进程。

Popen.kill()

杀死子进程。 在 POSIX 操作系统上,此函数会发送 SIGKILL 给子进程。 在 Windows 上 kill() 则是 terminate() 的别名。

以下属性也是可用的:

Popen.args

args 参数传递给 Popen — 一个程序参数的序列或者一个简单字符串。

3.3 新版功能.

Popen.stdin

如果 stdin 参数为 PIPE,此属性是一个类似 open() 返回的可写的流对象。如果 encodingerrors 参数被指定或者 universal_newlines 参数为 True,则此流是一个文本流,否则是字节流。如果 stdin 参数非 PIPE, 此属性为 None

Popen.stdout

如果 stdout 参数是 PIPE,此属性是一个类似 open() 返回的可读流。从流中读取子进程提供的输出。如果 encodingerrors 参数被指定或者 universal_newlines 参数为 True,此流为文本流,否则为字节流。如果 stdout 参数非 PIPE,此属性为 None

Popen.stderr

如果 stderr 参数是 PIPE,此属性是一个类似 open() 返回的可读流。从流中读取子进程提供的输出。如果 encodingerrors 参数被指定或者 universal_newlines 参数为 True,此流为文本流,否则为字节流。如果 stderr 参数非 PIPE,此属性为 None

警告

使用 communicate() 而非 .stdin.write.stdout.read 或者 .stderr.read 来避免由于任意其他 OS 管道缓冲区被子进程填满阻塞而导致的死锁。

Popen.pid

子进程的进程号。

注意如果你设置了 shell 参数为 True,则这是生成的子 shell 的进程号。

Popen.returncode

此进程的退出码,由 poll()wait() 设置(以及直接由 communicate() 设置)。一个 None 值 表示此进程仍未结束。

一个负值 -N 表示子进程被信号 N 中断 (仅 POSIX).

Windows Popen 助手

STARTUPINFO 类和以下常数仅在 Windows 有效。

classsubprocess.STARTUPINFO(**, dwFlags=0, hStdInput=None, hStdOutput=None, hStdError=None, wShowWindow=0, lpAttributeList=None*)

Popen 创建时部分支持 Windows 的 STARTUPINFO 结构。接下来的属性仅能通过关键词参数设置。

在 3.7 版更改: 仅关键词参数支持被加入。

  • dwFlags

    一个位字段,用于确定进程在创建窗口时是否使用某些 STARTUPINFO 属性。

    si = subprocess.STARTUPINFO()
    si.dwFlags = subprocess.STARTF_USESTDHANDLES | subprocess.STARTF_USESHOWWINDOW
  • hStdInput

    如果 dwFlags 被指定为 STARTF_USESTDHANDLES,则此属性是进程的标准输入句柄,如果 STARTF_USESTDHANDLES 未指定,则默认的标准输入是键盘缓冲区。

  • hStdOutput

    如果 dwFlags 被指定为 STARTF_USESTDHANDLES,则此属性是进程的标准输出句柄。除此之外,此此属性将被忽略并且默认标准输出是控制台窗口缓冲区。

  • hStdError

    如果 dwFlags 被指定为 STARTF_USESTDHANDLES,则此属性是进程的标准错误句柄。除此之外,此属性将被忽略并且默认标准错误为控制台窗口的缓冲区。

  • wShowWindow

    如果 dwFlags 指定了 STARTF_USESHOWWINDOW,此属性可为能被指定为 函数 ShowWindow 的nCmdShow 的形参的任意值,除了 SW_SHOWDEFAULT。如此之外,此属性被忽略。

    SW_HIDE 被提供给此属性。它在 Popenshell=True 调用时使用。

  • lpAttributeList

    STARTUPINFOEX 给出的用于进程创建的额外属性字典。

    支持的属性:

    • handle_list

      将被继承的句柄的序列。如果非空, close_fds 必须为 true。

      当传递给 Popen 构造函数时,这些句柄必须暂时地能被 os.set_handle_inheritable() 继承,否则 OSError 将以 Windows error ERROR_INVALID_PARAMETER (87) 抛出。

      警告

      在多线程进程中,请谨慎使用,以便在将此功能与对继承所有句柄的其他进程创建函数——例如 os.system() 的并发调用——相结合时,避免泄漏标记为可继承的句柄。这也应用于临时性创建可继承句柄的标准句柄重定向。

    3.7 新版功能.

Windows 常数

subprocess 模块曝出以下常数。

subprocess.STD_INPUT_HANDLE

标准输入设备,这是控制台输入缓冲区 CONIN$

subprocess.STD_OUTPUT_HANDLE

标准输出设备。最初,这是活动控制台屏幕缓冲区 CONOUT$

subprocess.STD_ERROR_HANDLE

标准错误设备。最初,这是活动控制台屏幕缓冲区 CONOUT$

subprocess.SW_HIDE

隐藏窗口。另一个窗口将被激活。

subprocess.STARTF_USESTDHANDLES

指明 STARTUPINFO.hStdInput, STARTUPINFO.hStdOutputSTARTUPINFO.hStdError 属性包含额外的信息。

subprocess.STARTF_USESHOWWINDOW

指明 STARTUPINFO.wShowWindow 属性包含额外的信息。

subprocess.CREATE_NEW_CONSOLE

新的进程将有新的控制台,而不是继承父进程的(默认)控制台。

subprocess.CREATE_NEW_PROCESS_GROUP

用于指明将创建一个新的进程组的 Popen``creationflags 形参。 这个旗标对于在子进程上使用 os.kill() 来说是必须的。

如果指定了 CREATE_NEW_CONSOLE 则这个旗标会被忽略。

subprocess.ABOVE_NORMAL_PRIORITY_CLASS

用于指明一个新进程将具有高于平均的优先级的 Popen``creationflags 形参。

3.7 新版功能.

subprocess.BELOW_NORMAL_PRIORITY_CLASS

用于指明一个新进程将具有低于平均的优先级的 Popen``creationflags 形参。

3.7 新版功能.

subprocess.HIGH_PRIORITY_CLASS

用于指明一个新进程将具有高优先级的 Popen``creationflags 形参。

3.7 新版功能.

subprocess.IDLE_PRIORITY_CLASS

用于指明一个新进程将具有空闲(最低)优先级的 Popen``creationflags 形参。

3.7 新版功能.

subprocess.NORMAL_PRIORITY_CLASS

用于指明一个新进程将具有正常(默认)优先级的 Popen``creationflags 形参。

3.7 新版功能.

subprocess.REALTIME_PRIORITY_CLASS

用于指明一个新进程将具有实时优先级的 Popen``creationflags 形参。 你应当几乎永远不使用 REALTIME_PRIORITY_CLASS,因为这会中断管理鼠标输入、键盘输入以及后台磁盘刷新的系统线程。 这个类只适用于直接与硬件“对话”,或者执行短暂任务具有受限中断的应用。

3.7 新版功能.

subprocess.CREATE_NO_WINDOW

指明一个新进程将不会创建窗口的 Popen``creationflags 形参。

3.7 新版功能.

subprocess.DETACHED_PROCESS

指明一个新进程将不会继承其父控制台的 Popen``creationflags 形参。 这个值不能与 CREATE_NEW_CONSOLE 一同使用。

3.7 新版功能.

subprocess.CREATE_DEFAULT_ERROR_MODE

指明一个新进程不会继承调用方进程的错误模式的 Popen``creationflags 形参。 新进程会转为采用默认的错误模式。 这个特性特别适用于运行时禁用硬错误的多线程 shell 应用。

3.7 新版功能.

subprocess.CREATE_BREAKAWAY_FROM_JOB

指明一个新进程不会关联到任务的 Popen creationflags 形参。

3.7 新版功能.

较旧的高阶 API

在 Python 3.5 之前,这三个函数组成了 subprocess 的高阶 API。 现在你可以在许多情况下使用 run(),但有大量现在代码仍会调用这些函数。

subprocess.call(args, **, stdin=None, stdout=None, stderr=None, shell=False, cwd=None, timeout=None, *other_popen_kwargs)

运行由 args 所描述的命令。 等待命令完成,然后返回 returncode 属性。

需要捕获 stdout 或 stderr 的代码应当改用 run():

run(...).returncode

要屏蔽 stdout 或 stderr,可提供 DEVNULL 这个值。

上面显示的参数只是常见的一些。 完整的函数签名与 Popen 构造器的相同 —— 此函数会将所提供的 timeout 之外的全部参数直接传递给目标接口。

注解

请不要在此函数中使用 stdout=PIPEstderr=PIPE。 如果子进程向管道生成了足以填满 OS 管理缓冲区的输出而管道还未被读取时它将会阻塞。

在 3.3 版更改: timeout 被添加

subprocess.check_call(args, **, stdin=None, stdout=None, stderr=None, shell=False, cwd=None, timeout=None, *other_popen_kwargs)

Run command with arguments. Wait for command to complete. If the return code was zero then return, otherwise raise CalledProcessError. The CalledProcessError object will have the return code in the returncode attribute. If check_call() was unable to start the process it will propagate the exception that was raised.

需要捕获 stdout 或 stderr 的代码应当改用 run():

run(..., check=True)

要屏蔽 stdout 或 stderr,可提供 DEVNULL 这个值。

上面显示的参数只是常见的一些。 完整的函数签名与 Popen 构造器的相同 —— 此函数会将所提供的 timeout 之外的全部参数直接传递给目标接口。

注解

请不要在此函数中使用 stdout=PIPEstderr=PIPE。 如果子进程向管道生成了足以填满 OS 管理缓冲区的输出而管道还未被读取时它将会阻塞。

在 3.3 版更改: timeout 被添加

subprocess.check_output(args, **, stdin=None, stderr=None, shell=False, cwd=None, encoding=None, errors=None, universal_newlines=None, timeout=None, text=None, *other_popen_kwargs)

附带参数运行命令并返回其输出。

如果返回码非零则会引发 CalledProcessErrorCalledProcessError 对象将在 returncode 属性中保存返回码并在 output 属性中保存所有输出。

这相当于:

run(..., check=True, stdout=PIPE).stdout

上面显示的参数只是常见的一些。 完整的函数签名与 run() 的大致相同 —— 大部分参数会通过该接口直接传递。 存在一个与 run() 行为不同的 API 差异:传递 input=None 的行为将与 input=b'' (或 input='',具体取决于其他参数) 一样而不是使用父对象的标准输入文件处理。

默认情况下,此函数将把数据返回为已编码的字节串。 输出数据的实际编码格式将取决于发起调用的命令,因此解码为文本的操作往往需要在应用程序层级上进行处理。

此行为可以通过设置 text, encoding, errors 或将 universal_newlines 设为 True 来重载。

要在结果中同时捕获标准错误,请使用 stderr=subprocess.STDOUT:

>>> subprocess.check_output(
..."ls non_existent_file; exit 0",
...     stderr=subprocess.STDOUT,
...     shell=True)
'ls: non_existent_file: No such file or directory\n'

3.1 新版功能.

在 3.3 版更改: timeout 被添加

在 3.4 版更改: 增加了对 input 关键字参数的支持。

在 3.6 版更改: 增加了 encodingerrors

3.7 新版功能: text 作为 universal_newlines 的一个更具可读性的别名被添加。

使用 subprocess 模块替换旧函数

在这一节中,”a 改为 b” 意味着 b 可以被用作 a 的替代。

注解

在这一节中的所有 “a” 函数会在找不到被执行的程序时(差不多)静默地失败;”b” 替代函数则会改为引发 OSError

此外,在使用 check_output() 时如果替代函数所请求的操作产生了非零返回值则将失败并引发 CalledProcessError。 操作的输出仍能以所引发异常的 output 属性的方式被访问。

在下列例子中,我们假定相关的函数都已从 subprocess 模块中导入了。

替代 /bin/sh shell 命令替换

output=$(mycmd myarg)

改为:

output = check_output(["mycmd","myarg"])

替代 shell 管道

output=$(dmesg | grep hda)

改为:

p1 =Popen(["dmesg"], stdout=PIPE)
p2 =Popen(["grep","hda"], stdin=p1.stdout, stdout=PIPE)
p1.stdout.close()# Allow p1 to receive a SIGPIPE if p2 exits.
output = p2.communicate()[0]

启动 p2 之后再执行 p1.stdout.close() 调用很重要,这是为了让 p1 能在 p2 先于 p1 退出时接收到 SIGPIPE。

另外,对于受信任的输入,shell 本身的管道支持仍然可被直接使用:

output=$(dmesg | grep hda)

改为:

output = check_output("dmesg | grep hda", shell=True)

替代 os.system()

sts = os.system("mycmd"+" myarg")
# becomes
retcode = call("mycmd"+" myarg", shell=True)

注释:

  • 通过 shell 来调用程序通常是不必要的。
  • call() 返回值的编码方式与 os.system() 的不同。
  • os.system() 函数在命令运行期间会忽略 SIGINT 和 SIGQUIT 信号,但调用方必须在使用 subprocess 模块时分别执行此操作。

一个更现实的例子如下所示:

try:
    retcode = call("mycmd"+" myarg", shell=True)
if retcode <0:
print("Child was terminated by signal",-retcode, file=sys.stderr)
else:
print("Child returned", retcode, file=sys.stderr)
exceptOSErroras e:
print("Execution failed:", e, file=sys.stderr)

替代 os.spawn 函数族

P_NOWAIT 示例:

pid = os.spawnlp(os.P_NOWAIT,"/bin/mycmd","mycmd","myarg")
==>
pid =Popen(["/bin/mycmd","myarg"]).pid

P_WAIT 示例:

retcode = os.spawnlp(os.P_WAIT,"/bin/mycmd","mycmd","myarg")
==>
retcode = call(["/bin/mycmd","myarg"])

Vector 示例:

os.spawnvp(os.P_NOWAIT, path, args)
==>
Popen([path]+ args[1:])

Environment 示例:

os.spawnlpe(os.P_NOWAIT,"/bin/mycmd","mycmd","myarg", env)
==>
Popen(["/bin/mycmd","myarg"], env={"PATH":"/usr/bin"})

替代 os.popen(), os.popen2(), os.popen3()

(child_stdin, child_stdout)= os.popen2(cmd, mode, bufsize)
==>
p =Popen(cmd, shell=True, bufsize=bufsize,
          stdin=PIPE, stdout=PIPE, close_fds=True)
(child_stdin, child_stdout)=(p.stdin, p.stdout)

(child_stdin,
 child_stdout,
 child_stderr)= os.popen3(cmd, mode, bufsize)
==>
p =Popen(cmd, shell=True, bufsize=bufsize,
          stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
(child_stdin,
 child_stdout,
 child_stderr)=(p.stdin, p.stdout, p.stderr)

 (child_stdin, child_stdout_and_stderr)= os.popen4(cmd, mode, bufsize)
==>
p =Popen(cmd, shell=True, bufsize=bufsize,
          stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)
(child_stdin, child_stdout_and_stderr)=(p.stdin, p.stdout)

返回码以如下方式处理转写:

pipe = os.popen(cmd,'w')
...
rc = pipe.close()
if rc isnotNoneand rc >>8:
print("There were some errors")
==>
process =Popen(cmd, stdin=PIPE)
...
process.stdin.close()
if process.wait()!=0:
print("There were some errors")

来自 popen2 模块的替代函数

注解

如果 popen2 函数的 cmd 参数是一个字符串,命令会通过 /bin/sh 来执行。 如果是一个列表,命令会被直接执行。

(child_stdout, child_stdin)= popen2.popen2("somestring", bufsize, mode)
==>
p =Popen("somestring", shell=True, bufsize=bufsize,
          stdin=PIPE, stdout=PIPE, close_fds=True)
(child_stdout, child_stdin)=(p.stdout, p.stdin)

(child_stdout, child_stdin)= popen2.popen2(["mycmd","myarg"], bufsize, mode)
==>
p =Popen(["mycmd","myarg"], bufsize=bufsize,
          stdin=PIPE, stdout=PIPE, close_fds=True)
(child_stdout, child_stdin)=(p.stdout, p.stdin)

popen2.Popen3popen2.Popen4 基本上类似于 subprocess.Popen,不同之处在于:

  • Popen 如果执行失败会引发一个异常。
  • capturestderr 参数被替换为 stderr 参数。
  • 必须指定 stdin=PIPEstdout=PIPE
  • popen2 默认会关闭所有文件描述符,但对于 Popen 你必须指明 close_fds=True 以才能在所有平台或较旧的 Python 版本中确保此行为。

旧式的 Shell 发起函数

此模块还提供了以下来自 2.x commands 模块的旧版函数。 这些操作会隐式地发起调用系统 shell 并且上文所描述的有关安全与异常处理一致性保证都不适用于这些函数。

subprocess.getstatusoutput(cmd)

返回在 shell 中执行 cmd 产生的 (exitcode, output)

在 shell 中以 Popen.check_output() 执行字符串 cmd 并返回一个 2 元组 (exitcode, output)。 会使用当前区域设置的编码格式;

末尾的一个换行符会从输出中被去除。 命令的退出码可被解读为子进程的返回码。 例如:

>>> subprocess.getstatusoutput('ls /bin/ls')
(0,'/bin/ls')
>>> subprocess.getstatusoutput('cat /bin/junk')
(1,'cat: /bin/junk: No such file or directory')
>>> subprocess.getstatusoutput('/bin/junk')
(127,'sh: /bin/junk: not found')
>>> subprocess.getstatusoutput('/bin/kill $$')
(-15,'')

可用性: POSIX 和 Windows。

在 3.3.4 版更改: 添加了 Windows 支持。

此函数现在返回 (exitcode, output) 而不是像 Python 3.3.3 及更早的版本那样返回 (status, output)。 exitcode 的值与 returncode 相同。

subprocess.getoutput(cmd)

返回在 shell 中执行 cmd 产生的输出(stdout 和 stderr)。

类似于 getstatusoutput(),但退出码会被忽略并且返回值为包含命令输出的字符串。 例如:

>>> subprocess.getoutput('ls /bin/ls')
'/bin/ls'

可用性: POSIX 和 Windows。

在 3.3.4 版更改: 添加了 Windows 支持

备注

在 Windows 上将参数列表转换为一个字符串

在 Windows 上,args 序列会被转换为可使用以下规则来解析的字符串(对应于 MS C 运行时所使用的规则):

  1. 参数以空白符分隔,即空格符或制表符。
  2. 用双引号标示的字符串会被解读为单个参数,而不再考虑其中的空白符。 一个参数可以嵌套用引号标示的字符串。
  3. 带有一个反斜杠前缀的双引号会被解读为双引号字面值。
  4. 反斜杠会按字面值解读,除非它是作为双引号的前缀。
  5. 如果反斜杠被作为双引号的前缀,则每个反斜杠对会被解读为一个反斜杠字面值。 如果反斜杠数量为奇数,则最后一个反斜杠会如规则 3 所描述的那样转义下一个双引号。

网络和进程间通信

本章介绍的模块提供了网络和进程间通信的机制。

某些模块仅适用于同一台机器上的两个进程,例如 signalmmap 。 其他模块支持两个或多个进程可用于跨机器通信的网络协议。

本章中描述的模块列表是:

  • asyncio —- 异步 I/O
  • socket —- 底层网络接口
  • ssl —- 套接字对象的 TLS/SSL 包装器
  • select —- 等待 I/O 完成
  • selectors —- 高级 I/O 复用库
  • asyncore —- 异步套接字处理器
  • asynchat —- 异步套接字指令/响应处理程序
  • signal —- 设置异步事件处理程序
  • mmap —- 内存映射文件支持

asyncio —- 异步 I/O

Hello World!

import asyncio
async def main():
    print('Hello ...')
    await asyncio.sleep(1)
    print('... World!')
# Python 3.7+
asyncio.run(main())

asyncio 是用来编写 并发 代码的库,使用 async/await 语法。

asyncio 被用作多个提供高性能 Python 异步框架的基础,包括网络和网站服务,数据库连接库,分布式任务队列等等。

asyncio 往往是构建 IO 密集型和高层级 结构化 网络代码的最佳选择。

asyncio 提供一组 高层级 API 用于:

  • 并发地 运行 Python 协程 并对其执行过程实现完全控制;
  • 执行 网络 IO 和 IPC;
  • 控制 子进程;
  • 通过 队列 实现分布式任务;
  • 同步 并发代码;

此外,还有一些 低层级 API 以支持 库和框架的开发者 实现:

  • 创建和管理 事件循环,以提供异步 API 用于 网络化, 运行 子进程,处理 OS 信号 等等;
  • 使用 transports 实现高效率协议;
  • 通过 async/await 语法 桥接 基于回调的库和代码。

参考

注解

asyncio 的源代码可以在 Lib/asyncio/ 中找到。

socket —- 底层网络接口

源代码: Lib/socket.py


这个模块提供了访问 BSD 套接字 的接口。在所有现代 Unix 系统、Windows、macOS 和其他一些平台上可用。

注解

一些行为可能因平台不同而异,因为调用的是操作系统的套接字API。

这个Python接口是用Python的面向对象风格对Unix系统调用和套接字库接口的直译:函数 socket() 返回一个 套接字对象 ,其方法是对各种套接字系统调用的实现。形参类型一般与C接口相比更高级:例如在Python文件 read()write() 操作中,接收操作的缓冲区分配是自动的,发送操作的缓冲区长度是隐式的。

套接字协议族

根据系统以及构建选项,此模块提供了各种套接字协议簇。

特定的套接字对象需要的地址格式将根据此套接字对象被创建时指定的地址族被自动选择。套接字地址表示如下:

  • 一个绑定在文件系统节点上的 AF_UNIX 套接字的地址表示为一个字符串,使用文件系统字符编码和 'surrogateescape' 错误回调方法(see PEP 383)。一个地址在 Linux 的抽象命名空间被返回为带有初始的 null 字节的 字节类对象 ;注意在这个命名空间种的套接字可能与普通文件系统套接字通信,所以打算运行在 Linux 上的程序可能需要解决两种地址类型。当传递为参数时,一个字符串或字节类对象可以用于任一类型的地址。

    在 3.3 版更改: 之前,AF_UNIX 套接字路径被假设使用 UTF-8 编码。

    在 3.5 版更改: 现在接受可写的 字节类对象。

  • 一对 (host, port) 被用作 AF_INET 地址族,其中 host 是一个表示互联网域名标记形式的主机名例如 'daring.cwi.nl' 或者 IPv4 地址例如 '100.50.200.5' 的字符串,而 port 是一个整数值。

    • 对于 IPv4 地址,有两种可接受的特殊形式被用来代替一个主机地址: '' 代表 INADDR_ANY,用来绑定到所有接口;字符串 '<broadcast>' 代表 INADDR_BROADCAST。此行为不兼容 IPv6,因此,如果你的 Python 程序打算支持 IPv6,则可能需要避开这些。
  • 对于 AF_INET6 地址族,使用一个四元组 (host, port, flowinfo, scope_id),其中 flowinfoscope_id 代表了 C 库 struct sockaddr_in6 中的 sin6_flowinfosin6_scope_id 成员。对于 socket 模块中的方法, flowinfoscope_id 可以被省略,只为了向后兼容。注意,省略 scope_id 可能会导致操作带有领域 (Scope) 的 IPv6 地址时出错。

    在 3.7 版更改: 对于多播地址(其 scope_id 起作用),地址 中可以不包含 %scope_id (或 zone id )部分,这部分是多余的,可以放心省略(推荐)。

  • AF_NETLINK 套接字由一对 (pid, groups) 表示。

  • 指定 AF_TIPC 地址族可以使用仅 Linux 支持的 TIPC 协议。TIPC 是一种开放的、非基于 IP 的网络协议,旨在用于集群计算环境。其地址用元组表示,其中的字段取决于地址类型。一般元组形式为 (addr_type, v1, v2, v3 [, scope]),其中:

    • addr_typeTIPC_ADDR_NAMESEQTIPC_ADDR_NAMETIPC_ADDR_ID 中的一个。

    • scopeTIPC_ZONE_SCOPETIPC_CLUSTER_SCOPETIPC_NODE_SCOPE 中的一个。

    • 如果 addr_typeTIPC_ADDR_NAME,那么 v1 是服务器类型,v2 是端口标识符,v3 应为 0。

      如果 addr_typeTIPC_ADDR_NAMESEQ,那么 v1 是服务器类型,v2 是端口号下限,而 v3 是端口号上限。

      如果 addr_typeTIPC_ADDR_ID,那么 v1 是节点 (node),v2 是 ref,v3 应为 0。

  • AF_CAN 地址族使用元组 (interface, ),其中 interface 是表示网络接口名称的字符串,如 'can0'。网络接口名 '' 可以用于接收本族所有网络接口的数据包。

    • CAN_ISOTP 协议接受一个元组 (interface, rx_addr, tx_addr),其中两个额外参数都是无符号长整数,都表示 CAN 标识符(标准或扩展标识符)。
    • CAN_J1939 协议接受一个元组 (interface, name, pgn, addr),其中额外参数有:表示 ECU 名称的 64 位无符号整数,表示参数组号 (Parameter Group Number, PGN) 的 32 位无符号整数,以及表示地址的 8 位整数。
  • PF_SYSTEM 协议簇的 SYSPROTO_CONTROL 协议接受一个字符串或元组 (id, unit)。其中字符串是内核控件的名称,该控件使用动态分配的 ID。而如果 ID 和内核控件的单元 (unit) 编号都已知,或使用了已注册的 ID,可以采用元组。

    3.3 新版功能.

  • AF_BLUETOOTH 支持以下协议和地址格式:

    • BTPROTO_L2CAP 接受 (bdaddr, psm),其中 bdaddr 为字符串格式的蓝牙地址,psm 是一个整数。

    • BTPROTO_RFCOMM 接受 (bdaddr, channel),其中 bdaddr 为字符串格式的蓝牙地址,channel 是一个整数。

    • BTPROTO_HCI 接受 (device_id,),其中 device_id 为整数或字符串,它表示接口对应的蓝牙地址(具体取决于你的系统,NetBSD 和 DragonFlyBSD 需要蓝牙地址字符串,其他系统需要整数)。

      在 3.2 版更改: 添加了对 NetBSD 和 DragonFlyBSD 的支持。

    • BTPROTO_SCO 接受 bdaddr,其中 bdaddrbytes 对象,其中含有字符串格式的蓝牙地址(如 b'12:23:34:45:56:67' ),FreeBSD 不支持此协议。

  • AF_ALG 是一个仅 Linux 可用的、基于套接字的接口,用于连接内核加密算法。算法套接字可用包括 2 至 4 个元素的元组来配置 (type, name [, feat [, mask]]),其中:

    • type 是表示算法类型的字符串,如 aeadhashskcipherrng
    • name 是表示算法类型和操作模式的字符串,如 sha256hmac(sha256)cbc(aes)drbg_nopr_ctr_aes256
    • featmask 是无符号 32 位整数。

    Availability: Linux 2.6.38, some algorithm types require more recent Kernels.

    3.6 新版功能.

  • AF_VSOCK 用于支持虚拟机与宿主机之间的通讯。该套接字用 (CID, port) 元组表示,其中 Context ID (CID) 和 port 都是整数。

    Availability: Linux >= 4.8 QEMU >= 2.8 ESX >= 4.0 ESX Workstation >= 6.5.

    3.7 新版功能.

  • AF_PACKET 是一个底层接口,直接连接至网卡。数据包使用元组 (ifname, proto[, pkttype[, hatype[, addr]]]) 表示,其中:

    • ifname - 指定设备名称的字符串。
    • proto - 一个用网络字节序表示的整数,指定以太网协议版本号。
    • pkttype - 指定数据包类型的整数(可选):
      • PACKET_HOST (默认) - 寻址到本地主机的数据包。
      • PACKET_BROADCAST - 物理层广播的数据包。
      • PACKET_MULTIHOST - 发送到物理层多播地址的数据包。
      • PACKET_OTHERHOST - 被(处于混杂模式的)网卡驱动捕获的、发送到其他主机的数据包。
      • PACKET_OUTGOING - 来自本地主机的、回环到一个套接字的数据包。
    • hatype - 可选整数,指定 ARP 硬件地址类型。
    • addr - 可选的类字节串对象,用于指定硬件物理地址,其解释取决于各设备。
  • AF_QIPCRTR 是一个仅 Linux 可用的、基于套接字的接口,用于与高通平台中协处理器上运行的服务进行通信。该地址簇用一个 (node, port) 元组表示,其中 nodeport 为非负整数。

    3.8 新版功能.

  • IPPROTO_UDPLITE 是一种 UDP 的变体,允许指定数据包的哪一部分计算入校验码内。它添加了两个可以修改的套接字选项。self.setsockopt(IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV, length) 修改传出数据包的哪一部分计算入校验码内,而 self.setsockopt(IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV, length) 将过滤掉计算入校验码的数据太少的数据包。在这两种情况下,length 都应在 range(8, 2**16, 8) 范围内。

    对于 IPv4,应使用 socket(AF_INET, SOCK_DGRAM, IPPROTO_UDPLITE) 来构造这样的套接字;对于 IPv6,应使用 socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDPLITE) 来构造这样的套接字。

    Availability: Linux >= 2.6.20, FreeBSD >= 10.1-RELEASE

    3.9 新版功能.

如果你在 IPv4/v6 套接字地址的 host 部分中使用了一个主机名,此程序可能会表现不确定行为,因为 Python 使用 DNS 解析返回的第一个地址。套接字地址在实际的 IPv4/v6 中以不同方式解析,根据 DNS 解析和/或 host 配置。为了确定行为,在 host 部分中使用数字的地址。

所有的错误都抛出异常。对于无效的参数类型和内存溢出异常情况可能抛出普通异常;从 Python 3.3 开始,与套接字或地址语义有关的错误抛出 OSError 或它的子类之一(常用 socket.error)。

可以用 setblocking() 设置非阻塞模式。一个基于超时的 generalization 通过 settimeout() 支持。

模块内容

socket 模块包含下列元素。

异常

exception socket.error

一个被弃用的 OSError 的别名。

在 3.3 版更改: 根据 PEP 3151,这个类是 OSError 的别名。

exception socket.herror

OSError 的子类,本异常通常表示与地址相关的错误,比如那些在 POSIX C API 中使用了 h_errno 的函数,包括 gethostbyname_ex()gethostbyaddr()。附带的值是一对 (h_errno, string),代表库调用返回的错误。h_errno 是一个数字,而 string 表示 h_errno 的描述,它们由 C 函数 hstrerror() 返回。

在 3.3 版更改: 此类是 OSError 的子类。

exception socket.gaierror

OSError 的子类,本异常来自 getaddrinfo()getnameinfo(),表示与地址相关的错误。附带的值是一对 (error, string),代表库调用返回的错误。string 表示 error 的描述,它由 C 函数 gai_strerror() 返回。数字值 error 与本模块中定义的 EAI_* 常量之一匹配。

在 3.3 版更改: 此类是 OSError 的子类。

exception socket.timeout

TimeoutError 的已被弃用的别名。

OSError 的子类,当套接字发生超时,且事先已调用过 settimeout() (或隐式地通过 setdefaulttimeout() )启用了超时,则会抛出此异常。附带的值是一个字符串,其值总是 “timed out”。

在 3.3 版更改: 此类是 OSError 的子类。

在 3.10 版更改: 这个类是 TimeoutError 的别名。

常量

AF_* 和 SOCK_* 常量现在都在 AddressFamilySocketKind 这两个 IntEnum 集合内。

3.4 新版功能.

socket.AF_UNIX
socket.AF_INET
socket.AF_INET6

这些常量表示地址(和协议)簇,用于 socket() 的第一个参数。如果 AF_UNIX 常量未定义,即表示不支持该协议。不同系统可能会有更多其他常量可用。

socket.SOCK_STREAM
socket.SOCK_DGRAM
socket.SOCK_RAW
socket.SOCK_RDM
socket.SOCK_SEQPACKET

这些常量表示套接字类型,用于 socket() 的第二个参数。不同系统可能会有更多其他常量可用。(一般只有 SOCK_STREAMSOCK_DGRAM 可用)

socket.SOCK_CLOEXEC
socket.SOCK_NONBLOCK

这两个常量(如果已定义)可以与上述套接字类型结合使用,允许你设置这些原子性相关的 flags (从而避免可能的竞争条件和单独调用的需要)。

参见

Secure File Descriptor Handling (安全地处理文件描述符) 提供了更详尽的解释。

可用性: Linux >= 2.6.27。

3.2 新版功能.

SO_*
socket.SOMAXCONN
MSG_*
SOL_*
SCM_*
IPPROTO_*
IPPORT_*
INADDR_*
IP_*
IPV6_*
EAI_*
AI_*
NI_*
TCP_*

此列表内的许多常量,记载在 Unix 文档中的套接字和/或 IP 协议部分,同时也定义在本 socket 模块中。它们通常用于套接字对象的 setsockopt()getsockopt() 方法的参数中。在大多数情况下,仅那些在 Unix 头文件中有定义的符号会在本模块中定义,部分符号提供了默认值。

在 3.6 版更改: 添加了 SO_DOMAIN, SO_PROTOCOL, SO_PEERSEC, SO_PASSSEC, TCP_USER_TIMEOUT, TCP_CONGESTION

在 3.6.5 版更改: 在 Windows 上,如果 Windows 运行时支持,则 TCP_FASTOPENTCP_KEEPCNT 可用。

在 3.7 版更改: 添加了 TCP_NOTSENT_LOWAT

在 Windows 上,如果 Windows 运行时支持,则 TCP_KEEPIDLETCP_KEEPINTVL 可用。

在 3.10 版更改: 添加了 IP_RECVTOS。 还添加了 TCP_KEEPALIVE。 这个常量在 MacOS 上可以与在 Linux 上使用 TCP_KEEPIDLE 的相同方式被使用。

socket.AF_CAN
socket.PF_CAN
SOL_CAN_*
CAN_*

此列表内的许多常量,记载在 Linux 文档中,同时也定义在本 socket 模块中。

可用性: Linux >= 2.6.25。

3.3 新版功能.

socket.CAN_BCM
CAN_BCM_*

CAN 协议簇内的 CAN_BCM 是广播管理器(Bbroadcast Manager — BCM)协议,广播管理器常量在 Linux 文档中有所记载,在本 socket 模块中也有定义。

可用性: Linux >= 2.6.25。

注解

CAN_BCM_CAN_FD_FRAME 旗标仅在 Linux >= 4.8 时可用。

3.4 新版功能.

socket.CAN_RAW_FD_FRAMES

在 CAN_RAW 套接字中启用 CAN FD 支持,默认是禁用的。它使应用程序可以发送 CAN 和 CAN FD 帧。但是,从套接字读取时,也必须同时接受 CAN 和 CAN FD 帧。

此常量在 Linux 文档中有所记载。

可用性: Linux >= 3.6。

3.5 新版功能.

socket.CAN_RAW_JOIN_FILTERS

加入已应用的 CAN 过滤器,这样只有与所有 CAN 过滤器匹配的 CAN 帧才能传递到用户空间。

此常量在 Linux 文档中有所记载。

可用性: Linux >= 4.1。

3.9 新版功能.

socket.CAN_ISOTP

CAN 协议簇中的 CAN_ISOTP 就是 ISO-TP (ISO 15765-2) 协议。ISO-TP 常量在 Linux 文档中有所记载。

可用性: Linux >= 2.6.25。

3.7 新版功能.

socket.CAN_J1939

CAN 协议族中的 CAN_J1939 即 SAE J1939 协议。 J1939 常量记录在 Linux 文档中。

可用性: Linux >= 5.4。

3.9 新版功能.

socket.AF_PACKET
socket.PF_PACKET
PACKET_*

此列表内的许多常量,记载在 Linux 文档中,同时也定义在本 socket 模块中。

可用性: Linux >= 2.2。

socket.AF_RDS
socket.PF_RDS
socket.SOL_RDS
RDS_*

此列表内的许多常量,记载在 Linux 文档中,同时也定义在本 socket 模块中。

可用性: Linux >= 2.6.30。

3.3 新版功能.

socket.SIO_RCVALL
socket.SIO_KEEPALIVE_VALS
socket.SIO_LOOPBACK_FAST_PATH
RCVALL_*

Windows 的 WSAIoctl() 的常量。这些常量用于套接字对象的 ioctl() 方法的参数。

在 3.6 版更改: 添加了 SIO_LOOPBACK_FAST_PATH

TIPC_*

TIPC 相关常量,与 C socket API 导出的常量一致。更多信息请参阅 TIPC 文档。

socket.AF_ALG
socket.SOL_ALG
ALG_*

用于 Linux 内核加密算法的常量。

可用性: Linux >= 2.6.38。

3.6 新版功能.

socket.AF_VSOCK
socket.IOCTL_VM_SOCKETS_GET_LOCAL_CID
VMADDR*
SO_VM*

用于 Linux 宿主机/虚拟机通讯的常量。

可用性: Linux >= 4.8。

3.7 新版功能.

socket.AF_LINK

Availability: BSD, macOS.

3.4 新版功能.

socket.has_ipv6

本常量为一个布尔值,该值指示当前平台是否支持 IPv6。

socket.BDADDR_ANY
socket.BDADDR_LOCAL

这些是字符串常量,包含蓝牙地址,这些地址具有特殊含义。例如,当用 BTPROTO_RFCOMM 指定绑定套接字时, BDADDR_ANY 表示“任何地址”。

socket.HCI_FILTER
socket.HCI_TIME_STAMP
socket.HCI_DATA_DIR

BTPROTO_HCI 一起使用。 HCI_FILTER 在 NetBSD 或 DragonFlyBSD 上不可用。 HCI_TIME_STAMPHCI_DATA_DIR 在 FreeBSD、NetBSD 或 DragonFlyBSD 上不可用。

socket.AF_QIPCRTR

高通 IPC 路由协议的常数,用于与提供远程处理器的服务进行通信。

可用性: Linux >= 4.7。

函数

创建套接字

下列函数都能创建 套接字对象.

socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)

使用给定的地址族、套接字类型和协议号创建一个新的套接字。 地址族应为 AF_INET (默认值), AF_INET6, AF_UNIX, AF_CAN, AF_PACKETAF_RDS 之一。 套接字类型应为 SOCK_STREAM (默认值), SOCK_DGRAM, SOCK_RAW 或其他可能的 SOCK_ 常量之一。 协议号通常为零并且可以省略,或在协议族为 AF_CAN 的情况下,协议应为 CAN_RAW, CAN_BCM, CAN_ISOTPCAN_J1939 之一。

如果指定了 fileno,那么将从这一指定的文件描述符中自动检测 *family、*typeproto 的值。如果调用本函数时显式指定了 familytypeproto 参数,可以覆盖自动检测的值。这只会影响 Python 表示诸如 socket.getpeername() 一类函数的返回值的方式,而不影响实际的操作系统资源。与 socket.fromfd() 不同,fileno 将返回原先的套接字,而不是复制出新的套接字。这有助于在分离的套接字上调用 socket.close() 来关闭它。

新创建的套接字是 不可继承的。

引发一个 审计事件 socket.__new__ 附带参数 selffamilytypeprotocol

在 3.3 版更改: 添加了 AF_CAN 簇。添加了 AF_RDS 簇。

在 3.4 版更改: 添加了 CAN_BCM 协议。

在 3.4 版更改: 返回的套接字现在是不可继承的。

在 3.7 版更改: 添加了 CAN_ISOTP 协议。

在 3.7 版更改: 当将 SOCK_NONBLOCKSOCK_CLOEXEC 标志位应用于 type 上时,它们会被清除,且 socket.type 反映不出它们。但它们仍将传递给底层系统的 socket() 调用。因此,

sock = socket.socket(
    socket.AF_INET,
    socket.SOCK_STREAM | socket.SOCK_NONBLOCK)

仍将在支持 SOCK_NONBLOCK 的系统上创建一个非阻塞的套接字,但是 sock.type 会被置为 socket.SOCK_STREAM

在 3.9 版更改: 添加了 CAN_J1939 协议。

在 3.10 版更改: 添加了 IPPROTO_MPTCP 协议。

socket.socketpair([family[, type[, proto]]])

构建一对已连接的套接字对象,使用给定的地址簇、套接字类型和协议号。地址簇、套接字类型和协议号与上述 socket() 函数相同。默认地址簇为 AF_UNIX (需要当前平台支持,不支持则默认为 AF_INET )。

新创建的套接字都是 不可继承的。

在 3.2 版更改: 现在,返回的套接字对象支持全部套接字 API,而不是全部 API 的一个子集。

在 3.4 版更改: 返回的套接字现在都是不可继承的。

在 3.5 版更改: 添加了 Windows 支持。

socket.create_connection(address[, timeout[, source_address]])

连接到一个在互联网 address (以 (host, port) 2 元组表示) 上侦听的 TCP 服务,并返回套接字对象。 这是一个相比 socket.connect() 层级更高的函数:如果 host 是非数字的主机名,它将尝试将其解析为 AF_INETAF_INET6,然后依次尝试连接到所有可能的地址直到连接成功。 这使编写兼容 IPv4 和 IPv6 的客户端变得很容易。

传入可选参数 timeout 可以在套接字实例上设置超时(在尝试连接前)。如果未提供 timeout,则使用由 getdefaulttimeout() 返回的全局默认超时设置。

如果提供了 source_address,它必须为二元组 (host, port),以便套接字在连接之前绑定为其源地址。如果 host 或 port 分别为 ‘’ 或 0,则使用操作系统默认行为。

在 3.2 版更改: 添加了source_address 参数

socket.create_server(address, **, family=AF_INET, backlog=None, reuse_port=False, dualstack_ipv6=False*)

便捷函数,创建绑定到 address (二元组 (host, port) )的 TCP 套接字,返回套接字对象。

family 应设置为 AF_INETAF_INET6backlog 是传递给 socket.listen() 的队列大小,当它为 0 则表示默认的合理值。reuse_port 表示是否设置 SO_REUSEPORT 套接字选项。

如果 dualstack_ipv6 为 true 且平台支持,则套接字能接受 IPv4 和 IPv6 连接,否则将抛出 ValueError 异常。大多数 POSIX 平台和 Windows 应该支持此功能。启用此功能后,socket.getpeername() 在进行 IPv4 连接时返回的地址将是一个(映射到 IPv4 的)IPv6 地址。在默认启用该功能的平台上(如 Linux),如果 dualstack_ipv6 为 false,即显式禁用此功能。该参数可以与 has_dualstack_ipv6() 结合使用:

import socket
addr = ("", 8080)  # all interfaces, port 8080
if socket.has_dualstack_ipv6():
    s = socket.create_server(addr, family=socket.AF_INET6, dualstack_ipv6=True)
else:
    s = socket.create_server(addr)

注解

在 POSIX 平台上,设置 SO_REUSEADDR 套接字选项是为了立即重用以前绑定在同一 address 上并保持 TIME_WAIT 状态的套接字。

3.8 新版功能.

socket.has_dualstack_ipv6()

如果平台支持创建 IPv4 和 IPv6 连接都可以处理的 TCP 套接字,则返回 True

3.8 新版功能.

socket.fromfd(fd, family, type, proto=0)

复制文件描述符 fd (一个由文件对象的 fileno() 方法返回的整数),然后从结果中构建一个套接字对象。地址簇、套接字类型和协议号与上述 socket() 函数相同。文件描述符应指向一个套接字,但不会专门去检查——如果文件描述符是无效的,则对该对象的后续操作可能会失败。本函数很少用到,但是在将套接字作为标准输入或输出传递给程序(如 Unix inet 守护程序启动的服务器)时,可以使用本函数获取或设置套接字选项。套接字将处于阻塞模式。

新创建的套接字是 不可继承的。

在 3.4 版更改: 返回的套接字现在是不可继承的。

socket.fromshare(data)

根据 socket.share() 方法获得的数据实例化套接字。套接字将处于阻塞模式。

可用性: Windows。

3.3 新版功能.

socket.SocketType

这是一个 Python 类型对象,表示套接字对象的类型。它等同于 type(socket(...))

其他功能

socket 模块还提供多种网络相关服务:

socket.close(fd)

关闭一个套接字文件描述符。它类似于 os.close(),但专用于套接字。在某些平台上(特别是在 Windows 上),os.close() 对套接字文件描述符无效。

3.7 新版功能.

socket.getaddrinfo(host, port, family=0, type=0, proto=0, flags=0)

host/port 参数转换为 5 元组的序列,其中包含创建(连接到某服务的)套接字所需的所有参数。host 是域名,是字符串格式的 IPv4/v6 地址或 Noneport 是字符串格式的服务名称,如 'http' 、端口号(数字)或 None。传入 None 作为 hostport 的值,相当于将 NULL 传递给底层 C API。

可以指定 familytypeproto 参数,以缩小返回的地址列表。向这些参数分别传入 0 表示保留全部结果范围。flags 参数可以是 AI_* 常量中的一个或多个,它会影响结果的计算和返回。例如,AI_NUMERICHOST 会禁用域名解析,此时如果 host 是域名,则会抛出错误。

本函数返回一个列表,其中的 5 元组具有以下结构:

(family, type, proto, canonname, sockaddr)

在这些元组中,family, type, proto 都是整数且其作用是被传入 socket() 函数。 如果 AI_CANONNAMEflags 参数的一部分则 canonname 将是表示 host 规范名称的字符串;否则 canonname 将为空。 sockaddr 是一个描述套接字地址的元组,其具体格式取决于返回的 family (对于 AF_INET(address, port) 2 元组,对于 AF_INET6 则为 (address, port, flowinfo, scope_id) 4 元组),其作用是被传入 socket.connect() 方法。

引发一个 审计事件 socket.getaddrinfo 附带参数 hostportfamilytypeprotocol

下面的示例获取了 TCP 连接地址信息,假设该连接通过 80 端口连接至 example.org (如果系统未启用 IPv6,则结果可能会不同):

>>> socket.getaddrinfo("example.org", 80, proto=socket.IPPROTO_TCP)
[(<AddressFamily.AF_INET6: 10>, <AddressFamily.SOCK_STREAM: 1>,
 6, '', ('2606:2800:220:1:248:1893:25c8:1946', 80, 0, 0)),
 (<AddressFamily.AF_INET: 2>, <AddressFamily.SOCK_STREAM: 1>,
 6, '', ('93.184.216.34', 80))]

在 3.2 版更改: 现在可以使用关键字参数的形式来传递参数。

在 3.7 版更改: 对于 IPv6 多播地址,表示地址的字符串将不包含 %scope_id 部分。

socket.getfqdn([name])

Return a fully qualified domain name for name. If name is omitted or empty, it is interpreted as the local host. To find the fully qualified name, the hostname returned by gethostbyaddr() is checked, followed by aliases for the host, if available. The first name which includes a period is selected. In case no fully qualified domain name is available and name was provided, it is returned unchanged. If name was empty or equal to '0.0.0.0', the hostname from gethostname() is returned.

socket.gethostbyname(hostname)

将主机名转换为 IPv4 地址格式。IPv4 地址以字符串格式返回,如 '100.50.200.5'。如果主机名本身是 IPv4 地址,则原样返回。更完整的接口请参考 gethostbyname_ex()gethostbyname() 不支持 IPv6 名称解析,应使用 getaddrinfo() 来支持 IPv4/v6 双协议栈。

引发一个 审计事件 socket.gethostbyname,附带参数 hostname

socket.gethostbyname_ex(hostname)

Translate a host name to IPv4 address format, extended interface. Return a triple (hostname, aliaslist, ipaddrlist) where hostname is the host’s primary host name, aliaslist is a (possibly empty) list of alternative host names for the same address, and ipaddrlist is a list of IPv4 addresses for the same interface on the same host (often but not always a single address). gethostbyname_ex() does not support IPv6 name resolution, and getaddrinfo() should be used instead for IPv4/v6 dual stack support.

引发一个 审计事件 socket.gethostbyname,附带参数 hostname

socket.gethostname()

返回一个字符串,包含当前正在运行 Python 解释器的机器的主机名。

引发一个 审计事件 socket.gethostname,没有附带参数。

注意: gethostname() 并不总是返回全限定域名,必要的话请使用 getfqdn()

socket.gethostbyaddr(ip_address)

返回三元组 (hostname, aliaslist, ipaddrlist),其中 hostname 是响应给定 ip_address 的主要主机名,aliaslist 是相同地址的其他可用主机名的列表(可能为空),而 ipaddrlist 是 IPv4/v6 地址列表,包含相同主机名、相同接口的不同地址(很可能仅包含一个地址)。要查询全限定域名,请使用函数 getfqdn()gethostbyaddr() 支持 IPv4 和 IPv6。

引发一个 审计事件 socket.gethostbyaddr,附带参数 ip_address

socket.getnameinfo(sockaddr, flags)

将套接字地址 sockaddr 转换为二元组 (host, port)host 的形式可能是全限定域名,或是由数字表示的地址,具体取决于 flags 的设置。同样,port 可以包含字符串格式的端口名称或数字格式的端口号。

对于 IPv6 地址,如果 sockaddr 包含有意义的 scope_id,则 %scope_id 会被附加到主机部分。 这种情况通常发生在多播地址上。

引发一个 审计事件 socket.getnameinfo,附带参数 sockaddr

socket.getprotobyname(protocolname)

将一个互联网协议名称 (如 'icmp') 转换为能被作为 (可选的) 第三个参数传给 socket() 函数的常量。 这通常仅对以 “raw” 模式 (SOCK_RAW) 打开的套接字来说是必要的;对于正常的套接字模式,当该协议名称被省略或为零时会自动选择正确的协议。

socket.getservbyname(servicename[, protocolname])

将一个互联网服务名称和协议名称转换为该服务的端口号。 如果给出了可选的协议名称,它应为 'tcp''udp',否则将匹配任意的协议。

引发一个 审计事件 socket.getservbyname,附带参数 servicenameprotocolname

socket.getservbyport(port[, protocolname])

将一个互联网端口号和协议名称转换为该服务的服务名称。 如果给出了可选的协议名称,它应为 'tcp''udp',否则将匹配任意的协议。

引发一个 审计事件 socket.getservbyport,附带参数 portprotocolname

socket.ntohl(x)

将 32 位正整数从网络字节序转换为主机字节序。在主机字节序与网络字节序相同的计算机上,这是一个空操作。字节序不同将执行 4 字节交换操作。

socket.ntohs(x)

将 16 位正整数从网络字节序转换为主机字节序。在主机字节序与网络字节序相同的计算机上,这是一个空操作。字节序不同将执行 2 字节交换操作。

在 3.10 版更改: 如果 x 不能转为 16 位无符号整数则会引发 OverflowError

socket.htonl(x)

将 32 位正整数从主机字节序转换为网络字节序。在主机字节序与网络字节序相同的计算机上,这是一个空操作。字节序不同将执行 4 字节交换操作。

socket.htons(x)

将 16 位正整数从主机字节序转换为网络字节序。在主机字节序与网络字节序相同的计算机上,这是一个空操作。字节序不同将执行 2 字节交换操作。

在 3.10 版更改: 如果 x 不能转为 16 位无符号整数则会引发 OverflowError

socket.inet_aton(ip_string)

将 IPv4 地址从点分十进制字符串格式(如 ‘123.45.67.89’ )转换为 32 位压缩二进制格式,转换后为字节对象,长度为四个字符。与那些使用标准 C 库,且需要 struct in_addr 类型的对象的程序交换信息时,此功能很有用。 该类型即此函数返回的 32 位压缩二进制的 C 类型。

inet_aton() 也接受句点数少于三的字符串

如果传入本函数的 IPv4 地址字符串无效,则抛出 OSError。注意,具体什么样的地址有效取决于 inet_aton() 的底层 C 实现。

inet_aton() 不支持 IPv6,在 IPv4/v6 双协议栈下应使用 inet_pton() 来代替。

socket.inet_ntoa(packed_ip)

将 32 位压缩 IPv4 地址(一个 类字节对象,长 4 个字节)转换为标准的点分十进制字符串形式(如 ‘123.45.67.89’ )。与那些使用标准 C 库,且需要 struct in_addr 类型的对象的程序交换信息时,本函数很有用。 该类型即是本函数参数中的 32 位压缩二进制数据的 C 类型。

如果传入本函数的字节序列长度不是 4 个字节,则抛出 OSErrorinet_ntoa() 不支持 IPv6,在 IPv4/v6 双协议栈下应使用 inet_ntop() 来代替。

在 3.5 版更改: 现在接受可写的 字节类对象。

socket.inet_pton(address_family, ip_string)

将特定地址簇的 IP 地址(字符串)转换为压缩二进制格式。当库或网络协议需要接受 struct in_addr 类型的对象(类似 inet_aton() )或 struct in6_addr 类型的对象时,inet_pton() 很有用。

目前 address_family 支持 AF_INETAF_INET6。如果 IP 地址字符串 ip_string 无效,则抛出 OSError。注意,具体什么地址有效取决于 address_family 的值和 inet_pton() 的底层实现。

可用性: Unix(可能非所有平台都可用)、Windows。

在 3.4 版更改: 添加了 Windows 支持

socket.inet_ntop(address_family, packed_ip)

将压缩 IP 地址(一个 类字节对象,数个字节长)转换为标准的、特定地址簇的字符串形式(如 '7.10.0.5''5aef:2b::8' )。当库或网络协议返回 struct in_addr 类型的对象(类似 inet_ntoa() )或 struct in6_addr 类型的对象时,inet_ntop() 很有用。

目前 address_family 支持 AF_INETAF_INET6。如果字节对象 packed_ip 与指定的地址簇长度不符,则抛出 ValueError。针对 inet_ntop() 调用的错误则抛出 OSError

可用性: Unix(可能非所有平台都可用)、Windows。

在 3.4 版更改: 添加了 Windows 支持

在 3.5 版更改: 现在接受可写的 字节类对象。

socket.CMSG_LEN(length)

返回给定 length 所关联数据的辅助数据项的总长度(不带尾部填充)。此值通常用作 recvmsg() 接收一个辅助数据项的缓冲区大小,但是 RFC 3542 要求可移植应用程序使用 CMSG_SPACE(),以此将尾部填充的空间计入,即使该项在缓冲区的最后。如果 length 超出允许范围,则抛出 OverflowError

可用性: 大多数 Unix 平台,其他平台也可能可用。

3.3 新版功能.

socket.CMSG_SPACE(length)

返回 recvmsg() 所需的缓冲区大小,以接收给定 length 所关联数据的辅助数据项,带有尾部填充。接收多个项目所需的缓冲区空间是关联数据长度的 CMSG_SPACE() 值的总和。如果 length 超出允许范围,则抛出 OverflowError

请注意,某些系统可能支持辅助数据,但不提供本函数。还需注意,如果使用本函数的结果来设置缓冲区大小,可能无法精确限制可接收的辅助数据量,因为可能会有其他数据写入尾部填充区域。

可用性: 大多数 Unix 平台,其他平台也可能可用。

3.3 新版功能.

socket.getdefaulttimeout()

返回用于新套接字对象的默认超时(以秒为单位的浮点数)。值 None 表示新套接字对象没有超时。首次导入 socket 模块时,默认值为 None

socket.setdefaulttimeout(timeout)

设置用于新套接字对象的默认超时(以秒为单位的浮点数)。首次导入 socket 模块时,默认值为 None。可能的取值及其各自的含义请参阅 settimeout()

socket.sethostname(name)

将计算机的主机名设置为 name。如果权限不足将抛出 OSError

引发一个 审计事件 socket.sethostname,附带参数 name

可用性: Unix。

3.3 新版功能.

socket.if_nameindex()

返回一个列表,包含网络接口(网卡)信息二元组(整数索引,名称字符串)。系统调用失败则抛出 OSError

可用性: Unix, Windows。

3.3 新版功能.

在 3.8 版更改: 添加了 Windows 支持。

注解

在 Windows 中网络接口在不同上下文中具有不同的名称(所有名称见对应示例):

  • UUID: {FB605B73-AAC2-49A6-9A2F-25416AEA0573}
  • 名称: ethernet_32770
  • 友好名称: vEthernet (nat)
  • 描述: Hyper-V Virtual Ethernet Adapter

此函数返回列表中第二种形式的名称,在此示例中为 ethernet_32770

socket.if_nametoindex(if_name)

返回网络接口名称相对应的索引号。如果没有所给名称的接口,则抛出 OSError

可用性: Unix, Windows。

3.3 新版功能.

在 3.8 版更改: 添加了 Windows 支持。

参见

“Interface name” 为 if_nameindex() 中所描述的名称。

socket.if_indextoname(if_index)

返回网络接口索引号相对应的接口名称。如果没有所给索引号的接口,则抛出 OSError

可用性: Unix, Windows。

3.3 新版功能.

在 3.8 版更改: 添加了 Windows 支持。

参见

“Interface name” 为 if_nameindex() 中所描述的名称。

socket.send_fds(sock, buffers, fds[, flags[, address]])

将文件描述符列表 fds 通过一个 AF_UNIX 套接字 sock 进行发送。 fds 形参是由文件描述符构成的序列。 请查看 sendmsg() 获取这些形参的文档。

可用性: 支持 sendmsg()SCM_RIGHTS 机制的 Unix 系统。

3.9 新版功能.

socket.recv_fds(sock, bufsize, maxfds[, flags])

接收至多 maxfds 个来自 AF_UNIX 套接字 sock 的文件描述符。 返回 (msg, list(fds), flags, addr)。 请查看 recvmsg() 获取有些形参的文档。

可用性: 支持 recvmsg()SCM_RIGHTS 机制的 Unix 系统。

3.9 新版功能.

注解

位于文件描述符列表末尾的任何被截断整数。

套接字对象

套接字对象具有以下方法。除了 makefile(),其他都与套接字专用的 Unix 系统调用相对应。

在 3.2 版更改: 添加了对 上下文管理器 协议的支持。退出上下文管理器与调用 close() 等效。

socket.accept()

接受一个连接。此 socket 必须绑定到一个地址上并且监听连接。返回值是一个 (conn, address) 对,其中 conn 是一个 的套接字对象,用于在此连接上收发数据,address 是连接另一端的套接字所绑定的地址。

新创建的套接字是 不可继承的。

在 3.4 版更改: 该套接字现在是不可继承的。

在 3.5 版更改: 如果系统调用被中断,但信号处理程序没有触发异常,此方法现在会重试系统调用,而不是触发 InterruptedError 异常 (原因详见 PEP 475)。

socket.bind(address)

将套接字绑定到 address*。套接字必须尚未绑定。( *address 的格式取决于地址簇 —— 参见上文)

引发一个 审计事件 socket.bind,附带参数 selfaddress

socket.close()

将套接字标记为关闭。当 makefile() 创建的所有文件对象都关闭时,底层系统资源(如文件描述符)也将关闭。一旦上述情况发生,将来对套接字对象的所有操作都会失败。对端将接收不到任何数据(清空队列数据后)。

垃圾回收时,套接字会自动关闭,但建议显式 close() 它们,或在它们周围使用 with 语句。

在 3.6 版更改: 现在,如果底层的 close() 调用出错,会抛出 OSError

注解

close() 释放与连接相关联的资源,但不一定立即关闭连接。如果需要及时关闭连接,请在调用 close() 之前调用 shutdown()

socket.connect(address)

连接到 address 处的远程套接字。( address 的格式取决于地址簇 —— 参见上文)

如果连接被信号中断,则本方法将等待直至连接完成,或者如果信号处理句柄未引发异常并且套接字被阻塞或已超时则会在超时后引发 TimeoutError。 对于非阻塞型套接字,如果连接被信号中断则本方法将引发 InterruptedError 异常(或信号处理句柄所引发的异常)。

引发一个 审计事件 socket.connect,附带参数 selfaddress

在 3.5 版更改: 本方法现在将等待,直到连接完成,而不是在以下情况抛出 InterruptedError 异常。该情况为,连接被信号中断,信号处理程序未抛出异常,且套接字阻塞中或已超时(具体解释请参阅 PEP 475 )。

socket.connect_ex(address)

类似于 connect(address),但是对于 C 级别的 connect() 调用返回的错误,本函数将返回错误指示器,而不是抛出异常(对于其他问题,如“找不到主机”,仍然可以抛出异常)。如果操作成功,则错误指示器为 0,否则为 errno 变量的值。这对支持如异步连接很有用。

引发一个 审计事件 socket.connect,附带参数 selfaddress

socket.detach()

将套接字对象置于关闭状态,而底层的文件描述符实际并不关闭。返回该文件描述符,使其可以重新用于其他目的。

3.2 新版功能.

socket.dup()

创建套接字的副本。

新创建的套接字是 不可继承的。

在 3.4 版更改: 该套接字现在是不可继承的。

socket.fileno()

返回套接字的文件描述符(一个小整数),失败返回 -1。配合 select.select() 使用很有用。

在 Windows 下,此方法返回的小整数在允许使用文件描述符的地方无法使用(如 os.fdopen() )。Unix 无此限制。

socket.get_inheritable()

获取套接字文件描述符或套接字句柄的 可继承标志 :如果子进程可以继承套接字则为 True,否则为 False

3.4 新版功能.

socket.getpeername()

返回套接字连接到的远程地址。举例而言,这可以用于查找远程 IPv4/v6 套接字的端口号。(返回的地址格式取决于地址簇 —— 参见上文。)部分系统不支持此函数。

socket.getsockname()

返回套接字本身的地址。举例而言,这可以用于查找 IPv4/v6 套接字的端口号。(返回的地址格式取决于地址簇 —— 参见上文。)

socket.getsockopt(level, optname[, buflen])

返回指定套接字选项的值(参阅 Unix 手册页 getsockopt(2) )。所需的符号常量( SO_* 等)已定义在本模块中。如果未指定 buflen,则认为该选项值为整数,由本函数返回该整数值。如果指定 buflen,则它定义了用于存放选项值的缓冲区的最大长度,且该缓冲区将作为字节对象返回。对缓冲区的解码工作由调用者自行完成(针对编码为字节串的 C 结构,其解码方法请参阅可选的内置模块 struct )。

socket.getblocking()

如果套接字处于阻塞模式,返回 True,非阻塞模式返回 False

这相当于检测 socket.gettimeout() == 0

3.7 新版功能.

socket.gettimeout()

返回套接字操作相关的超时秒数(浮点数),未设置超时则返回 None。它反映最后一次调用 setblocking()settimeout() 后的设置。

socket.ioctl(control, option)

  • 平台

    Windows

ioctl() 方法是 WSAIoctl 系统接口的有限接口。请参考 Win32 文档 以获取更多信息。

在其他平台上,可以使用通用的 fcntl.fcntl()fcntl.ioctl() 函数,它们接受套接字对象作为第一个参数。

当前仅支持以下控制码: SIO_RCVALLSIO_KEEPALIVE_VALSSIO_LOOPBACK_FAST_PATH

在 3.6 版更改: 添加了 SIO_LOOPBACK_FAST_PATH

socket.listen([backlog])

启动一个服务器用于接受连接。如果指定 backlog,则它最低为 0(小于 0 会被置为 0),它指定系统允许暂未 accept 的连接数,超过后将拒绝新连接。未指定则自动设为合理的默认值。

在 3.5 版更改: backlog 参数现在是可选的。

socket.makefile(mode=’r’, buffering=None, **, encoding=None, errors=None, newline=None*)

返回与套接字关联的 文件对象。返回的对象的具体类型取决于 makefile() 的参数。这些参数的解释方式与内置的 open() 函数相同,其中 mode 的值仅支持 'r' (默认),'w''b'

套接字必须处于阻塞模式,它可以有超时,但是如果发生超时,文件对象的内部缓冲区可能会以不一致的状态结尾。

关闭 makefile() 返回的文件对象不会关闭原始套接字,除非所有其他文件对象都已关闭且在套接字对象上调用了 socket.close()

注解

在 Windows 上,由 makefile() 创建的文件类对象无法作为带文件描述符的文件对象使用,如无法作为 subprocess.Popen() 的流参数。

socket.recv(bufsize[, flags])

从套接字接收数据。返回值是一个字节对象,表示接收到的数据。bufsize 指定一次接收的最大数据量。可选参数 flags 的含义请参阅 Unix 手册页 *recv(2)*,它默认为零。

注解

为了最佳匹配硬件和网络的实际情况,bufsize 的值应为 2 的相对较小的幂,如 4096。

在 3.5 版更改: 如果系统调用被中断,但信号处理程序没有触发异常,此方法现在会重试系统调用,而不是触发 InterruptedError 异常 (原因详见 PEP 475)。

socket.recvfrom(bufsize[, flags])

从套接字接收数据。返回值是一对 (bytes, address),其中 bytes 是字节对象,表示接收到的数据,address 是发送端套接字的地址。可选参数 flags 的含义请参阅 Unix 手册页 recv(2)*,它默认为零。( *address 的格式取决于地址簇 —— 参见上文)

在 3.5 版更改: 如果系统调用被中断,但信号处理程序没有触发异常,此方法现在会重试系统调用,而不是触发 InterruptedError 异常 (原因详见 PEP 475)。

在 3.7 版更改: 对于多播 IPv6 地址,address 的第一项不会再包含 %scope_id 部分。 要获得完整的 IPv6 地址请使用 getnameinfo()

socket.recvmsg(bufsize[, ancbufsize[, flags]])

从套接字接收普通数据(至多 bufsize 字节)和辅助数据。ancbufsize 参数设置用于接收辅助数据的内部缓冲区的大小(以字节为单位),默认为 0,表示不接收辅助数据。可以使用 CMSG_SPACE()CMSG_LEN() 计算辅助数据缓冲区的合适大小,无法放入缓冲区的项目可能会被截断或丢弃。flags 参数默认为 0,其含义与 recv() 中的相同。

返回值是一个四元组: (data, ancdata, msg_flags, address)data 项是一个 bytes 对象,用于保存接收到的非辅助数据。ancdata 项是零个或多个元组 (cmsg_level, cmsg_type, cmsg_data) 组成的列表,表示接收到的辅助数据(控制消息):cmsg_levelcmsg_type 是分别表示协议级别和协议类型的整数,而 cmsg_data 是保存相关数据的 bytes 对象。msg_flags 项由各种标志按位或组成,表示接收消息的情况,详细信息请参阅系统文档。如果接收端套接字断开连接,则 address 是发送端套接字的地址(如果有),否则该值无指定。

某些系统上可以利用 AF_UNIX 套接字通过 sendmsg()recvmsg() 在进程之间传递文件描述符。使用此功能时(通常仅限于 SOCK_STREAM 套接字),recvmsg() 将在其辅助数据中返回以下格式的项 (socket.SOL_SOCKET, socket.SCM_RIGHTS, fds),其中 fds 是一个 bytes 对象,是新文件描述符表示为原生 C int 类型的二进制数组。如果 recvmsg() 在系统调用返回后抛出异常,它将首先关闭此机制接收到的所有文件描述符。

对于仅接收到一部分的辅助数据项,一些系统没有指示其截断长度。如果某个项目可能超出了缓冲区的末尾,recvmsg() 将发出 RuntimeWarning,并返回其在缓冲区内的部分,前提是该对象被截断于关联数据开始后。

在支持 SCM_RIGHTS 机制的系统上,下方的函数将最多接收 maxfds 个文件描述符,返回消息数据和包含描述符的列表(同时忽略意外情况,如接收到无关的控制消息)。

import socket, array
def recv_fds(sock, msglen, maxfds):
    fds = array.array("i")   # Array of ints
    msg, ancdata, flags, addr = sock.recvmsg(msglen, socket.CMSG_LEN(maxfds * fds.itemsize))
    for cmsg_level, cmsg_type, cmsg_data in ancdata:
        if cmsg_level == socket.SOL_SOCKET and cmsg_type == socket.SCM_RIGHTS:
            # Append data, ignoring any truncated integers at the end.
            fds.frombytes(cmsg_data[:len(cmsg_data) - (len(cmsg_data) % fds.itemsize)])
    return msg, list(fds)

可用性: 大多数 Unix 平台,其他平台也可能可用。

3.3 新版功能.

在 3.5 版更改: 如果系统调用被中断,但信号处理程序没有触发异常,此方法现在会重试系统调用,而不是触发 InterruptedError 异常 (原因详见 PEP 475)。

socket.recvmsg_into(buffers[, ancbufsize[, flags]])

从套接字接收普通数据和辅助数据,其行为与 recvmsg() 相同,但将非辅助数据分散到一系列缓冲区中,而不是返回新的字节对象。buffers 参数必须是可迭代对象,它迭代出可供写入的缓冲区(如 bytearray 对象),这些缓冲区将被连续的非辅助数据块填充,直到数据全部写完或缓冲区用完为止。在允许使用的缓冲区数量上,操作系统可能会有限制( sysconf()SC_IOV_MAX 值)。ancbufsizeflags 参数的含义与 recvmsg() 中的相同。

返回值为四元组: (nbytes, ancdata, msg_flags, address),其中 nbytes 是写入缓冲区的非辅助数据的字节总数,而 ancdatamsg_flagsaddressrecvmsg() 中的相同。

示例:

>>> import socket
>>> s1, s2 = socket.socketpair()
>>> b1 = bytearray(b'----')
>>> b2 = bytearray(b'0123456789')
>>> b3 = bytearray(b'--------------')
>>> s1.send(b'Mary had a little lamb')
22
>>> s2.recvmsg_into([b1, memoryview(b2)[2:9], b3])
(22, [], 0, None)
>>> [b1, b2, b3]
[bytearray(b'Mary'), bytearray(b'01 had a 9'), bytearray(b'little lamb---')]

可用性: 大多数 Unix 平台,其他平台也可能可用。

3.3 新版功能.

socket.recvfrom_into(buffer[, nbytes[, flags]])

从套接字接收数据,将其写入 buffer 而不是创建新的字节串。返回值是一对 (nbytes, address),其中 nbytes 是收到的字节数,address 是发送端套接字的地址。可选参数 flags 的含义请参阅 Unix 手册页 recv(2)*,它默认为零。( *address 的格式取决于地址簇 —— 参见上文)

socket.recv_into(buffer[, nbytes[, flags]])

从套接字接收至多 nbytes 个字节,将其写入缓冲区而不是创建新的字节串。如果 nbytes 未指定(或指定为 0),则接收至所给缓冲区的最大可用大小。返回接收到的字节数。可选参数 flags 的含义请参阅 Unix 手册页 *recv(2)*,它默认为零。

socket.send(bytes[, flags])

发送数据给套接字。本套接字必须已连接到远程套接字。可选参数 flags 的含义与上述 recv() 中的相同。本方法返回已发送的字节数。应用程序要负责检查所有数据是否已发送,如果仅传输了部分数据,程序需要自行尝试传输其余数据。

在 3.5 版更改: 如果系统调用被中断,但信号处理程序没有触发异常,此方法现在会重试系统调用,而不是触发 InterruptedError 异常 (原因详见 PEP 475)。

socket.sendall(bytes[, flags])

发送数据给套接字。本套接字必须已连接到远程套接字。可选参数 flags 的含义与上述 recv() 中的相同。与 send() 不同,本方法持续从 bytes 发送数据,直到所有数据都已发送或发生错误为止。成功后会返回 None。出错后会抛出一个异常,此时并没有办法确定成功发送了多少数据。

在 3.5 版更改: 每次成功发送数据后,套接字超时不再重置。现在,套接字超时是发送所有数据的最大总持续时间。

在 3.5 版更改: 如果系统调用被中断,但信号处理程序没有触发异常,此方法现在会重试系统调用,而不是触发 InterruptedError 异常 (原因详见 PEP 475)。

socket.sendto(bytes, address)

socket.sendto(bytes, flags, address)

发送数据给套接字。本套接字不应连接到远程套接字,而应由 address 指定目标套接字。可选参数 flags 的含义与上述 recv() 中的相同。本方法返回已发送的字节数。( address 的格式取决于地址簇 —— 参见上文。)

引发一个 审计事件 socket.sendto,附带参数 selfaddress

在 3.5 版更改: 如果系统调用被中断,但信号处理程序没有触发异常,此方法现在会重试系统调用,而不是触发 InterruptedError 异常 (原因详见 PEP 475)。

socket.sendmsg(buffers[, ancdata[, flags[, address]]])

将普通数据和辅助数据发送给套接字,将从一系列缓冲区中收集非辅助数据,并将其拼接为一条消息。buffers 参数指定的非辅助数据应为可迭代的 字节类对象 (如 bytes 对象),在允许使用的缓冲区数量上,操作系统可能会有限制( sysconf()SC_IOV_MAX 值)。ancdata 参数指定的辅助数据(控制消息)应为可迭代对象,迭代出零个或多个 (cmsg_level, cmsg_type, cmsg_data) 元组,其中 cmsg_levelcmsg_type 是分别指定协议级别和协议类型的整数,而 cmsg_data 是保存相关数据的字节类对象。请注意,某些系统(特别是没有 CMSG_SPACE() 的系统)可能每次调用仅支持发送一条控制消息。flags 参数默认为 0,与 send() 中的含义相同。如果 address 指定为除 None 以外的值,它将作为消息的目标地址。返回值是已发送的非辅助数据的字节数。

在支持 SCM_RIGHTS 机制的系统上,下方的函数通过一个 AF_UNIX 套接字来发送文件描述符列表 fds

import socket, array
def send_fds(sock, msg, fds):
    return sock.sendmsg([msg], [(socket.SOL_SOCKET, socket.SCM_RIGHTS, array.array("i", fds))])

可用性: 大多数 Unix 平台,其他平台也可能可用。

引发一个 审计事件 socket.sendmsg,附带参数 selfaddress

3.3 新版功能.

在 3.5 版更改: 如果系统调用被中断,但信号处理程序没有触发异常,此方法现在会重试系统调用,而不是触发 InterruptedError 异常 (原因详见 PEP 475)。

socket.sendmsg_afalg([msg, ]**, op[, iv[, assoclen[, flags*]]])

AF_ALG 套接字定制的 sendmsg() 版本。可为 AF_ALG 套接字设置模式、IV、AEAD 关联数据的长度和标志位。

可用性: Linux >= 2.6.38。

3.6 新版功能.

socket.sendfile(file, offset=0, count=None)

使用高性能的 os.sendfile 发送文件,直到达到文件的 EOF 为止,返回已发送的字节总数。file 必须是一个以二进制模式打开的常规文件对象。如果 os.sendfile 不可用(如 Windows)或 file 不是常规文件,将使用 send() 代替。offset 指示从哪里开始读取文件。如果指定了 count,它确定了要发送的字节总数,而不会持续发送直到达到文件的 EOF。返回时或发生错误时,文件位置将更新,在这种情况下,file.tell() 可用于确定已发送的字节数。套接字必须为 SOCK_STREAM 类型。不支持非阻塞的套接字。

3.5 新版功能.

socket.set_inheritable(inheritable)

设置套接字文件描述符或套接字句柄的 可继承标志。

3.4 新版功能.

socket.setblocking(flag)

设置套接字为阻塞或非阻塞模式:如果 flag 为 false,则将套接字设置为非阻塞,否则设置为阻塞。

本方法是某些 settimeout() 调用的简写:

  • sock.setblocking(True) 相当于 sock.settimeout(None)
  • sock.setblocking(False) 相当于 sock.settimeout(0.0)

在 3.7 版更改: 本方法不再对 socket.type 属性设置 SOCK_NONBLOCK 标志。

socket.settimeout(value)

为阻塞套接字的操作设置超时。value 参数可以是非负浮点数,表示秒,也可以是 None。如果赋为一个非零值,那么如果在操作完成前超过了超时时间 value,后续的套接字操作将抛出 timeout 异常。如果赋为 0,则套接字将处于非阻塞模式。如果指定为 None,则套接字将处于阻塞模式。

在 3.7 版更改: 本方法不再修改 socket.type 属性的 SOCK_NONBLOCK 标志。

socket.setsockopt(level, optname, value: int)

socket.setsockopt(level, optname, value: buffer)

socket.setsockopt(level, optname, None, optlen: int)

设置给定套接字选项的值(参阅 Unix 手册页 setsockopt(2) )。所需的符号常量( SO_* 等)已定义在本 socket 模块中。该值可以是整数、None 或表示缓冲区的 字节类对象。在后一种情况下,由调用者确保字节串中包含正确的数据位(关于将 C 结构体编码为字节串的方法,请参阅可选的内置模块 struct )。当 value 设置为 None 时,必须设置 optlen 参数。这相当于调用 setsockopt() C 函数时使用了 optval=NULLoptlen=optlen 参数。

在 3.5 版更改: 现在接受可写的 字节类对象。

在 3.6 版更改: 添加了 setsockopt(level, optname, None, optlen: int) 调用形式。

socket.shutdown(how)

关闭一半或全部的连接。如果 howSHUT_RD,则后续不再允许接收。如果 howSHUT_WR,则后续不再允许发送。如果 howSHUT_RDWR,则后续的发送和接收都不允许。

socket.share(process_id)

复制套接字,并准备将其与目标进程共享。目标进程必须以 process_id 形式提供。然后可以利用某种形式的进程间通信,将返回的字节对象传递给目标进程,还可以使用 fromshare() 在新进程中重新创建套接字。一旦本方法调用完毕,就可以安全地将套接字关闭,因为操作系统已经为目标进程复制了该套接字。

可用性: Windows。

3.3 新版功能.

注意此处没有 read()write() 方法,请使用不带 flags 参数的 recv()send() 来替代。

套接字对象还具有以下(只读)属性,这些属性与传入 socket 构造函数的值相对应。

socket.family

套接字的协议簇。

socket.type

套接字的类型。

socket.proto

套接字的协议。

关于套接字超时的说明

一个套接字对象可以处于以下三种模式之一:阻塞、非阻塞或超时。套接字默认以阻塞模式创建,但是可以调用 setdefaulttimeout() 来更改。

  • blocking mode (阻塞模式)中,操作将阻塞,直到操作完成或系统返回错误(如连接超时)。
  • non-blocking mode (非阻塞模式)中,如果操作无法立即完成,则操作将失败(不幸的是,不同系统返回的错误不同):位于 select 中的函数可用于了解套接字何时以及是否可以读取或写入。
  • timeout mode (超时模式)下,如果无法在指定的超时内完成操作(抛出 timeout 异常),或如果系统返回错误,则操作将失败。

注解

在操作系统层面上,超时模式 下的套接字在内部都设置为非阻塞模式。同时,阻塞和超时模式在文件描述符和套接字对象之间共享,这些描述符和对象均应指向同一个网络端点。如果,比如你决定使用套接字的 fileno(),这一实现细节可能导致明显的结果。

超时与 connect 方法

connect() 操作也受超时设置的约束,通常建议在调用 connect() 之前调用 settimeout(),或将超时参数直接传递给 create_connection()。但是,无论 Python 套接字超时设置如何,系统网络栈都有可能返回自带的连接超时错误。

超时与 accept 方法

如果 getdefaulttimeout() 的值不是 None,则 accept() 方法返回的套接字将继承该超时值。若是 None,返回的套接字行为取决于侦听套接字的设置:

  • 如果侦听套接字处于 阻塞模式超时模式,则 accept() 返回的套接字处于 阻塞模式
  • 如果侦听套接字处于 非阻塞模式,那么 accept() 返回的套接字是阻塞还是非阻塞取决于操作系统。如果要确保跨平台时的正确行为,建议手动覆盖此设置。

示例

以下是 4 个使用 TCP/IP 协议的最小示例程序:一台服务器,它将收到的所有数据原样返回(仅服务于一个客户端),还有一个使用该服务器的客户端。注意,服务器必须按序执行 socket(), bind(), listen(), accept() (可能需要重复执行 accept() 以服务多个客户端),而客户端仅需要按序执行 socket(), connect()。还须注意,服务器不在侦听套接字上发送 sendall()/recv(),而是在 accept() 返回的新套接字上发送。

前两个示例仅支持 IPv4。

# Echo server program
import socket
HOST = ''                 # Symbolic name meaning all available interfaces
PORT = 50007              # Arbitrary non-privileged port
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.bind((HOST, PORT))
    s.listen(1)
    conn, addr = s.accept()
    with conn:
        print('Connected by', addr)
        while True:
            data = conn.recv(1024)
            if not data: break
            conn.sendall(data)

# Echo client program
import socket
HOST = 'daring.cwi.nl'    # The remote host
PORT = 50007              # The same port as used by the server
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect((HOST, PORT))
    s.sendall(b'Hello, world')
    data = s.recv(1024)
print('Received', repr(data))

下两个例子与上两个很像,但是同时支持 IPv4 和 IPv6。 服务端将监听第一个可用的地址族(它本应同时监听两个)。 在大多数支持 IPv6 的系统上,IPv6 将有优先权并且服务端可能不会接受 IPv4 流量。 客户端将尝试连接到作为名称解析结果被返回的所有地址,并将流量发送给连接成功的第一个地址。

# Echo server program
import socket
import sys
HOST = None               # Symbolic name meaning all available interfaces
PORT = 50007              # Arbitrary non-privileged port
s = None
for res in socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC,
                              socket.SOCK_STREAM, 0, socket.AI_PASSIVE):
    af, socktype, proto, canonname, sa = res
    try:
        s = socket.socket(af, socktype, proto)
    except OSError as msg:
        s = None
        continue
    try:
        s.bind(sa)
        s.listen(1)
    except OSError as msg:
        s.close()
        s = None
        continue
    break
if s is None:
    print('could not open socket')
    sys.exit(1)
conn, addr = s.accept()
with conn:
    print('Connected by', addr)
    while True:
        data = conn.recv(1024)
        if not data: break
        conn.send(data)


# Echo client program
import socket
import sys
HOST = 'daring.cwi.nl'    # The remote host
PORT = 50007              # The same port as used by the server
s = None
for res in socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC, socket.SOCK_STREAM):
    af, socktype, proto, canonname, sa = res
    try:
        s = socket.socket(af, socktype, proto)
    except OSError as msg:
        s = None
        continue
    try:
        s.connect(sa)
    except OSError as msg:
        s.close()
        s = None
        continue
    break
if s is None:
    print('could not open socket')
    sys.exit(1)
with s:
    s.sendall(b'Hello, world')
    data = s.recv(1024)
print('Received', repr(data))

下面的例子演示了如何在 Windows 上使用原始套接字编写一个非常简单的网络嗅探器。 这个例子需要管理员权限来修改接口:

import socket
# the public network interface
HOST = socket.gethostbyname(socket.gethostname())
# create a raw socket and bind it to the public interface
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)
s.bind((HOST, 0))
# Include IP headers
s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
# receive all packages
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
# receive a package
print(s.recvfrom(65565))
# disabled promiscuous mode
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)

下面的例子演示了如何使用 socket 接口与采用原始套接字协议的 CAN 网络进行通信。 要改为通过广播管理器协议来使用 CAN,则要用以下方式打开一个 socket:

socket.socket(socket.AF_CAN, socket.SOCK_DGRAM, socket.CAN_BCM)

在绑定 (CAN_RAW) 或连接 (CAN_BCM) socket 之后,你将可以在 socket 对象上正常使用 socket.send() 以及 socket.recv() 操作(及同类操作)。

最后一个例子可能需要特别的权限:

import socket
import struct
# CAN frame packing/unpacking (see 'struct can_frame' in <linux/can.h>)
can_frame_fmt = "=IB3x8s"
can_frame_size = struct.calcsize(can_frame_fmt)
def build_can_frame(can_id, data):
    can_dlc = len(data)
    data = data.ljust(8, b'\x00')
    return struct.pack(can_frame_fmt, can_id, can_dlc, data)
def dissect_can_frame(frame):
    can_id, can_dlc, data = struct.unpack(can_frame_fmt, frame)
    return (can_id, can_dlc, data[:can_dlc])
# create a raw socket and bind it to the 'vcan0' interface
s = socket.socket(socket.AF_CAN, socket.SOCK_RAW, socket.CAN_RAW)
s.bind(('vcan0',))
while True:
    cf, addr = s.recvfrom(can_frame_size)
    print('Received: can_id=%x, can_dlc=%x, data=%s' % dissect_can_frame(cf))
    try:
        s.send(cf)
    except OSError:
        print('Error sending CAN frame')
    try:
        s.send(build_can_frame(0x01, b'\x01\x02\x03'))
    except OSError:
        print('Error sending CAN frame')

多次运行一个示例,且每次执行之间等待时间过短,可能导致这个错误:

OSError: [Errno 98] Address already in use

这是因为前一次运行使套接字处于 TIME_WAIT 状态,无法立即重用。

要防止这种情况,需要设置一个 socket 标志 socket.SO_REUSEADDR:

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST, PORT))

SO_REUSEADDR 标志告诉内核将处于 TIME_WAIT 状态的本地套接字重新使用,而不必等到固有的超时到期。

参见

关于套接字编程(C 语言)的介绍,请参阅以下文章:

  • An Introductory 4.3BSD Interprocess Communication Tutorial,作者 Stuart Sechrest
  • An Advanced 4.3BSD Interprocess Communication Tutorial,作者 Samuel J. Leffler et al,

两篇文章都在 UNIX 开发者手册,补充文档 1(第 PS1:7 和 PS1:8 节)中。那些特定于平台的参考资料,它们包含与套接字有关的各种系统调用,也是套接字语义细节的宝贵信息来源。对于 Unix,请参考手册页。对于 Windows,请参阅 WinSock(或 Winsock 2)规范。如果需要支持 IPv6 的 API,读者可能希望参考 RFC 3493,标题为 Basic Socket Interface Extensions for IPv6。

ssl —- 套接字对象的 TLS/SSL 包装器

源代码: Lib/ssl.py


This module provides access to Transport Layer Security (often known as “Secure Sockets Layer”) encryption and peer authentication facilities for network sockets, both client-side and server-side. This module uses the OpenSSL library. It is available on all modern Unix systems, Windows, macOS, and probably additional platforms, as long as OpenSSL is installed on that platform.

注解

某些行为可能与平台相关,因为调用了操作系统的套接字 API。已安装的OpenSSL 版本也可能会导致不同的行为。比如 TLSv 1.3 与 Open SSL 1.1.1 就不一样。

警告

在阅读 安全考量 前不要使用此模块。 这样做可能会导致虚假的安全感,因为ssl模块的默认设置不一定适合你的应用程序。

文档本文档记录ssl 模块的对象和函数;更多关于TLS,SSL,和证书的信息,请参阅下方的“详情”选项

本模块提供了一个类 ssl.SSLSocket,它派生自 socket.socket 类型,并提供类似套接字的包装器,也能够对通过带 SSL 套接字的数据进行加密和解密。 它支持一些额外方法例如 getpeercert(),该方法可从连接的另一端获取证书,还有 cipher(),该方法可获取安全连接所使用的密码。

对于更复杂的应用程序,ssl.SSLContext 类有助于管理设置项和证书,进而可以被使用 SSLContext.wrap_socket() 方法创建的 SSL 套接字继承。

在 3.5.3 版更改: 更新以支持和 OpenSSL 1.1.0 的链接

在 3.6 版更改: OpenSSL 0.9.8、1.0.0 和 1.0.1 已过时,将不再被支持。在 ssl 模块未来的版本中,最低需要 OpenSSL 1.0.2 或 1.1.0。

在 3.10 版更改: PEP 644 已经实现。ssl 模块需要 OpenSSL 1.1.1 以上版本的支持。

使用废弃的常量和函数会导致废弃警告。

方法、常量和异常处理

套接字创建

从 Python 3.2 和 2.7.9 开始,建议使用 SSLContext 实例的 SSLContext.wrap_socket() 来将套接字包装为 SSLSocket 对象。 辅助函数 create_default_context() 会返回一个新的带有安全默认设置的上下文。 旧的 wrap_socket() 函数已被弃用,因为它效率较差并且不支持服务器名称提示(SNI)和主机匹配。

客户端套接字实例,采用默认上下文和IPv4/IPv6双栈:

import socket
import ssl
hostname = 'www.python.org'
context = ssl.create_default_context()
with socket.create_connection((hostname, 443)) as sock:
    with context.wrap_socket(sock, server_hostname=hostname) as ssock:
        print(ssock.version())

客户端套接字示例,带有自定义上下文和IPv4:

hostname = 'www.python.org'
# PROTOCOL_TLS_CLIENT requires valid cert chain and hostname
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.load_verify_locations('path/to/cabundle.pem')
with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as sock:
    with context.wrap_socket(sock, server_hostname=hostname) as ssock:
        print(ssock.version())

服务器套接字实例,在localhost上监听IPv4:

context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain('/path/to/certchain.pem', '/path/to/private.key')
with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as sock:
    sock.bind(('127.0.0.1', 8443))
    sock.listen(5)
    with context.wrap_socket(sock, server_side=True) as ssock:
        conn, addr = ssock.accept()
        ...

上下文创建

便捷函数,可以帮助创建 SSLContext 对象,用于常见的目的。

ssl.create_default_context(purpose=Purpose.SERVER_AUTH, cafile=None, capath=None, cadata=None)

返回一个新的 SSLContext 对象,使用给定 purpose 的默认设置。 该设置由 ssl 模块选择,并且通常是代表一个比直接调用 SSLContext 构造器时更高的安全等级。

cafile, capath, cadata 代表用于进行证书核验的可选受信任 CA 证书,与 SSLContext.load_verify_locations() 的一致。 如果三个参数均为 None,此函数可以转而选择信任系统的默认 CA 证书。

设置为: PROTOCOL_TLS_CLIENTPROTOCOL_TLS_SERVEROP_NO_SSLv2OP_NO_SSLv3,带有不含 RC4 及未认证的高强度加密密码套件。 传入 SERVER_AUTH 作为 purpose,会将 verify_mode 设为 CERT_REQUIRED,并加载 CA 证书(若给出 *cafile、*capathcadata 之一)或用 SSLContext.load_default_certs() 加载默认CA证书。

keylog_filename 受支持并且设置了环境变量 SSLKEYLOGFILE 时,create_default_context() 会启用密钥日志记录。

注解

协议、选项、密码和其他设置可随时更改为更具约束性的值而无须事先弃用。 这些值代表了兼容性和安全性之间的合理平衡。

如果你的应用需要特定的设置,你应当创建一个 SSLContext 并自行应用设置。

注解

如果你发现当某些较旧的客户端或服务器尝试与用此函数创建的 SSLContext 进行连接时收到了报错提示 “Protocol or cipher suite mismatch”,这可能是因为它们只支持 SSL3.0 而它被此函数用 OP_NO_SSLv3 排除掉了。 SSL3.0 被广泛认为 完全不可用。 如果你仍希望继续使用此函数但仍允许 SSL 3.0 连接,你可以使用以下代码重新启用它们:

ctx = ssl.create_default_context(Purpose.CLIENT_AUTH)
ctx.options &= ~ssl.OP_NO_SSLv3

3.4 新版功能.

在 3.4.4 版更改: RC4 被从默认密码字符串中丢弃。

在 3.6 版更改: ChaCha20/Poly1305 被添加到默认密码字符串中。

3DES 被从默认密码字符串中丢弃。

在 3.8 版更改: 增加了对密钥日志记录至 SSLKEYLOGFILE 的支持。

在 3.10 版更改: 当前上下文使用 PROTOCOL_TLS_CLIENTPROTOCOL_TLS_SERVER 协议而非通用的 PROTOCOL_TLS

异常

exception ssl.SSLError

引发此异常以提示来自下层 SSL 实现(目前由 OpenSSL 库提供)的错误。 它表示在下层网络连接之上叠加的高层级加密和验证层存在某种问题。 此错误是 OSError 的一个子类型。 SSLError 实例的错误和消息是由 OpenSSL 库提供的。

在 3.3 版更改: SSLError 曾经是 socket.error 的一个子类型。

  • library

    一个字符串形式的助记符,用来指明发生错误的 OpenSSL 子模块,例如 SSL, PEMX509。 可能的取值范围依赖于 OpenSSL 的版本。

    3.3 新版功能.

  • reason

    一个字符串形式的助记符,用来指明发生错误的原因,例如 CERTIFICATE_VERIFY_FAILED。 可能的取值范围依赖于 OpenSSL 的版本。

    3.3 新版功能.

exception ssl.SSLZeroReturnError

SSLError 的子类,当尝试读取或写入且 SSL 连接已被完全关闭时会被引发。 请注意这并不意味着下层的传输(读取 TCP)已被关闭。

3.3 新版功能.

exception ssl.SSLWantReadError

SSLError 的子类,当尝试读取或写入数据,但在请求被满足之前还需要在下层的 TCP 传输上接收更多数据时会被 非阻塞型 SSL 套接字 引发。

3.3 新版功能.

exception ssl.SSLWantWriteError

SSLError 的子类,当尝试读取或写入数据,但在请求被满足之前还需要在下层的 TCP 传输上发送更多数据时会被 非阻塞型 SSL 套接字 引发。

3.3 新版功能.

exception ssl.SSLSyscallError

SSLError 的子类,当尝试在 SSL 套接字上执行操作时遇到系统错误时会被引发。 不幸的是,没有简单的方式能检查原始 errno 编号。

3.3 新版功能.

exception ssl.SSLEOFError

SSLError 的子类,当 SSL 连接被突然终止时会被引发。 通常,当遇到此错误时你不应再尝试重用下层的传输。

3.3 新版功能.

exception ssl.SSLCertVerificationError

SSLError 的子类,当证书验证失败时会被引发。

3.7 新版功能.

  • verify_code

    一个数字形式的错误编号,用于表示验证错误。

  • verify_message

    用于表示验证错误的人类可读的字符串。

exception ssl.CertificateError

SSLCertVerificationError 的别名。

在 3.7 版更改: 此异常现在是 SSLCertVerificationError 的别名。

随机生成

ssl.RAND_bytes(num)

返回 num 个高加密强度伪随机字节数据。 如果 PRNG 未使用足够的数据作为随机种子或者如果当前 RAND 方法不支持该操作则会引发 SSLErrorRAND_status() 可被用来检查 PRNG 的状态而 RAND_add() 可被用来为 PRNG 设置随机种子。

对于几乎所有应用程序都更推荐使用 os.urandom()

请阅读维基百科文章 Cryptographically secure pseudorandom number generator (CSPRNG) 以了解对于高加密强度生成器的具体要求。

3.3 新版功能.

ssl.RAND_pseudo_bytes(num)

返回 (bytes, is_cryptographic): bytes 是 num 个伪随机字节数据,如果所生成的字节数据为高加密强度则 is_cryptographic 为 True。 如果当前 RAND 方法不支持此操作则会引发 SSLError

所生成的伪随机字节序列如果具有足够的长度则将会具有唯一性,并是并非不可预测。 它们可被用于非加密目的以及加密协议中的特定目的,但通常不可被用于密钥生成等目的。

对于几乎所有应用程序都更推荐使用 os.urandom()

3.3 新版功能.

3.6 版后已移除: OpenSSL 已弃用了 ssl.RAND_pseudo_bytes(),请改用 ssl.RAND_bytes()

ssl.RAND_status()

如果 SSL 伪随机数生成器已使用‘足够的’随机性作为种子则返回 True,否则返回 False。 你可以使用 ssl.RAND_egd()ssl.RAND_add() 来增加伪随机数生成器的随机性。

ssl.RAND_add(bytes, entropy)

将给定的 bytes 混合到 SSL 伪随机数生成器中。 形参 entropy (float 类型) 是数据所包含的熵的下界 (因此你可以总是使用 0.0)。 请查看 RFC 1750 了解有关熵源的更多信息。

在 3.5 版更改: 现在接受可写的 字节类对象。

证书处理

ssl.match_hostname(cert, hostname)

验证 cert (使用 SSLSocket.getpeercert() 所返回的已解码格式) 是否匹配给定的 hostname。 所应用的规则是在 RFC 2818, RFC 5280RFC 6125 中描述的检查 HTTPS 服务器身份的规则。 除了 HTTPS,此函数还应当适用于各种基于 SSL 协议的服务器身份检查操作,例如 FTPS, IMAPS, POPS 等等。

失败时引发 CertificateError。 成功时此函数无返回值:

>>> cert = {'subject': ((('commonName', 'example.com'),),)}
>>> ssl.match_hostname(cert, "example.com")
>>> ssl.match_hostname(cert, "example.org")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/py3k/Lib/ssl.py", line 130, in match_hostname
ssl.CertificateError: hostname 'example.org' doesn't match 'example.com'

3.2 新版功能.

在 3.3.3 版更改: 此函数现在遵循 RFC 6125, 6.4.3 小节,它不会匹配多个通配符 (例如 *.*.com*a*.example.org) 也不匹配国际化域名 (IDN) 片段内部的通配符。 IDN A 标签例如 www*.xn--pthon-kva.org 仍然受支持,但 x*.python.org 不再能匹配 xn--tda.python.org

在 3.5 版更改: 现在支持匹配存在于证书的 subjectAltName 字段中的 IP 地址。

在 3.7 版更改: 此函数不再被用于 TLS 连接。 主机匹配现在是由 OpenSSL 执行的。

允许位于段的最左端且为唯一字符的通配符。 部分通配符例如 www*.example.com 已不再受支持。

3.7 版后已移除.

ssl.cert_time_to_seconds(cert_time)

返回距离 Unix 纪元零时的秒数,给定的 cert_time 字符串代表来自证书的 “notBefore” 或 “notAfter” 日期值,采用 "%b %d %H:%M:%S %Y %Z" strptime 格式(C 区域)。

以下为示例代码:

>>> import ssl
>>> timestamp = ssl.cert_time_to_seconds("Jan  5 09:34:43 2018 GMT")
>>> timestamp  
1515144883
>>> from datetime import datetime
>>> print(datetime.utcfromtimestamp(timestamp))  
2018-01-05 09:34:43

“notBefore” 或 “notAfter” 日期值必须使用 GMT (RFC 5280)。

在 3.5 版更改: 将输入时间解读为 UTC 时间,基于输入字符串中指明的 ‘GMT’ 时区。 在之前使用的是本地时区。 返回一个整数(不带输入格式中秒的分数部分)

ssl.get_server_certificate(addr, ssl_version=PROTOCOL_TLS_CLIENT, ca_certs=None[, timeout])

给出一个受 SSL 保护的服务器的地址 addr,形式为 (hostname, port-number) ,获取该服务器的证书,并以 PEM 编码的字符串返回。如果指定了 ssl_version,则使用该版本的 SSL 协议尝试连接服务器。如果指定了 ca_certs,它应该是一个包含根证书列表的文件,与 SSLContext.wrap_socket() 中同名参数的格式相同。该调用将尝试根据该根证书集来验证服务器的证书,如果验证失败则调用失败。参数 timeout 可用于指定超时时间。

在 3.3 版更改: 此函数现在是 IPv6 兼容的。-compatible.

在 3.5 版更改: 默认的 ssl_versionPROTOCOL_SSLv3 改为 PROTOCOL_TLS 以保证与现代服务器的最大兼容性。

在 3.10 版更改: 加入 timeout 参数。

ssl.DER_cert_to_PEM_cert(DER_cert_bytes)

根据给定的 DER 编码字节块形式的证书,返回同一证书的 PEM 编码字符串版本。

ssl.PEM_cert_to_DER_cert(PEM_cert_string)

根据给定的 ASCII PEM 字符串形式的证书,返回同一证书的 DER 编码字节序列。

ssl.get_default_verify_paths()

返回包含 OpenSSL 的默认 cafile 和 capath 的路径的命名元组。 此路径与 SSLContext.set_default_verify_paths() 所使用的相同。 返回值是一个 named tuple DefaultVerifyPaths:

  • cafile - 解析出的 cafile 路径或者如果文件不存在则为 None,
  • capath - 解析出的 capath 路径或者如果目录不存在则为 None,
  • openssl_cafile_env - 指向一个 cafile 的 OpenSSL 环境键,
  • openssl_cafile - 一个 cafile 的硬编码路径,
  • openssl_capath_env - 指向一个 capath 的 OpenSSL 环境键,
  • openssl_capath - 一个 capath 目录的硬编码路径

可用性: LibreSSL 会忽略环境变量 openssl_cafile_envopenssl_capath_env

3.4 新版功能.

ssl.enum_certificates(store_name)

从 Windows 的系统证书库中检索证书。 store_name 可以是 CA, ROOTMY 中的一个。 Windows 也可能会提供额外的证书库。

此函数返回一个包含 (cert_bytes, encoding_type, trust) 元组的列表。 encoding_type 指明 cert_bytes 的编码格式。 它可以为 x509_asn 以表示 X.509 ASN.1 数据或是 pkcs_7_asn 以表示 PKCS#7 ASN.1 数据。 trust 以 OIDS 集合的形式指明证书的目的,或者如果证书对于所有目的都可以信任则为 True

示例:

>>> ssl.enum_certificates("CA")
[(b'data...', 'x509_asn', {'1.3.6.1.5.5.7.3.1', '1.3.6.1.5.5.7.3.2'}),
 (b'data...', 'x509_asn', True)]

可用性: Windows。

3.4 新版功能.

ssl.enum_crls(store_name)

Windows 的系统证书库中检索 CRL。 store_name 可以是 CA, ROOTMY 中的一个。 Windows 也可能会提供额外的证书库。

此函数返回一个包含 (cert_bytes, encoding_type, trust) 元组的列表。 encoding_type 指明 cert_bytes 的编码格式。 它可以为 x509_asn 以表示 X.509 ASN.1 数据或是 pkcs_7_asn 以表示 PKCS#7 ASN.1 数据。

可用性: Windows。

3.4 新版功能.

ssl.wrap_socket(sock, keyfile=None, certfile=None, server_side=False, cert_reqs=CERT_NONE, ssl_version=PROTOCOL_TLS, ca_certs=None, do_handshake_on_connect=True, suppress_ragged_eofs=True, ciphers=None)

接受一个 socket.socket 的实例 sock,并返回一个 ssl.SSLSocket 的实例,该类型是 socket.socket 的子类型,它将下层的套接字包装在一个 SSL 上下文中。 sock 必须是一个 SOCK_STREAM 套接字;其他套接字类型不被支持。

在内部,该函数会创建一个 SSLContext,其协议版本为 ssl_versionSSLContext.options 设为 cert_reqs*。 如果设置了 *keyfile, certfile, ca_certsciphers 等形参,则参数值会被传给 SSLContext.load_cert_chain(), SSLContext.load_verify_locations() 以及 SSLContext.set_ciphers()

参数 server_side, do_handshake_on_connectsuppress_ragged_eofs 具有与 SSLContext.wrap_socket() 相同的含义。

3.7 版后已移除: 从 Python 3.2 和 2.7.9 开始,建议使用 SSLContext.wrap_socket() 来代替 wrap_socket()。 模块级函数的功能受限并且将创建不安全的客户端套接字,不带服务器名称提示或主机名匹配。

常量

所有常量现在都是 enum.IntEnumenum.IntFlag 多项集的成员。

3.6 新版功能.

ssl.CERT_NONE

SSLContext.verify_modewrap_socket()cert_reqs 形参可能的取值。 PROTOCOL_TLS_CLIENT 除外,这是默认的模式。 对于客户端套接字,几乎任何证书都是可接受的。 验证错误例如不受信任或过期的证书错误会被忽略并且不会中止 TLS/SSL 握手。

在服务器模式下,不会从客户端请求任何证书,因此客户端不会发送任何用于客户端证书身份验证的证书。

参见下文对于 安全考量 的讨论。

ssl.CERT_OPTIONAL

SSLContext.verify_modewrap_socket()cert_reqs 形参可能的取值。 CERT_OPTIONAL 具有与 CERT_REQUIRED 相同的含义。 对于客户端套接字推荐改用 CERT_REQUIRED

在服务器模式下,客户端证书请求会被发送给客户端。 客户端可以忽略请求也可以发送一个证书以执行 TLS 客户端证书身份验证。 如果客户端选择发送证书,则将对其执行验证。 任何验证错误都将立即中止 TLS 握手。

使用此设置要求将一组有效的 CA 证书传递给 SSLContext.load_verify_locations() 或是作为 wrap_socket()ca_certs 形参值。

ssl.CERT_REQUIRED

SSLContext.verify_modewrap_socket()cert_reqs 形参可能的取值。 在此模式下,需要从套接字连接的另一端获取证书;如果未提供证书或验证失败则将引发 SSLError。 此模式 不能 在客户端模式下对证书进行验证,因为它不会匹配主机名。 check_hostname 也必须被启用以验证证书的真实性。 PROTOCOL_TLS_CLIENT 会使用 CERT_REQUIRED 并默认启用 check_hostname

对于服务器套接字,此模式会提供强制性的 TLS 客户端证书验证。 客户端证书请求会被发送给客户端并且客户端必须提供有效且受信任的证书。

使用此设置要求将一组有效的 CA 证书传递给 SSLContext.load_verify_locations() 或是作为 wrap_socket()ca_certs 形参值。

class ssl.VerifyMode

CERT_* 常量的 enum.IntEnum 多项集。

3.6 新版功能.

ssl.VERIFY_DEFAULT

SSLContext.verify_flags 可能的取值。 在此模式下,证书吊销列表(CRL)并不会被检查。 OpenSSL 默认不要求也不验证 CRL。

3.4 新版功能.

ssl.VERIFY_CRL_CHECK_LEAF

SSLContext.verify_flags 可能的取值。 在此模式下, 只会检查对等证书而不检查任何中间 CA 证书。 此模式要求提供由对等证书颁发者(其直接上级 CA)签名的有效 CRL。 如果未使用 SSLContext.load_verify_locations 加载正确的 CRL,则验证将失败。

3.4 新版功能.

ssl.VERIFY_CRL_CHECK_CHAIN

SSLContext.verify_flags 可能的取值。 在此模式下,会检查对等证书链中所有证书的 CRL。

3.4 新版功能.

ssl.VERIFY_X509_STRICT

SSLContext.verify_flags 可能的取值,用于禁用已损坏 X.509 证书的绕过操作。

3.4 新版功能.

ssl.VERIFY_ALLOW_PROXY_CERTS

SSLContext.verify_flags 的可能取值,启用代理证书验证。

3.10 新版功能.

ssl.VERIFY_X509_TRUSTED_FIRST

SSLContext.verify_flags 可能的取值。 它指示 OpenSSL 在构建用于验证某个证书的信任链时首选受信任的证书。 此旗标将默认被启用。

3.4.4 新版功能.

ssl.VERIFY_X509_PARTIAL_CHAIN

SSLContext.verify_flags 的可能取值。它指示 OpenSSL 接受信任存储中的中间 CA 作为信任锚,与自我签名的根 CA 证书的方式相同。这样就能信任中间 CA 颁发的证书,而不一定非要去信任其祖先的根 CA。

3.10 新版功能.

class ssl.VerifyFlags

VERIFY_* 常量的 enum.IntFlag 多项集。

3.6 新版功能.

ssl.PROTOCOL_TLS

选择客户端和服务器均支持的最高协议版本。 此选项名称并不准确,实际上 “SSL” 和 “TLS” 协议均可被选择。

3.6 新版功能.

3.10 版后已移除: TLS 客户端和服务器需要不同的默认设置来实现安全通信。通用的 TLS 协议常量已废弃,而采用 PROTOCOL_TLS_CLIENTPROTOCOL_TLS_SERVER

ssl.PROTOCOL_TLS_CLIENT

自动协商为客户端和服务器都支持的最高版本协议,并配置当前上下文客户端的连接。该协议默认启用 CERT_REQUIREDcheck_hostname

3.6 新版功能.

ssl.PROTOCOL_TLS_SERVER

自动协商为客户端和服务器都支持的最高版本协议,并配置上下文服务器端的连接。

3.6 新版功能.

ssl.PROTOCOL_SSLv23

PROTOCOL_TLS 的别名。

3.6 版后已移除: 请改用 PROTOCOL_TLS

ssl.PROTOCOL_SSLv2

选择 SSL 版本 2 作为通道加密协议。

如果 OpenSSL 编译时使用了 OPENSSL_NO_SSL2 旗标则此协议将不可用。

警告

SSL 版本 2 并不安全。 极不建议使用它。

3.6 版后已移除: OpenSSL 已经移除了对 SSLv2 的支持。

ssl.PROTOCOL_SSLv3

选择 SSL 版本 3 作为通道加密协议。

如果 OpenSSL 编译时使用了 OPENSSL_NO_SSLv3 旗标则此协议将不可用。

警告

SSL 版本 3 并不安全。 极不建议使用它。

3.6 版后已移除: OpenSSL 已经废弃了所有特定于版本的协议。请换用带有 SSLContext.minimum_versionSSLContext.maximum_version 的默认协议 PROTOCOL_TLS_SERVERPROTOCOL_TLS_CLIENT

ssl.PROTOCOL_TLSv1

选择 TLS 版本 1.0 作为通道加密协议。

3.6 版后已移除: OpenSSL 已经废弃了所有特定于版本的协议。

ssl.PROTOCOL_TLSv1_1

选择 TLS 版本 1.1 作为通道加密协议。 仅适用于 openssl 版本 1.0.1+。

3.4 新版功能.

3.6 版后已移除: OpenSSL 已经废弃了所有特定于版本的协议。

ssl.PROTOCOL_TLSv1_2

选用 TLS 1.2 版本作为隧道加密协议。只适用于 openssl 1.0.1 以上版本。

3.4 新版功能.

3.6 版后已移除: OpenSSL 已经废弃了所有特定于版本的协议。

ssl.OP_ALL

对存在于其他 SSL 实现中的各种缺陷启用绕过操作。 默认会设置此选项。 没有必要设置与 OpenSSL 的 SSL_OP_ALL 常量同名的旗标。

3.2 新版功能.

ssl.OP_NO_SSLv2

阻止 SSLv2 连接。 此选项仅可与 PROTOCOL_TLS 结合使用。 它会阻止对等方选择 SSLv2 作为协议版本。

3.2 新版功能.

3.6 版后已移除: SSLv2 已被弃用

ssl.OP_NO_SSLv3

阻止 SSLv3 连接。 此选项仅可与 PROTOCOL_TLS 结合使用。 它会阻止对等方选择 SSLv3 作为协议版本。

3.2 新版功能.

3.6 版后已移除: SSLv3 已被弃用

ssl.OP_NO_TLSv1

阻止 TLSv1 连接。 此选项仅可与 PROTOCOL_TLS 结合使用。 它会阻止对等方选择 TLSv1 作为协议版本。

3.2 新版功能.

3.7 版后已移除: 此选项自 OpenSSL 1.1.0 起已被弃用,请改用新的 SSLContext.minimum_versionSSLContext.maximum_version

ssl.OP_NO_TLSv1_1

阻止 TLSv1.1 连接。 此选项仅可与 PROTOCOL_TLS 结合使用。 它会阻止对等方选择 TLSv1.1 作为协议版本。 仅适用于 openssl 版本 1.0.1+。

3.4 新版功能.

3.7 版后已移除: 此选项自 OpenSSL 1.1.0 起已被弃用。

ssl.OP_NO_TLSv1_2

阻止 TLSv1.2 连接。 此选项仅可与 PROTOCOL_TLS 结合使用。 它会阻止对等方选择 TLSv1.2 作为协议版本。 仅适用于 openssl 版本 1.0.1+。

3.4 新版功能.

3.7 版后已移除: 此选项自 OpenSSL 1.1.0 起已被弃用。

ssl.OP_NO_TLSv1_3

阻止 TLSv1.3 连接。 此选项仅可与 PROTOCOL_TLS 结合使用。 它会阻止对等方选择 TLSv1.3 作为协议版本。 TLS 1.3 适用于 OpenSSL 1.1.1 或更新的版本。 当 Python 编译是基于较旧版本的 OpenSSL 时,该旗标默认为 0

3.7 新版功能.

3.7 版后已移除: 此选项自 OpenSSL 1.1.0 起已被弃用。 它被添加到 2.7.15, 3.6.3 和 3.7.0 是为了向下兼容 OpenSSL 1.0.2。

ssl.OP_NO_RENEGOTIATION

禁用所有 TLSv1.2 和更早版本的重协商操作。 不发送 HelloRequest 消息,并忽略通过 ClientHello 发起的重协商请求。

此选项仅适用于 OpenSSL 1.1.0h 及更新的版本。

3.7 新版功能.

ssl.OP_CIPHER_SERVER_PREFERENCE

使用服务器的密码顺序首选项,而不是客户端的首选项。 此选项在客户端套接字和 SSLv2 服务器套接字上无效。

3.3 新版功能.

ssl.OP_SINGLE_DH_USE

阻止对于单独的 SSL 会话重用相同的 DH 密钥。 这会提升前向保密性但需要更多的计算资源。 此选项仅适用于服务器套接字。

3.3 新版功能.

ssl.OP_SINGLE_ECDH_USE

阻止对于单独的 SSL 会话重用相同的 ECDH 密钥。 这会提升前向保密性但需要更多的计算资源。 此选项仅适用于服务器套接字。

3.3 新版功能.

ssl.OP_ENABLE_MIDDLEBOX_COMPAT

在 TLS 1.3 握手中发送虚拟更改密码规格(CCS)消息以使得 TLS 1.3 连接看起来更像是 TLS 1.2 连接。

此选项仅适用于 OpenSSL 1.1.1 及更新的版本。

3.8 新版功能.

ssl.OP_NO_COMPRESSION

在 SSL 通道上禁用压缩。 这适用于应用协议支持自己的压缩方案的情况。

3.3 新版功能.

class ssl.Options

OP_* 常量的 enum.IntFlag 多项集。

ssl.OP_NO_TICKET

阻止客户端请求会话凭据。

3.6 新版功能.

ssl.OP_IGNORE_UNEXPECTED_EOF

忽略 TLS 连接的意外关闭。

此选项仅适用于 OpenSSL 3.0.0 及更新的版本。

3.10 新版功能.

ssl.HAS_ALPN

OpenSSL 库是否具有对 RFC 7301 中描述的 应用层协议协商 TLS 扩展的内置支持。

3.5 新版功能.

ssl.HAS_NEVER_CHECK_COMMON_NAME

OpenSSL 库是否具有对不检测目标通用名称的内置支持且 SSLContext.hostname_checks_common_name 为可写状态。

3.7 新版功能.

ssl.HAS_ECDH

OpenSSL 库是否具有对基于椭圆曲线的 Diffie-Hellman 密钥交换的内置支持。 此常量应当为真值,除非发布者明确地禁用了此功能。

3.3 新版功能.

ssl.HAS_SNI

OpenSSL 库是否具有对 服务器名称提示 扩展(在 RFC 6066 中定义)的内置支持。

3.2 新版功能.

ssl.HAS_NPN

OpenSSL 库是否具有对 应用层协议协商 中描述的 下一协议协商 的内置支持。 当此常量为真值时,你可以使用 SSLContext.set_npn_protocols() 方法来公告你想要支持的协议。

3.3 新版功能.

ssl.HAS_SSLv2

OpenSSL 库是否具有对 SSL 2.0 协议的内置支持。

3.7 新版功能.

ssl.HAS_SSLv3

OpenSSL 库是否具有对 SSL 3.0 协议的内置支持。

3.7 新版功能.

ssl.HAS_TLSv1

OpenSSL 库是否具有对 TLS 1.0 协议的内置支持。

3.7 新版功能.

ssl.HAS_TLSv1_1

OpenSSL 库是否具有对 TLS 1.1 协议的内置支持。

3.7 新版功能.

ssl.HAS_TLSv1_2

OpenSSL 库是否具有对 TLS 1.2 协议的内置支持。

3.7 新版功能.

ssl.HAS_TLSv1_3

OpenSSL 库是否具有对 TLS 1.3 协议的内置支持。

3.7 新版功能.

ssl.CHANNEL_BINDING_TYPES

受支持的 TLS 通道绑定类型组成的列表。 此列表中的字符串可被用作传给 SSLSocket.get_channel_binding() 的参数。

3.3 新版功能.

ssl.OPENSSL_VERSION

解释器所加载的 OpenSSL 库的版本字符串:

>>> ssl.OPENSSL_VERSION
'OpenSSL 1.0.2k  26 Jan 2017'

3.2 新版功能.

ssl.OPENSSL_VERSION_INFO

代表 OpenSSL 库的版本信息的五个整数所组成的元组:

>>> ssl.OPENSSL_VERSION_INFO
(1, 0, 2, 11, 15)

3.2 新版功能.

ssl.OPENSSL_VERSION_NUMBER

OpenSSL 库的原始版本号,以单个整数表示:

>>> ssl.OPENSSL_VERSION_NUMBER
268443839
>>> hex(ssl.OPENSSL_VERSION_NUMBER)
'0x100020bf'

3.2 新版功能.

ssl.ALERT_DESCRIPTION_HANDSHAKE_FAILURE
ssl.ALERT_DESCRIPTION_INTERNAL_ERROR
ALERT_DESCRIPTION_*

来自 RFC 5246 等文档的警报描述。 IANA TLS Alert Registry 中包含了这个列表及对定义其含义的 RFC 引用。

被用作 SSLContext.set_servername_callback() 中的回调函数的返回值。

3.4 新版功能.

class ssl.AlertDescription

ALERT_DESCRIPTION_* 常量的 enum.IntEnum 多项集。

3.6 新版功能.

Purpose.SERVER_AUTH

用于 create_default_context()SSLContext.load_default_certs() 的参数。表示上下文可用于验证网络服务器(因此,它将被用于创建客户端套接字)。

3.4 新版功能.

Purpose.CLIENT_AUTH

用于 create_default_context()SSLContext.load_default_certs() 的参数。 表示上下文可用于验证网络客户(因此,它将被用于创建服务器端套接字)。

3.4 新版功能.

class ssl.SSLErrorNumber

SSL_ERROR_* 常量的 enum.IntEnum 多项集。

3.6 新版功能.

class ssl.TLSVersion

SSLContext.maximum_versionSSLContext.minimum_version 中的 SSL 和 TLS 版本的 enum.IntEnum 多项集。

3.7 新版功能.

TLSVersion.MINIMUM_SUPPORTED
TLSVersion.MAXIMUM_SUPPORTED

受支持的最低和最高 SSL 或 TLS 版本。 这些常量被称为魔术常量。 它们的值并不反映可用的最低和最高 TLS/SSL 版本。

TLSVersion.SSLv3
TLSVersion.TLSv1
TLSVersion.TLSv1_1
TLSVersion.TLSv1_2
TLSVersion.TLSv1_3

SSL 3.0 至 TLS 1.3。

3.10 版后已移除: 所有 TLSVersion 成员,除 TLSVersion.TLSv1_2TLSVersion.TLSv1_3 之外均已废弃。

SSL 套接字

class ssl.SSLSocket(socket.socket)

SSL 套接字提供了 套接字对象 的下列方法:

  • accept()
  • bind()
  • close()
  • connect()
  • detach()
  • fileno()
  • getpeername(), getsockname()
  • getsockopt(), setsockopt()
  • gettimeout(), settimeout(), setblocking()
  • listen()
  • makefile()
  • recv(), recv_into() (but passing a non-zero flags argument is not allowed)
  • send(), sendall() (with the same limitation)
  • sendfile() (but os.sendfile will be used for plain-text sockets only, else send() will be used)
  • shutdown()

但是,由于 SSL(和 TLS)协议在 TCP 之上具有自己的框架,因此 SSL 套接字抽象在某些方面可能与常规的 OS 层级套接字存在差异。

SSLSocket 的实例必须使用 SSLContext.wrap_socket() 方法来创建。

在 3.5 版更改: 新增了 sendfile() 方法。

在 3.5 版更改: shutdown() 不会在每次接收或发送字节数据后重置套接字超时。 现在套接字超时为关闭的最大总持续时间。

3.6 版后已移除: 直接创建 SSLSocket 实例的做法已被弃用,请使用 SSLContext.wrap_socket() 来包装套接字。

在 3.7 版更改: SSLSocket 的实例必须使用 wrap_socket() 来创建。 在较早的版本中,直接创建实例是可能的。 但这从未被记入文档或是被正式支持。

在 3.10 版更改: Python 内部现在使用 SSL_read_exSSL_write_ex。这些函数支持读取和写入大于 2GB 的数据。写入零长数据不再出现违反协议的错误。

SSL 套接字还具有下列方法和属性:

SSLSocket.read(len=1024, buffer=None)

从 SSL 套接字读取至多 len 个字节的数据并将结果作为 bytes 实例返回。 如果指定了 buffer,则改为读取到缓冲区,并返回所读取的字节数。

如果套接字为 非阻塞型 则会引发 SSLWantReadErrorSSLWantWriteError 且读取将阻塞。

由于在任何时候重新协商都是可能的,因此调用 read() 也可能导致写入操作。

在 3.5 版更改: 套接字超时在每次接收或发送字节数据后不会再被重置。 现在套接字超时为读取至多 len 个字节数据的最大总持续时间。

3.6 版后已移除: 请使用 recv() 来代替 read()

SSLSocket.write(buf)

buf 写入到 SSL 套接字并返回所写入的字节数。 buf 参数必须为支持缓冲区接口的对象。

如果套接字为 非阻塞型 则会引发 SSLWantReadErrorSSLWantWriteError 且读取将阻塞。

由于在任何时候重新协商都是可能的,因此调用 write() 也可能导致读取操作。

在 3.5 版更改: 套接字超时在每次接收或发送字节数据后不会再被重置。 现在套接字超时为写入 buf 的最大总持续时间。

3.6 版后已移除: 请使用 send() 来代替 write()

注解

read()write() 方法是读写未加密的应用级数据,并将其解密/加密为带加密的线路级数据的低层级方法。 这些方法需要有激活的 SSL 连接,即握手已完成而 SSLSocket.unwrap() 尚未被调用。

通常你应当使用套接字 API 方法例如 recv()send() 来代替这些方法。

SSLSocket.do_handshake()

执行 SSL 设置握手。

在 3.4 版更改: 当套接字的 contextcheck_hostname 属性为真值时此握手方法还会执行 match_hostname()

在 3.5 版更改: 套接字超时在每次接收或发送字节数据时不会再被重置。 现在套接字超时为握手的最大总持续时间。

在 3.7 版更改: 主机名或 IP 地址会在握手期间由 OpenSSL 进行匹配。 函数 match_hostname() 将不再被使用。 在 OpenSSL 拒绝主机名和 IP 地址的情况下,握手将提前被中止并向对等方发送 TLS 警告消息。

SSLSocket.getpeercert(binary_form=False)

如果连接另一端的对等方没有证书,则返回 None。 如果 SSL 握手还未完成,则会引发 ValueError

如果 binary_form 形参为 False,并且从对等方接收到了证书,此方法将返回一个 dict 实例。 如果证书未通过验证,则字典将为空。 如果证书通过验证,它将返回由多个密钥组成的字典,其中包括 subject (证书颁发给的主体) 和 issuer (颁发证书的主体)。 如果证书包含一个 Subject Alternative Name 扩展的实例 (see RFC 3280),则字典中还将有一个 subjectAltName 键。

subjectissuer 字段都是包含在证书中相应字段的数据结构中给出的相对专有名称(RDN)序列的元组,每个 RDN 均为 name-value 对的序列。 这里是一个实际的示例:

{'issuer': ((('countryName', 'IL'),),
            (('organizationName', 'StartCom Ltd.'),),
            (('organizationalUnitName',
              'Secure Digital Certificate Signing'),),
            (('commonName',
              'StartCom Class 2 Primary Intermediate Server CA'),)),
 'notAfter': 'Nov 22 08:15:19 2013 GMT',
 'notBefore': 'Nov 21 03:09:52 2011 GMT',
 'serialNumber': '95F0',
 'subject': ((('description', '571208-SLe257oHY9fVQ07Z'),),
             (('countryName', 'US'),),
             (('stateOrProvinceName', 'California'),),
             (('localityName', 'San Francisco'),),
             (('organizationName', 'Electronic Frontier Foundation, Inc.'),),
             (('commonName', '*.eff.org'),),
             (('emailAddress', 'hostmaster@eff.org'),)),
 'subjectAltName': (('DNS', '*.eff.org'), ('DNS', 'eff.org')),
 'version': 3}

注解

要验证特定服务的证书,你可以使用 match_hostname() 函数。

如果 binary_form 形参为 True,并且提供了证书,此方法会将整个证书的 DER 编码形式作为字节序列返回,或者如果对等方未提供证书则返回 None。 对等方是否提供证书取决于 SSL 套接字的角色:

  • 对于客户端 SSL 套接字,服务器将总是提供证书,无论是否需要进行验证;
  • 对于服务器 SSL 套接字,客户端将仅在服务器要求时才提供证书;因此如果你使用了 CERT_NONE (而不是 CERT_OPTIONALCERT_REQUIRED) 则 getpeercert() 将返回 None

在 3.2 版更改: 返回的字典包括额外的条目例如 issuernotBefore

在 3.4 版更改: 如果握手未完成则会引发 ValueError。 返回的字典包括额外的 X509v3 扩展条目例如 crlDistributionPoints, caIssuersOCSP URI。

在 3.9 版更改: IPv6 地址字符串不再附带末尾换行符。

SSLSocket.cipher()

返回由三个值组成的元组,其中包含所使用的密码名称,定义其使用方式的 SSL 协议版本,以及所使用的加密比特位数。 如果尚未建立连接,则返回 None

SSLSocket.shared_ciphers()

返回在握手期间由客户端共享的密码列表。 所返回列表的每个条目都是由三个值组成的元组,其中包括密码名称,定义其使用方式的 SSL 协议版本,以及密码所使用的加密比特位数。 如果尚未建立连接或套接字为客户端套接字则 shared_ciphers() 将返回 None

3.5 新版功能.

SSLSocket.compression()

以字符串形式返回所使用的压缩算法,或者如果连接没有使用压缩则返回 None

如果高层级的协议支持自己的压缩机制,你可以使用 OP_NO_COMPRESSION 来禁用 SSL 层级的压缩。

3.3 新版功能.

SSLSocket.get_channel_binding(cb_type=’tls-unique’)

为当前连接获取字节串形式的通道绑定数据。 如果尚未连接或握手尚未完成则返回 None

cb_type 形参允许选择需要的通道绑定类型。 有效的通道绑定类型在 CHANNEL_BINDING_TYPES 列表中列出。 目前只支持由 RFC 5929 所定义的 ‘tls-unique’ 通道绑定。 如果请求了一个不受支持的通道绑定类型则将引发 ValueError

3.3 新版功能.

SSLSocket.selected_alpn_protocol()

返回在 TLS 握手期间所选择的协议。 如果 SSLContext.set_alpn_protocols() 未被调用,如果另一方不支持 ALPN,如果此套接字不支持任何客户端所用的协议,或者如果握手尚未发生,则将返回 None

3.5 新版功能.

SSLSocket.selected_npn_protocol()

返回在Return the higher-level protocol that was selected during the TLS/SSL 握手期间所选择的高层级协议。 如果 SSLContext.set_npn_protocols() 未被调用,或者如果另一方不支持 NPN,或者如果握手尚未发生,则将返回 None

3.3 新版功能.

3.10 版后已移除: NPN 已被 ALPN 取代。

SSLSocket.unwrap()

执行 SSL 关闭握手,这会从下层的套接字中移除 TLS 层,并返回下层的套接字对象。 这可被用来通过一个连接将加密操作转为非加密。 返回的套接字应当总是被用于同连接另一方的进一步通信,而不是原始的套接字。

SSLSocket.verify_client_post_handshake()

向一个 TLS 1.3 客户端请求握手后身份验证(PHA)。 只有在初始 TLS 握手之后且双方都启用了 PHA 的情况下才能为服务器端套接字的 TLS 1.3 连接启用 PHA。

此方法不会立即执行证书交换。 服务器端会在下一次写入事件期间发送 CertificateRequest 并期待客户端在下一次读取事件期间附带证书进行响应。

如果有任何前置条件未被满足(例如非 TLS 1.3,PHA 未启用),则会引发 SSLError

注解

仅在 OpenSSL 1.1.1 且 TLS 1.3 被启用时可用。 没有 TLS 1.3 支持,此方法将引发 NotImplementedError

3.8 新版功能.

SSLSocket.version()

以字符串形式返回由连接协商确定的实际 SSL 协议版本,或者如果未建立安全连接则返回 None。 在撰写本文档时,可能的返回值包括 "SSLv2", "SSLv3", "TLSv1", "TLSv1.1""TLSv1.2"。 最新的 OpenSSL 版本可能会定义更多的返回值。

3.5 新版功能.

SSLSocket.pending()

返回在连接上等待被读取的已解密字节数。

SSLSocket.context

此 SSL 套接字所联结的 SSLContext 对象。 如果 SSL 套接字是使用已弃用的 wrap_socket() 函数 (而非 SSLContext.wrap_socket()) 创建的,则这将是为此 SSL 套接字创建的自定义上下文对象。

3.2 新版功能.

SSLSocket.server_side

一个布尔值,对于服务器端套接字为 True 而对于客户端套接字则为 False

3.2 新版功能.

SSLSocket.server_hostname

服务器的主机名: str 类型,对于服务器端套接字或者如果构造器中未指定主机名则为 None

3.2 新版功能.

在 3.7 版更改: 现在该属性将始终为 ASCII 文本。 当 server_hostname 为一个国际化域名(IDN)时,该属性现在会保存为 A 标签形式 ("xn--pythn-mua.org") 而非 U 标签形式 ("pythön.org")。

SSLSocket.session

用于 SSL 连接的 SSLSession。 该会话将在执行 TLS 握手后对客户端和服务器端套接字可用。 对于客户端套接字该会话可以在调用 do_handshake() 之前被设置以重用一个会话。

3.6 新版功能.

SSLSocket.session_reused

3.6 新版功能.

SSL 上下文

3.2 新版功能.

SSL 上下文可保存各种比单独 SSL 连接寿命更长的数据,例如 SSL 配置选项,证书和私钥等。 它还可为服务器端套接字管理缓存,以加快来自相同客户端的重复连接。

class ssl.SSLContext(protocol=None)

创建一个新的 SSL 上下文。 你可以传入 protocol,它必须为此模块中定义的 PROTOCOL_* 常量之一。 该形参指定要使用哪个 SSL 协议版本。 通常,服务器会选择一个特定的协议版本,而客户端必须适应服务器的选择。 大多数版本都不能与其他版本互操作。 如果未指定,则默认值为 PROTOCOL_TLS;它提供了与其他版本的最大兼容性。

这个表显示了客户端(横向)的哪个版本能够连接服务器(纵向)的哪个版本。

客户端 / 服务器 SSLv2 SSLv3 TLS TLSv1 TLSv1.1 TLSv1.2
SSLv2
SSLv3
TLS (SSLv23)
TLSv1
TLSv1.1
TLSv1.2

参见

create_default_context()ssl 为特定目标选择安全设置。

在 3.6 版更改: 上下文会使用安全默认值来创建。 默认设置的选项有 OP_NO_COMPRESSION, OP_CIPHER_SERVER_PREFERENCE, OP_SINGLE_DH_USE, OP_SINGLE_ECDH_USE, OP_NO_SSLv2 (except for PROTOCOL_SSLv2) 和 OP_NO_SSLv3 (except for PROTOCOL_SSLv3)。 初始密码集列表只包含 HIGH 密码,不包含 NULL 密码和 MD5 密码 (PROTOCOL_SSLv2 除外)。

3.10 版后已移除: 不带协议参数的 SSLContext 已废弃。将来,上下文类会要求使用 PROTOCOL_TLS_CLIENTPROTOCOL_TLS_SERVER 协议。

在 3.10 版更改: 现在默认的密码套件只包含安全的 AES 和 ChaCha20 密码,具有前向保密性和安全级别2。禁止使用少于 2048 位的 RSA 和 DH 密钥以及少于 224 位的ECC密钥。 PROTOCOL_TLSPROTOCOL_TLS_CLIENTPROTOCOL_TLS_SERVER 至少使用 TLS 1.2 版本。

SSLContext 对象具有以下方法和属性:

SSLContext.cert_store_stats()

获取以字典表示的有关已加载的 X.509 证书数量,被标记为 CA 证书的 X.509 证书数量以及证书吊销列表的统计信息。

具有一个 CA 证书和一个其他证书的上下文示例:

>>> context.cert_store_stats()
{'crl': 0, 'x509_ca': 1, 'x509': 2}

3.4 新版功能.

SSLContext.load_cert_chain(certfile, keyfile=None, password=None)

加载一个私钥及对应的证书。 certfile 字符串必须为以 PEM 格式表示的单个文件路径,该文件中包含证书以及确立证书真实性所需的任意数量的 CA 证书。 如果存在 keyfile 字符串,它必须指向一个包含私钥的文件。 否则私钥也将从 certfile 中提取。

password 参数可以是一个函数,调用时将得到用于解密私钥的密码。 它在私钥被加密且需要密码时才会被调用。 它调用时将不带任何参数,并且应当返回一个字符串、字节串或字节数组。 如果返回值是一个字符串,在用它解密私钥之前它将以 UTF-8 进行编码。 或者也可以直接将字符串、字节串或字节数组值作为 password 参数提供。 如果私钥未被加密且不需要密码则它将被忽略。

如果未指定 password 参数且需要一个密码,将会使用 OpenSSL 内置的密码提示机制来交互式地提示用户输入密码。

如果私钥不能匹配证书则会引发 SSLError

在 3.3 版更改: 新增可选参数 password

SSLContext.load_default_certs(purpose=Purpose.SERVER_AUTH)

从默认位置加载一组默认的 “证书颁发机构” (CA) 证书。 在 Windows 上它将从 CAROOT 系统存储中加载 CA 证书。 在其他系统上它会调用 SSLContext.set_default_verify_paths()。 将来该方法也可能会从其他位置加载 CA 证书。

purpose 旗标指明要加载哪一类 CA 证书。 默认设置 Purpose.SERVER_AUTH 加载被标记且被信任用于 TLS Web 服务器验证(客户端套接字)的证书。 Purpose.CLIENT_AUTH 则加载用于在服务器端进行客户端证书验证的 CA 证书。

3.4 新版功能.

SSLContext.load_verify_locations(cafile=None, capath=None, cadata=None)

verify_mode 不为 CERT_NONE 时加载一组用于验证其他对等方证书的 “证书颁发机构” (CA) 证书。 必须至少指定 cafilecapath 中的一个。

此方法还可加载 PEM 或 DER 格式的证书吊销列表 (CRL),为此必须正确配置 SSLContext.verify_flags

如果存在 cafile 字符串,它应为 PEM 格式的级联 CA 证书文件的路径。

如果存在 capath 字符串,它应为包含多个 PEM 格式的 CA 证书的目录的路径,并遵循 OpenSSL 专属布局

如果存在 cadata 对象,它应为一个或多个 PEM 编码的证书的 ASCII 字符串或者 DER 编码的证书的 bytes-like object。 与 capath 一样 PEM 编码的证书之外的多余行会被忽略,但至少要有一个证书。

在 3.4 版更改: 新增可选参数 cadata

SSLContext.get_ca_certs(binary_form=False)

获取已离开法人 “证书颁发机构” (CA) 证书列表。 如果 binary_form 形参为 False 则每个列表条目都是一个类似于 SSLSocket.getpeercert() 输出的字典。 在其他情况下此方法将返回一个 DER 编码的证书的列表。 返回的列表不包含来自 capath 的证书,除非 SSL 连接请求并加载了一个证书。

注解

capath 目录中的证书不会被加载,除非它们已至少被使用过一次。

3.4 新版功能.

SSLContext.get_ciphers()

获取已启用密码的列表。 该列表将按密码的优先级排序。

示例:

>>> ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
>>> ctx.set_ciphers('ECDHE+AESGCM:!ECDSA')
>>> ctx.get_ciphers()
[{'aead': True,
  'alg_bits': 256,
  'auth': 'auth-rsa',
  'description': 'ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=RSA  '
                 'Enc=AESGCM(256) Mac=AEAD',
  'digest': None,
  'id': 50380848,
  'kea': 'kx-ecdhe',
  'name': 'ECDHE-RSA-AES256-GCM-SHA384',
  'protocol': 'TLSv1.2',
  'strength_bits': 256,
  'symmetric': 'aes-256-gcm'},
 {'aead': True,
  'alg_bits': 128,
  'auth': 'auth-rsa',
  'description': 'ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH     Au=RSA  '
                 'Enc=AESGCM(128) Mac=AEAD',
  'digest': None,
  'id': 50380847,
  'kea': 'kx-ecdhe',
  'name': 'ECDHE-RSA-AES128-GCM-SHA256',
  'protocol': 'TLSv1.2',
  'strength_bits': 128,
  'symmetric': 'aes-128-gcm'}]

3.6 新版功能.

SSLContext.set_default_verify_paths()

从构建 OpenSSL 库时定义的文件系统路径中加载一组默认的 “证书颁发机构” (CA) 证书。 不幸的是,没有一种简单的方式能知道此方法是否执行成功:如果未找到任何证书也不会返回错误。 不过,当 OpenSSL 库是作为操作系统的一部分被提供时,它的配置应当是正确的。

SSLContext.set_ciphers(ciphers)

为使用此上下文创建的套接字设置可用密码。 它应当为 OpenSSL 密码列表格式 的字符串。 如果没有可被选择的密码(由于编译时选项或其他配置禁止使用所指定的任何密码),则将引发 SSLError

注解

在连接后,SSL 套接字的 SSLSocket.cipher() 方法将给出当前所选择的密码。

TLS 1.3 密码套件不能通过 set_ciphers() 禁用。

SSLContext.set_alpn_protocols(protocols)

指定在 SSL/TLS 握手期间套接字应当通告的协议。 它应为由 ASCII 字符串组成的列表,例如 ['http/1.1', 'spdy/2'],按首选顺序排列。 协议的选择将在握手期间发生,并依据 RFC 7301 来执行。 在握手成功后,SSLSocket.selected_alpn_protocol() 方法将返回已达成一致的协议。

如果 HAS_ALPNFalse 则此方法将引发 NotImplementedError

3.5 新版功能.

SSLContext.set_npn_protocols(protocols)

指定在Specify which protocols the socket should advertise during the SSL/TLS 握手期间套接字应当通告的协议。 它应为由字符串组成的列表,例如 ['http/1.1', 'spdy/2'],按首选顺序排列。 协议的选择将在握手期间发生,并将依据 应用层协议协商 来执行。 在握手成功后,SSLSocket.selected_npn_protocol() 方法将返回已达成一致的协议。

如果 HAS_NPNFalse 则此方法将引发 NotImplementedError

3.3 新版功能.

3.10 版后已移除: NPN 已被 ALPN 取代。

SSLContext.sni_callback

注册一个回调函数,当 TLS 客户端指定了一个服务器名称提示时,该回调函数将在 SSL/TLS 服务器接收到 TLS Client Hello 握手消息后被调用。 服务器名称提示机制的定义见 RFC 6066 section 3 - Server Name Indication。

每个 SSLContext 只能设置一个回调。 如果 sni_callback 被设置为 None 则会禁用回调。 对该函数的后续调用将禁用之前注册的回调。

此回调函数将附带三个参数来调用;第一个参数是 ssl.SSLSocket,第二个参数是代表客户端准备与之通信的服务器的字符串 (或者如果 TLS Client Hello 不包含服务器名称则为 None) 而第三个参数是原来的 SSLContext。 服务器名称参数为文本形式。 对于国际化域名,服务器名称是一个 IDN A 标签 ("xn--pythn-mua.org")。

此回调的一个典型用法是将 ssl.SSLSocketSSLSocket.context 属性修改为一个 SSLContext 类型的新对象,该对象代表与服务器相匹配的证书链。

由于 TLS 连接处于早期协商阶段,因此仅能使用有限的方法和属性例如 SSLSocket.selected_alpn_protocol()SSLSocket.contextSSLSocket.getpeercert(), SSLSocket.getpeercert(), SSLSocket.cipher()SSLSocket.compress() 方法要求 TLS 连接已经过 TLS Client Hello 因而将既不包含返回有意义的值,也不能安全地调用它们。

sni_callback 函数必须返回 None 以允许 TLS 协商继续进行。 如果想要 TLS 失败,则可以返回常量 ALERT_DESCRIPTION_*。 其他返回值将导致 TLS 的致命错误 ALERT_DESCRIPTION_INTERNAL_ERROR

如果从 sni_callback 函数引发了异常,则 TLS 连接将终止并发出 TLS 致命警告消息 ALERT_DESCRIPTION_HANDSHAKE_FAILURE

如果 OpenSSL library 库在构建时定义了 OPENSSL_NO_TLSEXT 则此方法将返回 NotImplementedError

3.7 新版功能.

SSLContext.set_servername_callback(server_name_callback)

这是被保留用于向下兼容的旧式 API。 在可能的情况下,你应当改用 sni_callback。 给出的 server_name_callback 类似于 sni_callback,不同之处在于当服务器主机名是 IDN 编码的国际化域名时,server_name_callback 会接收到一个已编码的 U 标签 ("pythön.org")。

如果发生了服务器名称解码错误。 TLS 连接将终止并向客户端发出 ALERT_DESCRIPTION_INTERNAL_ERROR 最严重 TLS 警告消息。

3.4 新版功能.

SSLContext.load_dh_params(dhfile)

加载密钥生成参数用于 Diffie-Hellman (DH) 密钥交换。 使用 DH 密钥交换能以消耗(服务器和客户端的)计算资源为代价提升前向保密性。 dhfile 参数应当为指向一个包含 PEM 格式的 DH 形参的文件的路径。

此设置不会应用于客户端套接字。 你还可以使用 OP_SINGLE_DH_USE 选项来进一步提升安全性。

3.3 新版功能.

SSLContext.set_ecdh_curve(curve_name)

为基于椭圆曲线的 Elliptic Curve-based Diffie-Hellman (ECDH) 密钥交换设置曲线名称。 ECDH 显著快于常规 DH 同时据信同样安全。 curve_name 形参应为描述某个知名椭圆曲线的字符串,例如受到广泛支持的曲线 prime256v1

此设置不会应用于客户端套接字。 你还可以使用 OP_SINGLE_ECDH_USE 选项来进一步提升安全性。

如果 HAS_ECDHFalse 则此方法将不可用。

3.3 新版功能.

参见

SSLContext.wrap_socket(sock, server_side=False, do_handshake_on_connect=True, suppress_ragged_eofs=True, server_hostname=None, session=None)

包装一个现有的 Python 套接字 sock 并返回一个 SSLContext.sslsocket_class 的实例 (默认为 SSLSocket)。 返回的 SSL 套接字会绑定上下文、设置以及证书。 sock 必须是一个 SOCK_STREAM 套接字;其他套接字类型不被支持。

形参 server_side 是一个布尔值,它标明希望从该套接字获得服务器端行为还是客户端行为。

对于客户端套接字,上下文的构造会延迟执行;如果下层的套接字尚未连接,上下文的构造将在对套接字调用 connect() 之后执行。 对于服务器端套接字,如果套接字没有远端对等方,它会被视为一个监听套接字,并且服务器端 SSL 包装操作会在通过 accept() 方法所接受的客户端连接上自动执行。 此方法可能会引发 SSLError

在客户端连接上,可选形参 server_hostname 指定所要连接的服务的主机名。 这允许单个服务器托管具有单独证书的多个基于 SSL 的服务,很类似于 HTTP 虚拟主机。 如果 server_side 为真值则指定 server_hostname 将引发 ValueError

形参 do_handshake_on_connect 指明是否要在调用 socket.connect() 之后自动执行 SSL 握手,还是要通过发起调用 SSLSocket.do_handshake() 方法让应用程序显式地调用它。 显式地调用 SSLSocket.do_handshake() 可给予程序对握手中所涉及的套接字 I/O 阻塞行为的控制。

形参 suppress_ragged_eofs 指明 SSLSocket.recv() 方法应当如何从连接的另一端发送非预期的 EOF 信号。 如果指定为 True (默认值),它将返回正常的 EOF (空字节串对象) 来响应从下层套接字引发的非预期的 EOF 错误;如果指定为 False,它将向调用方引发异常。

session,参见 session

在 3.5 版更改: 总是允许传送 server_hostname,即使 OpenSSL 没有 SNI。

在 3.6 版更改: 增加了 session 参数。

在 3.7 版更改: 此方法返回 SSLContext.sslsocket_class 的实例而非硬编码的 SSLSocket

SSLContext.sslsocket_class

SSLContext.wrap_socket() 的返回类型,默认为 SSLSocket。 该属性可以在类实例上被重载以便返回自定义的 SSLSocket 的子类。

3.7 新版功能.

SSLContext.wrap_bio(incoming, outgoing, server_side=False, server_hostname=None, session=None)

包装 BIO 对象 incomingoutgoing 并返回一个 SSLContext.sslobject_class (默认为 SSLObject) 的实例。 SSL 例程将从 BIO 中读取输入数据并将数据写入到 outgoing BIO。

server_side, server_hostnamesession 形参具有与 SSLContext.wrap_socket() 中相同的含义。

在 3.6 版更改: 增加了 session 参数。

在 3.7 版更改: 此方法返回 SSLContext.sslobject_class 的实例则非硬编码的 SSLObject

SSLContext.sslobject_class

SSLContext.wrap_bio() 的返回类型,默认为 SSLObject。 该属性可以在类实例上被重载以便返回自定义的 SSLObject 的子类。

3.7 新版功能.

SSLContext.session_stats()

获取该上下文创建或管理的 SSL 会话的统计数据。返回一个字典,将每块信息 <https://www.openssl.org/docs/man1.1.1/ssl/SSL_CTX_sess_number.html>_ 映射到数字值。例如,下面是自该上下文创建以来会话缓存中的总点击率和失误率:

>>> stats = context.session_stats()
>>> stats['hits'], stats['misses']
(0, 0)

是否要将匹配 SSLSocket.do_handshake() 中对等方证书的主机名。 该上下文的 verify_mode 必须被设为 CERT_OPTIONALCERT_REQUIRED,并且你必须将 server_hostname 传给 wrap_socket() 以便匹配主机名。 启用主机名检查会自动将 verify_modeCERT_NONE 设为 CERT_REQUIRED。 只要启用了主机名检查就无法将其设回 CERT_NONEPROTOCOL_TLS_CLIENT 协议默认启用主机名检查。 对于其他协议,则必须显式地启用主机名检查。

示例:

import socket, ssl
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
context.verify_mode = ssl.CERT_REQUIRED
context.check_hostname = True
context.load_default_certs()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ssl_sock = context.wrap_socket(s, server_hostname='www.verisign.com')
ssl_sock.connect(('www.verisign.com', 443))

3.4 新版功能.

在 3.7 版更改: 现在当主机名检查被启用且 verify_modeCERT_NONEverify_mode 会自动更改为 CERT_REQUIRED。 在之前版本中同样的操作将失败并引发 ValueError

SSLContext.keylog_filename

每当生成或接收到密钥时,将 TLS 密钥写入到一个密钥日志文件。 密钥日志文件的设计仅适用于调试目的。 文件的格式由 NSS 指明并为许多流量分析工具例如 Wireshark 所使用。 日志文件会以追加模式打开。 写入操作会在线程之间同步,但不会在进程之间同步。

3.8 新版功能.

SSLContext.maximum_version

一个代表所支持的最高 TLS 版本的 TLSVersion 枚举成员。 该值默认为 TLSVersion.MAXIMUM_SUPPORTED。 这个属性对于 PROTOCOL_TLS, PROTOCOL_TLS_CLIENTPROTOCOL_TLS_SERVER 以外的其他协议来说都是只读的。

maximum_version, minimum_versionSSLContext.options 等属性都会影响上下文所支持的 SSL 和 TLS 版本。 这个实现不会阻止无效的组合。 例如一个 optionsOP_NO_TLSv1_2maximum_version 设为 TLSVersion.TLSv1_2 的上下文将无法建立 TLS 1.2 连接。

3.7 新版功能.

SSLContext.minimum_version

SSLContext.maximum_version 类似,区别在于它是所支持的最低版本或为 TLSVersion.MINIMUM_SUPPORTED

3.7 新版功能.

SSLContext.num_tickets

控制 TLS_PROTOCOL_SERVER 上下文的 TLS 1.3 会话凭据数量。这个设置不会影响 TLS 1.0 - 1.2 的连接。

3.8 新版功能.

SSLContext.options

一个代表此上下文中所启用的 SSL 选项集的整数。 默认值为 OP_ALL,但你也可以通过在选项间进行 OR 运算来指定其他选项例如 OP_NO_SSLv2

在 3.6 版更改: SSLContext.options 返回 Options 旗标:

>>> ssl.create_default_context().options  
<Options.OP_ALL|OP_NO_SSLv3|OP_NO_SSLv2|OP_NO_COMPRESSION: 2197947391>

3.7 版后已移除: 自 OpenSSL 1.1.0 起,所有 OP_NO_SSL*OP_NO_TLS* 选项已被弃用,请改用新的 SSLContext.minimum_versionSSLContext.maximum_version

SSLContext.post_handshake_auth

启用 TLS 1.3 握手后客户端身份验证。 握手后验证默认是被禁用的,服务器只能在初始握手期间请求 TLS 客户端证书。 当启用时,服务器可以在握手之后的任何时候请求 TLS 客户端证书。

当在客户端套接字上启用时,客户端会向服务器发信号说明它支持握手后身份验证。

当在服务器端套接字上启用时,SSLContext.verify_mode 也必须被设为 CERT_OPTIONALCERT_REQUIRED。 实际的客户端证书交换会被延迟直至 SSLSocket.verify_client_post_handshake() 被调用并执行了一些 I/O 操作后再进行。

3.8 新版功能.

SSLContext.protocol

构造上下文时所选择的协议版本。 这个属性是只读的。

SSLContext.hostname_checks_common_name

在没有目标替代名称扩展的情况下 check_hostname 是否要回退为验证证书的通用名称(默认为真值)。

3.7 新版功能.

在 3.10 版更改: 此旗标在 OpenSSL 1.1.1k 之前的版本上不起作用。 Python 3.8.9, 3.9.3, 和 3.10 包含了针对之前版本的变通处理。

SSLContext.security_level

整数值,代表上下文的 安全级别。 本属性只读。

3.10 新版功能.

SSLContext.verify_flags

证书验证操作的标志位。可以用“或”的方式组合在一起设置 VERIFY_CRL_CHECK_LEAF 这类标志。默认情况下,OpenSSL 既不需要也不验证证书吊销列表(CRL)。

3.4 新版功能.

在 3.6 版更改: SSLContext.verify_flags 返回 VerifyFlags 旗标:

>>> ssl.create_default_context().verify_flags  
<VerifyFlags.VERIFY_X509_TRUSTED_FIRST: 32768>

是否要尝试验证其他对等方的证书以及如果验证失败应采取何种行为。 该属性值必须为 CERT_NONE, CERT_OPTIONALCERT_REQUIRED 之一。

在 3.6 版更改: SSLContext.verify_mode 返回 VerifyMode 枚举:

>>> ssl.create_default_context().verify_mode
<VerifyMode.CERT_REQUIRED: 2>

证书

总的来说证书是公钥/私钥系统的一个组成部分。 在这个系统中,每 个 主体 (可能是一台机器、一个人或者一个组织) 都会分配到唯一的包含两部分的加密密钥。 一部分密钥是公开的,称为 公钥;另一部分密钥是保密的,称为 私钥。 这两个部分是互相关联的,就是说如果你用其中一个部分来加密一条消息,你将能用并且 只能 用另一个部分来解密它。

在一个证书中包含有两个主体的相关信息。 它包含 目标方 的名称和目标方的公钥。 它还包含由第二个主体 颁发方 所发布的声明:目标方的身份与他们所宣称的一致,包含的公钥也确实是目标方的公钥。 颁发方的声明使用颁发方的私钥进行签名,该私钥的内容只有颁发方自己才知道。 但是,任何人都可以找到颁发方的公钥,用它来解密这个声明,并将其与证书中的其他信息进行比较来验证颁发方声明的真实性。 证书还包含有关其有效期限的信息。 这被表示为两个字段,即 “notBefore” 和 “notAfter”。

在 Python 中应用证书时,客户端或服务器可以用证书来证明自己的身份。 还可以要求网络连接的另一方提供证书,提供的证书可以用于验证以满足客户端或服务器的验证要求。 如果验证失败,连接尝试可被设置为引发一个异常。 验证是由下层的 OpenSSL 框架来自动执行的;应用程序本身不必关注其内部的机制。 但是应用程序通常需要提供一组证书以允许此过程的发生。

Python 使用文件来包含证书。 它们应当采用 “PEM” 格式 (参见 RFC 1422),这是一种带有头部行和尾部行的 base-64 编码包装形式:

-----BEGIN CERTIFICATE-----
... (certificate in base64 PEM encoding) ...
-----END CERTIFICATE-----

证书链

包含证书的 Python 文件可以包含一系列的证书,有时被称为 证书链*。 这个证书链应当以 “作为” 客户端或服务器的主体的专属证书打头,然后是证书颁发方的证书,然后是 *上述 证书的颁发方的证书,证书链就这样不断上溯直到你得到一个 自签名 的证书,即具有相同目标方和颁发方的证书,有时也称为 根证书。 在证书文件中这些证书应当被拼接为一体。 例如,假设我们有一个包含三个证书的证书链,以我们的服务器证书打头,然后是为我们的服务器证书签名的证书颁发机构的证书,最后是为证书颁发机构的证书颁发证书的机构的根证书:

-----BEGIN CERTIFICATE-----
... (certificate for your server)...
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
... (the certificate for the CA)...
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
... (the root certificate for the CA's issuer)...
-----END CERTIFICATE-----

CA 证书

如果你想要求对连接的另一方的证书进行验证,你必须提供一个 “CA 证书” 文件,其中包含了你愿意信任的每个颁发方的证书链。 同样地,这个文件的内容就是这些证书链拼接在一起的结果。 为了进行验证,Python 将使用它在文件中找到的第一个匹配的证书链。 可以通过调用 SSLContext.load_default_certs() 来使用系统平台的证书文件,这可以由 create_default_context() 自动完成。

合并的密钥和证书

私钥往往与证书存储在相同的文件中;在此情况下,只需要将 certfile 形参传给 SSLContext.load_cert_chain()wrap_socket()。 如果私钥是与证书一起存储的,则它应当放在证书链的第一个证书之前:

-----BEGIN RSA PRIVATE KEY-----
... (private key in base64 encoding) ...
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
... (certificate in base64 PEM encoding) ...
-----END CERTIFICATE-----

自签名证书

如果你准备创建一个提供 SSL 加密连接服务的服务器,你需要为该服务获取一份证书。 有许多方式可以获取合适的证书,例如从证书颁发机构购买。 另一种常见做法是生成自签名证书。 生成自签名证书的最简单方式是使用 OpenSSL 软件包,代码如下所示:

% openssl req -new -x509 -days 365 -nodes -out cert.pem -keyout cert.pem
Generating a 1024 bit RSA private key
.......++++++
.............................++++++
writing new private key to 'cert.pem'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:MyState
Locality Name (eg, city) []:Some City
Organization Name (eg, company) [Internet Widgits Pty Ltd]:My Organization, Inc.
Organizational Unit Name (eg, section) []:My Group
Common Name (eg, YOUR name) []:myserver.mygroup.myorganization.com
Email Address []:ops@myserver.mygroup.myorganization.com
%

自签名证书的缺点在于它是它自身的根证书,因此不会存在于别人的已知(且信任的)根证书缓存当中。

例子

检测 SSL 支持

要检测一个 Python 安装版中是否带有 SSL 支持,用户代码应当使用以下例程:

try:
    import ssl
except ImportError:
    pass
else:
    ...  # do something that requires SSL support

客户端操作

这个例子创建了一个 SSL 上下文并使用客户端套接字的推荐安全设置,包括自动证书验证:

>>> context = ssl.create_default_context()

如果你喜欢自行调整安全设置,你可能需要从头创建一个上下文(但是请请注意避免不正确的设置):

>>> context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
>>> context.load_verify_locations("/etc/ssl/certs/ca-bundle.crt")

(这段代码假定你的操作系统将所有 CA 证书打包存放于 /etc/ssl/certs/ca-bundle.crt;如果不是这样,你将收到报错信息,必须修改此位置)

PROTOCOL_TLS_CLIENT 协议配置用于证书验证和主机名验证的上下文。 verify_mode 设为 CERT_REQUIREDcheck_hostname 设为 True。 所有其他协议都会使用不安全的默认值创建 SSL 上下文。

当你使用此上下文去连接服务器时,CERT_REQUIREDcheck_hostname 会验证服务器证书;它将确认服务器证书使用了某个 CA 证书进行签名,检查签名是否正确,并验证其他属性例如主机名的有效性和身份真实性:

>>> conn = context.wrap_socket(socket.socket(socket.AF_INET),
...                            server_hostname="www.python.org")
>>> conn.connect(("www.python.org", 443))

你可以随后获取该证书:

>>> cert = conn.getpeercert()

可视化检查显示证书能够证明目标服务 (即 HTTPS 主机 www.python.org) 的身份:

>>> pprint.pprint(cert)
{'OCSP': ('http://ocsp.digicert.com',),
 'caIssuers': ('http://cacerts.digicert.com/DigiCertSHA2ExtendedValidationServerCA.crt',),
 'crlDistributionPoints': ('http://crl3.digicert.com/sha2-ev-server-g1.crl',
                           'http://crl4.digicert.com/sha2-ev-server-g1.crl'),
 'issuer': ((('countryName', 'US'),),
            (('organizationName', 'DigiCert Inc'),),
            (('organizationalUnitName', 'www.digicert.com'),),
            (('commonName', 'DigiCert SHA2 Extended Validation Server CA'),)),
 'notAfter': 'Sep  9 12:00:00 2016 GMT',
 'notBefore': 'Sep  5 00:00:00 2014 GMT',
 'serialNumber': '01BB6F00122B177F36CAB49CEA8B6B26',
 'subject': ((('businessCategory', 'Private Organization'),),
             (('1.3.6.1.4.1.311.60.2.1.3', 'US'),),
             (('1.3.6.1.4.1.311.60.2.1.2', 'Delaware'),),
             (('serialNumber', '3359300'),),
             (('streetAddress', '16 Allen Rd'),),
             (('postalCode', '03894-4801'),),
             (('countryName', 'US'),),
             (('stateOrProvinceName', 'NH'),),
             (('localityName', 'Wolfeboro'),),
             (('organizationName', 'Python Software Foundation'),),
             (('commonName', 'www.python.org'),)),
 'subjectAltName': (('DNS', 'www.python.org'),
                    ('DNS', 'python.org'),
                    ('DNS', 'pypi.org'),
                    ('DNS', 'docs.python.org'),
                    ('DNS', 'testpypi.org'),
                    ('DNS', 'bugs.python.org'),
                    ('DNS', 'wiki.python.org'),
                    ('DNS', 'hg.python.org'),
                    ('DNS', 'mail.python.org'),
                    ('DNS', 'packaging.python.org'),
                    ('DNS', 'pythonhosted.org'),
                    ('DNS', 'www.pythonhosted.org'),
                    ('DNS', 'test.pythonhosted.org'),
                    ('DNS', 'us.pycon.org'),
                    ('DNS', 'id.python.org')),
 'version': 3}

现在 SSL 通道已建立并已验证了证书,你可以继续与服务器对话了:

>>> conn.sendall(b"HEAD / HTTP/1.0\r\nHost: linuxfr.org\r\n\r\n")
>>> pprint.pprint(conn.recv(1024).split(b"\r\n"))
[b'HTTP/1.1 200 OK',
 b'Date: Sat, 18 Oct 2014 18:27:20 GMT',
 b'Server: nginx',
 b'Content-Type: text/html; charset=utf-8',
 b'X-Frame-Options: SAMEORIGIN',
 b'Content-Length: 45679',
 b'Accept-Ranges: bytes',
 b'Via: 1.1 varnish',
 b'Age: 2188',
 b'X-Served-By: cache-lcy1134-LCY',
 b'X-Cache: HIT',
 b'X-Cache-Hits: 11',
 b'Vary: Cookie',
 b'Strict-Transport-Security: max-age=63072000; includeSubDomains',
 b'Connection: close',
 b'',
 b'']

服务器端操作

对于服务器操作,通常你需要在文件中存放服务器证书和私钥各一份。 你将首先创建一个包含密钥和证书的上下文,这样客户端就能检查你的身份真实性。 然后你将打开一个套接字,将其绑定到一个端口,在其上调用 listen(),并开始等待客户端连接:

import socket, ssl
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.load_cert_chain(certfile="mycertfile", keyfile="mykeyfile")
bindsocket = socket.socket()
bindsocket.bind(('myaddr.mydomain.com', 10023))
bindsocket.listen(5)

当有客户端连接时,你将在套接字上调用 accept() 以从另一端获取新的套接字,并使用上下文的 SSLContext.wrap_socket() 方法来为连接创建一个服务器端 SSL 套接字:

while True:
    newsocket, fromaddr = bindsocket.accept()
    connstream = context.wrap_socket(newsocket, server_side=True)
    try:
        deal_with_client(connstream)
    finally:
        connstream.shutdown(socket.SHUT_RDWR)
        connstream.close()

随后你将从 connstream 读取数据并对其进行处理,直至你结束与客户端的会话(或客户端结束与你的会话):

def deal_with_client(connstream):
    data = connstream.recv(1024)
    # empty data means the client is finished with us
    while data:
        if not do_something(connstream, data):
            # we'll assume do_something returns False
            # when we're finished with client
            break
        data = connstream.recv(1024)
    # finished with client

并返回至监听新的客户端连接(当然,真正的服务器应当会在单独的线程中处理每个客户端连接,或者将套接字设为 非阻塞模式 并使用事件循环)。

关于非阻塞套接字的说明

在非阻塞模式下 SSL 套接字的行为与常规套接字略有不同。 当使用非阻塞模式时,你需要注意下面这些事情:

  • 如果一个 I/O 操作会阻塞,大多数 SSLSocket 方法都将引发 SSLWantWriteErrorSSLWantReadError 而非 BlockingIOError。 如果有必要在下层套接字上执行读取操作将引发 SSLWantReadError,在下层套接字上执行写入操作则将引发 SSLWantWriteError。 请注意尝试 写入 到 SSL 套接字可能需要先从下层套接字 读取*,而尝试从 SSL 套接字 *读取 则可能需要先向下层套接字 写入

    在 3.5 版更改: 在较早的 Python 版本中,SSLSocket.send() 方法会返回零值而非引发 SSLWantWriteErrorSSLWantReadError

  • 调用 select() 将告诉你可以从 OS 层级的套接字读取(或向其写入),但这并不意味着在上面的 SSL 层有足够的数据。 例如,可能只有部分 SSL 帧已经到达。 因此,你必须准备好处理 SSLSocket.recv()SSLSocket.send() 失败的情况,并在再次调用 select() 之后重新尝试。

  • 相反地,由于 SSL 层具有自己的帧机制,一个 SSL 套接字可能仍有可读取的数据而 select() 并不知道这一点。 因此,你应当先调用 SSLSocket.recv() 取走所有潜在的可用数据,然后只在必要时对 select() 调用执行阻塞。

    (当然,类似的保留规则在使用其他原语例如 poll(),或 selectors 模块中的原语时也适用)

  • SSL 握手本身将是非阻塞的: SSLSocket.do_handshake() 方法必须不断重试直至其成功返回。 下面是一个使用 select() 来等待套接字就绪的简短例子:

    while True:
        try:
            sock.do_handshake()
            break
        except ssl.SSLWantReadError:
            select.select([sock], [], [])
        except ssl.SSLWantWriteError:
            select.select([], [sock], [])

参见

asyncio 模块支持 非阻塞 SSL 套接字 并提供了更高层级的 API。 它会使用 selectors 模块来轮询事件并处理 SSLWantWriteError, SSLWantReadErrorBlockingIOError 等异常。 它还会异步地执行 SSL 握手。

内存 BIO 支持

3.5 新版功能.

自从 SSL 模块在 Python 2.6 起被引入之后,SSLSocket 类提供了两个互相关联但彼此独立的功能分块:

  • SSL 协议处理
  • 网络 IO

网络 IO API 与 socket.socket 所提供的功能一致,SSLSocket 也是从那里继承而来的。 这允许 SSL 套接字被用作常规套接字的替代,使得向现有应用程序添加 SSL 支持变得非常容易。

将 SSL 协议处理与网络 IO 结合使用通常都能运行良好,但在某些情况下则不能。 此情况的一个例子是 async IO 框架,该框架要使用不同的 IO 多路复用模型而非 (基于就绪状态的) “在文件描述器上执行选择/轮询” 模型,该模型是 socket.socket 和内部 OpenSSL 套接字 IO 例程正常运行的假设前提。 这种情况在该模型效率不高的 Windows 平台上最为常见。 为此还提供了一个 SSLSocket 的简化形式,称为 SSLObject

class ssl.SSLObject

SSLSocket 的简化形式,表示一个不包含任何网络 IO 方法的 SSL 协议实例。 这个类通常由想要通过内存缓冲区为 SSL 实现异步 IO 的框架作者来使用。

这个类在低层级 SSL 对象上实现了一个接口,与 OpenSSL 所实现的类似。 此对象会捕获 SSL 连接的状态但其本身不提供任何网络 IO。 IO 需要通过单独的 “BIO” 对象来执行,该对象是 OpenSSL 的 IO 抽象层。

这个类没有公有构造器。 SSLObject 实例必须使用 wrap_bio() 方法来创建。 此方法将创建 SSLObject 实例并将其绑定到一个 BIO 对。 其中 incoming BIO 用来将数据从 Python 传递到 SSL 协议实例,而 outgoing BIO 用来进行数据反向传递。

可以使用以下方法:

  • context
  • server_side
  • server_hostname
  • session
  • session_reused
  • read()
  • write()
  • getpeercert()
  • selected_alpn_protocol()
  • selected_npn_protocol()
  • cipher()
  • shared_ciphers()
  • compression()
  • pending()
  • do_handshake()
  • verify_client_post_handshake()
  • unwrap()
  • get_channel_binding()
  • version()

SSLSocket 相比,此对象缺少下列特性:

  • 任何形式的网络 IO; recv()send() 仅对下层的 MemoryBIO 缓冲区执行读取和写入。
  • 不存在 do_handshake_on_connect 机制。 你必须总是手动调用 do_handshake() 来开始握手操作。
  • 不存在对 suppress_ragged_eofs 的处理。 所有违反协议的文件结束条件将通过 SSLEOFError 异常来报告。
  • 方法 unwrap() 的调用不返回任何东西,不会如 SSL 套接字那样返回下层的套接字。
  • server_name_callback 回调被传给 SSLContext.set_servername_callback() 时将获得一个 SSLObject 实例而非 SSLSocket 实例作为其第一个形参。

有关 SSLObject 用法的一些说明:

  • SSLObject 上的所有 IO 都是 非阻塞的。 这意味着例如 read() 在其需要比 incoming BIO 可用的更多数据时将会引发 SSLWantReadError
  • 不存在模块层级的 wrap_bio() 调用,就像 wrap_socket() 那样。 SSLObject 总是通过 SSLContext 来创建。

在 3.7 版更改: SSLObject 的实例必须使用 wrap_bio() 来创建。 在较早的版本中,直接创建实例是可能的。 但这从未被记入文档或是被正式支持。

SSLObject 会使用内存缓冲区与外部世界通信。 MemoryBIO 类提供了可被用于此目的的内存缓冲区。 它包装了一个 OpenSSL 内存 BIO (Basic IO) 对象:

class ssl.MemoryBIO

一个可被用来在 Python 和 SSL 协议实例之间传递数据的内存缓冲区。

  • pending

    返回当前存在于内存缓冲区的字节数。

  • eof

    一个表明内存 BIO 目前是否位于文件末尾的布尔值。

  • read(n=- 1)

    从内存缓冲区读取至多 n 个字节。 如果 n 未指定或为负值,则返回全部字节数据。

  • write(buf)

    将字节数据从 buf 写入到内存 BIO。 buf 参数必须为支持缓冲区协议的对象。

    返回值为写入的字节数,它总是与 buf 的长度相等。

  • write_eof()

    将一个 EOF 标记写入到内存 BIO。 在此方法被调用以后,再调用 write() 将是非法的。 属性 eof will 在缓冲区当前的所有数据都被读取之后将变为真值。

SSL 会话

3.6 新版功能.

class ssl.SSLSession

session 所使用的会话对象。

  • id
  • time
  • timeout
  • ticket_lifetime_hint
  • has_ticket

安全考量

最佳默认值

针对 客户端使用,如果你对于安全策略没有任何特殊要求,则强烈推荐你使用 create_default_context() 函数来创建你的 SSL 上下文。 它将加载系统的受信任 CA 证书,启用证书验证和主机名检查,并尝试合理地选择安全的协议和密码设置。

例如,以下演示了你应当如何使用 smtplib.SMTP 类来创建指向一个 SMTP 服务器的受信任且安全的连接:

>>> import ssl, smtplib
>>> smtp = smtplib.SMTP("mail.python.org", port=587)
>>> context = ssl.create_default_context()
>>> smtp.starttls(context=context)
(220, b'2.0.0 Ready to start TLS')

如果连接需要客户端证书,可使用 SSLContext.load_cert_chain() 来添加。

作为对比,如果你通过自行调用 SSLContext 构造器来创建 SSL 上下文,它默认将不会启用证书验证和主机名检查。 如果你这样做,请阅读下面的段落以达到良好的安全级别。

手动设置

验证证书

当直接调用 SSLContext 构造器时,默认会使用 CERT_NONE。 由于它不会验证对等方的身份真实性,因此是不安全的,特别是在客户端模式下,大多数时候你都希望能保证你所连接的服务器的身份真实性。 因此,当处于客户端模式时,强烈推荐使用 CERT_REQUIRED。 但是,光这样还不够;你还必须检查服务器证书,这可以通过调用 SSLSocket.getpeercert() 来获取并匹配目标服务。 对于许多协议和应用来说,服务可通过主机名来标识;在此情况下,可以使用 match_hostname() 函数。 这种通用检测会在 SSLContext.check_hostname 被启用时自动执行。

在 3.7 版更改: 主机名匹配现在是由 OpenSSL 来执行的。 Python 不会再使用 match_hostname()

在服务器模式下,如果你想要使用 SSL 层来验证客户端(而不是使用更高层级的验证机制),你也必须要指定 CERT_REQUIRED 并以类似方式检查客户端证书。

协议版本

SSL 版本 2 和 3 被认为是不安全的因而使用它们会有风险。 如果你想要客户端和服务器之间有最大的兼容性,推荐使用 PROTOCOL_TLS_CLIENTPROTOCOL_TLS_SERVER 作为协议版本。 SSLv2 和 SSLv3 默认会被禁用。

>>> client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
>>> client_context.minimum_version = ssl.TLSVersion.TLSv1_3
>>> client_context.maximum_version = ssl.TLSVersion.TLSv1_3

前面创建的 SSL 上下文将只允许 TLSv1.2 及更新版本(如果你的系统支持)的服务器连接。 PROTOCOL_TLS_CLIENT 默认会使用证书验证和主机名检查。 你必须将证书加载到上下文中。

密码选择

如果你有更高级的安全要求,也可以通过 SSLContext.set_ciphers() 方法在协商 SSL 会话时对所启用的加密进行微调。 从 Python 3.2.3 开始,ssl 默认会禁用某些较弱的加密,但你还可能希望进一步限制加密选项。 请确保仔细阅读 OpenSSL 文档中有关 加密列表格式 的部分。 如果你想要检查给定的加密列表启用了哪些加密,可以使用 SSLContext.get_ciphers() 或所在系统的 openssl ciphers 命令。

多进程

如果使用此模块作为多进程应用的一部分(例如使用 multiprocessingconcurrent.futures 模块),请注意 OpenSSL 的内部随机数字生成器并不能正确处理分支进程。 应用程序必须修改父进程的 PRNG 状态,如果它们要使用任何包含 os.fork() 的 SSL 特性的话。 任何对 RAND_add(), RAND_bytes()RAND_pseudo_bytes() 都可以 做到这一点。

TLS 1.3

3.7 新版功能.

TLS 1.3 协议的行为与低版本的 TLS/SSL 略有不同。某些 TLS 1.3 新特性还不可用。

  • TLS 1.3 使用一组不同的加密套件集。 默认情况下所有 AES-GCM 和 ChaCha20 加密套件都会被启用。 SSLContext.set_ciphers() 方法还不能启用或禁用任何 TLS 1.3 加密,但 SSLContext.get_ciphers() 会返回它们。
  • 会话凭据不再会作为初始握手的组成部分被发送而是以不同的方式来处理。 SSLSocket.sessionSSLSession 与 TLS 1.3 不兼容。
  • 客户端证书在初始握手期间也不会再被验证。 服务器可以在任何时候请求证书。 客户端会在它们从服务器发送或接收应用数据时处理证书请求。
  • 早期数据、延迟的 TLS 客户端证书请求、签名算法配置和密钥重生成等 TLS 1.3 特性尚未被支持。

参见

Class socket.socket

下层 socket 类的文档

SSL/TLS 高强度加密:概述

Apache HTTP Server文档介绍

RFC 1422: 因特网电子邮件的隐私加强:第二部分:基于证书的密钥管理

Steve Kent

RFC 4086: 确保安全的随机性要求

Donald E., Jeffrey I. Schiller

RFC 5280: 互联网 X.509 公钥基础架构证书和证书吊销列表 (CRL) 配置文件

D. Cooper

RFC 5246: 传输层安全性 (TLS) 协议版本 1.2

T. Dierks et. al.

RFC 6066: 传输层安全性 (TLS) 的扩展

D. Eastlake

IANA TLS: 传输层安全性 (TLS) 的参数

IANA

RFC 7525: 传输层安全性 (TLS) 和数据报传输层安全性 (DTLS) 的安全使用建议

IETF

Mozilla 的服务器端 TLS 建议

Mozilla

select —- 等待 I/O 完成

该模块提供了对 select()poll() 函数的访问,这些函数在大多数操作系统中是可用的。在 Solaris 及其衍生版本上可用 devpoll(),在 Linux 2.5+ 上可用 epoll(),在大多数 BSD 上可用 kqueue()。注意,在 Windows 上,本模块仅适用于套接字;在其他操作系统上,本模块也适用于其他文件类型(特别地,在 Unix 上也适用于管道)。本模块不能用于常规文件,不能检测出(自上次读取文件后)文件是否有新数据写入。

注解

selectors 模块是在 select 模块原型的基础上进行高级且高效的 I/O 复用。推荐用户改用 selectors 模块,除非用户希望对 OS 级的函数原型进行精确控制。

该模块定义以下内容:

exception select.error

一个被弃用的 OSError 的别名。

在 3.3 版更改: 根据 PEP 3151,这个类是 OSError 的别名。

select.devpoll()

(仅支持 Solaris 及其衍生版本)返回一个 /dev/poll 轮询对象,

devpoll() 对象与实例化时允许的文件描述符数量有关,如果在程序中降低了此数值,devpoll() 调用将失败。如果程序提高了此数值,devpoll() 可能会返回一个不完整的活动文件描述符列表。

新的文件描述符是 不可继承的。

3.3 新版功能.

在 3.4 版更改: 新的文件描述符现在是不可继承的。

select.epoll(sizehint=- 1, flags=0)

(仅支持 Linux 2.5.44 或更高版本)返回一个 edge poll 对象,该对象可作为 I/O 事件的边缘触发或水平触发接口。

sizehint 指示 epoll 预计需要注册的事件数。它必须为正数,或为 -1 以使用默认值。它仅在 epoll_create1() 不可用的旧系统上会被用到,其他情况下它没有任何作用(尽管仍会检查其值)。

flags 已经弃用且完全被忽略。但是,如果提供该值,则它必须是 0select.EPOLL_CLOEXEC,否则会抛出 OSError 异常。

epoll 对象支持上下文管理器:当在 with 语句中使用时,新建的文件描述符会在运行至语句块结束时自动关闭。

新的文件描述符是 不可继承的。

在 3.3 版更改: 增加了 flags 参数。

在 3.4 版更改: 增加了对 with 语句的支持。新的文件描述符现在是不可继承的。

3.4 版后已移除: flags 参数。现在默认采用 select.EPOLL_CLOEXEC 标志。使用 os.set_inheritable() 来让文件描述符可继承。

select.poll()

(部分操作系统不支持)返回一个 poll 对象,该对象支持注册和注销文件描述符,支持对描述符进行轮询以获取 I/O 事件。请参阅下方 Poll 对象 获取 poll 对象所支持的方法。

select.kqueue()

(仅支持 BSD)返回一个内核队列对象,请参阅下方 Kqueue 对象 获取 kqueue 对象所支持的方法。

新的文件描述符是 不可继承的。

在 3.4 版更改: 新的文件描述符现在是不可继承的。

select.kevent(ident, filter=KQ_FILTER_READ, flags=KQ_EV_ADD, fflags=0, data=0, udata=0)

(仅支持 BSD)返回一个内核事件对象,请参阅下方 Kevent 对象 获取 kevent 对象所支持的方法。

select.select(rlist, wlist, xlist[, timeout])

这是一个明白直观的 Unix select() 系统调用接口。 前三个参数是由‘可等待对象’组成的序列:可以是代表文件描述符的整数,或是带有名为 fileno() 的返回这样的整数的无形参方法的对象:

  • rlist:等待,直到可以开始读取
  • wlist:等待,直到可以开始写入
  • xlist:等待“异常情况”(请参阅当前系统的手册,以获取哪些情况称为异常情况)

允许空的可迭代对象,但是否接受三个空的可迭代对象则取决于具体平台。 (已知在 Unix 上可行但在 Windows 上不可行。) 可选的 timeout 参数以一个浮点数表示超时秒数。 当省略 timeout 参数时该函数将阻塞直到至少有一个文件描述符准备就绪。 超时值为零表示执行轮询且永不阻塞。

返回值是三个列表,包含已就绪对象,返回的三个列表是前三个参数的子集。当超时时间已到且没有文件描述符就绪时,返回三个空列表。

可迭代对象中可接受的对象类型有 Python 文件对象 (例如 sys.stdin 以及 open()os.popen() 所返回的对象),由 socket.socket() 返回的套接字对象等。 你也可以自定义一个 wrapper 类,只要它具有适当的 fileno() 方法(该方法要确实返回一个文件描述符,而不能只是一个随机整数)。

注解

Windows 上不接受文件对象,但接受套接字。在 Windows 上,底层的 select() 函数由 WinSock 库提供,且不处理不是源自 WinSock 的文件描述符。

在 3.5 版更改: 现在,当本函数被信号中断时,重试超时将从头开始计时,不会抛出 InterruptedError 异常。除非信号处理程序抛出异常(相关原理请参阅 PEP 475)。

select.PIPE_BUF

当一个管道已经被 select()poll() 或本模块中的某个接口报告为可写入时,可以在不阻塞该管道的情况下写入的最小字节数。它不适用于套接字等其他类型的文件类对象。

POSIX 上须保证该值不小于 512。

可用性: Unix

3.2 新版功能.

/dev/poll 轮询对象

Solaris 及其衍生版本具备 /dev/pollselect() 复杂度为 O(最高文件描述符),poll() 为 O(文件描述符数量),而 /dev/poll 为 O(活动的文件描述符)。

/dev/poll 的行为与标准 poll() 对象十分类似。

devpoll.close()

关闭轮询对象的文件描述符。

3.4 新版功能.

devpoll.closed

如果轮询对象已关闭,则返回 True

3.4 新版功能.

devpoll.fileno()

返回轮询对象的文件描述符对应的数字。

3.4 新版功能.

devpoll.register(fd[, eventmask])

在轮询对象中注册文件描述符。这样,将来调用 poll() 方法时将检查文件描述符是否有未处理的 I/O 事件。fd 可以是整数,也可以是带有 fileno() 方法的对象(该方法返回一个整数)。文件对象已经实现了 fileno(),因此它们也可以用作参数。

eventmask 是可选的位掩码,用于指定要检查的事件类型。这些常量与 poll() 对象所用的相同。本参数的默认值是常量 POLLINPOLLPRIPOLLOUT 的组合。

警告

注册已注册过的文件描述符不会报错,但是结果是不确定的。正确的操作是先注销或直接修改它。与 poll() 相比,这是一个重要的区别。

devpoll.modify(fd[, eventmask])

此方法先执行 unregister() 后执行 register()。直接执行此操作效率(稍微)高一些。

devpoll.unregister(fd)

删除轮询对象正在跟踪的某个文件描述符。与 register() 方法类似,fd 可以是整数,也可以是带有 fileno() 方法的对象(该方法返回一个整数)。

尝试删除从未注册过的文件描述符将被安全地忽略。

devpoll.poll([timeout])

轮询已注册的文件描述符的集合,并返回一个列表,列表可能为空,也可能有多个 (fd, event) 二元组,其中包含了要报告事件或错误的描述符。fd 是文件描述符,event 是一个位掩码,表示该描述符所报告的事件 —- POLLIN 表示可以读取,POLLOUT 表示该描述符可以写入,依此类推。空列表表示调用超时,没有任何文件描述符报告事件。如果指定了 timeout*,它将指定系统等待事件时,等待多长时间后返回(以毫秒为单位)。如果 *timeout 为空,-1 或 None,则本调用将阻塞,直到轮询对象发生事件为止。

在 3.5 版更改: 现在,当本函数被信号中断时,重试超时将从头开始计时,不会抛出 InterruptedError 异常。除非信号处理程序抛出异常(相关原理请参阅 PEP 475)。

边缘触发和水平触发的轮询 (epoll) 对象

https://linux.die.net/man/4/epoll

eventmask

常量 含意
EPOLLIN 可读
EPOLLOUT 可写
EPOLLPRI 紧急数据读取
EPOLLERR 在关联的文件描述符上有错误情况发生
EPOLLHUP 关联的文件描述符已挂起
EPOLLET 设置触发方式为边缘触发,默认为水平触发
EPOLLONESHOT 设置 one-shot 模式。触发一次事件后,该描述符会在轮询对象内部被禁用。
EPOLLEXCLUSIVE 当已关联的描述符发生事件时,仅唤醒一个 epoll 对象。默认(如果未设置此标志)是唤醒所有轮询该描述符的 epoll 对象。
EPOLLRDHUP 流套接字的对侧关闭了连接或关闭了写入到一半的连接。
EPOLLRDNORM 等同于 EPOLLIN
EPOLLRDBAND 可以读取优先数据带。
EPOLLWRNORM 等同于 EPOLLOUT
EPOLLWRBAND 可以写入优先级数据。
EPOLLMSG 忽略

3.6 新版功能: 增加了 EPOLLEXCLUSIVE。仅支持 Linux Kernel 4.5 或更高版本。

epoll.close()

关闭用于控制 epoll 对象的文件描述符。

epoll.closed

如果 epoll 对象已关闭,则返回 True

epoll.fileno()

返回文件描述符对应的数字,该描述符用于控制 epoll 对象。

epoll.fromfd(fd)

根据给定的文件描述符创建 epoll 对象。

epoll.register(fd[, eventmask])

在 epoll 对象中注册一个文件描述符。

epoll.modify(fd, eventmask)

修改一个已注册的文件描述符。

epoll.unregister(fd)

从 epoll 对象中删除一个已注册的文件描述符。

在 3.9 版更改: 此方法不会再忽略 EBADF 错误。

epoll.poll(timeout=None, maxevents=- 1)

等待事件发生,timeout 是浮点数,单位为秒。

在 3.5 版更改: 现在,当本函数被信号中断时,重试超时将从头开始计时,不会抛出 InterruptedError 异常。除非信号处理程序抛出异常(相关原理请参阅 PEP 475)。

Poll 对象

大多数 Unix 系统支持 poll() 系统调用,为服务器提供了更好的可伸缩性,使服务器可以同时服务于大量客户端。poll() 的伸缩性更好,因为该调用内部仅列出所关注的文件描述符,而 select() 会构造一个 bitmap,在其中将所关注的描述符所对应的 bit 打开,然后重新遍历整个 bitmap。因此 select() 复杂度是 O(最高文件描述符),而 poll() 是 O(文件描述符数量)。

poll.register(fd[, eventmask])

在轮询对象中注册文件描述符。这样,将来调用 poll() 方法时将检查文件描述符是否有未处理的 I/O 事件。fd 可以是整数,也可以是带有 fileno() 方法的对象(该方法返回一个整数)。文件对象已经实现了 fileno(),因此它们也可以用作参数。

eventmask 是可选的位掩码,用于指定要检查的事件类型,它可以是常量 POLLINPOLLPRIPOLLOUT 的组合,如下表所述。如果未指定本参数,默认将会检查所有 3 种类型的事件。

常量 含意
POLLIN 有要读取的数据
POLLPRI 有紧急数据需要读取
POLLOUT 准备输出:写不会阻塞
POLLERR 某种错误条件
POLLHUP 挂起
POLLRDHUP 流套接字的对侧关闭了连接,或关闭了写入到一半的连接
POLLNVAL 无效的请求:描述符未打开

注册已注册过的文件描述符不会报错,且等同于只注册一次该描述符。

poll.modify(fd, eventmask)

修改一个已注册的文件描述符,等同于 register(fd, eventmask)。尝试修改未注册的文件描述符会抛出 OSError 异常,错误码为 ENOENT

poll.unregister(fd)

删除轮询对象正在跟踪的某个文件描述符。与 register() 方法类似,fd 可以是整数,也可以是带有 fileno() 方法的对象(该方法返回一个整数)。

尝试删除从未注册过的文件描述符会抛出 KeyError 异常。

poll.poll([timeout])

轮询已注册的文件描述符的集合,并返回一个列表,列表可能为空,也可能有多个 (fd, event) 二元组,其中包含了要报告事件或错误的描述符。fd 是文件描述符,event 是一个位掩码,表示该描述符所报告的事件 —- POLLIN 表示可以读取,POLLOUT 表示该描述符可以写入,依此类推。空列表表示调用超时,没有任何文件描述符报告事件。如果指定了 timeout*,它将指定系统等待事件时,等待多长时间后返回(以毫秒为单位)。如果 *timeout 为空、负数 或 None,则本调用将阻塞,直到轮询对象发生事件为止。

在 3.5 版更改: 现在,当本函数被信号中断时,重试超时将从头开始计时,不会抛出 InterruptedError 异常。除非信号处理程序抛出异常(相关原理请参阅 PEP 475)。

Kqueue 对象

kqueue.close()

关闭用于控制 kqueue 对象的文件描述符。

kqueue.closed

如果 kqueue 对象已关闭,则返回 True

kqueue.fileno()

返回文件描述符对应的数字,该描述符用于控制 epoll 对象。

kqueue.fromfd(fd)

根据给定的文件描述符创建 kqueue 对象。

kqueue.control(changelist, max_events[, timeout]) → eventlist

Kevent 的低级接口

  • changelist 必须是一个可迭代对象,迭代出 kevent 对象,否则置为 None
  • max_events 必须是 0 或一个正整数。
  • timeout 单位为秒(一般为浮点数),默认为 None,即永不超时。

在 3.5 版更改: 现在,当本函数被信号中断时,重试超时将从头开始计时,不会抛出 InterruptedError 异常。除非信号处理程序抛出异常(相关原理请参阅 PEP 475)。

Kevent 对象

https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2

kevent.ident

用于区分事件的标识值。其解释取决于筛选器,但该值通常是文件描述符。在构造函数中,该标识值可以是整数或带有 fileno() 方法的对象。kevent 在内部存储整数。

kevent.filter

内核筛选器的名称。

常量 含意
KQ_FILTER_READ 获取描述符,并在有数据可读时返回
KQ_FILTER_WRITE 获取描述符,并在有数据可写时返回
KQ_FILTER_AIO AIO 请求
KQ_FILTER_VNODE 当在 fflag 中监视的一个或多个请求事件发生时返回
KQ_FILTER_PROC 监视进程ID上的事件
KQ_FILTER_NETDEV Watch for events on a network device [not available on macOS]
KQ_FILTER_SIGNAL 每当监视的信号传递到进程时返回
KQ_FILTER_TIMER 建立一个任意的计时器
kevent.flags

筛选器操作。

常量 含意
KQ_EV_ADD 添加或修改事件
KQ_EV_DELETE 从队列中删除事件
KQ_EV_ENABLE Permitscontrol() 返回事件
KQ_EV_DISABLE 禁用事件
KQ_EV_ONESHOT 在第一次发生后删除事件
KQ_EV_CLEAR 检索事件后重置状态
KQ_EV_SYSFLAGS 内部事件
KQ_EV_FLAG1 内部事件
KQ_EV_EOF 筛选特定EOF条件
KQ_EV_ERROR 请参阅返回值
kevent.fflags

筛选特定标志。

KQ_FILTER_READKQ_FILTER_WRITE 筛选标志:

常量 含意
KQ_NOTE_LOWAT 套接字缓冲区的低水线

KQ_FILTER_VNODE 筛选标志:

常量 含意
KQ_NOTE_DELETE 已调用 unlink()
KQ_NOTE_WRITE 发生写入
KQ_NOTE_EXTEND 文件已扩展
KQ_NOTE_ATTRIB 属性已更改
KQ_NOTE_LINK 链接计数已更改
KQ_NOTE_RENAME 文件已重命名
KQ_NOTE_REVOKE 对文件的访问权限已被撤销

KQ_FILTER_PROC filter flags:

常量 含意
KQ_NOTE_EXIT 进程已退出
KQ_NOTE_FORK 该进程调用了 fork()
KQ_NOTE_EXEC 进程已执行新进程
KQ_NOTE_PCTRLMASK 内部筛选器标志
KQ_NOTE_PDATAMASK 内部筛选器标志
KQ_NOTE_TRACK fork() 执行进程
KQ_NOTE_CHILD NOTE_TRACK 的子进程上返回
KQ_NOTE_TRACKERR 无法附加到子对象

KQ_FILTER_NETDEV filter flags (not available on macOS):

常量 含意
KQ_NOTE_LINKUP 链接已建立
KQ_NOTE_LINKDOWN 链接已断开
KQ_NOTE_LINKINV 链接状态无效
kevent.data

筛选特定数据。

kevent.udata

用户自定义值。

selectors —- 高级 I/O 复用库

3.4 新版功能.

源码: Lib/selectors.py


概述

此模块允许高层级且高效率的 I/O 复用,它建立在 select 模块原型的基础之上。 推荐用户改用此模块,除非他们希望对所使用的 OS 层级原型进行精确控制。

它定义了一个 BaseSelector 抽象基类,以及多个实际的实现 (KqueueSelector, EpollSelector…),它们可被用于在多个文件对象上等待 I/O 就绪通知。 在下文中,”文件对象” 是指任何具有 fileno() 方法的对象,或是一个原始文件描述器。

DefaultSelector 是一个指向当前平台上可用的最高效实现的别名:这应为大多数用户的默认选择。

注解

受支持的文件对象类型取决于具体平台:在 Windows 上,支持套接字但不支持管道,而在 Unix 上两者均受支持(某些其他类型也可能受支持,例如 fifo 或特殊文件设备等)。

类的层次结构:

BaseSelector
+-- SelectSelector
+-- PollSelector
+-- EpollSelector
+-- DevpollSelector
+-- KqueueSelector

下文中,events 一个位掩码,指明哪些 I/O 事件要在给定的文件对象上执行等待。 它可以是以下模块级常量的组合:

常量 含意
EVENT_READ 可读
EVENT_WRITE 可写

class selectors.SelectorKey

SelectorKey 是一个 namedtuple,用来将文件对象关联到其下层的文件描述器、选定事件掩码和附加数据等。 它会被某些 BaseSelector 方法返回。

  • fileobj

    已注册的文件对象。

  • fd

    下层的文件描述器。

  • events

    必须在此文件对象上被等待的事件。

  • data

    可选的关联到此文件对象的不透明数据:例如,这可被用来存储各个客户端的会话 ID。

class selectors.BaseSelector

一个 BaseSelector,用来在多个文件对象上等待 I/O 事件就绪。 它支持文件流注册、注销,以及在这些流上等待 I/O 事件的方法。 它是一个抽象基类,因此不能被实例化。 请改用 DefaultSelector,或者 SelectSelector, KqueueSelector 等。 如果你想要指明使用某个实现,并且你的平台支持它的话。 BaseSelector 及其具体实现支持 context manager 协议。

  • abstractmethod register(fileobj, events, data=None)

    注册一个用于选择的文件对象,在其上监视 I/O 事件。

    fileobj 是要监视的文件对象。 它可以是整数形式的文件描述符或者具有 fileno() 方法的对象。 events 是要监视的事件的位掩码。 data 是一个不透明对象。

    这将返回一个新的 SelectorKey 实例,或在出现无效事件掩码或文件描述符时引发 ValueError,或在文件对象已被注册时引发 KeyError

  • abstractmethod unregister(fileobj)

    注销对一个文件对象的选择,移除对它的监视。 在文件对象被关闭之前应当先将其注销。

    fileobj 必须是之前已注册的文件对象。

    这将返回已关联的 SelectorKey 实例,或者如果 fileobj 未注册则会引发 KeyError。 It will raise ValueError 如果 fileobj 无效(例如它没有 fileno() 方法或其 fileno() 方法返回无效值)。

  • modify(fileobj, events, data=None)

    更改已注册文件对象所监视的事件或所附带的数据。

    这等价于 BaseSelector.unregister(fileobj)()BaseSelector.register(fileobj, events, data)(),区别在于它可以被更高效地实现。

    这将返回一个新的 SelectorKey 实例,或在出现无效事件掩码或文件描述符时引发 ValueError,或在文件对象未被注册时引发 KeyError

  • abstractmethod select(timeout=None)

    等待直到有已注册的文件对象就绪,或是超过时限。

    如果 timeout > 0,这指定以秒数表示的最大等待时间。 如果 timeout <= 0,调用将不会阻塞,并将报告当前就绪的文件对象。 如果 timeoutNone,调用将阻塞直到某个被监视的文件对象就绪。

    这将返回由 (key, events) 元组构成的列表,每项各表示一个就绪的文件对象。

    key 是对应于就绪文件对象的 SelectorKey 实例。 events 是在此文件对象上等待的事件位掩码。

    注解

    如果当前进程收到一个信号,此方法可在任何文件对象就绪之前或超出时限时返回:在此情况下,将返回一个空列表。

    在 3.5 版更改: 现在当被某个信号中断时,如果信号处理程序没有引发异常,选择器会用重新计算的超时值进行重试(请查看 PEP 475 其理由),而不是在超时之前返回空的事件列表。

  • close()

    关闭选择器。

    必须调用这个方法以确保下层资源会被释放。 选择器被关闭后将不可再使用。

  • get_key(fileobj)

    返回关联到某个已注册文件对象的键。

    此方法将返回关联到文件对象的 SelectorKey 实例,或在文件对象未注册时引发 KeyError

  • abstractmethod get_map()

    返回从文件对象到选择器键的映射。

    这将返回一个将已注册文件对象映射到与其相关联的 SelectorKey 实例的 Mapping 实例。

class selectors.DefaultSelector

默认的选择器类,使用当前平台上可用的最高效实现。 这应为大多数用户的默认选择。

class selectors.SelectSelector

基于 select.select() 的选择器。

class selectors.PollSelector

基于 select.poll() 的选择器。

class selectors.EpollSelector

基于 select.epoll() 的选择器。

  • fileno()

    此方法将返回由下层 select.epoll() 对象所使用的文件描述符。

class selectors.DevpollSelector

基于 select.devpoll() 的选择器。

  • fileno()

    此方法将返回由下层 select.devpoll() 对象所使用的文件描述符。

3.5 新版功能.

class selectors.KqueueSelector

基于 select.kqueue() 的选择器。

  • fileno()

    此方法将返回由下层 select.kqueue() 对象所使用的文件描述符。

例子

下面是一个简单的回显服务器实现:

import selectors
import socket
sel = selectors.DefaultSelector()
def accept(sock, mask):
    conn, addr = sock.accept()  # Should be ready
    print('accepted', conn, 'from', addr)
    conn.setblocking(False)
    sel.register(conn, selectors.EVENT_READ, read)
def read(conn, mask):
    data = conn.recv(1000)  # Should be ready
    if data:
        print('echoing', repr(data), 'to', conn)
        conn.send(data)  # Hope it won't block
    else:
        print('closing', conn)
        sel.unregister(conn)
        conn.close()
sock = socket.socket()
sock.bind(('localhost', 1234))
sock.listen(100)
sock.setblocking(False)
sel.register(sock, selectors.EVENT_READ, accept)
while True:
    events = sel.select()
    for key, mask in events:
        callback = key.data
        callback(key.fileobj, mask)

asyncore —- 异步套接字处理器

源码: Lib/asyncore.py

3.6 版后已移除: 请使用 asyncio 替代。


注解

该模块仅为提供向后兼容。我们推荐在新代码中使用 asyncio

该模块提供用于编写异步套接字服务客户端与服务端的基础构件。

只有两种方法让单个处理器上的程序“同一时间完成不止一件事”。 多线程编程是最简单和最流行的方法,但是还有另一种非常不同的技术,它可以让你拥有多线程的几乎所有优点,而无需实际使用多线程。 它仅仅在你的程序主要受 I/O 限制时有用,那么。 如果你的程序受处理器限制,那么先发制人的预定线程可能就是你真正需要的。 但是,网络服务器很少受处理器限制。

如果你的操作系统在其 I/O 库中支持 select() 系统调用(几乎所有操作系统),那么你可以使用它来同时处理多个通信通道;在 I/O 正在“后台”时进行其他工作。 虽然这种策略看起来很奇怪和复杂,特别是起初,它在很多方面比多线程编程更容易理解和控制。 asyncore 模块为您解决了许多难题,使得构建复杂的高性能网络服务器和客户端的任务变得轻而易举。 对于“会话”应用程序和协议,伴侣 asynchat 模块是非常宝贵的。

这两个模块背后的基本思想是创建一个或多个网络 通道 ,类的实例 asyncore.dispatcherasynchat.async_chat 。 创建通道会将它们添加到全局映射中,如果你不为它提供自己的 映射 ,则由 loop() 函数使用。

一旦创建了初始通道,调用 loop() 函数将激活通道服务,该服务将一直持续到最后一个通道(包括在异步服务期间已添加到映射中的任何通道)关闭。

asyncore.loop([timeout[, use_poll[, map[, count]]]])

进入一个轮询循环,其在循环计数超出或所有打开的通道关闭后终止。 所有参数都是可选的。 count 形参默认为 None ,导致循环仅在所有通道关闭时终止。 timeout 形参为适当的 select()poll() 调用设置超时参数,以秒为单位; 默认值为30秒。 use_poll 形参,如果为 True ,则表示 poll() 应优先使用 select() (默认为False)。

map 形参是一个条目为所监视通道的字典。 当通道关闭时它们会被从映射中删除。 如果省略 map,则会使用一个全局映射。 通道 (asyncore.dispatcher, asynchat.async_chat 及其子类的实例) 可以在映射中任意混合。

class asyncore.dispatcher

dispatcher 类是对低层级套接字对象的轻量包装器。 要让它更有用处,可以从异步循环调用一些事件处理方法。 在其他方面,它可以被当作是普通的非阻塞型套接字对象。

在特定时间或特定连接状态下触发的低层级事件可通知异步循环发生了特定的高层级事件。 例如,如果我们请求了一个套接字以连接到另一台主机,我们会在套接字首次变得可写时得知连接已建立(在此刻你将知道可以向其写入并预期能够成功)。 包含的高层级事件有:

事件 描述
handle_connect() 由首个读取或写入事件引起
handle_close() 由不带可用数据的读取事件引起
handle_accepted() 由在监听套接字上的读取事件引起

在异步处理过程中,每个已映射通道的 readable()writable() 方法会被用来确定是否要将通道的套接字添加到已执行 select()poll() 用于读取和写入事件的通道列表中。

因此,通道事件的集合要大于基本套接字事件。 可以在你的子类中被重载的全部方法集合如下:

  • handle_read()

    当异步循环检测到通道的套接字上的 read() 调用将要成功时会被调用。

  • handle_write()

    当异步循环检测到一个可写套接字可以被写入时会被调用。 通常此方法将实现必要的缓冲机制以保证运行效率。 例如:

    def handle_write(self):
        sent = self.send(self.buffer)
        self.buffer = self.buffer[sent:]
  • handle_expt()

    当一个套接字连接存在带外(OOB)数据时会被调用。 这几乎从来不会发生,因为 OOB 虽然受支持但很少被使用。

  • handle_connect()

    当活动打开方的套接字实际建立连接时会被调用。 可能会发送一条“欢迎”消息,或者向远程端点发起协议协商等。

  • handle_close()

    当套接字关闭时会被调用。

  • handle_error()

    当一个异常被引发并且未获得其他处理时会被调用。 默认版本将打印精简的回溯信息。

  • handle_accept()

    当可以与发起对本地端点的 connect() 调用的新远程端点建立连接时会在侦听通道(被动打开方)上被调用。 在 3.2 版中已被弃用;请改用 handle_accepted()

    3.2 版后已移除.

  • handle_accepted(sock, addr)

    当与发起对本地端点的 connect() 调用的新远程端点已建立连接时会在侦听通道(被动打开方)上被调用。 sock 是可被用于在连接上发送和接收数据的 新建 套接字对象,而 addr 是绑定到连接另一端的套接字的地址。

    3.2 新版功能.

  • readable()

    每次在异步循环之外被调用以确定是否应当将一个通道的套接字添加到可能在其上发生读取事件的列表中。 默认方法会简单地返回 True,表示在默认情况下,所有通道都希望能读取事件。

  • writable()

    每次在异步循环之外被调用以确定是否应当将一个通道的套接字添加到可能在其上发生写入事件的列表中。 默认方法会简单地返回 True,表示在默认情况下,所有通道都希望能写入事件。

此外,每个通道都委托或扩展了许多套接字方法。 它们大部分都与其套接字的对应方法几乎一样。

  • create_socket(family=socket.AF_INET, type=socket.SOCK_STREAM)

    这与普通套接字的创建相同,并会使用同样的创建选项。

    在 3.3 版更改: familytype 参数可以被省略。

  • connect(address)

    与普通套接字对象一样,address 是一个元组,它的第一个元素是要连接的主机,第二个元素是端口号。

  • send(data)

    data 发送到套接字的远程端点。

  • recv(buffer_size)

    从套接字的远程端点读取至多 buffer_size 个字节。 读到空字节串表明通道已从另一端被关闭。

    请注意 recv() 可能会引发 BlockingIOError,即使 select.select()select.poll() 报告套接字已准备好被读取。

  • listen(backlog)

    侦听与套接字的连接。 backlog 参数指明排入连接队列的最大数量且至少应为 1;最大值取决于具体系统(通常为 5)。

  • bind(address)

    将套接字绑定到 address。 套接字必须尚未被绑定。 要将套接字标记为可重用的 (设置 SO_REUSEADDR 选项),请调用 dispatcher 对象的 set_reuse_addr() 方法。

  • accept()

    接受一个连接。 此套接字必须绑定到一个地址上并且侦听连接。 返回值可以是 None 或一个 (conn, address) 对,其中 conn 是一个可用来在此连接上发送和接收数据的 新的 套接字对象,而 address 是绑定到连接另一端套接字的地址。 当返回 None 时意味着连接没有建立,在此情况下服务器应当忽略此事件并继续侦听后续的入站连接。

  • close()

    关闭套接字。 在此套接字对象上的后续操作都将失败。 远程端点将不再接收任何数据(在排入队列的数据被清空之后)。 当套接字被垃圾回收时会自动关闭。

class asyncore.dispatcher_with_send

dispatcher 的一个添加了简单缓冲输出功能的子类,适用于简单客户端。 对于更复杂的用法请使用 asynchat.async_chat

class asyncore.file_dispatcher

file_dispatcher 接受一个文件描述符或 file object 以及一个可选的 map 参数,并对其进行包装以配合 poll()loop() 函数使用。 如果提供一个文件对象或任何具有 fileno() 方法的对象,其方法将被调用并传递给 file_wrapper 构造器。

可用性: Unix。

class asyncore.file_wrapper

file_wrapper 接受一个整数形式的文件描述符并调用 os.dup() 来复制其句柄,以便原始句柄可以独立于 file_wrapper 被关闭。 这个类实现了足够的方法来模拟套接字以供 file_dispatcher 类使用。

可用性: Unix。

asyncore 示例基本 HTTP 客户端

下面是一个非常基本的 HTTP 客户端,它使用了 dispatcher 类来实现套接字处理:

import asyncore
class HTTPClient(asyncore.dispatcher):
    def __init__(self, host, path):
        asyncore.dispatcher.__init__(self)
        self.create_socket()
        self.connect( (host, 80) )
        self.buffer = bytes('GET %s HTTP/1.0\r\nHost: %s\r\n\r\n' %
                            (path, host), 'ascii')
    def handle_connect(self):
        pass
    def handle_close(self):
        self.close()
    def handle_read(self):
        print(self.recv(8192))
    def writable(self):
        return (len(self.buffer) > 0)
    def handle_write(self):
        sent = self.send(self.buffer)
        self.buffer = self.buffer[sent:]
client = HTTPClient('www.python.org', '/')
asyncore.loop()

asyncore 示例基本回显服务器

下面是一个基本的回显服务器,它使用了 dispatcher 类来接受连接并将入站连接发送给处理程序:

import asyncore
class EchoHandler(asyncore.dispatcher_with_send):
    def handle_read(self):
        data = self.recv(8192)
        if data:
            self.send(data)
class EchoServer(asyncore.dispatcher):
    def __init__(self, host, port):
        asyncore.dispatcher.__init__(self)
        self.create_socket()
        self.set_reuse_addr()
        self.bind((host, port))
        self.listen(5)
    def handle_accepted(self, sock, addr):
        print('Incoming connection from %s' % repr(addr))
        handler = EchoHandler(sock)
server = EchoServer('localhost', 8080)
asyncore.loop()

asynchat —- 异步套接字指令/响应处理程序

源代码: Lib/asynchat.py

3.6 版后已移除: 请使用 asyncio 替代。


注解

该模块仅为提供向后兼容。我们推荐在新代码中使用 asyncio

此模块在 asyncore 框架之上构建,简化了异步客户端和服务器并使得处理元素为以任意字符串结束或者为可变长度的协议更加容易。 asynchat 定义了一个可以由你来子类化的抽象类 async_chat,提供了 collect_incoming_data()found_terminator() 等方法的实现。 它使用与 asyncore 相同的异步循环,并且可以在通道映射中自由地混合 asyncore.dispatcherasynchat.async_chat 这两种类型的通道。 一般来说 asyncore.dispatcher 服务器通道在接收到传入的连接请求时会生成新的 asynchat.async_chat 通道对象。

class asynchat.async_chat

这个类是 asyncore.dispatcher 的抽象子类。 对于实际使用的代码你必须子类化 async_chat,提供有意义的 collect_incoming_data()found_terminator() 方法。 asyncore.dispatcher 的方法也可以被使用,但它们在消息/响应上下文中并不是全都有意义。

asyncore.dispatcher 类似,async_chat 也定义了一组通过对 select() 调用之后的套接字条件进行分析所生成的事件。 一旦启动轮询循环 async_chat 对象的方法就会被事件处理框架调用而无须程序员方面做任何操作。

两个可被修改的类属性,用以提升性能,甚至也可能会节省内存。

  • ac_in_buffer_size

    异步输入缓冲区大小 (默认为 4096)。

  • ac_out_buffer_size

    异步输出缓冲区大小 (默认为 4096)。

asyncore.dispatcher 不同,async_chat 允许你定义一个 FIFO 队列 producers。 其中的生产者只需要一个方法 more(),该方法应当返回要在通道上传输的数据。 生产者通过让其 more() 方法返回空字节串对象来表明其处于耗尽状态 (意即 它已不再包含数据)。 此时 async_chat 对象会将该生产者从队列中移除并开始使用下一个生产者,如果有下一个的话。 当生产者队列为空时 handle_write() 方法将不执行任何操作。 你要使用通道对象的 set_terminator() 方法来描述如何识别来自远程端点的入站传输的结束或是重要的中断点。

要构建一个可用的 async_chat 子类,你的输入方法 collect_incoming_data()found_terminator() 必须要处理通道异步接收的数据。 这些参数的描述见下文。

async_chat.close_when_done()

None 推入生产者队列。 当此生产者被弹出队列时它将导致通道被关闭。

async_chat.collect_incoming_data(data)

调用时附带 data,其中包含任意数量的已接收数据。 必须被重载的默认方法将引发一个 NotImplementedError 异常。

async_chat.discard_buffers()

在紧急情况下此方法将丢弃输入和/或输出缓冲区以及生产者队列中的任何数据。

async_chat.found_terminator()

当输入数据流能匹配 set_terminator() 所设定的终结条件时会被调用。 必须被重载的默认方法将引发一个 NotImplementedError 异常。 被缓冲的输入数据应当可以通过实例属性来获取。

async_chat.get_terminator()

返回通道的当前终结器。

async_chat.push(data)

将数据推入通道的队列以确保其被传输。 要让通道将数据写到网络中你只需要这样做就足够了,虽然以更复杂的方式使用你自己的生产者也是有可能的,例如为了实现加密和分块。

async_chat.push_with_producer(producer)

获取一个生产者对象并将其加入到与通道相关联的生产者队列中。 当所有当前已推入的生产者都已被耗尽时通道将通过调用其 more() 方法来耗用此生产者的数据并将数据发送至远程端点。

async_chat.set_terminator(term)

设置可在通道上被识别的终结条件。 term 可以是三种类型值中的任意一种 ,对应于处理入站协议数据的三种不同方式。

term 描述
string 当在输入流中发现该字符串时将会调用 found_terminator()
integer 当接收到指定数量的字符时将会调用 found_terminator()
None 通道会不断地持续收集数据

请注意终结器之后的任何数据将可在 found_terminator() 被调用后由通道来读取。

asynchat 示例

下面的例子片段显示了如何通过 async_chat 来读取 HTTP 请求。 Web 服务器可以为每个入站的客户端连接创建 http_request_handler 对象。 请注意在初始时通道终结器会被设置为匹配 HTTP 标头末尾的空行,并且会用一个旗标来指明标头正在被读取。

一旦完成了标头的读取,如果请求类型为 POST (表明输入流中存在更多的数据) 则会使用 Content-Length: 标头来设置一个数值终结器以从通道读取适当数量的数据。

一旦完成了对所有相关输入的处理,将会在设置通道终结器为 None 以确保忽略掉 Web 客户端所发送的任何无关数据之后调用 handle_request() 方法。

import asynchat
class http_request_handler(asynchat.async_chat):
    def __init__(self, sock, addr, sessions, log):
        asynchat.async_chat.__init__(self, sock=sock)
        self.addr = addr
        self.sessions = sessions
        self.ibuffer = []
        self.obuffer = b""
        self.set_terminator(b"\r\n\r\n")
        self.reading_headers = True
        self.handling = False
        self.cgi_data = None
        self.log = log
    def collect_incoming_data(self, data):
        """Buffer the data"""
        self.ibuffer.append(data)
    def found_terminator(self):
        if self.reading_headers:
            self.reading_headers = False
            self.parse_headers(b"".join(self.ibuffer))
            self.ibuffer = []
            if self.op.upper() == b"POST":
                clen = self.headers.getheader("content-length")
                self.set_terminator(int(clen))
            else:
                self.handling = True
                self.set_terminator(None)
                self.handle_request()
        elif not self.handling:
            self.set_terminator(None)  # browsers sometimes over-send
            self.cgi_data = parse(self.headers, b"".join(self.ibuffer))
            self.handling = True
            self.ibuffer = []
            self.handle_request()

signal —- 设置异步事件处理程序

该模块提供了在 Python 中使用信号处理程序的机制。

一般规则

signal.signal() 函数允许定义在接收到信号时执行的自定义处理程序。少量的默认处理程序已经设置: SIGPIPE 被忽略(因此管道和套接字上的写入错误可以报告为普通的 Python 异常)以及如果父进程没有更改 SIGINT ,则其会被翻译成 KeyboardInterrupt 异常。

一旦设置,特定信号的处理程序将保持安装,直到它被显式重置( Python 模拟 BSD 样式接口而不管底层实现),但 SIGCHLD 的处理程序除外,它遵循底层实现。

执行 Python 信号处理程序

Python 信号处理程序不会在低级( C )信号处理程序中执行。相反,低级信号处理程序设置一个标志,告诉 virtual machine 稍后执行相应的 Python 信号处理程序(例如在下一个 bytecode 指令)。这会导致:

  • 捕获同步错误是没有意义的,例如 SIGFPESIGSEGV ,它们是由 C 代码中的无效操作引起的。Python 将从信号处理程序返回到 C 代码,这可能会再次引发相同的信号,导致 Python 显然的挂起。 从Python 3.3开始,你可以使用 faulthandler 模块来报告同步错误。
  • 纯 C 中实现的长时间运行的计算(例如在大量文本上的正则表达式匹配)可以在任意时间内不间断地运行,而不管接收到任何信号。计算完成后将调用 Python 信号处理程序。

信号与线程

Python 信号处理程序总是会在主 Python 主解释器的主线程中执行,即使信号是在另一个线程中接收的。 这意味着信号不能被用作线程间通信的手段。 你可以改用 threading 模块中的同步原语。

此外,只有主解释器的主线程才被允许设置新的信号处理程序。

模块内容

在 3.5 版更改: 信号( SIG* ),处理程序( SIG_DFLSIG_IGN)和 sigmask( SIG_BLOCKSIG_UNBLOCKSIG_SETMASK )下面列出的相关常量变成了 enumsgetsignal()pthread_sigmask()sigpending()sigwait() 函数返回人类可读的 enums

signal 模块中定义的变量是:

signal.SIG_DFL

这是两种标准信号处理选项之一;它只会执行信号的默认函数。 例如,在大多数系统上,对于 SIGQUIT 的默认操作是转储核心并退出,而对于 SIGCHLD 的默认操作是简单地忽略它。

signal.SIG_IGN

这是另一个标准信号处理程序,它将简单地忽略给定的信号。

signal.SIGABRT

来自 abort(3) 的中止信号。

signal.SIGALRM

来自 alarm(2) 的计时器信号。

可用性: Unix。

signal.SIGBREAK

来自键盘的中断 (CTRL + BREAK)。

可用性: Windows。

signal.SIGBUS

总线错误 (非法的内存访问)。

可用性: Unix。

signal.SIGCHLD

子进程被停止或终结。

可用性: Unix。

signal.SIGCLD

SIGCHLD 的别名。

signal.SIGCONT

如果进程当前已停止则继续执行它

可用性: Unix。

signal.SIGFPE

浮点异常。 例如除以零。

参见

当除法或求余运算的第二个参数为零时会引发 ZeroDivisionError

signal.SIGHUP

在控制终端上检测到挂起或控制进程的终止。

可用性: Unix。

signal.SIGILL

非法指令。

signal.SIGINT

来自键盘的中断 (CTRL + C)。

默认的动作是引发 KeyboardInterrupt

signal.SIGKILL

终止信号。

它不能被捕获、阻塞或忽略。

可用性: Unix。

signal.SIGPIPE

损坏的管道:写入到没有读取器的管道。

默认的动作是忽略此信号。

可用性: Unix。

signal.SIGSEGV

段错误:无效的内存引用。

signal.SIGTERM

终结信号。

signal.SIGUSR1

用户自定义信号 1。

可用性: Unix。

signal.SIGUSR2

用户自定义信号 2。

可用性: Unix。

signal.SIGWINCH

窗口调整大小信号。

可用性: Unix。

SIG*

所有信号编号都是符号化定义的。 例如,挂起信号被定义为 signal.SIGHUP;变量的名称与 C 程序中使用的名称相同,具体见 <signal.h>。 ‘signal()‘ 的 Unix 手册页面列出了现有的信号 (在某些系统上这是 signal(2)*,在其他系统中此列表则是在 *signal(7) 中)。 请注意并非所有系统都会定义相同的信号名称集;只有系统所定义的名称才会由此模块来定义。

signal.CTRL_C_EVENT

对应于 Ctrl+C 击键事件的信号。此信号只能用于 os.kill()

可用性: Windows。

3.2 新版功能.

signal.CTRL_BREAK_EVENT

对应于 Ctrl+Break 击键事件的信号。此信号只能用于 os.kill()

可用性: Windows。

3.2 新版功能.

signal.NSIG

比最高信号数多一。

signal.ITIMER_REAL

实时递减间隔计时器,并在到期时发送 SIGALRM

signal.ITIMER_VIRTUAL

仅在进程执行时递减间隔计时器,并在到期时发送 SIGVTALRM 。

signal.ITIMER_PROF

当进程执行时以及当系统替进程执行时都会减小间隔计时器。 这个计时器与 ITIMER_VIRTUAL 相配结,通常被用于分析应用程序在用户和内核空间中花费的时间。 SIGPROF 会在超期时被发送。

signal.SIG_BLOCK

pthread_sigmask()how 形参的一个可能的值,表明信号将会被阻塞。

3.3 新版功能.

signal.SIG_UNBLOCK

pthread_sigmask()how 形参的是个可能的值,表明信号将被解除阻塞。

3.3 新版功能.

signal.SIG_SETMASK

pthread_sigmask()how 形参的一个可能的值,表明信号掩码将要被替换。

3.3 新版功能.

signal 模块定义了一个异常:

exception signal.ItimerError

作为来自下层 setitimer()getitimer() 实现错误的信号被引发。 如果将无效的定时器或负的时间值传给 setitimer() 就导致这个错误。 此错误是 OSError 的子类型。

3.3 新版功能: 此错误是 IOError 的子类型,现在则是 OSError 的别名。

signal 模块定义了以下函数:

signal.alarm(time)

如果 time 值非零,则此函数将要求将一个 SIGALRM 信号在 time 秒内发往进程。 任何在之前排入计划的警报都会被取消(在任何时刻都只能有一个警报被排入计划)。 后续的返回值将是任何之前设置的警报被传入之前的秒数。 如果 time 值为零,则不会将任何警报排入计划,并且任何已排入计划的警报都会被取消。 如果返回值为零,则目前没有任何警报被排入计划。

可用性: Unix。 更多信息请参见手册页面 *alarm(2)*。

signal.getsignal(signalnum)

返回当前用于信号 signalnum 的信号处理程序。 返回值可以是一个 Python 可调用对象,或是特殊值 signal.SIG_IGN, signal.SIG_DFLNone 之一。 在这里,signal.SIG_IGN 表示信号在之前被忽略,signal.SIG_DFL 表示之前在使用默认的信号处理方式,而 None 表示之前的信号处理程序未由 Python 安装。

signal.strsignal(signalnum)

返回信号 signalnum 的系统描述,例如 “Interrupt”, “Segmentation fault” 等等。 如果信号无法被识别则返回 None

3.8 新版功能.

signal.valid_signals()

返回本平台上的有效信号编号集。 这可能会少于 range(1, NSIG),如果某些信号被系统保留作为内部使用的话。

3.8 新版功能.

signal.pause()

使进程休眠直至接收到一个信号;然后将会调用适当的处理程序。 返回空值。

可用性: Unix。 更多信息请参见手册页面 *signal(2)*。

signal.raise_signal(signum)

向调用方进程发送一个信号。 返回空值。

3.8 新版功能.

signal.pidfd_send_signal(pidfd, sig, siginfo=None, flags=0)

发送信号 sig 到文件描述符 pidfd 所指向的进程。 Python 目前不支持 siginfo 形参;它必须为 None。 提供 flags 参数是为了将来扩展;当前未定义旗标值。

更多信息请参阅 pidfd_send_signal(2) 手册页面。

可用性: Linux 5.1+

3.9 新版功能.

signal.pthread_kill(thread_id, signalnum)

将信号 signalnum 发送至与调用者在同一进程中另一线程 thread_id。 目标线程可被用于执行任何代码(Python或其它)。 但是,如果目标线程是在执行 Python 解释器,则 Python 信号处理程序将 由主解释器的主线程来执行。 因此,将信号发送给特定 Python 线程的唯一作用在于强制让一个正在运行的系统调用失败并抛出 InterruptedError

使用 threading.get_ident()threading.Thread 对象的 ident 属性为 thread_id 获取合适的值。

如果 signalnum 为 0,则不会发送信号,但仍然会执行错误检测;这可被用来检测目标线程是否仍在运行。

引发一个 审计事件 signal.pthread_kill,附带参数 thread_id, signalnum

可用性: Unix。 更多信息请参见手册页面 *pthread_kill(3)*。

3.3 新版功能.

signal.pthread_sigmask(how, mask)

获取和/或修改调用方线程的信号掩码。 信号掩码是一组传送过程目前为调用者而阻塞的信号集。 返回旧的信号掩码作为一组信号。

该调用的行为取决于 how 的值,具体见下。

  • SIG_BLOCK: 被阻塞信号集是当前集与 mask 参数的并集。
  • SIG_UNBLOCK: mask 中的信号会从当前已阻塞信号集中被移除。 允许尝试取消对一个非阻塞信号的阻塞。
  • SIG_SETMASK: 已阻塞信号集会被设为 mask 参数的值。

mask 是一个信号编号集合 (例如 {signal.SIGINT, signal.SIGTERM})。 请使用 valid_signals() 表示包含所有信号的完全掩码。

例如,signal.pthread_sigmask(signal.SIG_BLOCK, []) 会读取调用方线程的信号掩码。

SIGKILLSIGSTOP 不能被阻塞。

可用性: Unix。 更多信息请参见手册页面 sigprocmask(2) 和 *pthread_sigmask(3)*。

3.3 新版功能.

signal.setitimer(which, seconds, interval=0.0)

设置由 which 指明的给定间隔计时器 (signal.ITIMER_REAL, signal.ITIMER_VIRTUALsignal.ITIMER_PROF 之一) 在 seconds 秒 (接受浮点数值,为与 alarm() 之差) 之后开始并在每 interval 秒间隔时 (如果 interval 不为零) 启动。 由 which 指明的间隔计时器可通过将 seconds 设为零来清空。

当一个间隔计时器启动时,会有信号发送至进程。 所发送的具体信号取决于所使用的计时器;signal.ITIMER_REAL 将发送 SIGALRM, signal.ITIMER_VIRTUAL 将发送 SIGVTALRM, 而 signal.ITIMER_PROF 将发送 SIGPROF.

原有的值会以元组: (delay, interval) 的形式被返回。

尝试传入无效的计时器将导致 ItimerError

可用性: Unix。

signal.getitimer(which)

返回由 which 指明的给定间隔计时器当前的值。

可用性: Unix。

signal.set_wakeup_fd(fd, **, warn_on_full_buffer=True*)

将唤醒文件描述符设为 fd。 当接收到信号时,会将信号编号以单个字节的形式写入 fd。 这可被其它库用来唤醒一次 poll 或 select 调用,以允许该信号被完全地处理。

原有的唤醒 fd 会被返回(或者如果未启用文件描述符唤醒则返回 -1)。 如果 fd 为 -1,文件描述符唤醒会被禁用。 如果不为 -1,则 fd 必须为非阻塞型。 需要由库来负责在重新调用 poll 或 select 之前从 fd 移除任何字节数据。

当启用线程用时,此函数只能从 主解释器的主线程 被调用;尝试从另一线程调用它将导致 ValueError 异常被引发。

使用此函数有两种通常的方式。 在两种方式下,当有信号到达时你都是用 fd 来唤醒,但之后它们在确定达到的一个或多个信号 which 时存在差异。

在第一种方式下,我们从 fd 的缓冲区读取数据,这些字节值会给你信号编号。 这种方式很简单,但在少数情况下会发生问题:通常 fd 将有缓冲区空间大小限制,如果信号到达得太多且太快,缓冲区可能会爆满,有些信号可能丢失。 如果你使用此方式,则你应当设置 warn_on_full_buffer=True,当信号丢失时这至少能将警告消息打印到 stderr。

在第二种方式下,我们 只会 将唤醒 fd 用于唤醒,而忽略实际的字节值。 在此情况下,我们所关心的只有 fd 的缓冲区为空还是不为空;爆满的缓冲区完全不会导致问题。 如果你使用此方式,则你应当设置 warn_on_full_buffer=False,这样你的用户就不会被虚假的警告消息所迷惑。

在 3.5 版更改: 在 Windows 上,此函数现在也支持套接字处理。

在 3.7 版更改: 添加了 warn_on_full_buffer 形参。

signal.siginterrupt(signalnum, flag)

更改系统调用重启行为:如果 flagFalse,系统调用将在被信号 signalnum 中断时重启,否则系统调用将被中断。 返回空值。

可用性: Unix。 更多信息请参见手册页面 *siginterrupt(3)*。

请注意用 signal() 安装信号处理程序将重启行为重置为可通过显式调用 siginterrupt() 并为给定信号的 flag 设置真值来实现中断。

signal.signal(signalnum, handler)

将信号 signalnum 的处理程序设为函数 handler*。 *handler 可以为接受两个参数(见下)的 Python 可调用对象,或者为特殊值 signal.SIG_IGNsignal.SIG_DFL 之一。 之前的信号处理程序将被返回。 (更多信息请参阅 Unix 手册页面 *signal(2)*。)

当启用线程用时,此函数只能从 主解释器的主线程 被调用;尝试从另一线程调用它将导致 ValueError 异常被引发。

handler 将附带两个参数调用:信号编号和当前堆栈帧 (None 或一个帧对象;有关帧对象的描述请参阅 类型层级结构描述 或者参阅 inspect 模块中的属性描述)。

在 Windows 上,signal() 调用只能附带 SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERMSIGBREAK。 任何其他值都将引发 ValueError。 请注意不是所有系统都定义了同样的信号名称集合;如果一个信号名称未被定义为 SIG* 模块层级常量则将引发 AttributeError

signal.sigpending()

检查正在等待传送给调用方线程的信号集合(即在阻塞期间被引发的信号)。 返回正在等待的信号集合。

可用性: Unix。 更多信息请参见手册页面 *sigpending(2)*。

3.3 新版功能.

signal.sigwait(sigset)

挂起调用方线程的执行直到信号集合 sigset 中指定的信号之一被传送。 此函数会接受该信号(将其从等待信号列表中移除),并返回信号编号。

可用性: Unix。 更多信息请参见手册页面 *sigwait(3)*。

3.3 新版功能.

signal.sigwaitinfo(sigset)

挂起调用方线程的执行直到信号集合 sigset 中指定的信号之一被传送。 此函数会接受该信号并将其从等待信号列表中移除。 如果 sigset 中的信号之一已经在等待调用方线程,此函数将立即返回并附带有关该信号的信息。 被传送信号的信号处理程序不会被调用。 如果该函数被某个不在 sigset 中的信号中断则会引发 InterruptedError

返回值是一个代表 siginfo_t 结构体所包含数据的对象,具体为: si_signo, si_code, si_errno, si_pid, si_uid, si_status, si_band

可用性: Unix。 更多信息请参见手册页面 *sigwaitinfo(2)*。

3.3 新版功能.

在 3.5 版更改: 当被某个 不在 sigset 中的信号中断时本函数将进行重试并且信号处理程序不会引发异常(请参阅 PEP 475 了解其理由)。

signal.sigtimedwait(sigset, timeout)

类似于 sigwaitinfo(),但会接受一个额外的 timeout 参数来指定超时限制。 如果将 timeout 指定为 0,则会执行轮询。 如果发生超时则返回 None

可用性: Unix。 更多信息请参见手册页面 *sigtimedwait(2)*。

3.3 新版功能.

在 3.5 版更改: 现在当此函数被某个不在 sigset 中的信号中断时将以计算出的 timeout 进行重试并且信号处理程序不会引发异常(请参阅 PEP 475 了解其理由)。

示例

这是一个最小示例程序。 它使用 alarm() 函数来限制等待打开一个文件所花费的时间;这在文件为无法开启的串行设备时会很有用处,此情况通常会导致 os.open() 无限期地挂起。 解决办法是在打开文件之前设置 5 秒钟的 alarm;如果操作耗时过长,将会发送 alarm 信号,并且处理程序会引发一个异常。

import signal, os
def handler(signum, frame):
    print('Signal handler called with signal', signum)
    raise OSError("Couldn't open device!")
# Set the signal handler and a 5-second alarm
signal.signal(signal.SIGALRM, handler)
signal.alarm(5)
# This open() may hang indefinitely
fd = os.open('/dev/ttyS0', os.O_RDWR)
signal.alarm(0)          # Disable the alarm

对于 SIGPIPE 的说明

将你的程序用管道输出到工具例如 head(1) 将会导致 SIGPIPE 信号在其标准输出的接收方提前关闭时被发送到你的进程。 这将引发一个异常例如 BrokenPipeError: [Errno 32] Broken pipe。 要处理这种情况,请对你的入口点进行包装以捕获此异常,如下所示:

import os
import sys
def main():
    try:
        # simulate large output (your code replaces this loop)
        for x in range(10000):
            print("y")
        # flush output here to force SIGPIPE to be triggered
        # while inside this try block.
        sys.stdout.flush()
    except BrokenPipeError:
        # Python flushes standard streams on exit; redirect remaining output
        # to devnull to avoid another BrokenPipeError at shutdown
        devnull = os.open(os.devnull, os.O_WRONLY)
        os.dup2(devnull, sys.stdout.fileno())
        sys.exit(1)  # Python exits with error code 1 on EPIPE
if __name__ == '__main__':
    main()

不要将 SIGPIPE 的处置方式设为 SIG_DFL 以避免 BrokenPipeError。 这样做还会在你的程序所写入的任何套接字连接中断时导致你的程序异常退出。

mmap —- 内存映射文件支持

内存映射文件对象的行为既像 bytearray 又像 文件对象。 你可以在大部分接受 bytearray 的地方使用 mmap 对象;例如,你可以使用 re 模块来搜索一个内存映射文件。 你也可以通过执行 obj[index] = 97 来修改单个字节,或者通过对切片赋值来修改一个子序列: obj[i1:i2] = b'...'。 你还可以在文件的当前位置开始读取和写入数据,并使用 seek() 前往另一个位置。

内存映射文件是由 mmap 构造函数创建的,其在 Unix 和 Windows 上是不同的。 无论哪种情况,你都必须为一个打开的文件提供文件描述符以进行更新。 如果你希望映射一个已有的 Python 文件对象,请使用该对象的 fileno() 方法来获取 fileno 参数的正确值。 否则,你可以使用 os.open() 函数来打开这个文件,这会直接返回一个文件描述符(结束时仍然需要关闭该文件)。

注解

如果要为可写的缓冲文件创建内存映射,则应当首先 flush() 该文件。 这确保了对缓冲区的本地修改在内存映射中可用。

对于 Unix 和 Windows 版本的构造函数,可以将 access 指定为可选的关键字参数。 access 接受以下四个值之一: ACCESS_READACCESS_WRITEACCESS_COPY 分别指定只读,直写或写时复制内存,或 ACCESS_DEFAULT 推迟到 protaccess 可以在 Unix 和 Windows 上使用。如果未指定 access ,则 Windows mmap 返回直写映射。这三种访问类型的初始内存值均取自指定的文件。向 ACCESS_READ 内存映射赋值会引发 TypeError 异常。 向 ACCESS_WRITE 内存映射赋值会影响内存和底层的文件。 向 ACCESS_COPY 内存映射赋值会影响内存,但不会更新底层的文件。

在 3.7 版更改: 添加了 ACCESS_DEFAULT 常量。

要映射匿名内存,应将 -1 作为 fileno 和 length 一起传递。

class mmap.mmap(fileno, length, tagname=None, access=ACCESS_DEFAULT[, offset])

( Windows 版本) 映射被文件句柄 fileno 指定的文件的 length 个字节,并创建一个 mmap 对象。如果 length 大于当前文件大小,则文件将扩展为包含 length 个字节。如果 length0,则映射的最大长度为当前文件大小。如果文件为空, Windows 会引发异常(你无法在Windows上创建空映射)。

如果 tagname 被指定且不是 None ,则是为映射提供标签名称的字符串。 Windows 允许你对同一文件拥有许多不同的映射。如果指定现有标签的名称,则会打开该标签,否则将创建该名称的新标签。如果省略此参数或设置为 None ,则创建的映射不带名称。避免使用 tag 参数将有助于使代码在Unix和Windows之间可移植。

offset 可以被指定为非负整数偏移量。 mmap 引用将相对于从文件开头的偏移。 offset 默认为0。 offeset 必须是 ALLOCATIONGRANULARITY 的倍数。

引发一个 审计事件 mmap.__new__ 附带参数 fileno, length, access, offset

class mmap.mmap(fileno, length, flags=MAP_SHARED, prot=PROT_WRITE|PROT_READ, access=ACCESS_DEFAULT[, offset])

(Unix 版本) 映射文件描述符 fileno 指定的文件的 length 个字节,并返回一个 mmap 对象。如果 length0 ,则当调用 mmap 时,映射的最大长度将为文件的当前大小。

flags 指明映射的性质。 MAP_PRIVATE 会创建私有的写入时拷贝映射,因此对 mmap 对象内容的修改将为该进程所私有。 而 MAP_SHARED 会创建与其他映射同一文件区域的进程所共享的映射。 默认值为 MAP_SHARED。 某些系统还具有额外的可用旗标,完整列表会在 MAP_* 常量 中指明。

如果指明了 prot*,它将给出所需的内存保护方式;最有用的两个值是 PROT_READPROT_WRITE,分别指明页面为可读或可写。 *prot 默认为 PROT_READ | PROT_WRITE

可以指定 access 作为替代 flagsprot 的可选关键字形参。 同时指定 flags, protaccess 将导致错误。 请参阅上文中 access 的描述了解有关如何使用此形参的信息。

offset 可以被指定为非负整数偏移量。 mmap 引用将相对于从文件开头的偏移。 offset 默认为 0。 offset 必须是 ALLOCATIONGRANULARITY 的倍数,它在 Unix 系统上等价于 PAGESIZE

To ensure validity of the created memory mapping the file specified by the descriptor fileno is internally automatically synchronized with physical backing store on macOS and OpenVMS.

这个例子演示了使用 mmap 的简单方式:

import mmap
# write a simple example file
with open("hello.txt", "wb") as f:
    f.write(b"Hello Python!\n")
with open("hello.txt", "r+b") as f:
    # memory-map the file, size 0 means whole file
    mm = mmap.mmap(f.fileno(), 0)
    # read content via standard file methods
    print(mm.readline())  # prints b"Hello Python!\n"
    # read content via slice notation
    print(mm[:5])  # prints b"Hello"
    # update content using slice notation;
    # note that new content must have same size
    mm[6:] = b" world!\n"
    # ... and read again using standard file methods
    mm.seek(0)
    print(mm.readline())  # prints b"Hello  world!\n"
    # close the map
    mm.close()

mmap 也可以在 with 语句中被用作上下文管理器:

import mmap
with mmap.mmap(-1, 13) as mm:
    mm.write(b"Hello world!")

3.2 新版功能: 上下文管理器支持。

下面的例子演示了如何创建一个匿名映射并在父进程和子进程之间交换数据。:

import mmap
import os
mm = mmap.mmap(-1, 13)
mm.write(b"Hello world!")
pid = os.fork()
if pid == 0:  # In a child process
    mm.seek(0)
    print(mm.readline())
    mm.close()

引发一个 审计事件 mmap.__new__ 附带参数 fileno, length, access, offset

映射内存的文件对象支持以下方法:

  • close()

    关闭 mmap。 后续调用该对象的其他方法将导致引发 ValueError 异常。 此方法将不会关闭打开的文件。

  • closed

    如果文件已关闭则返回 True

    3.2 新版功能.

  • find(sub[, start[, end]])

    返回子序列 sub 在对象内被找到的最小索引号,使得 sub 被包含在 [start, end*] 范围中。 可选参数 *startend 会被解读为切片表示法。 如果未找到则返回 -1

    在 3.5 版更改: 现在接受可写的 字节类对象。

  • flush([offset[, size]])

    将对文件的内存副本的修改刷新至磁盘。 如果不使用此调用则无法保证在对象被销毁前将修改写回存储。 如果指定了 offsetsize*,则只将对指定范围内字节的修改刷新至磁盘;在其他情况下,映射的全部范围都会被刷新。 *offset 必须为 PAGESIZEALLOCATIONGRANULARITY 的倍数。

    返回 None 以表示成功。 当调用失败时将引发异常。

    在 3.8 版更改: 在之前版本中,成功时将返回非零值;在 Windows 下当发生错误时将返回零。 在 Unix 下 成功时将返回零值;当发生错误时将引发异常。

  • madvise(option[, start[, length]])

    将有关内存区域的建议 option 发送至内核,从 start 开始扩展 length 个字节。 option 必须为系统中可用的 MADV_* 常量 之一。 如果省略 startlength,则会包含整个映射。 在某些系统中(包括 Linux),start 必须为 PAGESIZE 的倍数。

    可用性: 具有 madvise() 系统调用的系统。

    3.8 新版功能.

  • move(dest, src, count)

    将从偏移量 src 开始的 count 个字节拷贝到目标索引号 dest。 如果 mmap 创建时设置了 ACCESS_READ,则调用 move 将引发 TypeError 异常。

  • read([n])

    返回一个 bytes,其中包含从当前文件位置开始的至多 n 个字节。 如果参数省略,为 None 或负数,则返回从当前文件位置开始直至映射结尾的所有字节。 文件位置会被更新为返回字节数据之后的位置。

    在 3.3 版更改: 参数可被省略或为 None

  • read_byte()

    将当前文件位置上的一个字节以整数形式返回,并让文件位置前进 1。

  • readline()

    返回一个单独的行,从当前文件位置开始直到下一个换行符。 文件位置会被更新为返回字节数据之后的位置。

  • resize(newsize)

    改变映射以及下层文件的大小,如果存在的话。 如果 mmap 创建时设置了 ACCESS_READACCESS_COPY,则改变映射大小将引发 TypeError 异常。

  • rfind(sub[, start[, end]])

    返回子序列 sub 在对象内被找到的最大索引号,使得 sub 被包含在 [start, end*] 范围中。 可选参数 *startend 会被解读为切片表示法。 如果未找到则返回 -1

    在 3.5 版更改: 现在接受可写的 字节类对象。

  • seek(pos[, whence])

    设置文件的当前位置。 whence 参数为可选项并且默认为 os.SEEK_SET0 (绝对文件定位);其他值还有 os.SEEK_CUR1 (相对当前位置查找) 和 os.SEEK_END2 (相对文件末尾查找)。

  • size()

    返回文件的长度,该数值可以大于内存映射区域的大小。

  • tell()

    返回文件指针的当前位置。

  • write(bytes)

    bytes 中的字节数据写入文件指针当前位置的内存并返回写入的字节总数 (一定不小于 len(bytes),因为如果写入失败,将会引发 ValueError)。 在字节数据被写入后文件位置将会更新。 如果 mmap 创建时设置了 ACCESS_READ,则向其写入将引发 TypeError 异常。

    在 3.5 版更改: 现在接受可写的 字节类对象。

    在 3.6 版更改: 现在会返回写入的字节总数。

  • write_byte(byte)

    将整数值 byte 写入文件指针当前位置的内存;文件位置前进 1。 如果 mmap 创建时设置了 ACCESS_READ,则向其写入将引发 TypeError 异常。

MADV_* 常量

mmap.MADV_NORMAL
mmap.MADV_RANDOM
mmap.MADV_SEQUENTIAL
mmap.MADV_WILLNEED
mmap.MADV_DONTNEED
mmap.MADV_REMOVE
mmap.MADV_DONTFORK
mmap.MADV_DOFORK
mmap.MADV_HWPOISON
mmap.MADV_MERGEABLE
mmap.MADV_UNMERGEABLE
mmap.MADV_SOFT_OFFLINE
mmap.MADV_HUGEPAGE
mmap.MADV_NOHUGEPAGE
mmap.MADV_DONTDUMP
mmap.MADV_DODUMP
mmap.MADV_FREE
mmap.MADV_NOSYNC
mmap.MADV_AUTOSYNC
mmap.MADV_NOCORE
mmap.MADV_CORE
mmap.MADV_PROTECT
mmap.MADV_FREE_REUSABLE
mmap.MADV_FREE_REUSE

这些选项可被传给 mmap.madvise()。 不是每个选项都存在于每个系统中。

可用性: 具有 madvise() 系统调用的系统。

3.8 新版功能.

MAP_* 常量

mmap.MAP_SHARED
mmap.MAP_PRIVATE
mmap.MAP_DENYWRITE
mmap.MAP_EXECUTABLE
mmap.MAP_ANON
mmap.MAP_ANONYMOUS
mmap.MAP_POPULATE

这些是可被传给 mmap.mmap() 的各种旗标。 请注意某些选项在某些系统上可能不存在。

在 3.10 版更改: 增加了 MAP_POPULATE 常量。

互联网数据处理

本章介绍了一些支持处理因特网上常用数据格式的模块。

  • email —- 电子邮件与 MIME 处理包
    • email.message: 表示一封电子邮件信息
    • email.parser: 解析电子邮件信息
      • FeedParser API
      • Parser API
      • 附加说明
    • email.generator: 生成 MIME 文档
    • email.policy: Policy 对象
    • email.errors: 异常和缺陷类
    • email.headerregistry: 自定义标头对象
    • email.contentmanager: 管理 MIME 内容
      • 内容管理器实例
    • email: 示例
    • email.message.Message: 使用 compat32 API 来表示电子邮件消息
    • email.mime: 从头创建电子邮件和 MIME 对象
    • email.header: 国际化标头
    • email.charset: 表示字符集
    • email.encoders: 编码器
    • email.utils: 其他工具
    • email.iterators: 迭代器
  • json —- JSON 编码和解码器
    • 基本使用
    • 编码器和解码器
    • 异常
    • 标准符合性和互操作性
      • 字符编码
      • Infinite 和 NaN 数值
      • 对象中的重复名称
      • 顶级非对象,非数组值
      • 实现限制
    • 命令行界面
      • 命令行选项
  • mailcap —- Mailcap 文件处理
  • mailbox —- 操作多种格式的邮箱
    • Mailbox 对象
      • Maildir
      • mbox
      • MH
      • Babyl
      • MMDF
    • Message 对象
      • MaildirMessage
      • mboxMessage
      • MHMessage
      • BabylMessage
      • MMDFMessage
    • 异常
    • 例子
  • mimetypes —- 映射文件名到 MIME 类型
    • MimeTypes 对象
  • base64 —- Base16, Base32, Base64, Base85 数据编码
    • 安全考量
  • binhex —- 对binhex4文件进行编码和解码
    • 备注
  • binascii —- 二进制和 ASCII 码互转
  • quopri —- 编码与解码经过 MIME 转码的可打印数据
  • uu —- 对 uuencode 文件进行编码与解码

email —- 电子邮件与 MIME 处理包

源代码: Lib/email/init.py


email 包是一个用于管理电子邮件消息的库。 它 并非 被设计为执行向 SMTP (RFC 2821), NNTP 或其他服务器发送电子邮件消息的操作;这些是 smtplibnntplib 等模块的功能。 email 包试图尽可能地遵循 RFC,支持 RFC 5322RFC 6532,以及与 MIME 相关的各个 RFC 例如 RFC 2045, RFC 2046, RFC 2047, RFC 2183RFC 2231

email 包的总体结构可以分为三个主要组件,另外还有第四个组件用于控制其他组件的行为。

这个包的中心组件是代表电子邮件消息的“对象模型”。 应用程序主要通过在 message 子模块中定义的对象模型接口与这个包进行交互。 应用程序可以使用此 API 来询问有关现有电子邮件的问题、构造新的电子邮件,或者添加或移除自身也使用相同对象模型接口的电子邮件子组件。 也就是说,遵循电子邮件消息及其 MIME 子组件的性质,电子邮件对象模型是所有提供 EmailMessage API 的对象所构成的树状结构。

这个包的另外两个主要组件是 parsergenerator。 parser 接受电子邮件消息的序列化版本(字节流)并将其转换为 EmailMessage 对象树。 generator 接受 EmailMessage 并将其转回序列化的字节流。 (parser 和 generator 还能处理文本字符流,但不建议这种用法,因为这很容易导致某种形式的无效消息。

控制组件是 policy 模块。 每一个 EmailMessage、每一个 generator 和每一个 parser 都有一个相关联的 policy 对象来控制其行为。 通常应用程序只有在 EmailMessage 被创建时才需要指明控制策略,或者通过直接实例代 EmailMessage 来新建电子邮件,或者通过使用 parser 来解析输入流。 但是策略也可以在使用 generator 序列化消息时被更改。 例如,这允许从磁盘解析通用电子邮件消息,而在将消息发送到电子邮件服务器时使用标准 SMTP 设置对其进行序列化。

email 包会尽量地对应用程序隐藏各种控制类 RFC 的细节。 从概念上讲应用程序应当能够将电子邮件消息视为 Unicode 文本和二进制附件的结构化树,而不必担心在序列化时要如何表示它们。 但在实际中,经常有必要至少了解一部分控制类 MIME 消息及其结构的规划,特别是 MIME “内容类型” 的名称和性质以及它们是如何标识多部分文档的。 在大多数情况下这些知识应当仅对于更复杂的应用程序来说才是必需的,并且即便在那时它也应当仅是特定的高层级结构,而不是如何表示这些结构的细节信息。 由于 MIME 内容类型被广泛应用于现代因特网软件(而非只是电子邮件),因此这对许多程序员来说将是很熟悉的概念。

以下小节描述了 email 包的具体功能。 我们会从 message 对象模型开始,它是应用程序将要使用的主要接口,之后是 parsergenerator 组件。 然后我们会介绍 policy 控制组件,它将完成对这个库的主要组件的处理。

接下来的三个小节会介绍这个包可能引发的异常以及 parser 可能检测到的缺陷(即与 RFC 不相符)。 然后我们会介绍 headerregistrycontentmanager 子组件,它们分别提供了用于更精细地操纵标题和载荷的工具。 这两个组件除了包含使用与生成非简单消息的相关特性,还记录了它们的可扩展性 API,这将是高级应用程序所感兴趣的内容。

在此之后是一组使用之前小节所介绍的 API 的基本部分的示例。

前面的内容是 email 包的现代(对 Unicode 支持良好)API。 从 Message 类开始的其余小节则介绍了旧式 compat32 API,它会更直接地处理如何表示电子邮件消息的细节。 compat32 API 不会 向应用程序隐藏 RFC 的相关细节,但对于需要进行此种层级操作的应用程序来说将是很有用的工具。 此文档对于因向下兼容理由而仍然使用 compat32 API 的应用程序也是很适合的。

在 3.6 版更改: 文档经过重新组织和撰写以鼓励使用新的 EmailMessage/EmailPolicy API。

email 包文档的内容:

旧式 API:

json —- JSON 编码和解码器

源代码: Lib/json/init.py


JSON (JavaScript Object Notation),由 RFC 7159 (which obsoletes RFC 4627) 和 ECMA-404 指定,是一个受 JavaScript 的对象字面量语法启发的轻量级数据交换格式,尽管它不仅仅是一个严格意义上的 JavaScript 的字集 1

json 提供了与标准库 marshalpickle 相似的API接口。

对基本的 Python 对象层次结构进行编码:

>>> import json
>>> json.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}])
'["foo", {"bar": ["baz", null, 1.0, 2]}]'
>>> print(json.dumps("\"foo\bar"))
"\"foo\bar"
>>> print(json.dumps('\u1234'))
"\u1234"
>>> print(json.dumps('\\'))
"\\"
>>> print(json.dumps({"c": 0, "b": 0, "a": 0}, sort_keys=True))
{"a": 0, "b": 0, "c": 0}
>>> from io import StringIO
>>> io = StringIO()
>>> json.dump(['streaming API'], io)
>>> io.getvalue()
'["streaming API"]'

紧凑编码:

>>> import json
>>> json.dumps([1, 2, 3, {'4': 5, '6': 7}], separators=(',', ':'))
'[1,2,3,{"4":5,"6":7}]'

美化输出:

>>> import json
>>> print(json.dumps({'4': 5, '6': 7}, sort_keys=True, indent=4))
{
    "4": 5,
    "6": 7
}

JSON解码:

>>> import json
>>> json.loads('["foo", {"bar":["baz", null, 1.0, 2]}]')
['foo', {'bar': ['baz', None, 1.0, 2]}]
>>> json.loads('"\\"foo\\bar"')
'"foo\x08ar'
>>> from io import StringIO
>>> io = StringIO('["streaming API"]')
>>> json.load(io)
['streaming API']

特殊 JSON 对象解码:

>>> import json
>>> def as_complex(dct):
...     if '__complex__' in dct:
...         return complex(dct['real'], dct['imag'])
...     return dct
...
>>> json.loads('{"__complex__": true, "real": 1, "imag": 2}',
...     object_hook=as_complex)
(1+2j)
>>> import decimal
>>> json.loads('1.1', parse_float=decimal.Decimal)
Decimal('1.1')

扩展 JSONEncoder

>>> import json
>>> class ComplexEncoder(json.JSONEncoder):
...     def default(self, obj):
...         if isinstance(obj, complex):
...             return [obj.real, obj.imag]
...         # Let the base class default method raise the TypeError
...         return json.JSONEncoder.default(self, obj)
...
>>> json.dumps(2 + 1j, cls=ComplexEncoder)
'[2.0, 1.0]'
>>> ComplexEncoder().encode(2 + 1j)
'[2.0, 1.0]'
>>> list(ComplexEncoder().iterencode(2 + 1j))
['[2.0', ', 1.0', ']']

从命令行使用 json.tool 来验证并美化输出:

$ echo '{"json":"obj"}' | python -m json.tool
{
    "json": "obj"
}
$ echo '{1.2:3.4}' | python -m json.tool
Expecting property name enclosed in double quotes: line 1 column 2 (char 1)

注解

JSON 是 YAML 1.2 的一个子集。由该模块的默认设置生成的 JSON (尤其是默认的 “分隔符” 设置值)也是 YAML 1.0 and 1.1 的一个子集。因此该模块也能够用于序列化为 YAML。

注解

这个模块的编码器和解码器默认保护输入和输出的顺序。仅当底层的容器未排序时才会失去顺序。

在 Python 3.7 之前,dict 并不保证有序,因此输入和输出通常都是乱序的,除非是明确地请求 collections.OrderedDict。 从 Python 3.7 开始,普通的 dict 会保留顺序,因此不必再为 JSON 的生成和解析指定使用 collections.OrderedDict

基本使用

json.dump(obj, fp, **, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, *kw)

使用这个 转换表 将 obj 序列化为 JSON 格式化流形式的 fp (支持 .write() 的 file-like object)。

如果 skipkeys 是 true (默认为 False),那么那些不是基本对象的字典的键会被跳过;否则引发一个 TypeError

json 模块始终产生 str 对象而非 bytes 对象。因此,fp.write() 必须支持 str 输入。

如果 ensure_ascii 是 true (即默认值),输出保证将所有输入的非 ASCII 字符转义。如果 ensure_ascii 是 false,这些字符会原样输出。

如果 check_circular 是为假值 (默认为 True),那么容器类型的循环引用检验会被跳过并且循环引用会引发一个 OverflowError (或者更糟的情况)。

如果 allow_nan 是 false(默认为 True),那么在对严格 JSON 规格范围外的 float 类型值(naninf-inf)进行序列化时会引发一个 ValueError。如果 allow_nan 是 true,则使用它们的 JavaScript 等价形式(NaNInfinity-Infinity)。

如果 indent 是一个非负整数或者字符串,那么 JSON 数组元素和对象成员会被美化输出为该值指定的缩进等级。 如果缩进等级为零、负数或者 "",则只会添加换行符。 None (默认值) 选择最紧凑的表达。 使用一个正整数会让每一层缩进同样数量的空格。 如果 indent 是一个字符串 (比如 "\t"),那个字符串会被用于缩进每一层。

在 3.2 版更改: 现允许使用字符串作为 indent 而不再仅仅是整数。

当被指定时,separators 应当是一个 (item_separator, key_separator) 元组。当 indentNone 时,默认值取 (', ', ': '),否则取 (',', ': ')。为了得到最紧凑的 JSON 表达式,你应该指定其为 (',', ':') 以消除空白字符。

在 3.4 版更改: 现当 indent 不是 None 时,采用 (',', ': ') 作为默认值。

default 被指定时,其应该是一个函数,每当某个对象无法被序列化时它会被调用。它应该返回该对象的一个可以被 JSON 编码的版本或者引发一个 TypeError。如果没有被指定,则会直接引发 TypeError

如果 sort_keys 是 true(默认为 False),那么字典的输出会以键的顺序排序。

为了使用一个自定义的 JSONEncoder 子类(比如:覆盖了 default() 方法来序列化额外的类型), 通过 cls 关键字参数来指定;否则将使用 JSONEncoder

在 3.6 版更改: 所有可选形参现在都是 仅限关键字参数。

注解

picklemarshal 不同,JSON 不是一个具有框架的协议,所以尝试多次使用同一个 fp 调用 dump() 来序列化多个对象会产生一个不合规的 JSON 文件。

json.dumps(obj, **, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, *kw)

使用这个 转换表 将 obj 序列化为 JSON 格式的 str。 其参数的含义与 dump() 中的相同。

注解

JSON 中的键-值对中的键永远是 str 类型的。当一个对象被转化为 JSON 时,字典中所有的键都会被强制转换为字符串。这所造成的结果是字典被转换为 JSON 然后转换回字典时可能和原来的不相等。换句话说,如果 x 具有非字符串的键,则有 loads(dumps(x)) != x

json.load(fp, **, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, *kw)

使用这个 转换表 将 fp (一个支持 .read() 并包含一个 JSON 文档的 text file 或者 binary file) 反序列化为一个 Python 对象。

object_hook 是一个可选的函数,它会被调用于每一个解码出的对象字面量(即一个 dict)。object_hook 的返回值会取代原本的 dict。这一特性能够被用于实现自定义解码器(如 JSON-RPC 的类型提示)。

object_pairs_hook 是一个可选的函数,它会被调用于每一个有序列表对解码出的对象字面量。 object_pairs_hook 的返回值将会取代原本的 dict 。这一特性能够被用于实现自定义解码器。如果 object_hook 也被定义, object_pairs_hook 优先。

在 3.1 版更改: 添加了对 object_pairs_hook 的支持。

parse_float ,如果指定,将与每个要解码 JSON 浮点数的字符串一同调用。默认状态下,相当于 float(num_str) 。可以用于对 JSON 浮点数使用其它数据类型和语法分析程序 (比如 decimal.Decimal )。

parse_int ,如果指定,将与每个要解码 JSON 整数的字符串一同调用。默认状态下,相当于 int(num_str) 。可以用于对 JSON 整数使用其它数据类型和语法分析程序 (比如 float )。

parse_constant ,如果指定,将要与以下字符串中的一个一同调用: '-Infinity''Infinity''NaN' 。如果遇到无效的 JSON 数字则可以使用它引发异常。

在 3.1 版更改: parse_constant 不再调用 ‘null’ , ‘true’ , ‘false’ 。

要使用自定义的 JSONDecoder 子类,用 cls 指定他;否则使用 JSONDecoder 。额外的关键词参数会通过类的构造函数传递。

如果反序列化的数据不是有效 JSON 文档,引发 JSONDecodeError 错误。

在 3.6 版更改: 所有可选形参现在都是 仅限关键字参数。

在 3.6 版更改: fp 现在可以是 binary file 。输入编码应当是 UTF-8 , UTF-16 或者 UTF-32 。

json.loads(s, **, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, *kw)

使用这个 转换表 将 s (一个包含 JSON 文档的 str, bytesbytearray 实例) 反序列化为 Python 对象。

其他参数的含义与 load() 中的相同。

如果反序列化的数据不是有效 JSON 文档,引发 JSONDecodeError 错误。

在 3.6 版更改: s 现在可以为 bytesbytearray 类型。 输入编码应为 UTF-8, UTF-16 或 UTF-32。

在 3.9 版更改: 关键字参数 encoding 已被移除。

编码器和解码器

class json.JSONDecoder(**, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, strict=True, object_pairs_hook=None*)

简单的JSON解码器。

默认情况下,解码执行以下翻译:

JSON Python
object — 对象 dict
array list — 列表
string str
number (int) int
number (real) float
true True
false False
null None

它还将“NaN”、“Infinity”和“-Infinity”理解为它们对应的“float”值,这超出了JSON规范。

如果指定了 object_hook,它将被调用并传入每个已解码 JSON 对象的结果,并且其返回值将被用来替代给定的 dict。 它可被用于提供自定义的反序列化操作(例如支持 JSON-RPC 类提示)。

如果指定了 object_pairs_hook 则它将被调用并传入以对照值有序列表进行解码的每个 JSON 对象的结果。 object_pairs_hook 的结果值将被用来替代 dict。 这一特性可被用于实现自定义解码器。 如果还定义了 object_hook*,则 *object_pairs_hook 的优先级更高。

在 3.1 版更改: 添加了对 object_pairs_hook 的支持。

parse_float ,如果指定,将与每个要解码 JSON 浮点数的字符串一同调用。默认状态下,相当于 float(num_str) 。可以用于对 JSON 浮点数使用其它数据类型和语法分析程序 (比如 decimal.Decimal )。

parse_int ,如果指定,将与每个要解码 JSON 整数的字符串一同调用。默认状态下,相当于 int(num_str) 。可以用于对 JSON 整数使用其它数据类型和语法分析程序 (比如 float )。

parse_constant ,如果指定,将要与以下字符串中的一个一同调用: '-Infinity''Infinity''NaN' 。如果遇到无效的 JSON 数字则可以使用它引发异常。

如果 strict 为 false (默认为 True ),那么控制字符将被允许在字符串内。在此上下文中的控制字符编码在范围 0—31 内的字符,包括 '\t' (制表符), '\n''\r''\0'

如果反序列化的数据不是有效 JSON 文档,引发 JSONDecodeError 错误。

在 3.6 版更改: 所有形参现在都是 仅限关键字参数。

  • decode(s)

    返回 s 的 Python 表示形式(包含一个 JSON 文档的 str 实例)。

    如果给定的 JSON 文档无效则将引发 JSONDecodeError

  • raw_decode(s)

    s 中解码出 JSON 文档(以 JSON 文档开头的一个 str 对象)并返回一个 Python 表示形式为 2 元组以及指明该文档在 s 中结束位置的序号。

    这可以用于从一个字符串解码JSON文档,该字符串的末尾可能有无关的数据。

class json.JSONEncoder(**, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False, indent=None, separators=None, default=None*)

用于Python数据结构的可扩展JSON编码器。

默认支持以下对象和类型:

Python JSON
dict object — 对象
list, tuple array
str string
int, float, int 和 float 派生的枚举 number
True true
False false
None null

在 3.4 版更改: 添加了对 int 和 float 派生的枚举类的支持

为了将其拓展至识别其他对象,需要子类化并实现 default() 方法于另一种返回 o 的可序列化对象的方法如果可行,否则它应该调用超类实现(来引发 TypeError )。

如果 skipkeys 为假值(默认),则当尝试对非 str, int, floatNone 的键进行编码时将会引发 TypeError。 如果 skipkeys 为真值,这些条目将被直接跳过。

如果 ensure_ascii 是 true (即默认值),输出保证将所有输入的非 ASCII 字符转义。如果 ensure_ascii 是 false,这些字符会原样输出。

如果 check_circular 为 true (默认),那么列表,字典,和自定义编码的对象在编码期间会被检查重复循环引用防止无限递归(无限递归将导致 OverflowError )。否则,这样进行检查。

如果 allow_nan 为 true (默认),那么 NaNInfinity ,和 -Infinity 进行编码。此行为不符合 JSON 规范,但与大多数的基于 Javascript 的编码器和解码器一致。否则,它将是一个 ValueError 来编码这些浮点数。

如果 sort_keys 为 true (默认为: False ),那么字典的输出是按照键排序;这对回归测试很有用,以确保可以每天比较 JSON 序列化。

如果 indent 是一个非负整数或者字符串,那么 JSON 数组元素和对象成员会被美化输出为该值指定的缩进等级。 如果缩进等级为零、负数或者 "",则只会添加换行符。 None (默认值) 选择最紧凑的表达。 使用一个正整数会让每一层缩进同样数量的空格。 如果 indent 是一个字符串 (比如 "\t"),那个字符串会被用于缩进每一层。

在 3.2 版更改: 现允许使用字符串作为 indent 而不再仅仅是整数。

当被指定时,separators 应当是一个 (item_separator, key_separator) 元组。当 indentNone 时,默认值取 (', ', ': '),否则取 (',', ': ')。为了得到最紧凑的 JSON 表达式,你应该指定其为 (',', ':') 以消除空白字符。

在 3.4 版更改: 现当 indent 不是 None 时,采用 (',', ': ') 作为默认值。

default 被指定时,其应该是一个函数,每当某个对象无法被序列化时它会被调用。它应该返回该对象的一个可以被 JSON 编码的版本或者引发一个 TypeError。如果没有被指定,则会直接引发 TypeError

在 3.6 版更改: 所有形参现在都是 仅限关键字参数。

  • default(o)

    在子类中实现这种方法使其返回 o 的可序列化对象,或者调用基础实现(引发 TypeError )。

    例如,为了支持任意的迭代器,你可以这样来实现 default():

    def default(self, o):
       try:
           iterable = iter(o)
       except TypeError:
           pass
       else:
           return list(iterable)
       # Let the base class default method raise the TypeError
       return json.JSONEncoder.default(self, o)
  • encode(o)

    返回 Python o 数据结构的 JSON 字符串表达方式。例如:

    >>> json.JSONEncoder().encode({"foo": ["bar", "baz"]})
    '{"foo": ["bar", "baz"]}'
  • iterencode(o)

    编码给定对象 o ,并且让每个可用的字符串表达方式。例如:

    for chunk in json.JSONEncoder().iterencode(bigobject):
        mysocket.write(chunk)

异常

exception json.JSONDecodeError(msg, doc, pos)

拥有以下附加属性的 ValueError 的子类:

  • msg

    未格式化的错误消息。

  • doc

    正在解析的 JSON 文档。

  • pos

    The start index of doc where parsing failed.

  • lineno

    The line corresponding to pos.

  • colno

    The column corresponding to pos.

3.5 新版功能.

标准符合性和互操作性

JSON 格式由 RFC 7159ECMA-404 指定。此段落详细讲了这个模块符合 RFC 的级别。简单来说, JSONEncoderJSONDecoder 子类,和明确提到的参数以外的参数,不作考虑。

此模块不严格遵循于 RFC ,它实现了一些扩展是有效的 Javascript 但不是有效的 JSON。尤其是:

  • 无限和 NaN 数值是被接受并输出;
  • 对象内的重复名称是接受的,并且仅使用最后一对属性-值对的值。

自从 RFC 允许符合 RFC 的语法分析程序接收 不符合 RFC 的输入文本以来,这个模块的解串器在默认状态下默认符合 RFC 。

字符编码

RFC 要求使用 UTF-8 , UTF-16 ,或 UTF-32 之一来表示 JSON ,为了最大互通性推荐使用 UTF-8 。

RFC允许,尽管不是必须的,这个模块的序列化默认设置为 ensure_ascii=True ,这样消除输出以便结果字符串至容纳 ASCII 字符。

ensure_ascii 参数以外,此模块是严格的按照在 Python 对象和 Unicode strings 间的转换定义的,并且因此不能直接解决字符编码的问题。

RFC 禁止添加字符顺序标记( BOM )在 JSON 文本的开头,这个模块的序列化器不添加 BOM 标记在它的输出上。 RFC,准许 JSON 反序列化器忽略它们输入中的初始 BOM 标记,但不要求。此模块的反序列化器引发 ValueError 当存在初始 BOM 标记。

RFC 不会明确禁止包含字节序列的 JSON 字符串这不对应有效的 Unicode 字符(比如 不成对的 UTF-16 的替代物),但是它确实指出它们可能会导致互操作性问题。默认下,模块对这样的序列接受和输出(当在原始 str 存在时)代码点。

Infinite 和 NaN 数值

RFC 不允许 infinite 或者 NaN 数值的表达方式。尽管这样,默认情况下,此模块接受并且输出 Infinity-Infinity,和 NaN 好像它们是有效的JSON数字字面值

>>> # Neither of these calls raises an exception, but the results are not valid JSON
>>> json.dumps(float('-inf'))
'-Infinity'
>>> json.dumps(float('nan'))
'NaN'
>>> # Same when deserializing
>>> json.loads('-Infinity')
-inf
>>> json.loads('NaN')
nan

序列化器中, allow_nan 参数可用于替代这个行为。反序列化器中, parse_constant 参数,可用于替代这个行为。

对象中的重复名称

RFC 具体说明了 在 JSON对象里的名字应该是唯一的,但没有规定如何处理JSON对象中的重复名称。默认下,此模块不引发异常;作为替代,对于给定名它将忽略除姓-值对之外的所有对:

>>> weird_json = '{"x": 1, "x": 2, "x": 3}'
>>> json.loads(weird_json)
{'x': 3}

The object_pairs_hook parameter can be used to alter this behavior.

顶级非对象,非数组值

过时的 RFC 4627 指定的旧版本 JSON 要求 JSON 文本顶级值必须是 JSON 对象或数组( Python dictlist ),并且不能是 JSON null 值,布尔值,数值或者字符串值。 RFC 7159 移除这个限制,此模块没有并且从未在序列化器和反序列化器中实现这个限制。

无论如何,为了最大化地获取互操作性,你可能希望自己遵守该原则。

实现限制

一些 JSON 反序列化器的实现应该在以下方面做出限制:

  • 可接受的 JSON 文本大小
  • 嵌套 JSON 对象和数组的最高水平
  • JSON 数字的范围和精度
  • JSON 字符串的内容和最大长度

此模块不强制执行任何上述限制,除了相关的 Python 数据类型本身或者 Python 解释器本身的限制以外。

当序列化为 JSON ,在应用中当心此类限制这可能破坏你的 JSON 。特别是,通常将 JSON 数字反序列化为 IEEE 754 双精度数字,从而受到该表示方式的范围和精度限制。这是特别相关的,当序列化非常大的 Python int 值时,或者当序列化 “exotic” 数值类型的实例时比如 decimal.Decimal

命令行界面

源代码: Lib/json/tool.py


The json.tool module provides a simple command line interface to validate and pretty-print JSON objects.

如果未指定可选的 infileoutfile 参数,则将分别使用 sys.stdinsys.stdout:

$ echo '{"json": "obj"}' | python -m json.tool
{
    "json": "obj"
}
$ echo '{1.2:3.4}' | python -m json.tool
Expecting property name enclosed in double quotes: line 1 column 2 (char 1)

在 3.5 版更改: 输出现在将与输入顺序保持一致。 请使用 --sort-keys 选项来将输出按照键的字母顺序排序。

命令行选项

infile

要被验证或美化打印的 JSON 文件:

$ python -m json.tool mp_films.json
[
    {
        "title": "And Now for Something Completely Different",
        "year": 1971
    },
    {
        "title": "Monty Python and the Holy Grail",
        "year": 1975
    }
]

如果 infile 未指定,则从 sys.stdin 读取。

outfile

infile 输出写入到给定的 outfile。 在其他情况下写入到 sys.stdout

--sort-keys

将字典输出按照键的字母顺序排序。

3.5 新版功能.

--no-ensure-ascii

禁用非 ASCII 字符的转义,详情参见 json.dumps()

3.9 新版功能.

--json-lines

将每个输入行解析为单独的 JSON 对象。

3.8 新版功能.

--indent``,` `--tab``,` `--no-indent``,` `--compact

用于空白符控制的互斥选项。

3.9 新版功能.

-h``,` `--help

显示帮助消息。

mailcap —- Mailcap 文件处理

源代码: Lib/mailcap.py


Mailcap 文件可用来配置支持 MIME 的应用程序例如邮件阅读器和 Web 浏览器如何响应具有不同 MIME 类型的文件。 (“mailcap” 这个名称源自短语 “mail capability”。) 例如,一个 mailcap 文件可能包含 video/mpeg; xmpeg %s 这样的行。 然后,如果用户遇到 MIME 类型为 video/mpeg 的邮件消息或 Web 文档时,%s 将被替换为一个文件名 (通常属于临时文件) 并且会自动启动 xmpeg 程序来查看该文件。

mailcap 格式的说明文档见 RFC 1524, “A User Agent Configuration Mechanism For Multimedia Mail Format Information”,但它并不是一个互联网标准。 不过,mailcap 文件在大多数 Unix 系统上都受到支持。

mailcap.findmatch(caps, MIMEtype, key=’view’, filename=’/dev/null’, plist=[])

返回一个 2 元组;其中第一个元素是包含所要执行命令的字符串 (它可被传递给 os.system()),第二个元素是对应于给定 MIME 类型的 mailcap 条目。 如果找不到匹配的 MIME 类型,则将返回 (None, None)

key 是所需字段的名称,它代表要执行的活动类型;默认值是 ‘view’,因为在最通常的情况下你只是想要查看 MIME 类型数据的正文。 其他可能的值还有 ‘compose’ 和 ‘edit’,分别用于想要创建给定 MIME 类型正文或修改现有正文数据的情况。 请参阅 RFC 1524 获取这些字段的完整列表。

filename 是在命令行中用来替换 %s 的文件名;默认值 '/dev/null' 几乎肯定不是你想要的,因此通常你要通过指定一个文件名来重载它。

plist 可以是一个包含命名形参的列表;默认值只是一个空列表。 列表中的每个条目必须为包含形参名称的字符串、等于号 ('=') 以及形参的值。 Mailcap 条目可以包含形如 %{foo} 的命名形参,它将由名为 ‘foo’ 的形参的值所替换。 例如,如果命令行 showpartial %{id} %{number} %{total} 是在一个 mailcap 文件中,并且 plist 被设为 ['id=1', 'number=2', 'total=3'],则结果命令行将为 'showpartial 1 2 3'

在 mailcap 文件中,可以指定可选的 “test” 字段来检测某些外部条件(例如所使用的机器架构或窗口系统)来确定是否要应用 mailcap 行。 findmatch() 将自动检查此类条件并在检查未通过时跳过条目。

mailcap.getcaps()

返回一个将 MIME 类型映射到 mailcap 文件条目列表的字典。 此字典必须被传给 findmatch() 函数。 条目会被存储为字典列表,但并不需要了解此表示形式的细节。

此信息来自在系统中找到的所有 mailcap 文件。 用户的 mailcap 文件 $HOME/.mailcap 中的设置将覆盖系统 mailcap 文件 /etc/mailcap, /usr/etc/mailcap/usr/local/etc/mailcap 中的设置。

一个用法示例:

>>> import mailcap
>>> d = mailcap.getcaps()
>>> mailcap.findmatch(d, 'video/mpeg', filename='tmp1223')
('xmpeg tmp1223', {'view': 'xmpeg %s'})

mailbox —- 操作多种格式的邮箱

源代码: Lib/mailbox.py


本模块定义了两个类,MailboxMessage,用于访问和操作磁盘中的邮箱及其所包含的电子邮件。 Mailbox 提供了类似字典的从键到消息的映射。 Messageemail.message 模块的 Message 类增加了特定格式专属的状态和行为。 支持的邮箱格式有 Maildir, mbox, MH, Babyl 以及 MMDF。

Mailbox 对象

class mailbox.Mailbox

一个邮箱,它可以被检视和修改。

Mailbox 类定义了一个接口并且它不应被实例化。 而是应该让格式专属的子类继承 Mailbox 并且你的代码应当实例化一个特定的子类。

Mailbox 接口类似于字典,其中每个小键都有对应的消息。 键是由 Mailbox 实例发出的,它们将由实例来使用并且只对该 Mailbox 实例有意义。 键会持续标识一条消息,即使对应的消息已被修改,例如被另一条消息所替代。

可以使用 set 型方法 add() 将消息添加到 Mailbox 并可以使用 del 语句或 set 型方法 remove()discard() 将其移除。

Mailbox 接口语义在某些值得注意的方面与字典语义有所不同。 每次请求消息时,都会基于邮箱的当前状态生成一个新的表示形式(通常为 Message 实例)。 类似地,当向 Mailbox 实例添加消息时,所提供的消息表示形式的内容将被复制。 无论在哪种情况下 Mailbox 实例都不会保留对消息表示形式的引用。

默认的 Mailbox 迭代器会迭代消息表示形式,而不像默认的字典迭代器那样迭代键。 此外,在迭代期间修改邮箱是安全且有明确定义的。 在创建迭代器之后被添加到邮箱的消息将对该迭代不可见。 在迭代器产出消息之前被从邮箱移除的消息将被静默地跳过,但是使用来自迭代器的键也有可能导致 KeyError 异常,如果对应的消息后来被移除的话。

警告

在修改可能同时被其他某个进程修改的邮箱时要非常小心。 用于此种任务的最安全邮箱格式是 Maildir;请尽量避免使用 mbox 之类的单文件格式进行并发写入。 如果你正在修改一个邮箱,你 必须 在读取文件中的任何消息或者执行添加或删除消息等修改操作 之前 通过调用 lock() 以及 unlock() 方法来锁定它。 如果未锁定邮箱则将导致丢失消息或损坏整个邮箱的风险。

Mailbox 实例具有下列方法:

  • add(message)

    message 添加到邮箱并返回分配给它的键。

    形参 message 可以是 Message 实例、email.message.Message 实例、字符串、字节串或文件类对象(应当以二进制模式打开)。 如果 message 是适当的格式专属 Message 子类的实例(举例来说,如果它是一个 mboxMessage 实例而这是一个 mbox 实例),将使用其格式专属的信息。 在其他情况下,则会使用合理的默认值作为格式专属的信息。

    在 3.2 版更改: 增加了对二进制输入的支持。

  • remove(key)

    __delitem__(key)

    discard(key)

    从邮箱中删除对应于 key 的消息。

    当消息不存在时,如果此方法是作为 remove()__delitem__() 调用则会引发 KeyError 异常,而如果此方法是作为 discard() 调用则不会引发异常。 如果下层邮箱格式支持来自其他进程的并发修改则 discard() 的行为可能是更为适合的。

  • __setitem__(key, message)

    key 所对应的消息替换为 message*。 如果没有与 *key 所对应的消息则会引发 KeyError 异常。

    add() 一样,形参 message 可以是 Message 实例、email.message.Message 实例、字符串、字节串或文件类对象(应当以二进制模式打开)。 如果 message 是适当的格式专属 Message 子类的实例(举例来说,如果它是一个 mboxMessage 实例而这是一个 mbox 实例),将使用其格式专属的信息。 在其他情况下,当前与 key 所对应的消息的格式专属信息则会保持不变。

  • iterkeys()

    keys()

    如果通过 iterkeys() 调用则返回一个迭代所有键的迭代器,或者如果通过 keys() 调用则返回一个键列表。

  • itervalues()

    __iter__()

    values()

    如果通过 itervalues()__iter__() 调用则返回一个迭代所有消息的表示形式的迭代器,或者如果通过 values() 调用则返回一个由这些表示形式组成的列表。 消息会被表示为适当的格式专属 Message 子类的实例,除非当 Mailbox 实例被初始化时指定了自定义的消息工厂函数。

    注解

    __iter__() 的行为与字典不同,后者是对键进行迭代。

  • iteritems()

    items()

    如果通过 iteritems() 调用则返回一个迭代 (key, message) 对的迭代器,其中 key 为键而 message 为消息的表示形式,或者如果通过 items() 调用则返回一个由这种键值对组成的列表。 消息会被表示为适当的格式专属 Message 子类的实例,除非当 Mailbox 实例被初始化时指定了自定义的消息工厂函数。

  • get(key, default=None)

    __getitem__(key)

    返回对应于 key 的消息的表示形式。 当对应的消息不存在时,如果通过 get() 调用则返回 default 而如果通过 __getitem__() 调用此方法则会引发 KeyError 异常。 消息会被表示为适当的格式专属 Message 子类的实例,除非当 Mailbox 实例被初始化时指定了自定义的消息工厂函数。

  • get_message(key)

    将对应于 key 的消息的表示形式作为适当的格式专属 Message 子类的实例返回,或者如果对应的消息不存在则会引发 KeyError 异常。

  • get_bytes(key)

    返回对应于 key 的消息的字节表示形式,或者如果对应的消息不存在则会引发 KeyError 异常。

    3.2 新版功能.

  • get_string(key)

    返回对应于 key 的消息的字符串表示形式,或者如果对应的消息不存在则会引发 KeyError 异常。 消息是通过 email.message.Message 处理来将其转换为纯 7bit 表示形式的。

  • get_file(key)

    返回对应于 key 的消息的文件类表示形式,或者如果对应的消息不存在则会引发 KeyError 异常。 文件类对象的行为相当于以二进制模式打开。 当不再需要此文件时应当将其关闭。

    在 3.2 版更改: 此文件对象实际上是二进制文件;之前它被不正确地以文本模式返回。 并且,此文件类对象现在还支持上下文管理协议:你可以使用 with 语句来自动关闭它。

    注解

    不同于其他消息表示形式,文件类表示形式并不一定独立于创建它们的 Mailbox 实例或下层的邮箱。 每个子类都会提供更具体的文档。

  • __contains__(key)

    如果 key 有对应的消息则返回 True,否则返回 False

  • __len__()

    返回邮箱中消息的数量。

  • clear()

    从邮箱中删除所有消息。

  • pop(key, default=None)

    返回对应于 key 的消息的表示形式并删除该消息。 如果对应的消息不存在则返回 default。 消息会被表示为适当的格式专属 Message 子类的实例,除非当 Mailbox 实例被初始化时指定了自定义的消息工厂函数。

  • popitem()

    返回一个任意的 (key, message) 对,其中 key 为键而 message 为消息的表示形式,并删除对应的消息。 如果邮箱为空,则会引发 KeyError 异常。 消息会被表示为适当的格式专属 Message 子类的实例,除非当 Mailbox 实例被初始化时指定了自定义的消息工厂函数。

  • update(arg)

    形参 arg 应当是 keymessage 的映射或 (key, message) 对的可迭代对象。 用来更新邮箱以使得对于每个给定的 keymessage*,与 *key 相对应的消息会被设为 message*,就像通过使用 __setitem__() 一样。 类似于 __setitem__(),每个 *key 都必须在邮箱中有一个对应的消息否则将会引发 KeyError 异常,因此在通常情况下将 arg 设为 Mailbox 实例是不正确的。

    注解

    与字典不同,关键字参数是不受支持的。

  • flush()

    将所有待定的更改写入到文件系统。 对于某些 Mailbox 子类来说,更改总是被立即写入因而 flush() 并不会做任何事,但您仍然应当养成调用此方法的习惯。

  • lock()

    在邮箱上获取一个独占式咨询锁以使其他进程知道不能修改它。 如果锁无法被获取则会引发 ExternalClashError。 所使用的具体锁机制取决于邮箱的格式。 在对邮箱内容进行任何修改之前你应当 总是 锁定它。

  • unlock()

    释放邮箱上的锁,如果存在的话。

  • close()

    刷新邮箱,如果必要则将其解锁。 并关闭所有打开的文件。 对于某些 Mailbox 子类来说,此方法并不会做任何事。

Maildir

class mailbox.Maildir(dirname, factory=None, create=True)

Mailbox 的一个子类,用于 Maildir 格式的邮箱。 形参 factory 是一个可调用对象,它接受一个文件类消息表示形式(其行为相当于以二进制模式打开)并返回一个自定义的表示形式。 如果 factoryNone,则会使用 MaildirMessage 作为默认的消息表示形式。 如果 createTrue,则当邮箱不存在时会创建它。

如果 createTruedirname 路径存在,它将被视为已有的 maildir 而无需尝试验证其目录布局。

使用 dirname 这个名称而不使用 path 是出于历史原因。

Maildir 是一种基于目录的邮箱格式,它是针对 qmail 邮件传输代理而发明的,现在也得到了其他程序的广泛支持。 Maildir 邮箱中的消息存储在一个公共目录结构中的单独文件内。 这样的设计允许 Maildir 邮箱被多个彼此无关的程序访问和修改而不会导致数据损坏,因此文件锁定操作是不必要的。

Maildir 邮箱包含三个子目录,分别是: tmp, newcur。 消息会不时地在 tmp 子目录中创建然后移至 new 子目录来结束投递。 后续电子邮件客户端可能将消息移至 cur 子目录并将有关消息状态的信息存储在附带到其文件名的特殊 “info” 小节中。

Courier 电子邮件传输代理所引入的文件夹风格也是受支持的。 主邮箱中任何子目录只要其名称的第一个字符是 '.' 就会被视为文件夹。 文件夹名称会被 Maildir 表示为不带前缀 '.' 的形式。 每个文件夹自身都是一个 Maildir 邮箱但不应包含其他文件夹。 逻辑嵌套关系是使用 '.' 来划定层级,例如 “Archived.2005.07”。

注解

Maildir 规范要求使用在特定消息文件名中使用冒号 (':')。 但是,某些操作系统不允许将此字符用于文件名,如果你希望在这些操作系统上使用类似 Maildir 的格式,你应当指定改用另一个字符。 叹号 ('!') 是一个受欢迎的选择。 例如:

import mailboxmailbox.Maildir.colon = '!'

colon 属性也可以在每个实例上分别设置。

Maildir 实例具有 Mailbox 的所有方法及下列附加方法:

  • list_folders()

    返回所有文件夹名称的列表。

  • get_folder(folder)

    返回表示名称为 folder 的文件夹的 Maildir 实例。 如果文件夹不存在则会引发 NoSuchMailboxError 异常。

  • add_folder(folder)

    创建名称为 folder 的文件夹并返回表示它的 Maildir 实例。

  • remove_folder(folder)

    删除名称为 folder 的文件夹。 如果文件夹包含任何消息,则将引发 NotEmptyError 异常且该文件夹将不会被删除。

  • clean()

    从邮箱中删除最近 36 小时内未被访问过的临时文件。 Maildir 规范要求邮件阅读程序应当时常进行此操作。

Maildir 所实现的某些 Mailbox 方法值得进行特别的说明:

  • add(message)

    __setitem__(key, message)

    update(arg)

    警告

    这些方法会基于当前进程 ID 来生成唯一文件名。 当使用多线程时,可能发生未被检测到的名称冲突并导致邮箱损坏,除非是对线程进行协调以避免使用这些方法同时操作同一个邮箱。

  • flush()

    对 Maildir 邮箱的所有更改都会立即被应用,所以此方法并不会做任何事情。

  • lock()

    unlock()

    Maildir 邮箱不支持(或要求)锁定操作,所以此方法并不会做任何事情。

  • close()

    Maildir 实例不保留任何打开的文件并且下层的邮箱不支持锁定操作,所以此方法不会做任何事情。

  • get_file(key)

    根据主机平台的不同,当返回的文件保持打开状态时可能无法修改或移除下层的消息。

mbox

class mailbox.mbox(path, factory=None, create=True)

Mailbox 的子类,用于 mbox 格式的邮箱。 形参 factory 是一个可调用对象,它接受一个文件类消息表示形式(其行为相当于以二进制模式打开)并返回一个自定义的表示形式。 如果 factoryNone,则会使用 mboxMessage 作为默认的消息表示形式。 如果 createTrue,则当邮箱不存在时会创建它。

mbox 格式是在 Unix 系统上存储电子邮件的经典格式。 mbox 邮箱中的所有消息都存储在一个单独文件中,每条消息的开头由前五个字符为 “From “ 的行来指明。

还有一些 mbox 格式变种对原始格式中发现的缺点做了改进,mbox 只实现原始格式,有时被称为 mboxo*。 这意味着当存储消息时 *Content-Length 标头如果存在则会被忽略并且消息体中出现于行开头的任何 “From “ 会被转换为 “>From “,但是当读取消息时 “>From “ 则不会被转换为 “From “。

mbox 所实现的某些 Mailbox 方法值得进行特别的说明:

  • get_file(key)

    mbox 实例上调用 flush()close() 之后再使用文件可能产生无法预料的结果或者引发异常。

  • lock()

    unlock()

    使用三种锁机制 —- dot 锁,以及可能情况下的 flock()lockf() 系统调用。

参见

tin 上的 mbox 指南页面

该格式的规格说明,包括有关锁的详情。

在 Unix 上配置 Netscape Mail: 为何 Content-Length 格式是不好的

使用原始 mbox 格式而非其变种的一些理由。

“mbox” 是由多个彼此不兼容的邮箱格式构成的家族

有关 mbox 变种的历史。

MH

class mailbox.MH(path, factory=None, create=True)

Mailbox 的子类,用于 MH 格式的邮箱。 形参 factory 是一个可调用对象,它接受一个文件类消息表示形式(其行为相当于以二进制模式打开)并返回一个自定义的表示形式。 如果 factoryNone,则会使用 MHMessage 作为默认的消息表示形式。 如果 createTrue,则当邮箱不存在时会创建它。

MH 是一种基于目录的邮箱格式,它是针对 MH Message Handling System 电子邮件用户代理而发明的。 在 MH 邮箱的每条消息都放在单独文件中。 MH 邮箱中除了邮件消息还可以包含其他 MH 邮箱 (称为 文件夹)。 文件夹可以无限嵌套。 MH 邮箱还支持 序列,这是一种命名列表,用来对消息进行逻辑分组而不必将其移入子文件夹。 序列是在每个文件夹中名为 .mh_sequences 的文件内定义的。

MH 类可以操作 MH 邮箱,但它并不试图模拟 mh 的所有行为。 特别地,它并不会修改 context.mh_profile 文件也不会受其影响,这两个文件是 mh 用来存储状态和配置数据的。

MH 实例具有 Mailbox 的所有方法及下列附加方法:

  • list_folders()

    返回所有文件夹名称的列表。

  • get_folder(folder)

    返回表示名称为 folder 的文件夹的 MH 实例。 如果文件夹不存在则会引发 NoSuchMailboxError 异常。

  • add_folder(folder)

    创建名称为 folder 的文件夹并返回表示它的 MH 实例。

  • remove_folder(folder)

    删除名称为 folder 的文件夹。 如果文件夹包含任何消息,则将引发 NotEmptyError 异常且该文件夹将不会被删除。

  • get_sequences()

    返回映射到键列表的序列名称字典。 如果不存在任何序列,则返回空字典。

  • set_sequences(sequences)

    根据由映射到键列表的名称组成的字典 sequences 来重新定义邮箱中的序列,该字典与 get_sequences() 返回值的形式一样。

  • pack()

    根据需要重命名邮箱中的消息以消除序号中的空缺。 序列列表中的条目会做相应的修改。

    注解

    已发送的键会因此操作而失效并且不应当被继续使用。

MH 所实现的某些 Mailbox 方法值得进行特别的说明:

  • remove(key)

    __delitem__(key)

    discard(key)

    这些方法会立即删除消息。 通过在名称前加缀一个冒号作为消息删除标记的 MH 惯例不会被使用。

  • lock()

    unlock()

    使用三种锁机制 —- dot 锁,以及可能情况下下 flock()lockf() 系统调用。 对于 MH 邮箱来说,锁定邮箱意味着锁定 .mh_sequences 文件,并且仅在执行会影响单独消息文件的操作期间锁定单独消息文件。

  • get_file(key)

    根据主机平台的不同,当返回的文件保持打开状态时可能无法移除下层的消息。

  • flush()

    对 MH 邮箱的所有更改都会立即被应用,所以此方法并不会做任何事情。

  • close()

    MH 实例不保留任何打开的文件,所以此方法等价于 unlock()

参见

nmh - Message Handling System

nmh 的主页,这是原始 mh 的更新版本。

MH & nmh: Email for Users & Programmers

使用 GPL 许可证的介绍 mhnmh 的图书,包含有关该邮箱格式的各种信息。

Babyl

class mailbox.Babyl(path, factory=None, create=True)

Mailbox 的子类,用于 Babyl 格式的邮箱。 形参 factory 是一个可调用对象,它接受一个文件类表示形式(其行为相当于以二进制模式打开)并返回一个自定义的表示形式。 如果 factoryNone,则会使用 BabylMessage 作为默认的消息表示形式。 如果 createTrue,则当邮箱不存在时会创建它。

Babyl 是 Rmail 电子邮箱用户代理所使用单文件邮箱格式,包括在 Emacs 中。 每条消息的开头由一个包含 Control-Underscore ('\037') 和 Control-L ('\014') 这两个字符的行来指明。 消息的结束由下一条消息的开头来指明,或者当为最后一条消息时则由一个包含 Control-Underscore ('\037') 字符的行来指明。

Babyl 邮箱中的消息带有两组标头:原始标头和所谓的可见标头。 可见标头通常为原始标头经过重格式化和删减以更易读的子集。 Babyl 邮箱中的每条消息都附带了一个 标签 列表,即记录消息相关额外信息的短字符串,邮箱中所有的用户定义标签列表会存储于 Babyl 的选项部分。

Babyl 实例具有 Mailbox 的所有方法及下列附加方法:

  • get_labels()

    返回邮箱中使用的所有用户定义标签名称的列表。

    注解

    邮箱中存在哪些标签会通过检查实际的消息而非查询 Babyl 选项部分的标签列表,但 Babyl 选项部分会在邮箱被修改时更新。

Babyl 所实现的某些 Mailbox 方法使得进行特别的说明:

  • get_file(key)

    在 Babyl 邮箱中,消息的标头并不是与消息体存储在一起的。 要生成文件类表示形式,标头和消息体会被一起拷贝到一个 io.BytesIO 实例中,它具有与文件相似的 API。 因此,文件类对象实际上独立于下层邮箱,但与字符串表达形式相比并不会更节省内存。

  • lock()

    unlock()

    使用三种锁机制 —- dot 锁,以及可能情况下的 flock()lockf() 系统调用。

参见

Format of Version 5 Babyl Files

Babyl 格式的规格说明。

Reading Mail with Rmail

Rmail 的帮助手册,包含了有关 Babyl 语义的一些信息。

MMDF

class mailbox.MMDF(path, factory=None, create=True)

Mailbox 的子类,用于 MMDF 格式的邮箱。 形参 factory 是一个可调用对象,它接受一个文件类消息表示形式(其行为相当于以二进制模式打开)并返回一个自定义的表示形式。 如果 factoryNone,则会使用 MMDFMessage 作为默认的消息表示形式。 如果 createTrue,则当邮箱不存在时会创建它。

MMDF 是一种专用于电子邮件传输代理 Multichannel Memorandum Distribution Facility 的单文件邮箱格式。 每条消息使用与 mbox 消息相同的形式,但其前后各有包含四个 Control-A ('\001') 字符的行。 与 mbox 格式一样,每条消息的开头由一个前五个字符为 “From “ 的行来指明,但当存储消息时额外出现的 “From “ 不会被转换为 “>From “ 因为附加的消息分隔符可防止将这些内容误认为是后续消息的开头。

MMDF 所实现的某些 Mailbox 方法值得进行特别的说明:

  • get_file(key)

    MMDF 实例上调用 flush()close() 之后再使用文件可能产生无法预料的结果或者引发异常。

  • lock()

    unlock()

    使用三种锁机制 —- dot 锁,以及可能情况下的 flock()lockf() 系统调用。

参见

tin 上的 mmdf 指南页面

MMDF 格式的规格说明,来自新闻阅读器 tin 的文档。

MMDF

一篇描述 Multichannel Memorandum Distribution Facility 的维基百科文章。

Message 对象

class mailbox.Message(message=None)

email.message 模块的 Message 的子类。 mailbox.Message 的子类添加了特定邮箱格式专属的状态和行为。

如果省略了 message*,则新实例会以默认的空状态被创建。 如果 *message 是一个 email.message.Message 实例,其内容会被拷贝;此外,如果 message 是一个 Message 实例,则任何格式专属信息会尽可能地被拷贝。 如果 message 是一个字符串、字节串或文件,则它应当包含兼容 RFC 2822 的消息,该消息会被读取和解析。 文档应当以二进制模式打开,但文本模式的文件也会被接受以向下兼容。

各个子类所提供的格式专属状态和行为各有不同,但总的来说只有那些不仅限于特定邮箱的特性才会被支持(虽然这些特性可能专属于特定邮箱格式)。 例如,例如,单文件邮箱格式的文件偏移量和基于目录的邮箱格式的文件名都不会被保留,因为它们都仅适用于对应的原始邮箱。 但消息是否已被用户读取或标记为重要等状态则会被保留,因为它们适用于消息本身。

不要求用 Message 实例来表示使用 Mailbox 实例所提取到的消息。 在某些情况下,生成 Message 表示形式所需的时间和内存空间可能是不可接受的。 对于此类情况,Mailbox 实例还提供了字符串和文件类表示形式,并可在初始化 Mailbox 实例时指定自定义的消息工厂函数。

MaildirMessage

class mailbox.MaildirMessage(message=None)

具有 Maildir 专属行为的消息。 形参 message 的含义与 Message 构造器一致。

通常,邮件用户代理应用程序会在用户第一次打开并关闭邮箱之后将 new 子目录中的所有消息移至 cur 子目录,将这些消息记录为旧消息,无论它们是否真的已被阅读。 cur 下的每条消息都有一个 “info” 部分被添加到其文件名中以存储有关其状态的信息。 (某些邮件阅读器还会把 “info” 部分也添加到 new 下的消息中。) “info” 部分可以采用两种形式之一:它可能包含 “2,” 后面跟一个经标准化的旗标列表(例如 “2,FR”)或者它可能包含 “1,” 后面跟所谓的实验性信息。 Maildir 消息的标准旗标如下:

旗标 含意 说明
D 草稿 正在撰写中
F 已标记 已被标记为重要
P 已检视 转发,重新发送或退回
R 已回复 回复给
S 已查看 已阅读
T 已删除 标记为可被删除

MaildirMessage 实例提供以下方法:

  • get_subdir()

    返回 “new” (如果消息应当被存储在 new 子目录下) 或者 “cur” (如果消息应当被存储在 cur 子目录下)。

    注解

    消息通常会在其邮箱被访问后被从 new 移至 cur,无论该消息是否已被阅读。 如果 msg.get_flags()中的 `"S" 为True则说明消息msg` 已被阅读。

  • set_subdir(subdir)

    设置消息应当被存储到的子目录。 形参 subdir 必须为 “new” 或 “cur”。

  • get_flags()

    返回一个指明当前所设旗标的字符串。 如果消息符合标准的 Maildir 格式,则结果为零或按字母顺序各自出现一次的 'D', 'F', 'P', 'R', 'S''T' 的拼接。 如果未设任何旗标或者如果 “info” 包含实验性语义则返回空字符串。

  • set_flags(flags)

    设置由 flags 所指定的旗标并重置所有其它旗标。

  • add_flag(flag)

    设置由 flag 所指明的旗标而不改变其他旗标。 要一次性添加一个以上的旗标,flag 可以为包含一个以上字符的字符串。 当前 “info” 会被覆盖,无论它是否只包含实验性信息而非旗标。

  • remove_flag(flag)

    重置由 flag 所指明的旗标而不改变其他旗标。 要一次性移除一个以上的旗标,flag 可以为包含一个以上字符的字符串。 如果 “info” 包含实验性信息而非旗标,则当前的 “info” 不会被修改。

  • get_date()

    以表示 Unix 纪元秒数的浮点数形式返回消息的发送日期。

  • set_date(date)

    将消息的发送日期设为 date,一个表示 Unix 纪元秒数的浮点数。

  • get_info()

    返回一个包含消息的 “info” 的字符串。 这适用于访问和修改实验性的 “info” (即不是由旗标组成的列表)。

  • set_info(info)

    将 “info” 设为 info,这应当是一个字符串。

当一个 MaildirMessage 实例基于 mboxMessageMMDFMessage 实例被创建时,将会忽略 StatusX-Status 标头并进行下列转换:

结果状态 mboxMessageMMDFMessage 状态
“cur” 子目录 O 旗标
F 旗标 F 旗标
R 旗标 A 旗标
S 旗标 R 旗标
T 旗标 D 旗标

当一个 MaildirMessage 实例基于 MHMessage 实例被创建时,将进行下列转换:

结果状态 MHMessage 状态
“cur” 子目录 “unseen” 序列
“cur” 子目录和 S 旗标 非 “unseen” 序列
F 旗标 “flagged” 序列
R 旗标 “replied” 序列

当一个 MaildirMessage 实例基于 BabylMessage 实例被创建时,将进行下列转换:

结果状态 BabylMessage 状态
“cur” 子目录 “unseen” 标签
“cur” 子目录和 S 旗标 非 “unseen” 标签
P 旗标 “forwarded” 或 “resent” 标签
R 旗标 “answered” 标签
T 旗标 “deleted” 标签

mboxMessage

class mailbox.mboxMessage(message=None)

具有 mbox 专属行为的消息。 形参 message 的含义与 Message 构造器一致。

mbox 邮箱中的消息会一起存储在单个文件中。 发件人的信封地址和发送时间通常存储在指明每条消息的起始的以 “From “ 打头的行中,不过在 mbox 的各种实现之间此数据的确切格式具有相当大的差异。 指明消息状态的各种旗标,例如是否已读或标记为重要等等通常存储在 StatusX-Status 标头中。

传统的 mbox 消息旗标如下:

旗标 含意 说明
R 已阅读 已阅读
O 旧消息 之前已经过 MUA 检测
D 已删除 标记为可被删除
F 已标记 已被标记为重要
A 已回复 回复给

“R” 和 “O” 旗标存储在 Status 标头中,而 “D”, “F” 和 “A” 旗标存储在 X-Status 标头中。 旗标和标头通常会按上述顺序显示。

mboxMessage 实例提供了下列方法:

  • get_from()

    返回一个表示在 mbox 邮箱中标记消息起始的 “From “ 行的字符串。 开头的 “From “ 和末尾的换行符会被去除。

  • set_from(from_, time_=None)

    将 “From “ 行设为 from_*,这应当被指定为不带开头的 “From “ 或末尾的换行符。 为方便起见,可以指定 *time_ 并将经过适当的格式化再添加到 from_。 如果指定了 time_,它应当是一个 time.struct_time 实例,适合传入 time.strftime() 的元组或者 True (以使用 time.gmtime())。

  • get_flags()

    返回一个指明当前所设旗标的字符串。 如果消息符合规范格式,则结果为零或各自出现一次的 'R', 'O', 'D', 'F''A' 按上述顺序的拼接。

  • set_flags(flags)

    设置由 flags 所指明的旗标并重启所有其他旗标。 形参 flags 应当为零或各自出现多次的 'R', 'O', 'D', 'F''A' 按任意顺序的拼接。

  • add_flag(flag)

    设置由 flag 所指明的旗标而不改变其他旗标。 要一次性添加一个以上的旗标,flag 可以为包含一个以上字符的字符串。

  • remove_flag(flag)

    重置由 flag 所指明的旗标而不改变其他旗标。 要一次性移除一个以上的旗标,flag 可以为包含一个以上字符的字符串。

当一个 mboxMessage 实例基于 MaildirMessage 实例被创建时,”From “ 行会基于 MaildirMessage 实例的发送时间被生成,并进行下列转换:

结果状态 MaildirMessage 状态
R 旗标 S 旗标
O 旗标 “cur” 子目录
D 旗标 T 旗标
F 旗标 F 旗标
A 旗标 R 旗标

当一个 mboxMessage 实例基于 MHMessage 实例被创建时,将进行下列转换:

结果状态 MHMessage 状态
R 旗标 和 O 旗标 非 “unseen” 序列
O 旗标 “unseen” 序列
F 旗标 “flagged” 序列
A 旗标 “replied” 序列

当一个 mboxMessage 实例基于 BabylMessage 实例被创建时,将进行下列转换:

结果状态 BabylMessage 状态
R 旗标 和 O 旗标 非 “unseen” 标签
O 旗标 “unseen” 标签
D 旗标 “deleted” 标签
A 旗标 “answered” 标签

当一个 Message 实例基于 MMDFMessage 实例被创建时,”From “ 行会被拷贝并直接对应所有旗标。

结果状态 MMDFMessage 状态
R 旗标 R 旗标
O 旗标 O 旗标
D 旗标 D 旗标
F 旗标 F 旗标
A 旗标 A 旗标

MHMessage

class mailbox.MHMessage(message=None)

具有 MH 专属行为的消息。 形参 message 的含义与 Message 构造器一致。

MH 消息不支持传统意义上的标记或旗标,但它们支持序列,即对任意消息的逻辑分组。 某些邮件阅读程序 (但不包括标准 mhnmh) 以与其他格式使用旗标类似的方式来使用序列,如下所示:

序列 说明
unseen 未阅读,但之前已经过 MUA 检测
已回复 回复给
已标记 已被标记为重要

MHMessage 实例提供了下列方法:

  • get_sequences()

    返回一个包含此消息的序列的名称的列表。

  • set_sequences(sequences)

    设置包含此消息的序列的列表。

  • add_sequence(sequence)

    sequence 添加到包含此消息的序列的列表。

  • remove_sequence(sequence)

    sequence 从包含此消息的序列的列表中移除。

当一个 MHMessage 实例基于 MaildirMessage 实例被创建时,将进行下列转换:

结果状态 MaildirMessage 状态
“unseen” 序列 非 S 旗标
“replied” 序列 R 旗标
“flagged” 序列 F 旗标

当一个 MHMessage 实例基于 mboxMessageMMDFMessage 实例被创建时,将会忽略 StatusX-Status 标头并进行下列转换:

结果状态 mboxMessageMMDFMessage 状态
“unseen” 序列 非 R 旗标
“replied” 序列 A 旗标
“flagged” 序列 F 旗标

当一个 MHMessage 实例基于 BabylMessage 实例被创建时,将进入下列转换:

结果状态 BabylMessage 状态
“unseen” 序列 “unseen” 标签
“replied” 序列 “answered” 标签

BabylMessage

class mailbox.BabylMessage(message=None)

具有 Babyl 专属行为的消息。 形参 message 的含义与 Message 构造器一致。

某些消息标签被称为 属性,根据惯例被定义为具有特殊的含义。 这些属性如下所示:

标签 说明
unseen 未阅读,但之前已经过 MUA 检测
deleted 标记为可被删除
filed 复制到另一个文件或邮箱
answered 回复给
forwarded 已转发
edited 已被用户修改
resent 已重发

默认情况下,Rmail 只显示可见标头。 不过 BabylMessage 类会使用原始标头因为它们更完整。 如果需要可以显式地访问可见标头。

BabylMessage 实例提供了下列方法:

  • get_labels()

    返回邮件上的标签列表。

  • set_labels(labels)

    将消息上的标签列表设置为 labels

  • add_label(label)

    label 添加到消息上的标签列表中。

  • remove_label(label)

    从消息上的标签列表中删除 label

  • get_visible()

    返回一个 Message 实例,其标头为消息的可见标头而其消息体为空。

  • set_visible(visible)

    将消息的可见标头设为与 message 中的标头一致。 形参 visible 应当是一个 Message 实例,email.message.Message 实例,字符串或文件类对象(且应当以文本模式打开)。

  • update_visible()

    当一个 BabylMessage 实例的原始标头被修改时,可见标头不会自动进行对应修改。 此方法将按以下方式更新可见标头:每个具有对应原始标头的可见标头会被设为原始标头的值,每个没有对应原始标头的可见标头会被移除,而任何存在于原始标头但不存在于可见标头中的 Date, From, Reply-To, To, CCSubject 会被添加至可见标头。

当一个 BabylMessage 实例基于 MaildirMessage 实例被创建时,将进行下列转换:

结果状态 MaildirMessage 状态
“unseen” 标签 非 S 旗标
“deleted” 标签 T 旗标
“answered” 标签 R 旗标
“forwarded” 标签 P 旗标

当一个 BabylMessage 实例基于 mboxMessageMMDFMessage 实例被创建时,将会忽略 StatusX-Status 标头并进入下列转换:

结果状态 mboxMessageMMDFMessage 状态
“unseen” 标签 非 R 旗标
“deleted” 标签 D 旗标
“answered” 标签 A 旗标

当一个 BabylMessage 实例基于 MHMessage 实例被创建时,将进入下列转换:

结果状态 MHMessage 状态
“unseen” 标签 “unseen” 序列
“answered” 标签 “replied” 序列

MMDFMessage

class mailbox.MMDFMessage(message=None)

具有 MMDF 专属行为的消息。 形参 message 的含义与 Message 构造器一致。

与 mbox 邮箱中的消息类似,MMDF 消息会与将发件人的地址和发送日期作为以 “From “ 打头的初始行一起存储。 同样地,指明消息状态的旗标通常存储在 StatusX-Status 标头中。

传统的 MMDF 消息旗标与 mbox 消息的类似,如下所示:

旗标 含意 说明
R 已阅读 已阅读
O 旧消息 之前已经过 MUA 检测
D 已删除 标记为可被删除
F 已标记 已被标记为重要
A 已回复 回复给

“R” 和 “O” 旗标存储在 Status 标头中,而 “D”, “F” 和 “A” 旗标存储在 X-Status 标头中。 旗标和标头通常会按上述顺序显示。

MMDFMessage 实例提供了下列方法,与 mboxMessage 所提供的类似:

  • get_from()

    返回一个表示在 mbox 邮箱中标记消息起始的 “From “ 行的字符串。 开头的 “From “ 和末尾的换行符会被去除。

  • set_from(from_, time_=None)

    将 “From “ 行设为 from_*,这应当被指定为不带开头的 “From “ 或末尾的换行符。 为方便起见,可以指定 *time_ 并将经过适当的格式化再添加到 from_。 如果指定了 time_,它应当是一个 time.struct_time 实例,适合传入 time.strftime() 的元组或者 True (以使用 time.gmtime())。

  • get_flags()

    返回一个指明当前所设旗标的字符串。 如果消息符合规范格式,则结果为零或各自出现一次的 'R', 'O', 'D', 'F''A' 按上述顺序的拼接。

  • set_flags(flags)

    设置由 flags 所指明的旗标并重启所有其他旗标。 形参 flags 应当为零或各自出现多次的 'R', 'O', 'D', 'F''A' 按任意顺序的拼接。

  • add_flag(flag)

    设置由 flag 所指明的旗标而不改变其他旗标。 要一次性添加一个以上的旗标,flag 可以为包含一个以上字符的字符串。

  • remove_flag(flag)

    重置由 flag 所指明的旗标而不改变其他旗标。 要一次性移除一个以上的旗标,flag 可以为包含一个以上字符的字符串。

当一个 MMDFMessage 实例基于 MaildirMessage 实例被创建时,”From “ 行会基于 MaildirMessage 实例的发送日期被生成,并进入下列转换:

结果状态 MaildirMessage 状态
R 旗标 S 旗标
O 旗标 “cur” 子目录
D 旗标 T 旗标
F 旗标 F 旗标
A 旗标 R 旗标

当一个 MMDFMessage 实例基于 MHMessage 实例被创建时,将进行下列转换:

结果状态 MHMessage 状态
R 旗标 和 O 旗标 非 “unseen” 序列
O 旗标 “unseen” 序列
F 旗标 “flagged” 序列
A 旗标 “replied” 序列

当一个 MMDFMessage 实例基于 BabylMessage 实例被创建时,将进行下列转换:

结果状态 BabylMessage 状态
R 旗标 和 O 旗标 非 “unseen” 标签
O 旗标 “unseen” 标签
D 旗标 “deleted” 标签
A 旗标 “answered” 标签

当一个 MMDFMessage 实例基于 mboxMessage 实例被创建时,”From “ 行会被拷贝并直接对应所有旗标:

结果状态 mboxMessage 状态
R 旗标 R 旗标
O 旗标 O 旗标
D 旗标 D 旗标
F 旗标 F 旗标
A 旗标 A 旗标

异常

mailbox 模块中定义了下列异常类:

exception mailbox.Error

所有其他模块专属异常的基类。

exception mailbox.NoSuchMailboxError

在期望获得一个邮箱但未找到时被引发,例如当使用不存在的路径来实例化一个 Mailbox 子类时 (且将 create 形参设为 False),或是当打开一个不存在的路径时。

exception mailbox.NotEmptyError

在期望一个邮箱为空但不为空时被引发,例如当删除一个包含消息的文件夹时。

exception mailbox.ExternalClashError

在某些邮箱相关条件超出了程序控制范围导致其无法继续运行时被引发,例如当要获取的锁已被另一个程序获取时,或是当要生成的唯一性文件名已存在时。

exception mailbox.FormatError

在某个文件中的数据无法被解析时被引发,例如当一个 MH 实例尝试读取已损坏的 .mh_sequences 文件时。

例子

一个打印指定邮箱中所有消息的主题的简单示例:

import mailbox
for message in mailbox.mbox('~/mbox'):
    subject = message['subject']       # Could possibly be None.
    if subject and 'python' in subject.lower():
        print(subject)

要将所有邮件从 Babyl 邮箱拷贝到 MH 邮箱,请转换所有可转换的格式专属信息:

import mailbox
destination = mailbox.MH('~/Mail')
destination.lock()
for message in mailbox.Babyl('~/RMAIL'):
    destination.add(mailbox.MHMessage(message))
destination.flush()
destination.unlock()

这个示例将来自多个邮件列表的邮件分类放入不同的邮箱,小心避免由于其他程序的并发修改导致的邮件损坏,由于程序中断导致的邮件丢失,或是由于邮箱中消息格式错误导致的意外终止:

import mailbox
import email.errors
list_names = ('python-list', 'python-dev', 'python-bugs')
boxes = {name: mailbox.mbox('~/email/%s' % name) for name in list_names}
inbox = mailbox.Maildir('~/Maildir', factory=None)
for key in inbox.iterkeys():
    try:
        message = inbox[key]
    except email.errors.MessageParseError:
        continue                # The message is malformed. Just leave it.
    for name in list_names:
        list_id = message['list-id']
        if list_id and name in list_id:
            # Get mailbox to use
            box = boxes[name]
            # Write copy to disk before removing original.
            # If there's a crash, you might duplicate a message, but
            # that's better than losing a message completely.
            box.lock()
            box.add(message)
            box.flush()
            box.unlock()
            # Remove original message
            inbox.lock()
            inbox.discard(key)
            inbox.flush()
            inbox.unlock()
            break               # Found destination, so stop looking.
for box in boxes.itervalues():
    box.close()

mimetypes —- 映射文件名到 MIME 类型

源代码: Lib/mimetypes.py


mimetypes 模块可以在文件名或 URL 和关联到文件扩展名的 MIME 类型之间执行转换。 所提供的转换包括从文件名到 MIME 类型和从 MIME 类型到文件扩展名;后一种转换不支持编码格式。

该模块提供了一个类和一些便捷函数。 这些函数是该模块通常的接口,但某些应用程序可能也会希望使用类。

下列函数提供了此模块的主要接口。 如果此模块尚未被初始化,它们将会调用 init(),如果它们依赖于 init() 所设置的信息的话。

mimetypes.guess_type(url, strict=True)

根据 url 给出的文件名、路径或 URL 来猜测文件的类型,URL 可以为字符串或 path-like object。

返回值是一个元组 (type, encoding) 其中 type 在无法猜测(后缀不存在或者未知)时为 None,或者为 'type/subtype' 形式的字符串,可以作为 MIME content-type 标头。

encoding 在无编码格式时为 None,或者为程序所用的编码格式 (例如 compressgzip)。 它可以作为 Content-Encoding 标头,但 不可 作为 Content-Transfer-Encoding 标头。 映射是表格驱动的。 编码格式前缀对大小写敏感;类型前缀会先以大小写敏感方式检测再以大小写不敏感方式检测。

可选的 strict 参数是一个旗标,指明要将已知 MIME 类型限制在 IANA 已注册 的官方类型之内。 当 strictTrue 时(默认值),则仅支持 IANA 类型;当 strictFalse 时,则还支持某些附加的非标准但常用的 MIME 类型。

在 3.8 版更改: 增加了 path-like object 作为 url 的支持。

mimetypes.guess_all_extensions(type, strict=True)

根据由 type 给出的文件 MIME 类型猜测其扩展名。 返回值是由所有可能的文件扩展名组成的字符串列表,包括开头的点号 ('.')。 这些扩展名不保证能关联到任何特定的数据流,但是将会由 guess_type() 映射到 MIME 类型 type

可选的 strict 参数具有与 guess_type() 函数一致的含义。

mimetypes.guess_extension(type, strict=True)

根据由 type 给出的文件 MIME 类型猜测其扩展名。 返回值是一个表示文件扩展名的字符串,包括开头的点号 ('.')。 该扩展名不保证能关联到任何特定的数据流,但是将会由 guess_type() 映射到 MIME 类型 type*。 如果不能猜测出 *type 的扩展名,则将返回 None

可选的 strict 参数具有与 guess_type() 函数一致的含义。

有一些附加函数和数据项可被用于控制此模块的行为。

mimetypes.init(files=None)

初始化内部数据结构。 files 如果给出则必须是一个文件名序列,它应当被用于协助默认的类型映射。 如果省略则要使用的文件名会从 knownfiles 中获取; 在 Windows 上,将会载入当前注册表设置。 在 filesknownfiles 中指定的每个文件名的优先级将高于在它之前的文件名。 init() 允许被重复调用。

files 指定一个空列表将防止应用系统默认选项:将只保留来自内置列表的常用值。

如果 filesNone 则内部数据结构会完全重建为其初始默认值。 这是一个稳定操作并将在多次调用时产生相同的结果。

在 3.2 版更改: 在之前版本中,Windows 注册表设置会被忽略。

mimetypes.read_mime_types(filename)

载入在文件 filename 中给定的类型映射,如果文件存在的话。 返回的类型映射会是一个字典,其中的键值对为文件扩展名包括开头的点号 ('.') 与 'type/subtype' 形式的字符串。 如果文件 filename 不存在或无法被读取,则返回 None

mimetypes.add_type(type, ext, strict=True)

添加一个从 MIME 类型 type 到扩展名 ext 的映射。 当扩展名已知时,新类型将替代旧类型。 当类型已知时,扩展名将被添加到已知扩展名列表。

strictTrue 时(默认值),映射将被添加到官方 MIME 类型,否则添加到非标准类型。

mimetypes.inited

指明全局数据结构是否已被初始化的旗标。 这会由 init() 设为 True

mimetypes.knownfiles

通常安装的类型映射文件名列表。 这些文件一般被命名为 mime.types 并会由不同的包安装在不同的位置。

mimetypes.suffix_map

将后缀映射到其他后缀的字典。 它被用来允许识别已编码的文件,其编码格式和类型是由相同的扩展名来指明的。 例如,.tgz 扩展名被映射到 .tar.gz 以允许编码格式和类型被分别识别。

mimetypes.encodings_map

映射文件扩展名到编码格式类型的字典。

mimetypes.types_map

映射文件扩展名到 MIME 类型的字典。

mimetypes.common_types

映射文件扩展名到非标准但常见的 MIME 类型的字典。

此模块一个使用示例:

>>> import mimetypes
>>> mimetypes.init()
>>> mimetypes.knownfiles
['/etc/mime.types', '/etc/httpd/mime.types', ... ]
>>> mimetypes.suffix_map['.tgz']
'.tar.gz'
>>> mimetypes.encodings_map['.gz']
'gzip'
>>> mimetypes.types_map['.tgz']
'application/x-tar-gz'

MimeTypes 对象

MimeTypes 类可以被用于那些需要多个 MIME 类型数据库的应用程序;它提供了与 mimetypes 模块所提供的类似接口。

class mimetypes.MimeTypes(filenames=(), strict=True)

这个类表示 MIME 类型数据库。 默认情况下,它提供了对与此模块其余部分一致的数据库的访问权限。 这个初始数据库是此模块所提供数据库的一个副本,并可以通过使用 read()readfp() 方法将附加的 mime.types 样式文载入到数据库中来进行扩展。 如果不需要默认数据的话这个映射字典也可以在载入附加数据之前先被清空。

可选的 filenames 形参可被用来让附加文件被载入到默认数据库“之上”。

  • suffix_map

    将后缀映射到其他后缀的字典。 它被用来允许识别已编码的文件,其编码格式和类型是由相同的扩展名来指明的。 例如,.tgz 扩展名被映射到 .tar.gz 以允许编码格式和类型被分别识别。 这是在模块中定义的全局 suffix_map 的一个副本。

  • encodings_map

    映射文件扩展名到编码格式类型的字典。 这是在模块中定义的全局 encodings_map 的一个副本。

  • types_map

    包含两个字典的元组,将文件扩展名映射到 MIME 类型:第一个字典针对非标准类型而第二个字典针对标准类型。 它们会由 common_typestypes_map 来初始化。

  • types_map_inv

    包含两个字典的元组,将 MIME 类型映射到文件扩展名列表:第一个字典针对非标准类型而第二个字典针对标准类型。 它们会由 common_typestypes_map 来初始化。

  • guess_extension(type, strict=True)

    类似于 guess_extension() 函数,使用存储的表作为对象的一部分。

  • guess_type(url, strict=True)

    类似于 guess_type() 函数,使用存储的表作为对象的一部分。

  • guess_all_extensions(type, strict=True)

    类似于 guess_all_extensions() 函数,使用存储的表作为对象的一部分。

  • read(filename, strict=True)

    从名称为 filename 的文件载入 MIME 信息。 此方法使用 readfp() 来解析文件。

    如果 strictTrue,信息将被添加到标准类型列表,否则添加到非标准类型列表。

  • readfp(fp, strict=True)

    从打开的文件 fp 载入 MIME 类型信息。 文件必须具有标准 mime.types 文件的格式。

    如果 strictTrue,信息将被添加到标准类型列表,否则添加到非标准类型列表。

  • read_windows_registry(strict=True)

    从 Windows 注册表载入 MIME 类型信息。

    可用性: Windows。

    如果 strictTrue,信息将被添加到标准类型列表,否则添加到非标准类型列表。

    3.2 新版功能.

base64 —- Base16, Base32, Base64, Base85 数据编码

源代码: Lib/base64.py


此模块提供了将二进制数据编码为可打印的 ASCII 字符以及将这种编码格式解码回二进制数据的函数。 它为 RFC 4648 所定义的 Base16, Base32 和 Base64 算法及已成为事实标准的 Ascii85 和 Base85 编码格式提供了编码和解码函数。

RFC 4648 中的编码格式适用于编码二进制数据使得它能安全地通过电子邮件发送、用作 URL 的一部分,或者包括在 HTTP POST 请求之中。 此编码格式算法与 uuencode 程序并不相同。

此模块提供了两个接口。 较新的接口支持将 字节类对象 编码为 ASCII bytes,以及将 字节类对象 或包含 ASCII 的字符串解码为 bytes。 在 RFC 4648 中定义的几种 base-64 字母表(普通的以及 URL 和文件系统安全的)都受到支持。

旧的接口不提供从字符串的解码操作,但提供了操作 文件对象 的编码和解码函数。旧接口只支持标准的 Base64 字母表,并且按照 RFC 2045 的规范每 76 个字符增加一个换行符。注意:如果你需要支持 RFC 2045,那么使用 email 模块可能更加合适。

在 3.3 版更改: 新的接口提供的解码函数现在已经支持只包含 ASCII 的 Unicode 字符串。

在 3.4 版更改: 所有 类字节对象 现在已经被所有编码和解码函数接受。添加了对 Ascii85/Base85 的支持。

新的接口提供:

base64.b64encode(s, altchars=None)

对 bytes-like object s 进行 Base64 编码,并返回编码后的 bytes

可选项 altchars 必须是一个长 2 字节的 bytes-like object,它指定了用于替换 +/ 的字符。这允许应用程序生成 URL 或文件系统安全的 Base64 字符串。默认值是 None,使用标准 Base64 字母表。

base64.b64decode(s, altchars=None, validate=False)

解码 Base64 编码过的 bytes-like object 或 ASCII 字符串 s 并返回解码过的 bytes

可选项 altchars 必须是一个长 2 字节的 bytes-like object,它指定了用于替换 +/ 的字符。

如果 s 被不正确地填写,一个 binascii.Error 错误将被抛出。

如果 validate 值为 False (默认情况),则在填充检查前,将丢弃既不在标准 base-64 字母表之中也不在备用字母表中的字符。如果 validateTrue,这些非 base64 字符将导致 binascii.Error

base64.standard_b64encode(s)

编码 bytes-like object s,使用标准 Base64 字母表并返回编码过的 bytes

base64.standard_b64decode(s)

解码 bytes-like object 或 ASCII 字符串 s,使用标准 Base64 字母表并返回编码过的 bytes

base64.urlsafe_b64encode(s)

编码 bytes-like object s,使用 URL 与文件系统安全的字母表,使用 - 以及 _ 代替标准 Base64 字母表中的 +/。返回编码过的 bytes。结果中可能包含 =

base64.urlsafe_b64decode(s)

解码 bytes-like object 或 ASCII 字符串 s,使用 URL 与文件系统安全的字母表,使用 - 以及 _ 代替标准 Base64 字母表中的 +/。返回解码过的 bytes

base64.b32encode(s)

用 Base32 编码 bytes-like object s 并返回编码过的 bytes

base64.b32decode(s, casefold=False, map01=None)

解码 Base32 编码过的 bytes-like object 或 ASCII 字符串 s 并返回解码过的 bytes

可选的 casefold 是一个指定小写字幕是否可接受为输入的标志。为了安全考虑,默认值为 False

RFC 4648 允许可以选择将数码 0 (zero) 映射为字母 O (oh),并可以选择将数码 1 (one) 映射为字母 I (eye) 或字母 L (el)。 可选参数 map01 在不为 None 时,指定数码 1 应当映射为哪个字母 (当 map01 不为 None 时,数码 0 总是被映射为字母 O)。 出于安全考虑其默认值为 None,因而在输入中不允许 0 和 1。

如果 s 被错误地填写或输入中存在字母表之外的字符,将抛出 binascii.Error

base64.b32hexencode(s)

类似于 b32encode() 但是使用 Extended Hex Alphabet,如 RFC 4648 所定义。

3.10 新版功能.

base64.b32hexdecode(s, casefold=False)

类似于 b32decode() 但是使用 Extended Hex Alphabet,如 RFC 4648 所定义。

此版本不允许数码 0 (zero) 到字母 O (oh) 以及数码 1 (one) 到字母 I (eye) 或字母 L (el) 的映射,所有这些字符均包括在 Extended Hex Alphabet 当中并且不可相互替代。

3.10 新版功能.

base64.b16encode(s)

用 Base16 编码 bytes-like object s 并返回编码过的 bytes

base64.b16decode(s, casefold=False)

解码 Base16 编码过的 bytes-like object 或 ASCII 字符串 s 并返回解码过的 bytes

可选的 casefold 是一个指定小写字幕是否可接受为输入的标志。为了安全考虑,默认值为 False

如果 s 被错误地填写或输入中存在字母表之外的字符,将抛出 binascii.Error

base64.a85encode(b, **, foldspaces=False, wrapcol=0, pad=False, adobe=False*)

用 Ascii85 编码 bytes-like object s 并返回编码过的 bytes

foldspaces 是一个可选的标志,使用特殊的短序列 ‘y’ 代替 ‘btoa’ 提供的 4 个连续空格 (ASCII 0x20)。这个特性不被 “标准” Ascii85 编码支持。

wrapcol 控制了输出是否包含换行符 (b'\n'). 如果该值非零, 则每一行只有该值所限制的字符长度.

pad 控制在编码之前输入是否填充为4的倍数。请注意,btoa 实现总是填充。

adobe 控制编码后的字节序列是否要加上 <~~>,这是 Adobe 实现所使用的。

3.4 新版功能.

base64.a85decode(b, **, foldspaces=False, adobe=False, ignorechars=b’ \t\n\r\x0b’*)

解码 Ascii85 编码过的 bytes-like object 或 ASCII 字符串 s 并返回解码过的 bytes

foldspaces 旗标指明是否应接受 ‘y’ 短序列作为 4 个连续空格 (ASCII 0x20) 的快捷方式。 此特性不被 “标准” Ascii85 编码格式所支持。

adobe 控制输入序列是否为 Adobe Ascii85 格式 (即附加 <~ 和 ~>)。

ignorechars 应当是一个 bytes-like object 或 ASCII 字符串,其中包含要从输入中忽略的字符。 这应当只包含空白字符,并且默认包含 ASCII 中所有的空白字符。

3.4 新版功能.

base64.b85encode(b, pad=False)

用 base85(如 git 风格的二进制 diff 数据所用格式)编码 bytes-like object b 并返回编码后的 bytes

如果 pad 为真值,输入将以 b'\0' 填充以使其编码前长度为 4 字节的倍数。

3.4 新版功能.

base64.b85decode(b)

解码 base85 编码过的 bytes-like object 或 ASCII 字符串 b 并返回解码过的 bytes。 如有必要,填充会被隐式地移除。

3.4 新版功能.

旧式接口:

base64.decode(input, output)

解码二进制 input 文件的内容并将结果二进制数据写入 output 文件。 inputoutput 必须为 文件对象. input 将被读取直至 input.readline() 返回空字节串对象。

base64.decodebytes(s)

解码 bytes-like object s,该对象必须包含一行或多行 base64 编码的数据,并返回已解码的 bytes

3.1 新版功能.

base64.encode(input, output)

编码二进制 input 文件的内容并将经 base64 编码的数据写入 output 文件。 inputoutput 必须为 文件对象。 input 将被读取直到 input.read() 返回空字节串对象。 encode() 会在每输出 76 个字节之后插入一个换行符 (b'\n'),并会确保输出总是以换行符来结束,如 RFC 2045 (MIME) 所规定的那样。

base64.encodebytes(s)

编码 bytes-like object s,其中可以包含任意二进制数据,并返回包含经 base64 编码数据的 bytes,每输出 76 个字节之后将带一个换行符 (b'\n'),并会确保在末尾也有一个换行符,如 RFC 2045 (MIME) 所规定的那样。

3.1 新版功能.

此模块的一个使用示例:

>>> import base64
>>> encoded = base64.b64encode(b'data to be encoded')
>>> encoded
b'ZGF0YSB0byBiZSBlbmNvZGVk'
>>> data = base64.b64decode(encoded)
>>> data
b'data to be encoded'

安全考量

RFC 4648 中新增了安全事项部分(第 12 节);对于要部署到生产环境的任何代码都建议充分考虑此安全事项部分。

参见

模块 binascii

支持模块,包含ASCII到二进制和二进制到ASCII转换。

RFC 1521 - MIME (Multipurpose Internet Mail Extensions) 第一部分:规定并描述因特网消息体的格式的机制。

第 5.2 节,“Base64 内容转换编码格式” 提供了 base64 编码格式的定义。

binhex —- 对binhex4文件进行编码和解码

源代码: Lib/binhex.py

3.9 版后已移除.


此模块以binhe4格式对文件进行编码和解码,该格式允许Macintosh文件以ASCII格式表示。仅处理数据分支。

binhex 模块定义了以下功能:

binhex.binhex(input, output)

将带有文件名 输入 的二进制文件转换为binhex文件 输出 。输出参数可以是文件名或类文件对象( write()close() 方法的任何对象)。

binhex.hexbin(input, output)

解码binhex文件输入。 输入 可以是支持 read()close() 方法的文件名或类文件对象。生成的文件将写入名为 output 的文件,除非参数为 None ,在这种情况下,从binhex文件中读取输出文件名。

还定义了以下异常:

exception binhex.Error

当无法使用binhex格式编码某些内容时(例如,文件名太长而无法放入文件名字段中),或者输入未正确编码的binhex数据时,会引发异常。

备注

还有一个替代的、功能更强大的编码器和解码器接口,详细信息请参见源代码。

如果您在非Macintosh平台上编码或解码文本文件,它们仍将使用旧的Macintosh换行符约定(回车符作为行尾)。

binascii —- 二进制和 ASCII 码互转

binascii 模块包含很多在二进制和二进制表示的各种ASCII码之间转换的方法。 通常情况不会直接使用这些函数,而是使用像 uubase64 ,或 binhex 这样的封装模块。 为了执行效率高,binascii 模块含有许多用 C 写的低级函数,这些底层函数被一些高级模块所使用。

注解

a2b_* 函数接受只含有 ASCII 码的Unicode 字符串。其他函数只接受 字节类对象 (例如 bytesbytearray 和其他支持缓冲区协议的对象)。

在 3.3 版更改: ASCII-only unicode strings are now accepted by the a2b_* functions.

binascii 模块定义了以下函数:

binascii.a2b_uu(string)

将单行 uu 编码数据转换成二进制数据并返回。uu 编码每行的数据通常包含45 个(二进制)字节,最后一行除外。每行数据后面可能跟有空格。

binascii.b2a_uu(data, **, backtick=False*)

将二进制数据转换为 ASCII 编码字符,返回值是转换后的行数据,包括换行符。 data 的长度最多为45。如果 backtick 为ture,则零由 '‘` 而不是空格表示。

在 3.7 版更改: 增加 backtick 形参。

binascii.a2b_base64(string)

将 base64 数据块转换成二进制并以二进制数据形式返回。一次可以传递多行数据。

binascii.b2a_base64(data, **, newline=True*)

将二进制数据转换为一行用 base64 编码的ASCII字符串。返回值是转换后的行数据,如果 newline 为true,则返回值包括换行符。该函数的输出符合:rfc:3548。

在 3.6 版更改: 增加 newline 形参。

binascii.a2b_qp(data, header=False)

将一个引号可打印的数据块转换成二进制数据并返回。一次可以转换多行。如果可选参数 header 存在且为true,则数据中的下划线将被解码成空格。

binascii.b2a_qp(data, quotetabs=False, istext=True, header=False)

将二进制数据转换为一行或多行带引号可打印编码的ASCII字符串。返回值是转换后的行数据。如果可选参数 quotetabs 存在且为真值,则对所有制表符和空格进行编码。如果可选参数 istext 存在且为真值,则不对新行进行编码,但将对尾随空格进行编码。如果可选参数 header 存在且为true,则空格将被编码为下划线 RFC 1522。如果可选参数 header 存在且为假值,则也会对换行符进行编码;不进行换行转换编码可能会破坏二进制数据流。

binascii.a2b_hqx(string)

将 binhex4 格式的 ASCII 数据不进行 RLE 解压缩直接转换为二进制数据。该字符串应包含完整数量的二进制字节,或者(在binhex4 数据最后部分)剩余位为零。

3.9 版后已移除.

binascii.rledecode_hqx(data)

根据 binhex4 标准对数据执行 RLE 解压缩。该算法在一个字节的数据后使用 0x90 作为重复指示符,然后计数。计数 0 指定字节值 0x90 。该例程返回解压缩的数据,输入数据以孤立的重复指示符结束的情况下,将引发 Incomplete 异常。

在 3.2 版更改: 仅接受 bytestring 或 bytearray 对象作为输入。

3.9 版后已移除.

binascii.rlecode_hqx(data)

data 上执行 binhex4 游程编码压缩并返回结果。

3.9 版后已移除.

binascii.b2a_hqx(data)

执行 hexbin4 类型二进制到 ASCII 码的转换并返回结果字符串。输入数据应经过 RLE 编码,且数据长度可被3整除(除了最后一个片段)。

3.9 版后已移除.

binascii.crc_hqx(data, value)

value 作为初始 CRC 计算 data 的16位 CRC 值,返回其结果。这里使用 CRC-CCITT 生成多项式 x16 + x12 + x5 + 1 ,通常表示为0x1021。该 CRC 被用于 binhex4 格式。

binascii.crc32(data[, value])

计算 CRC-32 ,从 value 的初始 CRC 开始计算 data 的32位校验和。默认初始 CRC 为零。该算法与 ZIP 文件校验和一致。由于该算法被设计用作校验和算法,因此不适合用作通用散列算法。使用方法如下:

print(binascii.crc32(b"hello world"))
# Or, in two pieces:
crc = binascii.crc32(b"hello")
crc = binascii.crc32(b" world", crc)
print('crc32 = {:#010x}'.format(crc))

在 3.0 版更改: 校验结果始终是无符号类型的。要在所有Python版本和平台上生成相同的数值,请使用 crc32(data) & 0xffffffff

binascii.b2a_hex(data[, sep[, bytes_per_sep=1]])

binascii.hexlify(data[, sep[, bytes_per_sep=1]])

返回二进制数据 data 的十六进制表示形式。 data 的每个字节都被转换为相应的2位十六进制表示形式。因此返回的字节对象的长度是 data 的两倍。

使用:bytes.hex() 方法也可以方便地实现相似的功能(但仅返回文本字符串)。

如果指定了 sep*,它必须为单字符 str 或 bytes 对象。 它将被插入每个 *bytes_per_sep 输入字节之后。 分隔符位置默认从输出的右端开始计数,如果你希望从左端开始计数,请提供一个负的 bytes_per_sep 值。

>>> import binascii
>>> binascii.b2a_hex(b'\xb9\x01\xef')
b'b901ef'
>>> binascii.hexlify(b'\xb9\x01\xef', '-')
b'b9-01-ef'
>>> binascii.b2a_hex(b'\xb9\x01\xef', b'_', 2)
b'b9_01ef'
>>> binascii.b2a_hex(b'\xb9\x01\xef', b' ', -2)
b'b901 ef'

在 3.8 版更改: 添加了 sepbytes_per_sep 形参。

binascii.a2b_hex(hexstr)

binascii.unhexlify(hexstr)

返回由十六进制字符串 hexstr 表示的二进制数据。此函数功能与 b2a_hex() 相反。 hexstr 必须包含偶数个十六进制数字(可以是大写或小写),否则会引发 Error 异常。

使用:bytes.fromhex() 类方法也实现相似的功能(仅接受文本字符串参数,不限制其中的空白字符)。

exception binascii.Error

通常是因为编程错误引发的异常。

exception binascii.Incomplete

数据不完整引发的异常。通常不是编程错误导致的,可以通过读取更多的数据并再次尝试来处理该异常。

quopri —- 编码与解码经过 MIME 转码的可打印数据

源代码: Lib/quopri.py


此模块会执行转换后可打印的传输编码与解码,具体定义见 RFC 1521: “MIME (Multipurpose Internet Mail Extensions) Part One: Mechanisms for Specifying and Describing the Format of Internet Message Bodies”。 转换后可打印的编码格式被设计用于只包含相对较少的不可打印字符的数据;如果存在大量这样的字符,通过 base64 模块所提供的 base64 编码方案会更为紧凑,例如当发送图片文件时。

quopri.decode(input, output, header=False)

解码 input 文件的内容并将已解码二进制数据结果写入 output 文件。 inputoutput 必须为 二进制文件对象。 如果提供了可选参数 header 且为真值,下划线将被解码为空格。 此函数可用于解码“Q”编码的头数据,具体描述见 RFC 1522: “MIME (Multipurpose Internet Mail Extensions) Part Two: Message Header Extensions for Non-ASCII Text”。

quopri.encode(input, output, quotetabs, header=False)

编码 input 文件的内容并将转换后可打印的数据结果写入 output 文件。 inputoutput 必须为 二进制文件对象. quotetabs 是一个非可选的旗标,它控制是否要编码内嵌的空格与制表符;当为真值时将编码此类内嵌空白符,当为假值时则保持原样不进行编码。 请注意出现在行尾的空格与制表符总是会被编码,具体描述见 RFC 1521header 旗标控制空格符是否要编码为下划线,具体描述见 RFC 1522

quopri.decodestring(s, header=False)

类似 decode(),区别在于它接受一个源 bytes 并返回对应的已解码 bytes

quopri.encodestring(s, quotetabs=False, header=False)

类型 encode(),区别在于它接受一个源 bytes 并返回对应的已编码 bytes。 在默认情况下,它会发送 False 值给 encode() 函数的 quotetabs 形参。

uu —- 对 uuencode 文件进行编码与解码

源代码: Lib/uu.py


此模块使用 uuencode 格式来编码和解码文件,以便任意二进制数据可通过仅限 ASCII 码的连接进行传输。 在任何要求文件参数的地方,这些方法都接受文件类对象。 为了保持向下兼容,也接受包含路径名称的字符串,并且将打开相应的文件进行读写;路径名称 '-' 被解读为标准输入或输出。 但是,此接口已被弃用;在 Windows 中调用者最好是自行打开文件,并在需要时确保模式为 'rb' or 'wb'

此代码由 Lance Ellinghouse 贡献,并由 Jack Jansen 修改。

uu 模块定义了以下函数:

uu.encode(in_file, out_file, name=None, mode=None, **, backtick=False*)

使用 uuencode 将 in_file 文件编码为 out_file 文件。 经过 uuencoded 编码的文件将具有指定 namemode 作为解码该文件默认结果的标头。 默认值会相应地从 in_file'-' 以及 0o666 中提取。 如果 backtick 为真值,零会用 '‘` 而不是空格来表示。

在 3.7 版更改: 增加 backtick 形参。

uu.decode(in_file, out_file=None, mode=None, quiet=False)

调用此函数会解码 uuencod 编码的 in_file 文件并将结果放入 out_file 文件。 如果 out_file 是一个路径名称,mode 会在必须创建文件时用于设置权限位。 out_filemode 的默认值会从 uuencode 标头中提取。 但是,如果标头中指定的文件已存在,则会引发 uu.Error

如果输入由不正确的 uuencode 编码器生成,decode() 可能会打印一条警告到标准错误 ,这样 Python 可以从该错误中恢复。 将 quiet 设为真值可以屏蔽此警告。

exception uu.Error

Exception 的子类,此异常可由 uu.decode() 在多种情况下引发,如上文所述,此外还包括格式错误的标头或被截断的输入文件等。

结构化标记处理工具

Python 支持各种模块,以处理各种形式的结构化数据标记。 这包括使用标准通用标记语言(SGML)和超文本标记语言(HTML)的模块,以及使用可扩展标记语言(XML)的几个接口。

  • html —- 超文本标记语言支持
  • html.parser —- 简单的 HTML 和 XHTML 解析器
    • HTML 解析器的示例程序
    • HTMLParser 方法
    • 例子
  • html.entities —- HTML 一般实体的定义
  • XML处理模块
    • XML 漏洞
    • defusedxml
  • xml.etree.ElementTree —- ElementTree XML API
    • 教程
      • XML 树和元素
      • 解析 XML
      • 用于非阻塞解析的拉取 API
      • 查找感兴趣的元素
      • 修改XML文件
      • 构建 XML 文档
      • 解析带有命名空间的 XML
      • 其他资源
    • XPath支持
      • 示例
      • 支持的XPath语法
    • 参考
      • 函数
    • XInclude 支持
      • 示例
    • 参考
      • 函数
      • 元素对象
      • ElementTree 对象
      • QName 对象
      • TreeBuilder 对象
      • XMLParser对象
      • XMLPullParser对象
      • 异常
  • xml.dom —- 文档对象模型 API
    • 模块内容
    • DOM 中的对象
      • DOMImplementation 对象
      • 节点对象
      • 节点列表对象
      • 文档类型对象
      • Document 对象
      • 元素对象
      • Attr 对象
      • NamedNodeMap 对象
      • 注释对象
      • Text 和 CDATASection 对象
      • ProcessingInstruction 对象
      • 异常
    • 一致性
      • 类型映射
      • 访问器方法
  • xml.dom.minidom —- 最小化的 DOM 实现
    • DOM 对象
    • DOM 示例
    • minidom 和 DOM 标准
  • xml.dom.pulldom —- 支持构建部分 DOM 树
    • DOMEventStream 对象
  • xml.sax —- 支持 SAX2 解析器
    • SAXException 对象
  • xml.sax.handler —- SAX 处理句柄的基类
    • ContentHandler 对象
    • DTDHandler 对象
    • EntityResolver 对象
    • ErrorHandler 对象
    • LexicalHandler 对象
  • xml.sax.saxutils —- SAX 工具集
  • xml.sax.xmlreader —- 用于 XML 解析器的接口
    • XMLReader 对象
    • IncrementalParser 对象
    • Locator 对象
    • InputSource 对象
    • Attributes 接口
    • AttributesNS 接口
  • xml.parsers.expat —- 使用 Expat 的快速 XML 解析
    • XMLParser对象
    • ExpatError 异常
    • 示例
    • 内容模型描述
    • Expat 错误常量

html —- 超文本标记语言支持

源码: Lib/html/init.py


该模块定义了操作HTML的工具。

html.escape(s, quote=True)

将字符串 s 中的字符&<> 转换为安全的HTML序列。 如果需要在 HTML 中显示可能包含此类字符的文本,请使用此选项。 如果可选的标志 quote 为真值,则字符 (") 和 (') 也被转换;这有助于包含在由引号分隔的 HTML 属性中,如 <a href="...">

3.2 新版功能.

html.unescape(s)

将字符串 s 中的所有命名和数字字符引用 (例如 >, >, >) 转换为相应的Unicode字符。 此函数使用HTML 5标准为有效和无效字符引用定义的规则,以及 HTML 5 命名字符引用列表

3.4 新版功能.


html 包中的子模块是:

XML处理模块

源码: Lib/xml/


用于处理XML的Python接口分组在 xml 包中。

警告

XML 模块对于错误或恶意构造的数据是不安全的。 如果你需要解析不受信任或未经身份验证的数据。

值得注意的是 xml 包中的模块要求至少有一个 SAX 兼容的 XML 解析器可用。在 Python 中包含 Expat 解析器,因此 xml.parsers.expat 模块将始终可用。

xml.domxml.sax 包的文档是 DOM 和 SAX 接口的 Python 绑定的定义。

XML 处理子模块包括:

XML 漏洞

XML 处理模块对于恶意构造的数据是不安全的。 攻击者可能滥用 XML 功能来执行拒绝服务攻击、访问本地文件、生成与其它计算机的网络连接或绕过防火墙。

下表概述了已知的攻击以及各种模块是否容易受到攻击。

种类 sax etree minidom pulldom xmlrpc
billion laughs Vulnerable (1) Vulnerable (1) Vulnerable (1) Vulnerable (1) Vulnerable (1)
quadratic blowup Vulnerable (1) Vulnerable (1) Vulnerable (1) Vulnerable (1) Vulnerable (1)
external entity expansion Safe (5) Safe (2) Safe (3) Safe (5) 安全 (4)
DTD retrieval Safe (5) 安全 安全 Safe (5) 安全
decompression bomb 安全 安全 安全 安全 易受攻击
  1. Expat 2.4.1 and newer is not vulnerable to the “billion laughs” and “quadratic blowup” vulnerabilities. Items still listed as vulnerable due to potential reliance on system-provided libraries. Check pyexpat.EXPAT_VERSION.
  2. xml.etree.ElementTree 不会扩展外部实体并在实体发生时引发 ParserError
  3. xml.dom.minidom 不会扩展外部实体,只是简单地返回未扩展的实体。
  4. xmlrpclib 不扩展外部实体并省略它们。
  5. 从 Python 3.7.1 开始,默认情况下不再处理外部通用实体。

billion laughs / exponential entity expansion (狂笑/递归实体扩展)

Billion Laughs 攻击 — 也称为递归实体扩展 — 使用多级嵌套实体。 每个实体多次引用另一个实体,最终实体定义包含一个小字符串。 指数级扩展导致几千 GB 的文本,并消耗大量内存和 CPU 时间。

quadratic blowup entity expansion(二次爆炸实体扩展)

二次爆炸攻击类似于 Billion Laughs 攻击,它也滥用实体扩展。 它不是嵌套实体,而是一遍又一遍地重复一个具有几千个字符的大型实体。攻击不如递归情况有效,但它避免触发禁止深度嵌套实体的解析器对策。

external entity expansion

实体声明可以包含的不仅仅是替换文本。 它们还可以指向外部资源或本地文件。 XML 解析器访问资源并将内容嵌入到 XML 文档中。

DTD retrieval

Python 的一些 XML 库 xml.dom.pulldom 从远程或本地位置检索文档类型定义。 该功能与外部实体扩展问题具有相似的含义。

decompression bomb

Decompression bombs(解压炸弹,又名 ZIP bomb)适用于所有可以解析压缩 XML 流(例如 gzip 压缩的 HTTP 流或 LZMA 压缩的文件)的 XML 库。 对于攻击者来说,它可以将传输的数据量减少三个量级或更多。

PyPI上 defusedxml 的文档包含有关所有已知攻击向量的更多信息以及示例和参考。

defusedxml

defusedxml 是一个纯 Python 软件包,它修改了所有标准库 XML 解析器的子类,可以防止任何潜在的恶意操作。 对于解析不受信任的XML数据的任何服务器代码,建议使用此程序包。 该软件包还提供了有关更多 XML 漏洞(如 XPath 注入)的示例漏洞和扩展文档。互联网协议和支持

互联网协议和支持

  • webbrowser —- 方便的 Web 浏览器控制工具
    • 浏览器控制器对象
  • cgi —- 通用网关接口支持
    • 概述
    • 使用 cgi 模块
    • 更高层级的接口
    • 函数
    • 对于安全性的关注
    • 在 Unix 系统上安装你的 CGI 脚本
    • 测试你的 CGI 脚本
    • 调试 CGI 脚本
    • 常见问题和解决方案
  • cgitb —- 用于 CGI 脚本的回溯管理器
  • wsgiref —- WSGI 工具和参考实现
    • wsgiref.util — WSGI 环境工具
    • wsgiref.headers — WSGI 响应标头工具
    • wsgiref.simple_server — 一个简单的 WSGI HTTP 服务器
    • wsgiref.validate —- WSGI 一致性检查器
    • wsgiref.handlers — 服务器/网关基类
    • 例子
  • urllib —- URL 处理模块
  • urllib.request —- 用于打开 URL 的可扩展库
    • Request 对象
    • OpenerDirector 对象
    • BaseHandler 对象
    • HTTPRedirectHandler 对象
    • HTTPCookieProcessor 对象
    • ProxyHandler 对象
    • HTTPPasswordMgr 对象
    • HTTPPasswordMgrWithPriorAuth 对象
    • AbstractBasicAuthHandler 对象
    • HTTPBasicAuthHandler 对象
    • ProxyBasicAuthHandler 对象
    • AbstractDigestAuthHandler 对象
    • HTTPDigestAuthHandler 对象
    • ProxyDigestAuthHandler 对象
    • HTTPHandler 对象
    • HTTPSHandler 对象
    • FileHandler 对象
    • DataHandler 对象
    • FTPHandler 对象
    • CacheFTPHandler 对象
    • UnknownHandler 对象
    • HTTPErrorProcessor 对象
    • 例子
    • 已停用的接口
    • urllib.request Restrictions
  • urllib.response —- urllib 使用的 Response 类
  • urllib.parse 用于解析 URL
    • URL 解析
    • 解析ASCII编码字节
    • 结构化解析结果
    • URL Quoting
  • urllib.error —- urllib.request 引发的异常类
  • urllib.robotparser —- robots.txt 语法分析程序
  • http —- HTTP 模块
    • HTTP 状态码
  • http.client —- HTTP 协议客户端
    • HTTPConnection 对象
    • HTTPResponse 对象
    • 例子
    • HTTPMessage 对象
  • ftplib —- FTP 协议客户端
    • FTP 对象
    • FTP_TLS 对象
  • poplib —- POP3 协议客户端
    • POP3 对象
    • POP3 示例
  • imaplib —- IMAP4 协议客户端
    • IMAP4 Objects
    • IMAP4 Example
  • nntplib —- NNTP protocol client
    • NNTP Objects
      • Attributes
      • 方法
    • 工具函数
  • smtplib —-SMTP协议客户端
    • SMTP Objects
    • SMTP Example
  • smtpd —- SMTP 服务器
    • SMTPServer 对象
    • DebuggingServer 对象
    • PureProxy对象
    • MailmanProxy 对象
    • SMTPChannel 对象
  • telnetlib — Telnet 客户端
    • Telnet 对象
    • Telnet 示例
  • uuid —- UUID objects according to RFC 4122
    • 示例
  • socketserver —- A framework for network servers
    • Server Creation Notes
    • Server 对象
    • Request Handler Objects
    • 例子
      • socketserver.TCPServer Example
      • socketserver.UDPServer Example
      • Asynchronous Mixins
  • http.server —- HTTP 服务器
  • http.cookies —- HTTP状态管理
    • Cookie 对象
    • Morsel 对象
    • 示例
  • http.cookiejar —— HTTP 客户端的 Cookie 处理
    • CookieJar 和 FileCookieJar 对象
    • FileCookieJar subclasses and co-operation with web browsers
    • CookiePolicy 对象
    • DefaultCookiePolicy 对象
    • Cookie 对象
    • 例子
  • xmlrpc —- XMLRPC 服务端与客户端模块
  • xmlrpc.client —- XML-RPC 客户端访问
    • ServerProxy 对象
    • DateTime 对象
    • Binary 对象
    • Fault 对象
    • ProtocolError 对象
    • MultiCall 对象
    • Convenience Functions
    • Example of Client Usage
    • Example of Client and Server Usage
  • xmlrpc.server —- 基本 XML-RPC 服务器
    • SimpleXMLRPCServer Objects
      • SimpleXMLRPCServer Example
    • CGIXMLRPCRequestHandler
    • Documenting XMLRPC server
    • DocXMLRPCServer Objects
    • DocCGIXMLRPCRequestHandler
  • ipaddress —- IPv4/IPv6 操作库
    • 方便的工厂函数
    • IP 地址
      • 地址对象
      • Conversion to Strings and Integers
      • 运算符
        • 比较运算符
        • 算术运算符
    • IP网络的定义
      • Prefix, net mask and host mask
      • Network objects
      • 运算符
        • Logical operators
        • 迭代
        • Networks as containers of addresses
    • Interface objects
      • 运算符
        • Logical operators
    • Other Module Level Functions
    • Custom Exceptions

webbrowser —- 方便的 Web 浏览器控制工具

源码:Lib/webbrowser.py


webbrowser 模块提供了一个高层级接口,允许向用户显示基于 Web 的文档。 在大多数情况下,只需调用此模块的 open() 函数就可以了。

在 Unix 下,图形浏览器在 X11 下是首选,但如果图形浏览器不可用或 X11 显示不可用,则将使用文本模式浏览器。 如果使用文本模式浏览器,则调用进程将阻塞,直到用户退出浏览器。

如果存在环境变量 BROWSER ,则将其解释为 os.pathsep 分隔的浏览器列表,以便在平台默认值之前尝试。 当列表部分的值包含字符串 %s 时,它被解释为一个文字浏览器命令行,用于替换 %s 的参数 URL ;如果该部分不包含 %s,则它只被解释为要启动的浏览器的名称。

对于非 Unix 平台,或者当 Unix 上有远程浏览器时,控制过程不会等待用户完成浏览器,而是允许远程浏览器在显示界面上维护自己的窗口。 如果 Unix 上没有远程浏览器,控制进程将启动一个新的浏览器并等待。

脚本 webbrowser 可以用作模块的命令行界面。它接受一个 URL 作为参数。还接受以下可选参数:-n 如果可能,在新的浏览器窗口中打开 URL ; -t 在新的浏览器页面(“标签”)中打开 URL。这些选择当然是相互排斥的。用法示例:

python -m webbrowser -t "https://www.python.org"

定义了以下异常:

exception webbrowser.Error

发生浏览器控件错误时引发异常。

定义了以下函数:

webbrowser.open(url, new=0, autoraise=True)

使用默认浏览器显示 url*。 如果 *new 为 0,则尽可能在同一浏览器窗口中打开 url*。 如果 *new 为 1,则尽可能打开新的浏览器窗口。 如果 new 为 2,则尽可能打开新的浏览器页面(“标签”)。 如果 autoraise 为 “True”,则会尽可能置前窗口(请注意,在许多窗口管理器下,无论此变量的设置如何,都会置前窗口)。

请注意,在某些平台上,尝试使用此函数打开文件名,可能会起作用并启动操作系统的关联程序。 但是,这种方式不被支持也不可移植。

使用 url 参数会引发 auditing event webbrowser.open

webbrowser.open_new(url)

如果可能,在默认浏览器的新窗口中打开 url,否则,在唯一的浏览器窗口中打开 url

webbrowser.open_new_tab(url)

如果可能,在默认浏览器的新页面(“标签”)中打开 url,否则等效于 open_new()

webbrowser.get(using=None)

返回浏览器类型为 using 指定的控制器对象。 如果 usingNone,则返回适用于调用者环境的默认浏览器的控制器。

webbrowser.register(name, constructor, instance=None, **, preferred=False*)

注册 name 浏览器类型。 注册浏览器类型后, get() 函数可以返回该浏览器类型的控制器。 如果没有提供 instance,或者为 Noneconstructor 将在没有参数的情况下被调用,以在需要时创建实例。 如果提供了 instance,则永远不会调用 constructor,并且可能是 None

preferred 设置为 True 使得这个浏览器成为 get() 不带参数调用的首选结果。 否则,只有在您计划设置 BROWSER 变量,或使用与您声明的处理程序的名称相匹配的非空参数调用 get() 时,此入口点才有用。

在 3.7 版更改: 添加了仅关键字参数 preferred

预定义了许多浏览器类型。 此表给出了可以传递给 get() 函数的类型名称以及控制器类的相应实例化,这些都在此模块中定义。

类型名 类名 备注
‘mozilla’ Mozilla(‘mozilla’)
‘firefox’ Mozilla(‘mozilla’)
‘netscape’ Mozilla(‘netscape’)
‘galeon’ Galeon(‘galeon’)
‘epiphany’ Galeon(‘epiphany’)
‘skipstone’ BackgroundBrowser(‘skipstone’)
‘kfmclient’ Konqueror() (1)
‘konqueror’ Konqueror() (1)
‘kfm’ Konqueror() (1)
‘mosaic’ BackgroundBrowser(‘mosaic’)
‘opera’ Opera()
‘grail’ Grail()
‘links’ GenericBrowser(‘links’)
‘elinks’ Elinks(‘elinks’)
‘lynx’ GenericBrowser(‘lynx’)
‘w3m’ GenericBrowser(‘w3m’)
‘windows-default’ WindowsDefault (2)
‘macosx’ MacOSXOSAScript(‘default’) (3)
‘safari’ MacOSXOSAScript(‘safari’) (3)
‘google-chrome’ Chrome(‘google-chrome’)
‘chrome’ Chrome(‘chrome’)
‘chromium’ Chromium(‘chromium’)
‘chromium-browser’ Chromium(‘chromium-browser’)

注释:

  1. “Konqueror” 是 Unix 的 KDE 桌面环境的文件管理器,只有在 KDE 运行时才有意义。 一些可靠地检测 KDE 的方法会很好;仅检查 KDEDIR 变量是不够的。 另请注意,KDE 2的 konqueror 命令,会使用名称 “kfm”—-此实现选择运行的 Konqueror 的最佳策略。
  2. 仅限 Windows 平台。
  3. Only on macOS platform.

3.3 新版功能: 添加了对 Chrome/Chromium 的支持。

以下是一些简单的例子:

url = 'https://docs.python.org/'
# Open URL in a new tab, if a browser window is already open.
webbrowser.open_new_tab(url)
# Open URL in new window, raising the window if possible.
webbrowser.open_new(url)

浏览器控制器对象

浏览器控制器提供三个与模块级便捷函数相同的方法:

controller.open(url, new=0, autoraise=True)

使用此控制器处理的浏览器显示 url*。 如果 *new 为 1,则尽可能打开新的浏览器窗口。 如果 new 为 2,则尽可能打开新的浏览器页面(“标签”)。

controller.open_new(url)

如果可能,在此控制器处理的浏览器的新窗口中打开 url ,否则,在唯一的浏览器窗口中打开 url 。 别名 open_new()

controller.open_new_tab(url)

如果可能,在此控制器处理的浏览器的新页面(“标签”)中打开 url,否则等效于 open_new()

cgi —- 通用网关接口支持

源代码: Lib/cgi.py


通用网关接口 (CGI) 脚本的支持模块

本模块定义了一些工具供以 Python 编写的 CGI 脚本使用。

概述

CGI 脚本是由 HTTP 服务器发起调用,通常用来处理通过 HTML <FORM><ISINDEX> 元素提交的用户输入。

在大多数情况下,CGI 脚本存放在服务器的 cgi-bin 特殊目录下。 HTTP 服务器将有关请求的各种信息(例如客户端的主机名、所请求的 URL、查询字符串以及许多其他内容)放在脚本的 shell 环境中,然后执行脚本,并将脚本的输出发回到客户端。

脚本的输入也会被连接到客户端,并且有时表单数据也会以此方式来读取;在其他时候表单数据会通过 URL 的“查询字符串”部分来传递。 本模块的目标是处理不同的应用场景并向 Python 脚本提供一个更为简单的接口。 它还提供了一些工具为脚本调试提供帮助,而最近增加的还有对通过表单上传文件的支持(如果你的浏览器支持该功能的话)。

CGI 脚本的输出应当由两部分组成,并由一个空行分隔。 前一部分包含一些标头,它们告诉客户端后面会提供何种数据。 生成一个最小化标头部分的 Python 代码如下所示:

print("Content-Type: text/html")    # HTML is following
print()                             # blank line, end of headers

后一部分通常为 HTML,提供给客户端软件来显示格式良好包含标题的文本、内联图片等内容。 下面是打印一段简单 HTML 的 Python 代码:

print("<TITLE>CGI script output</TITLE>")
print("<H1>This is my first CGI script</H1>")
print("Hello, world!")

使用 cgi 模块

先在开头添加 import cgi

当你在编写一个新脚本时,请考虑加上这些语句:

import cgitb
cgitb.enable()

这会激活一个特殊的异常处理句柄,它将在发生任何错误时将详细错误报告显示到 web 浏览器中。 如果你不希望向你的脚本的用户显示你的程序的内部细节,你可以改为将报告保存到文件中,使用这样的代码即可:

import cgitb
cgitb.enable(display=0, logdir="/path/to/logdir")

在脚本开发期间使用此特性会很有帮助。 cgitb 所产生的报告提供了在追踪程序问题时能为你节省大量时间的信息。 你可以在完成测试你的脚本并确信它能正确工作之后再移除 cgitb 行。

To get at submitted form data, use the FieldStorage class. If the form contains non-ASCII characters, use the encoding keyword parameter set to the value of the encoding defined for the document. It is usually contained in the META tag in the HEAD section of the HTML document or by the Content-Type header. This reads the form contents from the standard input or the environment (depending on the value of various environment variables set according to the CGI standard). Since it may consume standard input, it should be instantiated only once.

FieldStorage 实例可以像 Python 字典一样来检索。 它允许通过 in 运算符进行成员检测,也支持标准字典方法 keys() 和内置函数 len()。 包含空字符串的表单字段会被忽略而不会出现在字典中;要保留这样的值,请在创建 FieldStorage 实例时为可选的 keep_blank_values 关键字形参提供一个真值。

举例来说,下面的代码(假定 Content-Type 标头和空行已经被打印)会检查字段 nameaddr 是否均被设为非空字符串:

form = cgi.FieldStorage()
if "name" not in form or "addr" not in form:
    print("<H1>Error</H1>")
    print("Please fill in the name and addr fields.")
    return
print("<p>name:", form["name"].value)
print("<p>addr:", form["addr"].value)
...further form processing here...

在这里的字段通过 form[key] 来访问,它们本身就是 FieldStorage (或 MiniFieldStorage,取决于表单的编码格式) 的实例。 实例的 value 属性会产生字段的字符串值。 getvalue() 方法直接返回这个字符串;它还接受可选的第二个参数作为当请求的键不存在时要返回的默认值。

如果提交的表单数据包含一个以上的同名字段,由 form[key] 所提取的对象将不是一个 FieldStorageMiniFieldStorage 实例而是由这种实例组成的列表。 类似地,在这种情况下,form.getvalue(key) 将会返回一个字符串列表。 如果你预计到这种可能性(当你的 HTML 表单包含多个同名字段时),请使用 getlist() 方法,它总是返回一个值的列表(这样你就不需要对只有单个项的情况进行特别处理)。 例如,这段代码拼接了任意数量的 username 字段,以逗号进行分隔:

value = form.getlist("username")
usernames = ",".join(value)

如果一个字段是代表上传的文件,请通过 value 属性访问该值或是通过 getvalue() 方法以字节形式将整个文件读入内存。 这可能不是你想要的结果。 你可以通过测试 filename 属性或 file 属性来检测上传的文件。 然后你可以从 file 属性读取数据,直到它作为 FieldStorage 实例的垃圾回收的一部分被自动关闭 (read()readline() 方法将返回字节数据):

fileitem = form["userfile"]
if fileitem.file:
    # It's an uploaded file; count lines
    linecount = 0
    while True:
        line = fileitem.file.readline()
        if not line: break
        linecount = linecount + 1

FieldStorage 对象还支持在 with 语句中使用,该语句结束时将自动关闭它们。

如果在获取上传文件的内容时遇到错误(例如,当用户点击回退或取消按钮中断表单提交时)该字段中对象的 done 属性值将被设为 -1。

文件上传标准草案考虑到了从一个字段上传多个文件的可能性(使用递归的 multipart/** 编码格式)。 当这种情况发生时,该条目将是一个类似字典的 FieldStorage 条目。 这可以通过检测它的 type 属性来确定,该属性应当是 multipart/form-data (或者可能是匹配 multipart/** 的其他 MIME 类型)。 在这种情况下,它可以像最高层级的表单对象一样被递归地迭代处理。

当一个表单按“旧”格式提交时(即以查询字符串或是单个 application/x-www-form-urlencoded 类型的数据部分的形式),这些条目实际上将是 MiniFieldStorage 类的实例。 在这种情况下,list, filefilename 属性将总是为 None

通过 POST 方式提交并且也带有查询字符串的表单将同时包含 FieldStorageMiniFieldStorage 条目。

在 3.4 版更改: file 属性会在创建 FieldStorage 实例的垃圾回收操作中被自动关闭。

在 3.5 版更改: 为 FieldStorage 类增加了上下文管理协议支持。

更高层级的接口

前面的部分解释了如何使用 FieldStorage 类来读取 CGI 表单数据。 本部分则会描述一个更高层级的接口,它被添加到此类中以允许人们以更为可读和自然的方式行事。 这个接口并不会完全取代前面的部分所描述的技巧 —- 例如它们在高效处理文件上传时仍然很有用处。

此接口由两个简单的方法组成。 你可以使用这两个方法以通用的方式处理表单数据,而无需担心在一个名称下提交的值是只有一个还是有多个。

在前面的部分中,你已学会当你预期用户在一个名称下提交超过一个值的时候编写以下代码:

item = form.getvalue("item")
if isinstance(item, list):
    # The user is requesting more than one item.
else:
    # The user is requesting only one item.

这种情况很常见,例如当一个表单包含具有相同名称的一组复选框的时候:

<input type="checkbox" name="item" value="1" />
<input type="checkbox" name="item" value="2" />

但是在多数情况下,一个表单中的一个特定名称只对应一个表单控件。 因此你可能会编写包含以下代码的脚本:

user = form.getvalue("user").upper()

这段代码的问题在于你绝不能预期客户端会向你的脚本提供合法的输入。 举例来说,如果一个好奇的用户向查询字符串添加了另一个 user=foo 对,则该脚本将会崩溃,因为在这种情况下 getvalue("user") 方法调用将返回一个列表而不是字符串。 在一个列表上调用 upper() 方法是不合法的(因为列表并没有这个方法)因而会引发 AttributeError 异常。

因此,读取表单数据值的正确方式应当总是使用检查所获取的值是单一值还是值列表的代码。 这很麻烦并且会使脚本缺乏可读性。

一种更便捷的方式是使用这个更高层级接口所提供的 getfirst()getlist() 方法。

FieldStorage.getfirst(name, default=None)

此方法总是只返回与表单字段 name 相关联的单一值。 此方法在同一名称下提交了多个值的情况下将仅返回第一个值。 请注意所接收的值顺序在不同浏览器上可能发生变化因而是不确定的。如果指定的表单字段或值不存在则此方法将返回可选形参 default 所指定的值。 如果未指定此形参则默认值为 None

FieldStorage.getlist(name)

此方法总是返回与表单字段 name 相关联的值列表。 如果 name 指定的表单字段或值不存在则此方法将返回一个空列表。 如果指定的表单字段只包含一个值则它将返回只有一项的列表。

使用这两个方法你将能写出优雅简洁的代码:

import cgi
form = cgi.FieldStorage()
user = form.getfirst("user", "").upper()    # This way it's safe.
for item in form.getlist("item"):
    do_something(item)

函数

这些函数在你想要更多控制,或者如果你想要应用一些此模块中在其他场景下实现的算法时很有用处。

cgi.parse(fp=None, environ=os.environ, keep_blank_values=False, strict_parsing=False, separator=’&’)

在环境中或从某个文件中解析一个查询 (文件默认为 sys.stdin)。 keep_blank_values, strict_parsingseparator 形参会被原样传给 urllib.parse.parse_qs()

cgi.parse_multipart(fp, pdict, encoding=’utf-8’, errors=’replace’, separator=’&’)

解析 multipart/form-data 类型(用于文件上传)的输入。 参数中 fp 为输入文件,pdict 为包含 Content-Type 标头中的其他形参的字典,encoding 为请求的编码格式。

urllib.parse.parse_qs() 那样返回一个字典:其中的键为字段名称,值为对应字段的值列表。 对于非文件字段,其值均为字符串列表。

这很容易使用,但如果你预期要上传巨量字节数据时就不太适合了 —- 在这种情况下,请改用更为灵活的 FieldStorage 类。

在 3.7 版更改: 增加了 encodingerrors 形参。 对于非文件字段,其值现在为字符串列表而非字节串列表。

在 3.10 版更改: 增加了 separator 形参。

cgi.parse_header(string)

将一个 MIME 标头 (例如 Content-Type) 解析为一个主值和一个参数字典。

cgi.test()

对 CGI 执行健壮性检测,适于作为主程序。 写入最小化的 HTTP 标头并以 HTML 格式来格式化提供给脚本的所有信息。

cgi.print_environ()

以 HTML 格式来格式化 shell 环境。

cgi.print_form(form)

以 HTML 格式来格式化表单。

cgi.print_directory()

以 HTML 格式来格式化当前目录。

cgi.print_environ_usage()

以 HTML 格式打印有用的环境变量列表(供 CGI 使用)。

对于安全性的关注

有一条重要的规则:如果你发起调用一个外部程序(通过 os.system(), os.popen() 或其他具有类似功能的函数),需要非常确定你不会把从客户端接收的任意字符串直接传给 shell。 这是一个著名的安全漏洞,网络中聪明的黑客可以通过它来利用容易上当的 CGI 脚本发起调用任何 shell 命令。 即便 URL 的一部分或字段名称也是不可信任的,因为请求并不一定是来自你的表单!

为了安全起见,如果你必须将从表单获取的字符串传给 shell 命令,你应当确保该字符串仅包含字母数字类字符、连字符、下划线和句点。

在 Unix 系统上安装你的 CGI 脚本

请阅读你的 HTTP 服务器的文档并咨询你所用系统的管理员来找到 CGI 脚本应当安装到哪个目录;通常是服务器目录树中的 cgi-bin 目录。

请确保你的脚本可被“其他人”读取和执行;Unix 文件模式应为八进制数 0o755 (使用 chmod 0755 filename)。 请确保脚本的第一行包含 #! 且位置是从第 1 列开始,后面带有 Python 解释器的路径名,例如:

#!/usr/local/bin/python

请确保该 Python 解释器存在并且可被“其他人”执行。

请确保你的脚本需要读取或写入的任何文件都分别是“其他人”可读取或可写入的 —- 它们的模式应为可读取 0o644 或可写入 0o666。 这是因为出于安全理由,HTTP 服务器是作为没有任何特殊权限的 “nobody” 用户来运行脚本的。 它只能读取(写入、执行)任何人都能读取(写入、执行)的文件。 执行时的当前目录(通常为服务器的 cgi-bin 目录)和环境变量集合也与你在登录时所得到的不同。 特别地,不可依赖于 shell 的可执行文件搜索路径 (PATH) 或 Python 模块搜索路径 (PYTHONPATH) 的任何相关设置。

如果你需要从 Python 的默认模块搜索路径之外的目录载入模块,你可以在导入其他模块之前在你的脚本中改变路径。 例如:

import sys
sys.path.insert(0, "/usr/home/joe/lib/python")
sys.path.insert(0, "/usr/local/lib/python")

(在此方式下,最后插入的目录将最先被搜索!)

针对非 Unix 系统的指导会有所变化;请查看你的 HTTP 服务器的文档(通常会有关于 CGI 脚本的部分)。

测试你的 CGI 脚本

很不幸,当你在命令行中尝试 CGI 脚本时它通常会无法运行,而能在命令行中完美运行的脚本则可能会在运行于服务器时神秘地失败。 但有一个理由使你仍然应当在命令行中测试你的脚本:如果它包含语法错误,Python 解释器将根本不会执行它,而 HTTP 服务器将很可能向客户端发送令人费解的错误信息。

假定你的脚本没有语法错误,但它仍然无法起作用,你将别无选择,只能继续阅读下一节。

调试 CGI 脚本

首先,请检查是否有安装上的小错误 —- 仔细阅读上面关于安装 CGI 脚本的部分可以使你节省大量时间。 如果你不确定你是否正确理解了安装过程,请尝试将此模块 (cgi.py) 的副本作为 CGI 脚本安装。 当作为脚本被发起调用时,该文件将以 HTML 格式转储其环境和表单内容。 请给它赋予正确的模式等,并向它发送一个请求。 如果它是安装在标准的 cgi-bin 目录下,应该可以通过在你的浏览器中输入表单的 URL 来向它发送请求。

http://yourhostname/cgi-bin/cgi.py?name=Joe+Blow&addr=At+Home

如果此操作给出类型为 404 的错误,说明服务器找不到此脚本 — 也许你需要将它安装到不同的目录。 如果它给出另一种错误,说明存在安装问题,你应当解决此问题才能继续操作。 如果你得到一个格式良好的环境和表单内容清单(在这个例子中,应当会列出的有字段 “addr” 值为 “At Home” 以及 “name” 值为 “Joe Blow”),则说明 cgi.py 脚本已正确安装。 如果你为自己的脚本执行了同样的过程,现在你应该能够调试它了。

下一步骤可以是在你的脚本中调用 cgi 模块的 test() 函数:用这一条语句替换它的主代码

cgi.test()

这将产生从安装 cgi.py 文件本身所得到的相同结果。

当某个常规 Python 脚本触发了未处理的异常,(无论出于什么原因:模块名称出错、文件无法打开等),Python 解释器就会打印出一条完整的跟踪信息并退出。在 CGI 脚本触发异常时,Python 解释器依然会如此,但最有可能的是,跟踪信息只会停留在某个 HTTP 服务日志文件中,或者被完全丢弃。

幸运的是,只要执行 某些 代码,就可以利用 cgitb 模块将跟踪信息发送给浏览器。将以下几行代码加到代码顶部:

import cgitb
cgitb.enable()

然后再运行一下看;发生问题时应能看到详细的报告,或许能让崩溃的原因更清晰一些。

如果怀疑是 cgitb 模块导入的问题,可以采用一个功能更强的方法(只用到内置模块):

import sys
sys.stderr = sys.stdout
print("Content-Type: text/plain")
print()
...your code here...

这得靠 Python 解释器来打印跟踪信息。输出的类型为纯文本,不经过任何 HTML 处理。如果代码正常,则客户端会显示原有的 HTML。如果触发了异常,很可能在输出前两行后会显示一条跟踪信息。因为不会继续进行 HTML 解析,所以跟踪信息肯定能被读到。

常见问题和解决方案

  • 大部分 HTTP 服务器会对 CGI 脚本的输出进行缓存,等脚本执行完毕再行输出。这意味着在脚本运行时,不可能在客户端屏幕上显示出进度情况。
  • 请查看上述安装说明。
  • 请查看 HTTP 服务器的日志文件。(在另一个单独窗口中执行 tail -f logfile 可能会很有用!)
  • 一定要先检查脚本是否有语法错误,做法类似:python script.py
  • 如果脚本没有语法错误,试着在脚本的顶部添加 import cgitb; cgitb.enable()
  • 当调用外部程序时,要确保其可被读取。通常这意味着采用绝对路径名——— 在 CGI 脚本中, PATH 的值通常没什么用。
  • 在读写外部文件时,要确保其能被 CGI 脚本归属的用户读写:通常是运行网络服务的用户,或由网络服务的 suexec 功能明确指定的一些用户。
  • 不要试图给 CGI 脚本赋予 set-uid 模式。这在大多数系统上都行不通,出于安全考虑也不应如此。

cgitb —- 用于 CGI 脚本的回溯管理器

源代码: Lib/cgitb.py


cgitb 模块提供了用于 Python 脚本的特殊异常处理程序。 (这个名称有一点误导性。 它最初是设计用来显示 HTML 格式的 CGI 脚本详细回溯信息。 但后来被一般化为也可显示纯文本格式的回溯信息。) 激活这个模块之后,如果发生了未被捕获的异常,将会显示详细的已格式化的报告。 报告显示内容包括每个层级的源代码摘录,还有当前正在运行的函数的参数和局部变量值,以帮助你调试问题。 你也可以选择将此信息保存至文件而不是将其发送至浏览器。

要启用此特性,只需简单地将此代码添加到你的 CGI 脚本的最顶端:

import cgitb
cgitb.enable()

enable() 函数的选项可以控制是将报告显示在浏览器中,还是将报告记录到文件供以后进行分析。

cgitb.enable(display=1, logdir=None, context=5, format=’html’)

此函数可通过设置 sys.excepthook 的值以使 cgitb 模块接管解释器默认的异常处理机制。

可选参数 display 默认为 1 并可被设为 0 来停止将回溯发送至浏览器。 如果给出了参数 logdir*,则回溯会被写入文件。 *logdir 的值应当是一个用于存放所写入文件的目录。 可选参数 context 是要在回溯中的当前源代码行前后显示的上下文行数;默认为 5。 如果可选参数 format"html",输出将为 HTML 格式。 任何其它值都会强制启用纯文本输出。 默认取值为 "html"

cgitb.text(info, context=5)

此函数用于处理 info (一个包含 sys.exc_info() 返回结果的 3 元组) 所描述的异常,将其回溯格式化为文本并将结果作为字符串返回。 可选参数 context 是要在回溯中的当前源码行前后显示的上下文行数;默认为 5

cgitb.html(info, context=5)

此函数用于处理 info (一个包含 sys.exc_info() 返回结果的 3 元组) 所描述的异常,将其回溯格式化为 HTML 并将结果作为字符串返回。 可选参数 context 是要在回溯中的当前源码行前后显示的上下文行数;默认为 5

cgitb.handler(info=None)

此函数使用默认设置处理异常(即在浏览器中显示报告,但不记录到文件)。 当你捕获了一个异常并希望使用 cgitb 来报告它时可以使用此函数。 可选的 info 参数应为一个包含异常类型,异常值和回溯对象的 3 元组,与 sys.exc_info() 所返回的元组完全一致。 如果未提供 info 参数,则会从 sys.exc_info() 获取当前异常。

wsgiref —- WSGI 工具和参考实现

Web 服务器网关接口(WSGI)是 Web 服务器软件和用 Python 编写的 Web 应用程序之间的标准接口。 具有标准接口能够让支持 WSGI 的应用程序与多种不同的 Web 服务器配合使用。

只有 Web 服务器和编程框架的开发者才需要了解 WSGI 设计的每个细节和边界情况。 你不需要了解 WSGI 的每个细节而只需要安装一个 WSGI 应用程序或编写使用现有框架的 Web 应用程序。

wsgiref 是一个 WSGI 规范的参考实现,可被用于将 WSGI 支持添加到 Web 服务器或框架中。 它提供了操作 WSGI 环境变量和响应标头的工具,实现 WSGI 服务器的基类,一个可部署 WSGI 应用程序的演示性 HTTP 服务器,以及一个用于检查 WSGI 服务器和应用程序是否符合 WSGI 规范的验证工具 (PEP 3333)。

参看 wsgi.readthedocs.io 获取有关 WSGI 的更多信息,以及教程和其他资源的链接。

wsgiref.util — WSGI 环境工具

本模块提供了多种工具配合 WSGI 环境使用。 WSGI 环境就是一个包含 PEP 3333 所描述的 HTTP 请求变量的目录。 所有接受 environ 形参的函数都会预期被得到一个符合 WSGI 规范的目录;请参阅 PEP 3333 来了解相关规范的细节。

wsgiref.util.guess_scheme(environ)

返回对于 wsgi.url_scheme 应为 “http” 还是 “https” 的猜测,具体方式是在 environ 中检查 HTTPS 环境变量。 返回值是一个字符串。

此函数适用于创建一个包装了 CGI 或 CGI 类协议例如 FastCGI 的网关。 通常,提供这种协议的服务器将包括一个 HTTPS 变量并会在通过 SSL 接收请求时将其值设为 “1”, “yes” 或 “on”。 这样,此函数会在找到上述值时返回 “https”,否则返回 “http”。

wsgiref.util.request_uri(environ, include_query=True)

使用 PEP 3333 的 “URL Reconstruction” 一节中的算法返回完整的请求 URL,可能包括查询字符串。 如果 include_query 为假值,则结果 URI 中将不包括查询字符串。

wsgiref.util.application_uri(environ)

类似于 request_uri(),区别在于 PATH_INFOQUERY_STRING 变量会被忽略。 结果为请求所指定的应用程序对象的基准 URI。

wsgiref.util.shift_path_info(environ)

将单个名称从 PATH_INFO 变换为 SCRIPT_NAME 并返回该名称。 environ 目录将被原地 修改;如果你需要保留原始 PATH_INFOSCRIPT_NAME 不变请使用一个副本。

如果 PATH_INFO 中没有剩余的路径节,则返回 None

通常,此例程被用来处理请求 URI 路径的每个部分,比如说将路径当作是一系列字典键。 此例程会修改传入的环境以使其适合发起调用位于目标 URI 上的其他 WSGI 应用程序,如果有一个 WSGI 应用程序位于 /foo,而请求 URI 路径为 /foo/bar/baz,且位于 /foo 的 WSGI 应用程序调用了 shift_path_info(),它将获得字符串 “bar”,而环境将被更新以适合传递给位于 /foo/bar 的 WSGI 应用程序。 也就是说,SCRIPT_NAME 将由 /foo 变为 /foo/bar,而 PATH_INFO 将由 /bar/baz 变为 /baz

PATH_INFO 只是 “/“ 时,此例程会返回一个空字符串并在 SCRIPT_NAME 末尾添加一个斜杠,虽然空的路径节通常会被忽略,并且 SCRIPT_NAME 通常也不以斜杠作为结束。 此行为是有意为之的,用来确保应用程序在使用此例程执行对象遍历时能区分以 /x 结束的和以 /x/ 结束的 URI。

wsgiref.util.setup_testing_defaults(environ)

以简短的默认值更新 environ 用于测试目的。

此例程会添加 WSGI 所需要的各种参数,包括 HTTP_HOST, SERVER_NAME, SERVER_PORT, REQUEST_METHOD, SCRIPT_NAME, PATH_INFO 以及 PEP 3333 中定义的所有 wsgi.* 变量。 它只提供默认值,而不会替换这些变量的现有设置。

此例程的目的是让 WSGI 服务器的单元测试以及应用程序设置测试环境更为容易。 它不应该被实际的 WSGI 服务器或应用程序所使用,因为它用的是假数据!

用法示例:

from wsgiref.util import setup_testing_defaults
from wsgiref.simple_server import make_server
# A relatively simple WSGI application. It's going to print out the
# environment dictionary after being updated by setup_testing_defaults
def simple_app(environ, start_response):
    setup_testing_defaults(environ)
    status = '200 OK'
    headers = [('Content-type', 'text/plain; charset=utf-8')]
    start_response(status, headers)
    ret = [("%s: %s\n" % (key, value)).encode("utf-8")
           for key, value in environ.items()]
    return ret
with make_server('', 8000, simple_app) as httpd:
    print("Serving on port 8000...")
    httpd.serve_forever()

除了上述的环境函数,wsgiref.util 模块还提供了以下辅助工具:

wsgiref.util.is_hop_by_hop(header_name)

如果 ‘header_name’ 是 RFC 2616 所定义的 HTTP/1.1 “Hop-by-Hop” 标头则返回 True

class wsgiref.util.FileWrapper(filelike, blksize=8192)

一个将文件类对象转换为 iterator 的包装器。 结果对象同时支持 __getitem__()__iter__() 迭代形式,以便兼容 Python 2.1 和 Jython。 当对象被迭代时,可选的 blksize 形参将被反复地传给 filelike 对象的 read() 方法以获取字节串并输出。 当 read() 返回空字节串时,迭代将结束并不可再恢复。

如果 filelike 具有 close() 方法,返回的对象也将具有 close() 方法,并且它将在被调用时发起调用 filelike 对象的 close() 方法。

用法示例:

from io import StringIO
from wsgiref.util import FileWrapper
# We're using a StringIO-buffer for as the file-like object
filelike = StringIO("This is an example file-like object"*10)
wrapper = FileWrapper(filelike, blksize=5)
for chunk in wrapper:
    print(chunk)

3.8 版后已移除: 对 序列协议 的支持已被弃用。

wsgiref.headers — WSGI 响应标头工具

此模块提供了一个单独的类 Headers,可以方便地使用一个映射类接口操作 WSGI 响应标头。

class wsgiref.headers.Headers([headers])

创建一个包装了 headers 的映射类对象,它必须为如 PEP 3333 所描述的由标头名称/值元组构成的列表。 headers 的默认值为一个空列表。

Headers 对象支持典型的映射操作包括 __getitem__(), get(), __setitem__(), setdefault(), __delitem__()__contains__()。 对于以上各个方法,映射的键是标头名称(大小写不敏感),而值是关联到该标头名称的第一个值。 设置一个标头会移除该标头的任何现有值,再将一个新值添加到所包装的标头列表末尾。 标头的现有顺序通常会保持不变,只在所包装的列表末尾添加新的标头。

与字典不同,当你试图获取或删除所包装的标头列表中不存在的键时 Headers 对象不会引发错误。 获取一个不存在的标头只是返回 None,而删除一个不存在的标头则没有任何影响。

Headers 对象还支持 keys(), values() 以及 items() 方法。 如果存在具有多个值的键则 keys()items() 所返回的列表可能包括多个相同的键。 对 Headers 对象执行 len() 的结果与 items() 的长度相同,也与所包装的标头列表的长度相同。 实际上,items() 方法只是返回所包装的标头列表的一个副本。

Headers 对象上调用 bytes() 将返回适用于作为 HTTP 响应标头来传输的已格式化字节串。 每个标头附带以逗号加空格分隔的值放置在一行中。 每一行都以回车符加换行符结束,且该字节串会以一个空行作为结束。

除了映射接口和格式化特性,Headers 对象还具有下列方法用来查询和添加多值标头,以及添加具有 MIME 参数的标头:

  • get_all(name)

    返回包含指定标头的所有值的列表。

    返回的列表项将按它们在原始标头列表中的出现或被添加到实例中的顺序排序,并可能包含重复项。 任何被删除并重新插入的字段总是会被添加到标头列表末尾。 如果给定名称的字段不存在,则返回一个空列表。

  • add_header(name, value, **_params)

    添加一个(可能有多个值)标头,带有通过关键字参数指明的可选的 MIME 参数。

    name 是要添加的标头字段。 可以使用关键字参数来为标头字段设置 MIME 参数。 每个参数必须为字符串或 None。 参数名中的下划线会被转换为连字符,因为连字符不可在 Python 标识符中出现,但许多 MIME 参数名都包括连字符。 如果参数值为字符串,它会以 name="value" 的形式被添加到标头值参数中。 如果为 None,则只会添加参数名。 (这适用于没有值的 MIME 参数。) 示例用法:

    h.add_header('content-disposition', 'attachment', filename='bud.gif')

    以上代码将添加一个这样的标头:

    Content-Disposition: attachment; filename="bud.gif"

在 3.5 版更改: headers 形参是可选的。

wsgiref.simple_server — 一个简单的 WSGI HTTP 服务器

此模块实现了一个简单的 HTTP 服务器 (基于 http.server) 来发布 WSGI 应用程序。 每个服务器实例会在给定的主机名和端口号上发布一个 WSGI 应用程序。 如果你想在一个主机名和端口号上发布多个应用程序,你应当创建一个通过解析 PATH_INFO 来选择每个请求要发起调用哪个应用程序的 WSGI 应用程序。 (例如,使用 wsgiref.util 中的 shift_path_info() 函数。)

wsgiref.simple_server.make_server(host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler)

创建一个新的 WSGI 服务器并在 hostport 上进行监听,接受对 app 的连接。 返回值是所提供的 server_class 的实例,并将使用指定的 handler_class 来处理请求。 app 必须是一个如 PEP 3333 所定义的 WSGI 应用程序对象。

用法示例:

from wsgiref.simple_server import make_server, demo_app
with make_server('', 8000, demo_app) as httpd:
    print("Serving HTTP on port 8000...")
    # Respond to requests until process is killed
    httpd.serve_forever()
    # Alternative: serve one request, then exit
    httpd.handle_request()

wsgiref.simple_server.demo_app(environ, start_response)

此函数是一个小巧但完整的 WSGI 应用程序,它返回一个包含消息 “Hello world!” 以及 environ 形参中提供的键/值对的文本页面。 它适用于验证 WSGI 服务器 (例如 wsgiref.simple_server) 是否能够正确地运行一个简单的 WSGI 应用程序。

class wsgiref.simple_server.WSGIServer(server_address, RequestHandlerClass)

创建一个 WSGIServer 实例。 server_address 应当是一个 (host,port) 元组,而 RequestHandlerClass 应当是 http.server.BaseHTTPRequestHandler 的子类,它将被用来处理请求。

你通常不需要调用此构造器,因为 make_server() 函数能为你处理所有的细节。

WSGIServerhttp.server.HTTPServer 的子类,因此它所有的方法 (例如 serve_forever()handle_request()) 都是可用的。 WSGIServer 还提供了以下 WSGI 专属的方法:

  • set_app(application)

    将可调用对象 application 设为将要接受请求的 WSGI 应用程序。

  • get_app()

    返回当前设置的应用程序可调用对象。

但是,你通常不需要使用这些附加的方法,因为 set_app() 通常会由 make_server() 来调用,而 get_app() 主要是针对请求处理句柄实例的。

class wsgiref.simple_server.WSGIRequestHandler(request, client_address, server)

为给定的 request 创建一个 HTTP 处理句柄 (例如套接字),client_address (一个 (host,port) 元组),以及 server (WSGIServer 实例)。

你不需要直接创建该类的实例;它们会根据 WSGIServer 对象的需要自动创建。 但是,你可以子类化该类并将其作为 handler_class 提供给 make_server() 函数。 一些可能在子类中重载的相关方法:

  • get_environ()

    返回包含针对一个请求的 WSGI 环境的字典。 默认实现会拷贝 WSGIServer 对象的 base_environ 字典属性的内容并添加从 HTTP 请求获取的各种标头。 对此方法的每个调用应当返回一个 PEP 3333 所指明的包含所有相关 CGI 环境变量的新字典。

  • get_stderr()

    返回应被用作 wsgi.errors 流的对象。 默认实现将只返回 sys.stderr

  • handle()

    处理 HTTP 请求。 默认的实现会使用 wsgiref.handlers 类创建一个处理句柄实例来实现实际的 WSGI 应用程序接口。

wsgiref.validate —- WSGI 一致性检查器

当创建新的 WSGI 应用程序对象、框架、服务器或中间件时,使用 wsgiref.validate 来验证新代码的一致性是很有用的。 此模块提供了一个创建 WSGI 应用程序对象的函数来验证 WSGI 服务器或网关与 WSGI 应用程序对象之间的通信,以便检查双方的协议一致性。

请注意这个工具并不保证完全符合 PEP 3333;此模块没有报告错误并不一定表示不存在错误。 但是,如果此模块确实报告了错误,那么几乎可以肯定服务器或应用程序不是 100% 符合要求的。

此模块是基于 Ian Bicking 的 “Python Paste” 库的 paste.lint 模块。

wsgiref.validate.validator(application)

包装 application 并返回一个新的 WSGI 应用程序对象。 返回的应用程序将转发所有请求到原始的 application*,并将检查 *application 和发起调用它的服务器是否都符合 WSGI 规范和 RFC 2616

任何被检测到的不一致性都会导致引发 AssertionError;但是请注意,如何对待这些错误取决于具体服务器。 例如,wsgiref.simple_server 和其他基于 wsgiref.handlers 的服务器(它们没有重载错误处理方法来做其他操作)将简单地输出一条消息报告发生了错误,并将回溯转给 sys.stderr 或者其他错误数据流。

这个包装器也可能会使用 warnings 模块生成输出来指明存在问题但实际上未被 PEP 3333 所禁止的行为。 除非它们被 Python 命令行选项或 warnings API 所屏蔽,否则任何此类警告都将被写入到 sys.stderr (不是 wsgi.errors,除非它们恰好是同一个对象)。

用法示例:

from wsgiref.validate import validator
from wsgiref.simple_server import make_server
# Our callable object which is intentionally not compliant to the
# standard, so the validator is going to break
def simple_app(environ, start_response):
    status = '200 OK'  # HTTP Status
    headers = [('Content-type', 'text/plain')]  # HTTP Headers
    start_response(status, headers)
    # This is going to break because we need to return a list, and
    # the validator is going to inform us
    return b"Hello World"
# This is the application wrapped in a validator
validator_app = validator(simple_app)
with make_server('', 8000, validator_app) as httpd:
    print("Listening on port 8000....")
    httpd.serve_forever()

wsgiref.handlers — 服务器/网关基类

此模块提供了用于实现 WSGI 服务器和网关的处理句柄基类。 这些基类可处理大部分与 WSGI 应用程序通信的工作,只要给予它们一个带有输入、输出和错误流的 CGI 类环境。

class wsgiref.handlers.CGIHandler

通过 sys.stdin, sys.stdout, sys.stderros.environ 发起基于 CGI 的调用。 这在当你有一个 WSGI 应用程序并想将其作为 CGI 脚本运行时很有用处。 只需发起调用 CGIHandler().run(app),其中 app 是你想要发起调用的 WSGI 应用程序。

该类是 BaseCGIHandler 的子类,它将设置 wsgi.run_once 为真值,wsgi.multithread 为假值,wsgi.multiprocess 为真值,并总是使用 sysos 来获取所需的 CGI 流和环境。

class wsgiref.handlers.IISCGIHandler

CGIHandler 的一个专门化替代,用于在 Microsoft 的 IIS Web 服务器上部署,无需设置 config allowPathInfo 选项 (IIS>=7) 或 metabase allowPathInfoForScriptMappings (IIS<7)。

默认情况下,IIS 给出的 PATH_INFO 会与前面的 SCRIPT_NAME 重复,导致想要实现路由的 WSGI 应用程序出现问题。 这个处理句柄会去除任何这样的重复路径。

IIS 可被配置为传递正确的 PATH_INFO,但这会导致另一个 PATH_TRANSLATED 出错的问题。 幸运的是这个变量很少被使用并且不被 WSGI 所保证。 但是在 IIS<7 上,这个设置只能在 vhost 层级上进行,影响到所有其他脚本映射,其中许多在受 PATH_TRANSLATED 问题影响时都会中断运行。 因此 IIS<7 的部署几乎从不附带这样的修正(即使 IIS7 也很少使用它,因为它仍然不带 UI)。

CGI 代码没有办法确定该选项是否已设置,因此提供了一个单独的处理句柄类。 它的用法与 CGIHandler 相同,即通过调用 IISCGIHandler().run(app),其中 app 是你想要发起调用的 WSGI 应用程序。

3.2 新版功能.

class wsgiref.handlers.BaseCGIHandler(stdin, stdout, stderr, environ, multithread=True, multiprocess=False)

类似于 CGIHandler,但是改用 sysos 模块,CGI 环境和 I/O 流会被显式地指定。 multithreadmultiprocess 值被用来为处理句柄实例所运行的任何应用程序设置 wsgi.multithreadwsgi.multiprocess 旗标。

该类是 SimpleHandler 的子类,旨在用于 HTTP “原始服务器” 以外的软件。 如果你在编写一个网关协议实现(例如 CGI, FastCGI, SCGI 等等),它使用 Status: 标头来发布 HTTP 状态,你可能会想要子类化该类而不是 SimpleHandler

class wsgiref.handlers.SimpleHandler(stdin, stdout, stderr, environ, multithread=True, multiprocess=False)

类似于 BaseCGIHandler,但被设计用于 HTTP 原始服务器。 如果你在编写一个 HTTP 服务器实现,你可能会想要子类化该类而不是 BaseCGIHandler

该类是 BaseHandler 的子类。 它重载了 __init__(), get_stdin(), get_stderr(), add_cgi_vars(), _write()_flush() 方法以支持通过构造器显式地设置环境和流。 所提供的环境和流存储在 stdin, stdout, stderrenviron 属性中。

stdoutwrite() 方法应该完整写入每个数据块,与 io.BufferedIOBase 一样。

class wsgiref.handlers.BaseHandler

这是适用于运行 WSGI 应用程序的抽象基类。 每个实例将处理一个单独的 HTTP 请求,不过在原则上你也可以创建一个可针对多个请求重用的子类。

BaseHandler 实例只有一个方法提供给外部使用:

  • run(app)

    运行指定的 WSGI 应用程序 app

所有的 BaseHandler 其他方法都是在运行应用程序过程中由该方法发起调用的,因此主要是为了允许定制运行过程。

以下方法必须在子类中被重载:

  • _write(data)

    缓冲字节数据 data 以便传输给客户端。 如果此方法真的传输了数据也是可以的;BaseHandler 只有在底层系统真有这样的区分时才会区分写入和刷新操作以提高效率。

  • _flush()

    强制将缓冲的数据传输给客户端。 如果此方法无任何操作也是可以的(例如,_write() 实际上已发送了数据)。

  • get_stdin()

    返回一个适合被用作当前所处理请求的 wsgi.input 的输入流对象。

  • get_stderr()

    返回一个适合被用作当前所处理请求的 wsgi.errors 的输出流对象。

  • add_cgi_vars()

    将当前请求的 CGI 变量插入到 environ 属性。

以下是一些你可能会想要重载的其他方法。 但这只是个简略的列表,它不包括每个可被重载的方法。 你应当在在尝试创建自定义的 BaseHandler 子类之前参阅文档字符串和源代码来了解更多信息。

用于自定义 WSGI 环境的属性和方法:

  • wsgi_multithread

    用于 wsgi.multithread 环境变量的值。 它在 BaseHandler 中默认为真值,但在其他子类中可能有不同的默认值(或是由构造器来设置)。

  • wsgi_multiprocess

    用于 wsgi.multiprocess 环境变量的值。 它在 BaseHandler 中默认为真值,但在其他子类中可能有不同的默认值(或是由构造器来设置)。

  • wsgi_run_once

    用于 wsgi.run_once 环境变量的值。 它在 BaseHandler 中默认为假值,但在 CGIHandler 中默认为真值。

  • os_environ

    要包括在每个请求的 WSGI 环境中的默认环境变量。 在默认情况下,这是当 wsgiref.handlers 被导入时的 os.environ 的一个副本,但也可以在类或实例层级上创建它们自己的子类。 请注意该字典应当被当作是只读的,因为默认值会在多个类和实际之间共享。

  • server_software

    如果设置了 origin_server 属性,该属性的值会被用来设置默认的 SERVER_SOFTWARE WSGI 环境变量,并且还会被用来设置 HTTP 响应中默认的 Server: 标头。 它会被不是 HTTP 原始服务器的处理句柄所忽略 (例如 BaseCGIHandlerCGIHandler)。

    在 3.3 版更改: 名称 “Python” 会被替换为实现专属的名称如 “CPython”, “Jython” 等等。

  • get_scheme()

    返回当前请求所使用的 URL 方案。 默认的实现会使用来自 wsgiref.utilguess_scheme() 函数根据当前请求的 environ 变量来猜测方案应该是 “http” 还是 “https”。

  • setup_environ()

    environ 属性设为填充了完整内容的 WSGI 环境。 默认的实现会使用上述的所有方法和属性,加上 get_stdin(), get_stderr()add_cgi_vars() 方法以及 wsgi_file_wrapper 属性。 它还会插入一个 SERVER_SOFTWARE 键,如果该键还不存在的话,只要 origin_server 属性为真值并且设置了 server_software 属性。

用于自定义异常处理的方法和属性:

  • log_exception(exc_info)

    exc_info 元素记录到服务器日志。 exc_info 是一个 (type, value, traceback) 元组。 默认的实现会简单地将回溯信息写入请求的 wsgi.errors 流并刷新它。 子类可以重载此方法来修改格式或重设输出目标,通过邮件将回溯信息发给管理员,或执行任何其他符合要求的动作。

  • traceback_limit

    要包括在默认 log_exception() 方法的回溯信息输出中的最大帧数。 如果为 None,则会包括所有的帧。

  • error_output(environ, start_response)

    此方法是一个为用户生成错误页面的 WSGI 应用程序。 它仅当将标头发送给客户端之前发生错误时会被发起调用。

    此方法可使用 sys.exc_info() 来访问当前错误信息,并应当在调用 start_response 时将该信息传递给它(如 PEP 3333 的 “Error Handling” 部分所描述的)。

    默认的实现只是使用 error_status, error_headers, 和 error_body 属性来生成一个输出页面。 子类可以重载此方法来产生更动态化的错误输出。

    但是请注意,从安全角度看来是不建议将诊断信息暴露给任何用户的;理想的做法是你应当通过特别处理来启用诊断输出,因此默认的实现并不包括这样的内容。

  • error_status

    用于错误响应的 HTTP 状态。 这应当是一个在 PEP 3333 中定义的字符串;它默认为代码 500 以相应的消息。

  • error_headers

    用于错误响应的 HTTP 标头。 这应当是由 WSGI 响应标头 ((name, value) 元组) 构成的列表,如 PEP 3333 所定义的。 默认的列表只是将内容类型设为 text/plain

  • error_body

    错误响应体。 这应当是一个 HTTP 响应体字节串。 它默认为纯文本 “A server error occurred. Please contact the administrator.”

用于 PEP 3333 的 “可选的平台专属文件处理” 特性的方法和属性:

  • wsgi_file_wrapper

    一个 wsgi.file_wrapper 工厂对象,或为 None。 该属性的默认值是 wsgiref.util.FileWrapper 类。

  • sendfile()

    重载以实现平台专属的文件传输。 此方法仅在应用程序的返回值是由 wsgi_file_wrapper 属性指定的类的实例时会被调用。 如果它能够成功地传输文件则应当返回真值,以使得默认的传输代码将不会被执行。 此方法的默认实现只会返回假值。

杂项方法和属性:

  • origin_server

    该属性在处理句柄的 _write()_flush() 被用于同客户端直接通信而不是通过需要 HTTP 状态为某种特殊 Status: 标头的 CGI 类网关协议时应当被设为真值

    该属性在 BaseHandler 中默认为真值,但在 BaseCGIHandlerCGIHandler 中则为假值。

  • http_version

    如果 origin_server 为真值,则该字符串属性会被用来设置给客户端的响应的 HTTP 版本。 它的默认值为 "1.0"

wsgiref.handlers.read_environ()

将来自 os.environ 的 CGI 变量转码为 PEP 3333 “bytes in unicode” 字符串,返回一个新的字典。 此函数被 CGIHandlerIISCGIHandler 用来替代直接使用 os.environ,后者不一定在所有使用 Python 3 的平台和 Web 服务器上都符合 WSGI 标准 — 特别是当 OS 的实际环境为 Unicode 时 (例如 Windows),或者当环境为字节数据,但被 Python 用来解码它的系统编码格式不是 ISO-8859-1 时 (例如使用 UTF-8 的 Unix 系统)。

如果你要实现自己的基于 CGI 的处理句柄,你可能会想要使用此例程而不是简单地从 os.environ 直接拷贝值。

3.2 新版功能.

例子

这是一个可运行的 “Hello World” WSGI 应用程序:

from wsgiref.simple_server import make_server
# Every WSGI application must have an application object - a callable
# object that accepts two arguments. For that purpose, we're going to
# use a function (note that you're not limited to a function, you can
# use a class for example). The first argument passed to the function
# is a dictionary containing CGI-style environment variables and the
# second variable is the callable object.
def hello_world_app(environ, start_response):
    status = '200 OK'  # HTTP Status
    headers = [('Content-type', 'text/plain; charset=utf-8')]  # HTTP Headers
    start_response(status, headers)
    # The returned object is going to be printed
    return [b"Hello World"]
with make_server('', 8000, hello_world_app) as httpd:
    print("Serving on port 8000...")
    # Serve until process is killed
    httpd.serve_forever()

一个发布当前目录的 WSGI 应用程序示例,接受通过命令行指定可选的目录和端口号 (默认值: 8000):

#!/usr/bin/env python3
'''
Small wsgiref based web server. Takes a path to serve from and an
optional port number (defaults to 8000), then tries to serve files.
Mime types are guessed from the file names, 404 errors are raised
if the file is not found. Used for the make serve target in Doc.
'''
import sys
import os
import mimetypes
from wsgiref import simple_server, util
def app(environ, respond):
    fn = os.path.join(path, environ['PATH_INFO'][1:])
    if '.' not in fn.split(os.path.sep)[-1]:
        fn = os.path.join(fn, 'index.html')
    type = mimetypes.guess_type(fn)[0]
    if os.path.exists(fn):
        respond('200 OK', [('Content-Type', type)])
        return util.FileWrapper(open(fn, "rb"))
    else:
        respond('404 Not Found', [('Content-Type', 'text/plain')])
        return [b'not found']
if __name__ == '__main__':
    path = sys.argv[1] if len(sys.argv) > 1 else os.getcwd()
    port = int(sys.argv[2]) if len(sys.argv) > 2 else 8000
    httpd = simple_server.make_server('', port, app)
    print("Serving {} on port {}, control-C to stop".format(path, port))
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        print("Shutting down.")
        httpd.server_close()

urllib —- URL 处理模块

源代码: Lib/urllib/


urllib 是一个收集了多个涉及 URL 的模块的包:

http —- HTTP 模块

源代码: Lib/http/init.py


http 是一个包,它收集了多个用于处理超文本传输协议的模块:

http 也是一个通过 http.HTTPStatus 枚举定义了一些 HTTP 状态码以及相关联消息的模块

class http.HTTPStatus

3.5 新版功能.

enum.IntEnum 的子类,它定义了组 HTTP 状态码,原理短语以及用英语书写的长描述文本。

用法:

>>> from http import HTTPStatus
>>> HTTPStatus.OK
<HTTPStatus.OK: 200>
>>> HTTPStatus.OK == 200
True
>>> HTTPStatus.OK.value
200
>>> HTTPStatus.OK.phrase
'OK'
>>> HTTPStatus.OK.description
'Request fulfilled, document follows'
>>> list(HTTPStatus)
[<HTTPStatus.CONTINUE: 100>, <HTTPStatus.SWITCHING_PROTOCOLS:101>, ...]

HTTP 状态码

已支持并且已在 http.HTTPStatus IANA 注册 的状态码有:

双字母代码 映射名 详情
100 CONTINUE:继续 HTTP/1.1 RFC 7231, 6.2.1 节
101 SWITCHING_PROTOCOLS HTTP/1.1 RFC 7231, 6.2.2 节
102 PROCESSING WebDAV RFC 2518, 10.1 节
103 EARLY_HINTS 用于指定提示 RFC 8297 的 HTTP 状态码
200 OK HTTP/1.1 RFC 7231, 6.3.1 节
201 CREATED HTTP/1.1 RFC 7231, 6.3.2 节
202 ACCEPTED HTTP/1.1 RFC 7231, 6.3.3 节
203 NON_AUTHORITATIVE_INFORMATION HTTP/1.1 RFC 7231, 6.3.4 节
204 NO_CONTENT: 没有内容 HTTP/1.1 RFC 7231, 6.3.5 节
205 RESET_CONTENT HTTP/1.1 RFC 7231, 6.3.6 节
206 PARTIAL_CONTENT HTTP/1.1 RFC 7233, 4.1 节
207 MULTI_STATUS WebDAV RFC 4918, 11.1 节
208 ALREADY_REPORTED WebDAV Binding Extensions RFC 5842, 7.1 节(实验性)
226 IM_USED Delta Encoding in HTTP RFC 3229, 10.4.1 节
300 MULTIPLE_CHOICES:有多种资源可选择 HTTP/1.1 RFC 7231, 6.4.1 节
301 MOVED_PERMANENTLY:永久移动 HTTP/1.1 RFC 7231, 6.4.2 节
302 FOUND:临时移动 HTTP/1.1 RFC 7231, 6.4.3 节
303 SEE_OTHER:已经移动 HTTP/1.1 RFC 7231, 6.4.4 节
304 NOT_MODIFIED:没有修改 HTTP/1.1 RFC 7232, 4.1 节
305 USE_PROXY:使用代理 HTTP/1.1 RFC 7231, 6.4.5 节
307 TEMPORARY_REDIRECT:临时重定向 HTTP/1.1 RFC 7231, 6.4.7 节
308 PERMANENT_REDIRECT:永久重定向 Permanent Redirect RFC 7238, Section 3 (Experimental)
400 BAD_REQUEST:错误请求 HTTP/1.1 RFC 7231, 6.5.1 节
401 UNAUTHORIZED:未授权 HTTP/1.1 Authentication RFC 7235, 3.1 节
402 PAYMENT_REQUIRED:保留,将来使用 HTTP/1.1 RFC 7231, 6.5.2 节
403 FORBIDDEN:禁止 HTTP/1.1 RFC 7231, 6.5.3 节
404 NOT_FOUND:没有找到 HTTP/1.1 RFC 7231, 6.5.4 节
405 METHOD_NOT_ALLOWED:该请求方法不允许 HTTP/1.1 RFC 7231, 6.5.5 节
406 NOT_ACCEPTABLE:不可接受 HTTP/1.1 RFC 7231, 6.5.6 节
407 PROXY_AUTHENTICATION_REQUIRED:要求使用代理验明正身 HTTP/1.1 Authentication RFC 7235, 3.1 节
408 REQUEST_TIMEOUT:请求超时 HTTP/1.1 RFC 7231, 6.5.7 节
409 CONFLICT:冲突 HTTP/1.1 RFC 7231, 6.5.8 节
410 GONE:已经不在了 HTTP/1.1 RFC 7231, 6.5.9 节
411 LENGTH_REQUIRED:长度要求 HTTP/1.1 RFC 7231, 6.5.10 节
412 PRECONDITION_FAILED:前提条件错误 HTTP/1.1 RFC 7232, 4.2 节
413 REQUEST_ENTITY_TOO_LARGE:请求体太大了 HTTP/1.1 RFC 7231, 6.5.11 节
414 REQUEST_URI_TOO_LONG:请求URI太长了 HTTP/1.1 RFC 7231, 6.5.12 节
415 UNSUPPORTED_MEDIA_TYPE:不支持的媒体格式 HTTP/1.1 RFC 7231, 6.5.13 节
416 REQUESTED_RANGE_NOT_SATISFIABLE HTTP/1.1 Range Requests RFC 7233, 4.4 节
417 EXPECTATION_FAILED:期望失败 HTTP/1.1 RFC 7231, 6.5.14 节
418 IM_A_TEAPOT HTCPCP/1.0 RFC 2324, Section 2.3.2
421 MISDIRECTED_REQUEST HTTP/2 RFC 7540, 9.1.2 节
422 UNPROCESSABLE_ENTITY:可加工实体 WebDAV RFC 4918, 11.2 节
423 LOCKED:锁着 WebDAV RFC 4918, 11.3 节
424 FAILED_DEPENDENCY:失败的依赖 WebDAV RFC 4918, 11.4 节
425 TOO_EARLY 使用 HTTP RFC 8470 中的早期数据
426 UPGRADE_REQUIRED:升级需要 HTTP/1.1 RFC 7231, 6.5.15 节
428 PRECONDITION_REQUIRED:先决条件要求 Additional HTTP Status Codes RFC 6585
429 TOO_MANY_REQUESTS:太多的请求 Additional HTTP Status Codes RFC 6585
431 REQUEST_HEADER_FIELDS_TOO_LARGE:请求头太大 Additional HTTP Status Codes RFC 6585
451 UNAVAILABLE_FOR_LEGAL_REASONS HTTP 状态码用于报告法律障碍 RFC 7725
500 INTERNAL_SERVER_ERROR:内部服务错误 HTTP/1.1 RFC 7231, 6.6.1 节
501 NOT_IMPLEMENTED:不可执行 HTTP/1.1 RFC 7231, 6.6.2 节
502 BAD_GATEWAY:无效网关 HTTP/1.1 RFC 7231, 6.6.3 节
503 SERVICE_UNAVAILABLE:服务不可用 HTTP/1.1 RFC 7231, 6.6.4 节
504 GATEWAY_TIMEOUT:网关超时 HTTP/1.1 RFC 7231, 6.6.5 节
505 HTTP_VERSION_NOT_SUPPORTED:HTTP版本不支持 HTTP/1.1 RFC 7231, 6.6.6 节
506 VARIANT_ALSO_NEGOTIATES:服务器存在内部配置错误 透明内容协商在: HTTP RFC 2295, 8.1 节(实验性)
507 INSUFFICIENT_STORAGE:存储不足 WebDAV RFC 4918, 11.5 节
508 LOOP_DETECTED:循环检测 WebDAV Binding Extensions RFC 5842, 7.2 节(实验性)
510 NOT_EXTENDED:不扩展 WebDAV Binding Extensions RFC 5842, 7.2 节(实验性)
511 NETWORK_AUTHENTICATION_REQUIRED:要求网络身份验证 Additional HTTP Status Codes RFC 6585, 6 节

为了保持向后兼容性,枚举值也以常量形式出现在 http.client 模块中,。 枚举名等于常量名 (例如 http.HTTPStatus.OK 也可以是 http.client.OK)。

在 3.7 版更改: 添加了 421 MISDIRECTED_REQUEST 状态码。

3.8 新版功能: 添加了 451 UNAVAILABLE_FOR_LEGAL_REASONS 状态码。

3.9 新版功能: 增加了 103 EARLY_HINTS, 418 IM_A_TEAPOT425 TOO_EARLY 状态码

ftplib —- FTP 协议客户端

源代码: Lib/ftplib.py


本模块定义了 FTP 类和一些相关项目。 FTP 类实现了 FTP 协议的客户端。 你可以用这个类来编写执行各种自动化 FTP 任务的 Python 程序,例如镜像其他 FTP 服务器等。 它还被 urllib.request 模块用来处理使用 FTP 的 URL。 有关 FTP (文件传输协议) 的更多信息,请参阅 RFC 959

默认编码为 UTF-8,遵循 RFC 2640

以下是使用 ftplib 模块的会话示例:

>>> from ftplib import FTP
>>> ftp = FTP('ftp.us.debian.org')  # connect to host, default port
>>> ftp.login()                     # user anonymous, passwd anonymous@
'230 Login successful.'
>>> ftp.cwd('debian')               # change into "debian" directory
'250 Directory successfully changed.'
>>> ftp.retrlines('LIST')           # list directory contents
-rw-rw-r--    1 1176     1176         1063 Jun 15 10:18 README
...
drwxr-sr-x    5 1176     1176         4096 Dec 19  2000 pool
drwxr-sr-x    4 1176     1176         4096 Nov 17  2008 project
drwxr-xr-x    3 1176     1176         4096 Oct 10  2012 tools
'226 Directory send OK.'
>>> with open('README', 'wb') as fp:
>>>     ftp.retrbinary('RETR README', fp.write)
'226 Transfer complete.'
>>> ftp.quit()
'221 Goodbye.'

这个模块定义了以下内容:

class ftplib.FTP(host=’’, user=’’, passwd=’’, acct=’’, timeout=None, source_address=None, **, encoding=’utf-8’*)

返回一个 FTP 类的新实例。当传入 host 时,将调用 connect(host) 方法。当传入 user 时,将额外调用 login(user, passwd, acct) 方法(其中 passwdacct 若没有传入则默认为空字符串)。可选参数 timeout 指定阻塞操作(如连接尝试)的超时(以秒为单位,如果未指定超时,将使用全局默认超时设置)。source_address 是一个 2 元组 (host, port),套接字在连接前绑定它,作为其源地址。encoding 参数指定目录和文件名的编码。

FTP 类支持 with 语句,例如:

>>> from ftplib import FTP
>>> with FTP("ftp1.at.proftpd.org") as ftp:
...     ftp.login()
...     ftp.dir()
... 
'230 Anonymous login ok, restrictions apply.'
dr-xr-xr-x   9 ftp      ftp           154 May  6 10:43 .
dr-xr-xr-x   9 ftp      ftp           154 May  6 10:43 ..
dr-xr-xr-x   5 ftp      ftp          4096 May  6 10:43 CentOS
dr-xr-xr-x   3 ftp      ftp            18 Jul 10  2008 Fedora
>>>

在 3.2 版更改: 添加了对 with 语句的支持。

在 3.3 版更改: 添加了 source_address 参数。

在 3.9 版更改: 如果 timeout 参数设置为 0,创建非阻塞套接字时,它将引发 ValueError 来阻止该操作。添加了 encoding 参数,且为了遵循 RFC 2640,该参数默认值从 Latin-1 改为了 UTF-8。

class ftplib.FTP_TLS(host=’’, user=’’, passwd=’’, acct=’’, keyfile=None, certfile=None, context=None, timeout=None, source_address=None, **, encoding=’utf-8’*)

一个 FTP 的子类,它为 FTP 添加了 TLS 支持,如 RFC 4217 所述。它将像通常一样连接到 21 端口,暗中保护在身份验证前的 FTP 控制连接。而保护数据连接需要用户明确调用 prot_p() 方法。context 是一个 ssl.SSLContext 对象,该对象可以将 SSL 配置选项、证书和私钥打包放入一个单独的(可以长久存在的)结构中。

keyfilecertfile 是可以代替 context 的传统方案,它们可以分别指向 PEM 格式的私钥和证书链文件,用于进行 SSL 连接。

3.2 新版功能.

在 3.3 版更改: 添加了 source_address 参数。

在 3.4 版更改: 本类现在支持使用 ssl.SSLContext.check_hostname服务器名称指示 进行主机名检查。

3.6 版后已移除: keyfilecertfile 已弃用并转而推荐 context。 请改用 ssl.SSLContext.load_cert_chain() 或让 ssl.create_default_context() 为你选择系统所信任的 CA 证书。

在 3.9 版更改: 如果 timeout 参数设置为 0,创建非阻塞套接字时,它将引发 ValueError 来阻止该操作。添加了 encoding 参数,且为了遵循 RFC 2640,该参数默认值从 Latin-1 改为了 UTF-8。

以下是使用 FTP_TLS 类的会话示例:

>>> ftps = FTP_TLS('ftp.pureftpd.org')
>>> ftps.login()
'230 Anonymous user logged in'
>>> ftps.prot_p()
'200 Data protection level set to "private"'
>>> ftps.nlst()
['6jack', 'OpenBSD', 'antilink', 'blogbench', 'bsdcam', 'clockspeed', 'djbdns-jedi', 'docs', 'eaccelerator-jedi', 'favicon.ico', 'francotone', 'fugu', 'ignore', 'libpuzzle', 'metalog', 'minidentd', 'misc', 'mysql-udf-global-user-variables', 'php-jenkins-hash', 'php-skein-hash', 'php-webdav', 'phpaudit', 'phpbench', 'pincaster', 'ping', 'posto', 'pub', 'public', 'public_keys', 'pure-ftpd', 'qscan', 'qtc', 'sharedance', 'skycache', 'sound', 'tmp', 'ucarp']

exception ftplib.error_reply

从服务器收到意外答复时,将引发本异常。

exception ftplib.error_temp

收到表示临时错误的错误代码(响应代码在 400—499 范围内)时,将引发本异常。

exception ftplib.error_perm

收到表示永久性错误的错误代码(响应代码在 500—599 范围内)时,将引发本异常。

exception ftplib.error_proto

从服务器收到不符合 FTP 响应规范的答复,比如以数字 1—5 开头时,将引发本异常。

ftplib.all_errors

所有异常的集合(一个元组),由于 FTP 连接出现问题(并非调用者的编码错误),FTP 实例的方法可能会引发这些异常。该集合包括上面列出的四个异常以及 OSErrorEOFError

参见

netrc 模块

.netrc 文件格式解析器。FTP 客户端在响应用户之前,通常使用 .netrc 文件来加载用户认证信息。

FTP 对象

一些方法可以按照两种方式来使用:一种处理文本文件,另一种处理二进制文件。方法名称与相应的命令相同,文本版中命令后面跟着 lines,二进制版中命令后面跟着 binary

FTP 实例具有下列方法:

FTP.set_debuglevel(level)

设置实例的调试级别,它控制着调试信息的数量。默认值 0 不产生调试信息。值 1 产生中等数量的调试信息,通常每个请求产生一行。大于或等于 2 的值产生的调试信息最多,FTP 控制连接上发送和接收的每一行都将被记录下来。

FTP.connect(host=’’, port=0, timeout=None, source_address=None)

连接到给定的主机和端口。默认端口号由 FTP 协议规范规定,为 21。偶尔才需要指定其他端口号。每个实例只应调用一次本函数,如果在创建实例时就传入了 host,则根本不应调用它。所有其他方法只能在建立连接后使用。可选参数 timeout 指定连接尝试的超时(以秒为单位)。如果没有传入 timeout,将使用全局默认超时设置。source_address 是一个 2 元组 (host, port),套接字在连接前绑定它,作为其源地址。

引发一个 审计事件 ftplib.connect,附带参数 self, host, port

在 3.3 版更改: 添加了 source_address 参数。

FTP.getwelcome()

返回服务器发送的欢迎消息,作为连接开始的回复。(该消息有时包含与用户有关的免责声明或帮助信息。)

FTP.login(user=’anonymous’, passwd=’’, acct=’’)

user 的身份登录。passwdacct 是可选参数,默认为空字符串。如果没有指定 user*,则默认为 'anonymous'。如果 *user'anonymous',那么默认的 passwd'anonymous@'。连接建立后,每个实例只应调用一次本函数;如果在创建实例时传入了 host 和 user,则完全不应该调用本函数。在客户端登录后,才允许执行大多数 FTP 命令。acct 参数提供记账信息 (“accounting information”);仅少数系统实现了该特性。

FTP.abort()

中止正在进行的文件传输。本方法并不总是有效,但值得一试。

FTP.sendcmd(cmd)

将一条简单的命令字符串发送到服务器,返回响应的字符串。

引发一个 审计事件 ftplib.sendcmd,附带参数 self, cmd

FTP.voidcmd(cmd)

将一条简单的命令字符串发送到服务器,并处理响应内容。如果收到的响应代码表示的是成功(代码范围 200—299),则不返回任何内容。否则将引发 error_reply

引发一个 审计事件 ftplib.sendcmd,附带参数 self, cmd

FTP.retrbinary(cmd, callback, blocksize=8192, rest=None)

以二进制传输模式下载文件。cmd 应为恰当的 RETR 命令:'RETR 文件名'callback 函数会在收到每个数据块时调用,传入的参数是表示数据块的一个字节。为执行实际传输,创建了底层套接字对象,可选参数 blocksize 指定了读取该对象时的最大块大小(这也是传入 callback 的数据块的最大大小)。已经选择了合理的默认值。rest 的含义与 transfercmd() 方法中的含义相同。

FTP.retrlines(cmd, callback=None)

按照初始化时的 encoding 参数指定的编码,获取文件或目录列表。cmd 应是恰当的 RETR 命令,也可以是诸如 LISTNLST 之类的命令(通常就只是字符串 'LIST')。LIST 获取文件列表以及那些文件的信息。NLST 获取文件名称列表。callback 函数会在每一行都调用,参数就是包含一行的字符串,删除了尾部的 CRLF。默认的 callback 会把行打印到 sys.stdout

FTP.set_pasv(val)

如果 val 为 true,则打开“被动”模式,否则禁用被动模式。默认下被动模式是打开的。

FTP.storbinary(cmd, fp, blocksize=8192, callback=None, rest=None)

以二进制传输模式存储文件。 cmd 应为恰当的 STOR 命令: "STOR filename"fp 是一个 文件对象 (以二进制模式打开),将使用它的 read() 方法读取它,用于提供要存储的数据,直到遇到 EOF,读取时的块大小为 blocksize*。 参数 *blocksize 的默认值为 8192。 可选参数 callback 是单参数函数,在每个数据块发送后都会以该数据块作为参数来调用它。rest 的含义与 transfercmd() 方法中的含义相同。

在 3.2 版更改: 添加了 rest 参数。

FTP.storlines(cmd, fp, callback=None)

以文本行模式存储文件。cmd 应为恰当的 STOR 命令 。 fp 是一个 文件对象 (以二进制模式打开),将使用它的 readline() 方法读取它的每一行,用于提供要存储的数据,直到遇到 EOF。 可选参数 callback 是单参数函数,在每行发送后都会以该行作为参数来调用它。

FTP.transfercmd(cmd, rest=None)

在 FTP 数据连接上开始传输数据。如果传输处于活动状态,传输命令由 cmd 指定,需发送 EPRTPORT 命令,然后接受连接 (accept)。如果服务器是被动服务器,需发送 EPSVPASV 命令,连接到服务器 (connect),然后启动传输命令。两种方式都将返回用于连接的套接字。

如果传入了可选参数 rest*,则一条 REST 命令会被发送到服务器,并以 *rest 作为参数。rest 通常表示请求文件中的字节偏移量,它告诉服务器重新开始发送文件的字节,从请求的偏移量处开始,跳过起始字节。但是请注意,transfercmd() 方法会将 rest 转换为字符串,但是不检查字符串的内容,转换用的编码是在初始化时指定的 encoding 参数。如果服务器无法识别 REST 命令,将引发 error_reply 异常。如果发生这种情况,只需不带 rest 参数调用 transfercmd()

FTP.ntransfercmd(cmd, rest=None)

类似于 transfercmd(),但返回一个元组,包括数据连接和数据的预计大小。如果预计大小无法计算,则返回的预计大小为 Nonecmdrest 的含义与 transfercmd() 中的相同。

FTP.mlsd(path=’’, facts=[])

使用 MLSD 命令以标准格式列出目录内容 (RFC 3659)。如果省略 path 则使用当前目录。facts 是字符串列表,表示所需的信息类型(如 ["type", "size", "perm"])。返回一个生成器对象,每个在 path 中找到的文件都将在该对象中生成两个元素的元组。第一个元素是文件名,第二个元素是该文件的 facts 的字典。该字典的内容受 facts 参数限制,但不能保证服务器会返回所有请求的 facts。

3.3 新版功能.

FTP.nlst(argument[, ])

返回一个文件名列表,文件名由 NLST 命令返回。可选参数 argument 是待列出的目录(默认为当前服务器目录)。可以使用多个参数,将非标准选项传递给 NLST 命令。

注解

如果目标服务器支持相关命令,那么 mlsd() 提供的 API 更好。

FTP.dir(argument[, ])

生成目录列表,即 LIST 命令所返回的结果,并将其打印到标准输出。可选参数 argument 是待列出的目录(默认为当前服务器目录)。可以使用多个参数,将非标准选项传递给 LIST 命令。如果最后一个参数是一个函数,它将被用作 callback 函数,与 retrlines() 中的相同,默认将打印到 sys.stdout。本方法返回 None

注解

如果目标服务器支持相关命令,那么 mlsd() 提供的 API 更好。

FTP.rename(fromname, toname)

将服务器上的文件 fromname 重命名为 toname

FTP.delete(filename)

将服务器上名为 filename 的文件删除。如果删除成功,返回响应文本,如果删除失败,在权限错误时引发 error_perm,在其他错误时引发 error_reply

FTP.cwd(pathname)

设置服务器端的当前目录。

FTP.mkd(pathname)

在服务器上创建一个新目录。

FTP.pwd()

返回服务器上当前目录的路径。

FTP.rmd(dirname)

将服务器上名为 dirname 的目录删除。

FTP.size(filename)

请求服务器上名为 filename 的文件大小。成功后以整数返回文件大小,未成功则返回 None。注意,SIZE 不是标准命令,但通常许多服务器的实现都支持该命令。

FTP.quit()

向服务器发送 QUIT 命令并关闭连接。 这是关闭一个连接的“礼貌”方式,但是如果服务器对 QUIT 命令的响应带有错误消息则这会引发一个异常。 这意味着对 close() 方法的调用,它将使得 FTP 实例对后继调用无效(见下文)。

FTP.close()

单方面关闭连接。 这不该被应用于已经关闭的连接,例如成功调用 quit() 之后的连接。 在此调用之后 FTP 实例不应被继续使用(在调用 close()quit() 之后你不能通过再次发起调用 login() 方法重新打开连接)。

FTP_TLS 对象

FTP_TLS 类继承自 FTP,它定义了下述其他对象:

FTP_TLS.ssl_version

欲采用的 SSL 版本(默认为 ssl.PROTOCOL_SSLv23)。

FTP_TLS.auth()

通过使用 TLS 或 SSL 来设置一个安全控制连接,具体取决于 ssl_version 属性是如何设置的。

在 3.4 版更改: 此方法现在支持使用 ssl.SSLContext.check_hostname服务器名称指示 进行主机名检查。

FTP_TLS.ccc()

将控制通道回复为纯文本。 这适用于发挥知道如何使用非安全 FTP 处理 NAT 而无需打开固定端口的防火墙的优势。

3.3 新版功能.

FTP_TLS.prot_p()

设置加密数据连接。

FTP_TLS.prot_c()

设置明文数据连接。

poplib —- POP3 协议客户端

源代码: Lib/poplib.py


本模块定义了一个 POP3 类,该类封装了到 POP3 服务器的连接过程,并实现了 RFC 1939 中定义的协议。POP3 类同时支持 RFC 1939 中最小的和可选的命令集。POP3 类还支持在 RFC 2595 中引入的 STLS 命令,用于在已建立的连接上启用加密通信。

本模块额外提供一个 POP3_SSL 类,在连接到 POP3 服务器时,该类为使用 SSL 作为底层协议层提供了支持。

注意,尽管 POP3 具有广泛的支持,但它已经过时。POP3 服务器的实现质量差异很大,而且大多很糟糕。如果邮件服务器支持 IMAP,则最好使用 imaplib.IMAP4 类,因为 IMAP 服务器一般实现得更好。

poplib 模块提供了两个类:

class poplib.POP3(host, port=POP3_PORT[, timeout])

本类实现实际的 POP3 协议。实例初始化时,连接就会建立。如果省略 port*,则使用标准 POP3 端口(110)。可选参数 *timeout 指定连接尝试的超时时间(以秒为单位,如果未指定超时,将使用全局默认超时设置)。

引发一个 审计事件 poplib.connect,附带参数 self, host, port

引发一个 审计事件 poplib.putline,附带参数 self, line

在 3.9 版更改: 如果 timeout 参数设置为 0,创建非阻塞套接字时,它将引发 ValueError 来阻止该操作。

class poplib.POP3_SSL(host, port=POP3_SSL_PORT, keyfile=None, certfile=None, timeout=None, context=None)

一个 POP3 的子类,它使用经 SSL 加密的套接字连接到服务器。如果端口 port 未指定,则使用 995,它是标准的 POP3-over-SSL 端口。timeout 的作用与 POP3 构造函数中的相同。context 是一个可选的 ssl.SSLContext 对象,该对象可以将 SSL 配置选项、证书和私钥打包放入一个单独的(可以长久存在的)结构中。

keyfilecertfile 是可以代替 context 的传统方案,它们可以分别指向 PEM 格式的私钥和证书链文件,用于进行 SSL 连接。

引发一个 审计事件 poplib.connect,附带参数 self, host, port

引发一个 审计事件 poplib.putline,附带参数 self, line

在 3.2 版更改: 添加了 context 参数。

在 3.4 版更改: 本类现在支持使用 ssl.SSLContext.check_hostname服务器名称指示 进行主机名检查。

3.6 版后已移除: keyfilecertfile 已弃用并转而推荐 context。 请改用 ssl.SSLContext.load_cert_chain() 或让 ssl.create_default_context() 为你选择系统所信任的 CA 证书。

在 3.9 版更改: 如果 timeout 参数设置为 0,创建非阻塞套接字时,它将引发 ValueError 来阻止该操作。

定义了一个异常,它是作为 poplib 模块的属性定义的:

exception poplib.error_proto

此模块的所有错误都将引发本异常(来自 socket 模块的错误不会被捕获)。异常的原因将以字符串的形式传递给构造函数。

参见

有关 Fetchmail 的常见问题

fetchmail POP/IMAP 客户端的“常见问题”收集了 POP3 服务器之间的差异和 RFC 不兼容的信息,如果要编写基于 POP 协议的应用程序,这可能会很有用。

POP3 对象

所有 POP3 命令均以同名的方法表示,小写,大多数方法返回的是服务器发送的响应文本。

POP3 实例具有下列方法:

POP3.set_debuglevel(level)

设置实例的调试级别,它控制着调试信息的数量。默认值 0 不产生调试信息。值 1 产生中等数量的调试信息,通常每个请求产生一行。大于或等于 2 的值产生的调试信息最多,FTP 控制连接上发送和接收的每一行都将被记录下来。

POP3.getwelcome()

返回 POP3 服务器发送的问候语字符串。

POP3.capa()

查询服务器支持的功能,这些功能在 RFC 2449 中有说明。返回一个 {'name': ['param'...]} 形式的字典。

3.4 新版功能.

POP3.user(username)

发送 user 命令,返回的响应应该指示需要一个密码。

POP3.pass_(password)

发送密码,响应包括邮件数和邮箱大小。注意:在调用 quit() 前,服务器上的邮箱都是锁定的。

POP3.apop(user, secret)

使用更安全的 APOP 身份验证登录到 POP3 服务器。

POP3.rpop(user)

使用 RPOP 身份验证(类似于 Unix r-命令)登录到 POP3 服务器。

POP3.stat()

获取邮箱状态。结果为 2 个整数组成的元组:(message count, mailbox size)

POP3.list([which])

请求消息列表,结果的形式为 (response, ['mesg_num octets', ...], octets)。如果设置了 which,它表示需要列出的消息。

POP3.retr(which)

检索编号为 which 的整条消息,并设置其已读标志位。结果的形式为 (response, ['line', ...], octets)

POP3.dele(which)

将编号为 which 的消息标记为待删除。在多数服务器上,删除操作直到 QUIT 才会实际执行(主要例外是 Eudora QPOP,它在断开连接时执行删除,故意违反了 RFC)。

POP3.rset()

移除邮箱中的所有待删除标记。

POP3.noop()

什么都不做。可以用于保持活动状态。

POP3.quit()

登出:提交更改,解除邮箱锁定,断开连接。

POP3.top(which, howmuch)

检索编号为 which 的消息,范围是消息头加上消息头往后数 howmuch 行。结果的形式为 (response, ['line', ...], octets)

本方法使用 POP3 TOP 命令,不同于 RETR 命令,它不设置邮件的已读标志位。不幸的是,TOP 在 RFC 中说明不清晰,且在小众的服务器软件中往往不可用。信任并使用它之前,请先手动对目标 POP3 服务器测试本方法。

POP3.uidl(which=None)

返回消息摘要(唯一 ID)列表。如果指定了 which,那么结果将包含那条消息的唯一 ID,形式为 'response mesgnum uid,如果未指定,那么结果为列表 (response, ['mesgnum uid', ...], octets)

POP3.utf8()

尝试切换至 UTF-8 模式。成功则返回服务器的响应,失败则引发 error_proto 异常。在 RFC 6856 中有说明。

3.5 新版功能.

POP3.stls(context=None)

在活动连接上开启 TLS 会话,在 RFC 2595 中有说明。仅在用户身份验证前允许这样做。

context 参数是一个 ssl.SSLContext 对象,该对象可以将 SSL 配置选项、证书和私钥打包放入一个单独的(可以长久存在的)结构中。

此方法支持通过 ssl.SSLContext.check_hostname服务器名称指示进行主机名检查。

3.4 新版功能.

POP3_SSL 实例没有额外方法。该子类的接口与其父类的相同。

POP3 示例

以下是一个最短示例(不带错误检查),该示例将打开邮箱,检索并打印所有消息:

import getpass, poplib
M = poplib.POP3('localhost')
M.user(getpass.getuser())
M.pass_(getpass.getpass())
numMessages = len(M.list()[1])
for i in range(numMessages):
    for j in M.retr(i+1)[1]:
        print(j)

模块的最后有一段测试,其中包含的用法示例更加广泛。

imaplib —- IMAP4 协议客户端

源代码: Lib/imaplib.py


本模块定义了三个类: IMAP4IMAP4_SSLIMAP4_stream 。这三个类封装了与IMAP4服务器的连接并实现了 RFC 2060 当中定义的大多数IMAP4rev1客户端协议。其与IMAP4( RFC 1730 )服务器后向兼容,但是 STATUS 指令在IMAP4中不支持。

imaplib 模块提供了三个类,其中 IMAP4 是基类:

class imaplib.IMAP4(host=’’, port=IMAP4_PORT, timeout=None)

This class implements the actual IMAP4 protocol. The connection is created and protocol version (IMAP4 or IMAP4rev1) is determined when the instance is initialized. If host is not specified, '' (the local host) is used. If port is omitted, the standard IMAP4 port (143) is used. The optional timeout parameter specifies a timeout in seconds for the connection attempt. If timeout is not given or is None, the global default socket timeout is used.

The IMAP4 class supports the with statement. When used like this, the IMAP4 LOGOUT command is issued automatically when the with statement exits. E.g.:

>>> from imaplib import IMAP4
>>> with IMAP4("domain.org") as M:
...     M.noop()
...
('OK', [b'Nothing Accomplished. d25if65hy903weo.87'])

在 3.5 版更改: 添加了对 with 语句的支持。

在 3.9 版更改: The optional timeout parameter was added.

Three exceptions are defined as attributes of the IMAP4 class:

exception IMAP4.error

Exception raised on any errors. The reason for the exception is passed to the constructor as a string.

exception IMAP4.abort

IMAP4 server errors cause this exception to be raised. This is a sub-class of IMAP4.error. Note that closing the instance and instantiating a new one will usually allow recovery from this exception.

exception IMAP4.readonly

This exception is raised when a writable mailbox has its status changed by the server. This is a sub-class of IMAP4.error. Some other client now has write permission, and the mailbox will need to be re-opened to re-obtain write permission.

There’s also a subclass for secure connections:

class imaplib.IMAP4_SSL(host=’’, port=IMAP4_SSL_PORT, keyfile=None, certfile=None, ssl_context=None, timeout=None)

This is a subclass derived from IMAP4 that connects over an SSL encrypted socket (to use this class you need a socket module that was compiled with SSL support). If host is not specified, '' (the local host) is used. If port is omitted, the standard IMAP4-over-SSL port (993) is used. ssl_context is a ssl.SSLContext object which allows bundling SSL configuration options, certificates and private keys into a single (potentially long-lived) structure.

keyfile and certfile are a legacy alternative to ssl_context - they can point to PEM-formatted private key and certificate chain files for the SSL connection. Note that the keyfile/certfile parameters are mutually exclusive with ssl_context, a ValueError is raised if keyfile/certfile is provided along with ssl_context.

The optional timeout parameter specifies a timeout in seconds for the connection attempt. If timeout is not given or is None, the global default socket timeout is used.

在 3.3 版更改: ssl_context parameter was added.

在 3.4 版更改: 本类现在支持使用 ssl.SSLContext.check_hostname服务器名称指示 进行主机名检查。

3.6 版后已移除: keyfile and certfile are deprecated in favor of ssl_context. Please use ssl.SSLContext.load_cert_chain() instead, or let ssl.create_default_context() select the system’s trusted CA certificates for you.

在 3.9 版更改: The optional timeout parameter was added.

The second subclass allows for connections created by a child process:

class imaplib.IMAP4_stream(command)

This is a subclass derived from IMAP4 that connects to the stdin/stdout file descriptors created by passing command to subprocess.Popen().

The following utility functions are defined:

imaplib.Internaldate2tuple(datestr)

Parse an IMAP4 INTERNALDATE string and return corresponding local time. The return value is a time.struct_time tuple or None if the string has wrong format.

imaplib.Int2AP(num)

Converts an integer into a bytes representation using characters from the set [A .. P].

imaplib.ParseFlags(flagstr)

Converts an IMAP4 FLAGS response to a tuple of individual flags.

imaplib.Time2Internaldate(date_time)

Convert date_time to an IMAP4 INTERNALDATE representation. The return value is a string in the form: "DD-Mmm-YYYY HH:MM:SS +HHMM" (including double-quotes). The date_time argument can be a number (int or float) representing seconds since epoch (as returned by time.time()), a 9-tuple representing local time an instance of time.struct_time (as returned by time.localtime()), an aware instance of datetime.datetime, or a double-quoted string. In the last case, it is assumed to already be in the correct format.

Note that IMAP4 message numbers change as the mailbox changes; in particular, after an EXPUNGE command performs deletions the remaining messages are renumbered. So it is highly advisable to use UIDs instead, with the UID command.

模块的最后有一段测试,其中包含的用法示例更加广泛。

参见

Documents describing the protocol, sources for servers implementing it, by the University of Washington’s IMAP Information Center can all be found at (Source Code) https://github.com/uw-imap/imap (Not Maintained).

IMAP4 Objects

All IMAP4rev1 commands are represented by methods of the same name, either upper-case or lower-case.

All arguments to commands are converted to strings, except for AUTHENTICATE, and the last argument to APPEND which is passed as an IMAP4 literal. If necessary (the string contains IMAP4 protocol-sensitive characters and isn’t enclosed with either parentheses or double quotes) each string is quoted. However, the password argument to the LOGIN command is always quoted. If you want to avoid having an argument string quoted (eg: the flags argument to STORE) then enclose the string in parentheses (eg: r'(\Deleted)').

Each command returns a tuple: (type, [data, ...]) where type is usually 'OK' or 'NO', and data is either the text from the command response, or mandated results from the command. Each data is either a bytes, or a tuple. If a tuple, then the first part is the header of the response, and the second part contains the data (ie: ‘literal’ value).

The message_set options to commands below is a string specifying one or more messages to be acted upon. It may be a simple message number ('1'), a range of message numbers ('2:4'), or a group of non-contiguous ranges separated by commas ('1:3,6:9'). A range can contain an asterisk to indicate an infinite upper bound ('3:*').

An IMAP4 instance has the following methods:

IMAP4.append(mailbox, flags, date_time, message)

Append message to named mailbox.

IMAP4.authenticate(mechanism, authobject)

Authenticate command —- requires response processing.

mechanism specifies which authentication mechanism is to be used - it should appear in the instance variable capabilities in the form AUTH=mechanism.

authobject must be a callable object:

data = authobject(response)

It will be called to process server continuation responses; the response argument it is passed will be bytes. It should return bytes data that will be base64 encoded and sent to the server. It should return None if the client abort response * should be sent instead.

在 3.5 版更改: string usernames and passwords are now encoded to utf-8 instead of being limited to ASCII.

IMAP4.check()

Checkpoint mailbox on server.

IMAP4.close()

Close currently selected mailbox. Deleted messages are removed from writable mailbox. This is the recommended command before LOGOUT.

IMAP4.copy(message_set, new_mailbox)

Copy message_set messages onto end of new_mailbox.

IMAP4.create(mailbox)

Create new mailbox named mailbox.

IMAP4.delete(mailbox)

Delete old mailbox named mailbox.

IMAP4.deleteacl(mailbox, who)

Delete the ACLs (remove any rights) set for who on mailbox.

IMAP4.enable(capability)

Enable capability (see RFC 5161). Most capabilities do not need to be enabled. Currently only the UTF8=ACCEPT capability is supported (see RFC 6855).

3.5 新版功能: The enable() method itself, and RFC 6855 support.

IMAP4.expunge()

Permanently remove deleted items from selected mailbox. Generates an EXPUNGE response for each deleted message. Returned data contains a list of EXPUNGE message numbers in order received.

IMAP4.fetch(message_set, message_parts)

Fetch (parts of) messages. message_parts should be a string of message part names enclosed within parentheses, eg: "(UID BODY[TEXT])". Returned data are tuples of message part envelope and data.

IMAP4.getacl(mailbox)

Get the ACLs for mailbox. The method is non-standard, but is supported by the Cyrus server.

IMAP4.getannotation(mailbox, entry, attribute)

Retrieve the specified ANNOTATIONs for mailbox. The method is non-standard, but is supported by the Cyrus server.

IMAP4.getquota(root)

Get the quota root‘s resource usage and limits. This method is part of the IMAP4 QUOTA extension defined in rfc2087.

IMAP4.getquotaroot(mailbox)

Get the list of quota roots for the named mailbox. This method is part of the IMAP4 QUOTA extension defined in rfc2087.

IMAP4.list([directory[, pattern]])

List mailbox names in directory matching pattern. directory defaults to the top-level mail folder, and pattern defaults to match anything. Returned data contains a list of LIST responses.

IMAP4.login(user, password)

Identify the client using a plaintext password. The password will be quoted.

IMAP4.login_cram_md5(user, password)

Force use of CRAM-MD5 authentication when identifying the client to protect the password. Will only work if the server CAPABILITY response includes the phrase AUTH=CRAM-MD5.

IMAP4.logout()

Shutdown connection to server. Returns server BYE response.

在 3.8 版更改: The method no longer ignores silently arbitrary exceptions.

IMAP4.lsub(directory=’””‘, pattern=’*‘)

List subscribed mailbox names in directory matching pattern. directory defaults to the top level directory and pattern defaults to match any mailbox. Returned data are tuples of message part envelope and data.

IMAP4.myrights(mailbox)

Show my ACLs for a mailbox (i.e. the rights that I have on mailbox).

IMAP4.namespace()

Returns IMAP namespaces as defined in RFC 2342.

IMAP4.noop()

Send NOOP to server.

IMAP4.open(host, port, timeout=None)

Opens socket to port at host. The optional timeout parameter specifies a timeout in seconds for the connection attempt. If timeout is not given or is None, the global default socket timeout is used. Also note that if the timeout parameter is set to be zero, it will raise a ValueError to reject creating a non-blocking socket. This method is implicitly called by the IMAP4 constructor. The connection objects established by this method will be used in the IMAP4.read(), IMAP4.readline(), IMAP4.send(), and IMAP4.shutdown() methods. You may override this method.

Raises an auditing event imaplib.open with arguments self, host, port.

在 3.9 版更改: 加入 timeout 参数。

IMAP4.partial(message_num, message_part, start, length)

Fetch truncated part of a message. Returned data is a tuple of message part envelope and data.

IMAP4.proxyauth(user)

Assume authentication as user. Allows an authorised administrator to proxy into any user’s mailbox.

IMAP4.read(size)

Reads size bytes from the remote server. You may override this method.

IMAP4.readline()

Reads one line from the remote server. You may override this method.

IMAP4.recent()

Prompt server for an update. Returned data is None if no new messages, else value of RECENT response.

IMAP4.rename(oldmailbox, newmailbox)

Rename mailbox named oldmailbox to newmailbox.

IMAP4.response(code)

Return data for response code if received, or None. Returns the given code, instead of the usual type.

IMAP4.search(charset, criterion[, ])

Search mailbox for matching messages. charset may be None, in which case no CHARSET will be specified in the request to the server. The IMAP protocol requires that at least one criterion be specified; an exception will be raised when the server returns an error. charset must be None if the UTF8=ACCEPT capability was enabled using the enable() command.

示例:

# M is a connected IMAP4 instance...
typ, msgnums = M.search(None, 'FROM', '"LDJ"')
# or:
typ, msgnums = M.search(None, '(FROM "LDJ")')

IMAP4.select(mailbox=’INBOX’, readonly=False)

Select a mailbox. Returned data is the count of messages in mailbox (EXISTS response). The default mailbox is 'INBOX'. If the readonly flag is set, modifications to the mailbox are not allowed.

IMAP4.send(data)

Sends data to the remote server. You may override this method.

Raises an auditing event imaplib.send with arguments self, data.

IMAP4.setacl(mailbox, who, what)

Set an ACL for mailbox. The method is non-standard, but is supported by the Cyrus server.

IMAP4.setannotation(mailbox, entry, attribute[, ])

Set ANNOTATIONs for mailbox. The method is non-standard, but is supported by the Cyrus server.

IMAP4.setquota(root, limits)

Set the quota root*‘s resource *limits. This method is part of the IMAP4 QUOTA extension defined in rfc2087.

IMAP4.shutdown()

Close connection established in open. This method is implicitly called by IMAP4.logout(). You may override this method.

IMAP4.socket()

Returns socket instance used to connect to server.

IMAP4.sort(sort_criteria, charset, search_criterion[, ])

The sort command is a variant of search with sorting semantics for the results. Returned data contains a space separated list of matching message numbers.

Sort has two arguments before the search_criterion argument(s); a parenthesized list of sort_criteria, and the searching charset. Note that unlike search, the searching charset argument is mandatory. There is also a uid sort command which corresponds to sort the way that uid search corresponds to search. The sort command first searches the mailbox for messages that match the given searching criteria using the charset argument for the interpretation of strings in the searching criteria. It then returns the numbers of matching messages.

This is an IMAP4rev1 extension command.

IMAP4.starttls(ssl_context=None)

Send a STARTTLS command. The ssl_context argument is optional and should be a ssl.SSLContext object. This will enable encryption on the IMAP connection.

3.2 新版功能.

在 3.4 版更改: 此方法现在支持使用 ssl.SSLContext.check_hostname服务器名称指示进行主机名检查。

IMAP4.status(mailbox, names)

Request named status conditions for mailbox.

IMAP4.store(message_set, command, flag_list)

Alters flag dispositions for messages in mailbox. command is specified by section 6.4.6 of RFC 2060 as being one of “FLAGS”, “+FLAGS”, or “-FLAGS”, optionally with a suffix of “.SILENT”.

For example, to set the delete flag on all messages:

typ, data = M.search(None, 'ALL')
for num in data[0].split():
   M.store(num, '+FLAGS', '\\Deleted')
M.expunge()

注解

Creating flags containing ‘]‘ (for example: “[test]“) violates RFC 3501 (the IMAP protocol). However, imaplib has historically allowed creation of such tags, and popular IMAP servers, such as Gmail, accept and produce such flags. There are non-Python programs which also create such tags. Although it is an RFC violation and IMAP clients and servers are supposed to be strict, imaplib nonetheless continues to allow such tags to be created for backward compatibility reasons, and as of Python 3.6, handles them if they are sent from the server, since this improves real-world compatibility.

IMAP4.subscribe(mailbox)

Subscribe to new mailbox.

IMAP4.thread(threading_algorithm, charset, search_criterion[, ])

The thread command is a variant of search with threading semantics for the results. Returned data contains a space separated list of thread members.

Thread members consist of zero or more messages numbers, delimited by spaces, indicating successive parent and child.

Thread has two arguments before the search_criterion argument(s); a threading_algorithm, and the searching charset. Note that unlike search, the searching charset argument is mandatory. There is also a uid thread command which corresponds to thread the way that uid search corresponds to search. The thread command first searches the mailbox for messages that match the given searching criteria using the charset argument for the interpretation of strings in the searching criteria. It then returns the matching messages threaded according to the specified threading algorithm.

This is an IMAP4rev1 extension command.

IMAP4.uid(command, arg[, ])

Execute command args with messages identified by UID, rather than message number. Returns response appropriate to command. At least one argument must be supplied; if none are provided, the server will return an error and an exception will be raised.

IMAP4.unsubscribe(mailbox)

Unsubscribe from old mailbox.

IMAP4.unselect()

imaplib.IMAP4.unselect() frees server’s resources associated with the selected mailbox and returns the server to the authenticated state. This command performs the same actions as imaplib.IMAP4.close(), except that no messages are permanently removed from the currently selected mailbox.

3.9 新版功能.

IMAP4.xatom(name[, ])

Allow simple extension commands notified by server in CAPABILITY response.

The following attributes are defined on instances of IMAP4:

IMAP4.PROTOCOL_VERSION

The most recent supported protocol in the CAPABILITY response from the server.

IMAP4.debug

Integer value to control debugging output. The initialize value is taken from the module variable Debug. Values greater than three trace each command.

IMAP4.utf8_enabled

Boolean value that is normally False, but is set to True if an enable() command is successfully issued for the UTF8=ACCEPT capability.

3.5 新版功能.

IMAP4 Example

以下是一个最短示例(不带错误检查),该示例将打开邮箱,检索并打印所有消息:

import getpass, imaplib
M = imaplib.IMAP4()
M.login(getpass.getuser(), getpass.getpass())
M.select()
typ, data = M.search(None, 'ALL')
for num in data[0].split():
    typ, data = M.fetch(num, '(RFC822)')
    print('Message %s\n%s\n' % (num, data[0][1]))
M.close()
M.logout()

nntplib —- NNTP protocol client

Source code: Lib/nntplib.py


This module defines the class NNTP which implements the client side of the Network News Transfer Protocol. It can be used to implement a news reader or poster, or automated news processors. It is compatible with RFC 3977 as well as the older RFC 977 and RFC 2980.

Here are two small examples of how it can be used. To list some statistics about a newsgroup and print the subjects of the last 10 articles:

>>> s = nntplib.NNTP('news.gmane.io')
>>> resp, count, first, last, name = s.group('gmane.comp.python.committers')
>>> print('Group', name, 'has', count, 'articles, range', first, 'to', last)
Group gmane.comp.python.committers has 1096 articles, range 1 to 1096
>>> resp, overviews = s.over((last - 9, last))
>>> for id, over in overviews:
...     print(id, nntplib.decode_header(over['subject']))
...
1087 Re: Commit privileges for Łukasz Langa
1088 Re: 3.2 alpha 2 freeze
1089 Re: 3.2 alpha 2 freeze
1090 Re: Commit privileges for Łukasz Langa
1091 Re: Commit privileges for Łukasz Langa
1092 Updated ssh key
1093 Re: Updated ssh key
1094 Re: Updated ssh key
1095 Hello fellow committers!
1096 Re: Hello fellow committers!
>>> s.quit()
'205 Bye!'

To post an article from a binary file (this assumes that the article has valid headers, and that you have right to post on the particular newsgroup):

>>> s = nntplib.NNTP('news.gmane.io')
>>> f = open('article.txt', 'rb')
>>> s.post(f)
'240 Article posted successfully.'
>>> s.quit()
'205 Bye!'

The module itself defines the following classes:

class nntplib.NNTP(host, port=119, user=None, password=None, readermode=None, usenetrc=False[, timeout])

Return a new NNTP object, representing a connection to the NNTP server running on host host, listening at port port. An optional timeout can be specified for the socket connection. If the optional user and password are provided, or if suitable credentials are present in /.netrc and the optional flag usenetrc is true, the AUTHINFO USER and AUTHINFO PASS commands are used to identify and authenticate the user to the server. If the optional flag readermode is true, then a mode reader command is sent before authentication is performed. Reader mode is sometimes necessary if you are connecting to an NNTP server on the local machine and intend to call reader-specific commands, such as group. If you get unexpected NNTPPermanentErrors, you might need to set readermode. The NNTP class supports the with statement to unconditionally consume OSError exceptions and to close the NNTP connection when done, e.g.:

>>> from nntplib import NNTP
>>> with NNTP('news.gmane.io') as n:
...     n.group('gmane.comp.python.committers')
... 
('211 1755 1 1755 gmane.comp.python.committers', 1755, 1, 1755, 'gmane.comp.python.committers')
>>>

Raises an auditing event nntplib.connect with arguments self, host, port.

All commands will raise an auditing event nntplib.putline with arguments self and line, where line is the bytes about to be sent to the remote host.

在 3.2 版更改: usenetrc is now False by default.

在 3.3 版更改: 添加了对 with 语句的支持。

在 3.9 版更改: 如果 timeout 参数设置为 0,创建非阻塞套接字时,它将引发 ValueError 来阻止该操作。

class nntplib.NNTP_SSL(host, port=563, user=None, password=None, ssl_context=None, readermode=None, usenetrc=False[, timeout])

Return a new NNTP_SSL object, representing an encrypted connection to the NNTP server running on host host, listening at port port. NNTP_SSL objects have the same methods as NNTP objects. If port is omitted, port 563 (NNTPS) is used. ssl_context is also optional, and is a SSLContext object.All other parameters behave the same as for NNTP.

Note that SSL-on-563 is discouraged per RFC 4642, in favor of STARTTLS as described below. However, some servers only support the former.

Raises an auditing event nntplib.connect with arguments self, host, port.

All commands will raise an auditing event nntplib.putline with arguments self and line, where line is the bytes about to be sent to the remote host.

3.2 新版功能.

在 3.4 版更改: 本类现在支持使用 ssl.SSLContext.check_hostname服务器名称指示进行主机名检查。

在 3.9 版更改: 如果 timeout 参数设置为 0,创建非阻塞套接字时,它将引发 ValueError 来阻止该操作。

exception nntplib.NNTPError

Derived from the standard exception Exception, this is the base class for all exceptions raised by the nntplib module. Instances of this class have the following attribute:

  • response

    The response of the server if available, as a str object.

exception nntplib.NNTPReplyError

从服务器收到意外答复时,将引发本异常。

exception nntplib.NNTPTemporaryError

Exception raised when a response code in the range 400—499 is received.

exception nntplib.NNTPPermanentError

Exception raised when a response code in the range 500—599 is received.

exception nntplib.NNTPProtocolError

Exception raised when a reply is received from the server that does not begin with a digit in the range 1—5.

exception nntplib.NNTPDataError

Exception raised when there is some error in the response data.

NNTP Objects

When connected, NNTP and NNTP_SSL objects support the following methods and attributes.

Attributes

NNTP.nntp_version

An integer representing the version of the NNTP protocol supported by the server. In practice, this should be 2 for servers advertising RFC 3977 compliance and 1 for others.

3.2 新版功能.

NNTP.nntp_implementation

A string describing the software name and version of the NNTP server, or None if not advertised by the server.

3.2 新版功能.

方法

The response that is returned as the first item in the return tuple of almost all methods is the server’s response: a string beginning with a three-digit code. If the server’s response indicates an error, the method raises one of the above exceptions.

Many of the following methods take an optional keyword-only argument file. When the file argument is supplied, it must be either a file object opened for binary writing, or the name of an on-disk file to be written to. The method will then write any data returned by the server (except for the response line and the terminating dot) to the file; any list of lines, tuples or objects that the method normally returns will be empty.

在 3.2 版更改: Many of the following methods have been reworked and fixed, which makes them incompatible with their 3.1 counterparts.

NNTP.quit()

Send a QUIT command and close the connection. Once this method has been called, no other methods of the NNTP object should be called.

NNTP.getwelcome()

返回服务器发送的欢迎消息,作为连接开始的回复。(该消息有时包含与用户有关的免责声明或帮助信息。)

NNTP.getcapabilities()

Return the RFC 3977 capabilities advertised by the server, as a dict instance mapping capability names to (possibly empty) lists of values. On legacy servers which don’t understand the CAPABILITIES command, an empty dictionary is returned instead.

>>> s = NNTP('news.gmane.io')
>>> 'POST' in s.getcapabilities()
True

3.2 新版功能.

NNTP.login(user=None, password=None, usenetrc=True)

Send AUTHINFO commands with the user name and password. If user and password are None and usenetrc is true, credentials from ~/.netrc will be used if possible.

Unless intentionally delayed, login is normally performed during the NNTP object initialization and separately calling this function is unnecessary. To force authentication to be delayed, you must not set user or password when creating the object, and must set usenetrc to False.

3.2 新版功能.

NNTP.starttls(context=None)

Send a STARTTLS command. This will enable encryption on the NNTP connection. The context argument is optional and should be a ssl.SSLContext object.

Note that this may not be done after authentication information has been transmitted, and authentication occurs by default if possible during a NNTP object initialization. See NNTP.login() for information on suppressing this behavior.

3.2 新版功能.

在 3.4 版更改: 此方法现在支持使用 ssl.SSLContext.check_hostname服务器名称指示 进行主机名检查。

NNTP.newgroups(date, **, file=None*)

Send a NEWGROUPS command. The date argument should be a datetime.date or datetime.datetime object. Return a pair (response, groups) where groups is a list representing the groups that are new since the given date. If file is supplied, though, then groups will be empty.

>>> from datetime import date, timedelta
>>> resp, groups = s.newgroups(date.today() - timedelta(days=3))
>>> len(groups) 
85
>>> groups[0] 
GroupInfo(group='gmane.network.tor.devel', last='4', first='1', flag='m')

NNTP.newnews(group, date, **, file=None*)

Send a NEWNEWS command. Here, group is a group name or '*', and date has the same meaning as for newgroups(). Return a pair (response, articles) where articles is a list of message ids.

This command is frequently disabled by NNTP server administrators.

NNTP.list(group_pattern=None, **, file=None*)

Send a LIST or LIST ACTIVE command. Return a pair (response, list) where list is a list of tuples representing all the groups available from this NNTP server, optionally matching the pattern string group_pattern. Each tuple has the form (group, last, first, flag), where group is a group name, last and first are the last and first article numbers, and flag usually takes one of these values:

  • y: Local postings and articles from peers are allowed.
  • m: The group is moderated and all postings must be approved.
  • n: No local postings are allowed, only articles from peers.
  • j: Articles from peers are filed in the junk group instead.
  • x: No local postings, and articles from peers are ignored.
  • =foo.bar: Articles are filed in the foo.bar group instead.

If flag has another value, then the status of the newsgroup should be considered unknown.

This command can return very large results, especially if group_pattern is not specified. It is best to cache the results offline unless you really need to refresh them.

在 3.2 版更改: group_pattern was added.

NNTP.descriptions(grouppattern)

Send a LIST NEWSGROUPS command, where grouppattern is a wildmat string as specified in RFC 3977 (it’s essentially the same as DOS or UNIX shell wildcard strings). Return a pair (response, descriptions), where descriptions is a dictionary mapping group names to textual descriptions.

>>> resp, descs = s.descriptions('gmane.comp.python.*')
>>> len(descs) 
295
>>> descs.popitem() 
('gmane.comp.python.bio.general', 'BioPython discussion list (Moderated)')

NNTP.description(group)

Get a description for a single group group. If more than one group matches (if ‘group’ is a real wildmat string), return the first match. If no group matches, return an empty string.

This elides the response code from the server. If the response code is needed, use descriptions().

NNTP.group(name)

Send a GROUP command, where name is the group name. The group is selected as the current group, if it exists. Return a tuple (response, count, first, last, name) where count is the (estimated) number of articles in the group, first is the first article number in the group, last is the last article number in the group, and name is the group name.

NNTP.over(message_spec, **, file=None*)

Send an OVER command, or an XOVER command on legacy servers. message_spec can be either a string representing a message id, or a (first, last) tuple of numbers indicating a range of articles in the current group, or a (first, None) tuple indicating a range of articles starting from first to the last article in the current group, or None to select the current article in the current group.

Return a pair (response, overviews). overviews is a list of (article_number, overview) tuples, one for each article selected by message_spec. Each overview is a dictionary with the same number of items, but this number depends on the server. These items are either message headers (the key is then the lower-cased header name) or metadata items (the key is then the metadata name prepended with ":"). The following items are guaranteed to be present by the NNTP specification:

  • the subject, from, date, message-id and references headers
  • the :bytes metadata: the number of bytes in the entire raw article (including headers and body)
  • the :lines metadata: the number of lines in the article body

The value of each item is either a string, or None if not present.

It is advisable to use the decode_header() function on header values when they may contain non-ASCII characters:

>>> _, _, first, last, _ = s.group('gmane.comp.python.devel')
>>> resp, overviews = s.over((last, last))
>>> art_num, over = overviews[0]
>>> art_num
117216
>>> list(over.keys())
['xref', 'from', ':lines', ':bytes', 'references', 'date', 'message-id', 'subject']
>>> over['from']
'=?UTF-8?B?Ik1hcnRpbiB2LiBMw7Z3aXMi?= <martin@v.loewis.de>'
>>> nntplib.decode_header(over['from'])
'"Martin v. Löwis" <martin@v.loewis.de>'

3.2 新版功能.

NNTP.help(**, file=None*)

Send a HELP command. Return a pair (response, list) where list is a list of help strings.

NNTP.stat(message_spec=None)

Send a STAT command, where message_spec is either a message id (enclosed in '<' and '>') or an article number in the current group. If message_spec is omitted or None, the current article in the current group is considered. Return a triple (response, number, id) where number is the article number and id is the message id.

>>> _, _, first, last, _ = s.group('gmane.comp.python.devel')
>>> resp, number, message_id = s.stat(first)
>>> number, message_id
(9099, '<20030112190404.GE29873@epoch.metaslash.com>')

NNTP.next()

Send a NEXT command. Return as for stat().

NNTP.last()

Send a LAST command. Return as for stat().

NNTP.article(message_spec=None, **, file=None*)

Send an ARTICLE command, where message_spec has the same meaning as for stat(). Return a tuple (response, info) where info is a namedtuple with three attributes number, message_id and lines (in that order). number is the article number in the group (or 0 if the information is not available), message_id the message id as a string, and lines a list of lines (without terminating newlines) comprising the raw message including headers and body.

>>> resp, info = s.article('<20030112190404.GE29873@epoch.metaslash.com>')
>>> info.number
0
>>> info.message_id
'<20030112190404.GE29873@epoch.metaslash.com>'
>>> len(info.lines)
65
>>> info.lines[0]
b'Path: main.gmane.org!not-for-mail'
>>> info.lines[1]
b'From: Neal Norwitz <neal@metaslash.com>'
>>> info.lines[-3:]
[b'There is a patch for 2.3 as well as 2.2.', b'', b'Neal']

NNTP.head(message_spec=None, **, file=None*)

Same as article(), but sends a HEAD command. The lines returned (or written to file) will only contain the message headers, not the body.

NNTP.body(message_spec=None, **, file=None*)

Same as article(), but sends a BODY command. The lines returned (or written to file) will only contain the message body, not the headers.

NNTP.post(data)

Post an article using the POST command. The data argument is either a file object opened for binary reading, or any iterable of bytes objects (representing raw lines of the article to be posted). It should represent a well-formed news article, including the required headers. The post() method automatically escapes lines beginning with . and appends the termination line.

If the method succeeds, the server’s response is returned. If the server refuses posting, a NNTPReplyError is raised.

NNTP.ihave(message_id, data)

Send an IHAVE command. message_id is the id of the message to send to the server (enclosed in '<' and '>'). The data parameter and the return value are the same as for post().

NNTP.date()

Return a pair (response, date). date is a datetime object containing the current date and time of the server.

NNTP.slave()

Send a SLAVE command. Return the server’s response.

NNTP.set_debuglevel(level)

Set the instance’s debugging level. This controls the amount of debugging output printed. The default, 0, produces no debugging output. A value of 1 produces a moderate amount of debugging output, generally a single line per request or response. A value of 2 or higher produces the maximum amount of debugging output, logging each line sent and received on the connection (including message text).

The following are optional NNTP extensions defined in RFC 2980. Some of them have been superseded by newer commands in RFC 3977.

NNTP.xhdr(hdr, str, **, file=None*)

Send an XHDR command. The hdr argument is a header keyword, e.g. 'subject'. The str argument should have the form 'first-last' where first and last are the first and last article numbers to search. Return a pair (response, list), where list is a list of pairs (id, text), where id is an article number (as a string) and text is the text of the requested header for that article. If the file parameter is supplied, then the output of the XHDR command is stored in a file. If file is a string, then the method will open a file with that name, write to it then close it. If file is a file object, then it will start calling write() on it to store the lines of the command output. If file is supplied, then the returned list is an empty list.

NNTP.xover(start, end, **, file=None*)

Send an XOVER command. start and end are article numbers delimiting the range of articles to select. The return value is the same of for over(). It is recommended to use over() instead, since it will automatically use the newer OVER command if available.

工具函数

The module also defines the following utility function:

nntplib.decode_header(header_str)

Decode a header value, un-escaping any escaped non-ASCII characters. header_str must be a str object. The unescaped value is returned. Using this function is recommended to display some headers in a human readable form:

>>> decode_header("Some subject")
'Some subject'
>>> decode_header("=?ISO-8859-15?Q?D=E9buter_en_Python?=")
'Débuter en Python'
>>> decode_header("Re: =?UTF-8?B?cHJvYmzDqG1lIGRlIG1hdHJpY2U=?=")
'Re: problème de matrice'

smtplib —-SMTP协议客户端

源代码: Lib/smtplib.py


The smtplib module defines an SMTP client session object that can be used to send mail to any internet machine with an SMTP or ESMTP listener daemon. For details of SMTP and ESMTP operation, consult RFC 821 (Simple Mail Transfer Protocol) and RFC 1869 (SMTP Service Extensions).

class smtplib.SMTP(host=’’, port=0, local_hostname=None, [timeout, ]source_address=None)

An SMTP instance encapsulates an SMTP connection. It has methods that support a full repertoire of SMTP and ESMTP operations. If the optional host and port parameters are given, the SMTP connect() method is called with those parameters during initialization. If specified, local_hostname is used as the FQDN of the local host in the HELO/EHLO command. Otherwise, the local hostname is found using socket.getfqdn(). If the connect() call returns anything other than a success code, an SMTPConnectError is raised. The optional timeout parameter specifies a timeout in seconds for blocking operations like the connection attempt (if not specified, the global default timeout setting will be used). If the timeout expires, TimeoutError is raised. The optional source_address parameter allows binding to some specific source address in a machine with multiple network interfaces, and/or to some specific source TCP port. It takes a 2-tuple (host, port), for the socket to bind to as its source address before connecting. If omitted (or if host or port are '' and/or 0 respectively) the OS default behavior will be used.

正常使用时,只需要初始化或 connect 方法,sendmail() 方法,再加上 SMTP.quit() 方法即可。下文包括了一个示例。

SMTP 类支持 with 语句。当这样使用时,with 语句一退出就会自动发出 SMTP QUIT 命令。例如:

>>> from smtplib import SMTP
>>> with SMTP("domain.org") as smtp:
...     smtp.noop()
...
(250, b'Ok')
>>>

All commands will raise an auditing event smtplib.SMTP.send with arguments self and data, where data is the bytes about to be sent to the remote host.

在 3.3 版更改: 添加了对 with 语句的支持。

在 3.3 版更改: source_address argument was added.

3.5 新版功能: The SMTPUTF8 extension (RFC 6531) is now supported.

在 3.9 版更改: If the timeout parameter is set to be zero, it will raise a ValueError to prevent the creation of a non-blocking socket

class smtplib.SMTP_SSL(host=’’, port=0, local_hostname=None, keyfile=None, certfile=None, [timeout, ]context=None, source_address=None)

SMTP_SSL 实例与 SMTP 实例的行为完全相同。在开始连接就需要 SSL,且 starttls() 不适合的情况下,应该使用 SMTP_SSL。如果未指定 host*,则使用 localhost。如果 *port 为 0,则使用标准 SMTP-over-SSL 端口(465)。可选参数 local_hostnametimeoutsource_address 的含义与 SMTP 类中的相同。可选参数 context 是一个 SSLContext 对象,可以从多个方面配置安全连接。

keyfile and certfile are a legacy alternative to context, and can point to a PEM formatted private key and certificate chain file for the SSL connection.

在 3.3 版更改: 增加了 context

在 3.3 版更改: source_address argument was added.

在 3.4 版更改: 本类现在支持使用 ssl.SSLContext.check_hostname服务器名称指示 进行主机名检查。

3.6 版后已移除: keyfilecertfile 已弃用并转而推荐 context。 请改用 ssl.SSLContext.load_cert_chain() 或让 ssl.create_default_context() 为你选择系统所信任的 CA 证书。

在 3.9 版更改: If the timeout parameter is set to be zero, it will raise a ValueError to prevent the creation of a non-blocking socket

class smtplib.LMTP(host=’’, port=LMTP_PORT, local_hostname=None, source_address=None[, timeout])

LMTP 协议与 ESMTP 非常相似,它很大程度上基于标准的 SMTP 客户端。将 Unix 套接字用于 LMTP 是很常见的,因此 connect() 方法支持 Unix 套接字,也支持常规的 host:port 服务器。可选参数 local_hostname 和 source_address 的含义与 SMTP 类中的相同。要指定 Unix 套接字,host 必须使用绝对路径,以 ‘/‘ 开头。

Authentication is supported, using the regular SMTP mechanism. When using a Unix socket, LMTP generally don’t support or require any authentication, but your mileage might vary.

在 3.9 版更改: The optional timeout parameter was added.

A nice selection of exceptions is defined as well:

exception smtplib.SMTPException

Subclass of OSError that is the base exception class for all the other exceptions provided by this module.

在 3.4 版更改: SMTPException became subclass of OSError

exception smtplib.SMTPServerDisconnected

This exception is raised when the server unexpectedly disconnects, or when an attempt is made to use the SMTP instance before connecting it to a server.

exception smtplib.SMTPResponseException

Base class for all exceptions that include an SMTP error code. These exceptions are generated in some instances when the SMTP server returns an error code. The error code is stored in the smtp_code attribute of the error, and the smtp_error attribute is set to the error message.

exception smtplib.SMTPSenderRefused

Sender address refused. In addition to the attributes set by on all SMTPResponseException exceptions, this sets ‘sender’ to the string that the SMTP server refused.

exception smtplib.SMTPRecipientsRefused

All recipient addresses refused. The errors for each recipient are accessible through the attribute recipients, which is a dictionary of exactly the same sort as SMTP.sendmail() returns.

exception smtplib.SMTPDataError

The SMTP server refused to accept the message data.

exception smtplib.SMTPConnectError

Error occurred during establishment of a connection with the server.

exception smtplib.SMTPHeloError

The server refused our HELO message.

exception smtplib.SMTPNotSupportedError

The command or option attempted is not supported by the server.

3.5 新版功能.

exception smtplib.SMTPAuthenticationError

SMTP authentication went wrong. Most probably the server didn’t accept the username/password combination provided.

参见

RFC 821 - Simple Mail Transfer Protocol

Protocol definition for SMTP. This document covers the model, operating procedure, and protocol details for SMTP.

RFC 1869 - SMTP Service Extensions

Definition of the ESMTP extensions for SMTP. This describes a framework for extending SMTP with new commands, supporting dynamic discovery of the commands provided by the server, and defines a few additional commands.

SMTP Objects

An SMTP instance has the following methods:

SMTP.set_debuglevel(level)

Set the debug output level. A value of 1 or True for level results in debug messages for connection and for all messages sent to and received from the server. A value of 2 for level results in these messages being timestamped.

在 3.5 版更改: Added debuglevel 2.

SMTP.docmd(cmd, args=’’)

Send a command cmd to the server. The optional argument args is simply concatenated to the command, separated by a space.

This returns a 2-tuple composed of a numeric response code and the actual response line (multiline responses are joined into one long line.)

In normal operation it should not be necessary to call this method explicitly. It is used to implement other methods and may be useful for testing private extensions.

If the connection to the server is lost while waiting for the reply, SMTPServerDisconnected will be raised.

SMTP.connect(host=’localhost’, port=0)

连接到某个主机的某个端口。默认是连接到 localhost 的标准 SMTP 端口(25)上。如果主机名以冒号 (':') 结尾,后跟数字,则该后缀将被删除,且数字将视作要使用的端口号。如果在实例化时指定了 host,则构造函数会自动调用本方法。返回包含响应码和响应消息的 2 元组,它们由服务器在其连接响应中发送。

Raises an auditing event smtplib.connect with arguments self, host, port.

SMTP.helo(name=’’)

Identify yourself to the SMTP server using HELO. The hostname argument defaults to the fully qualified domain name of the local host. The message returned by the server is stored as the helo_resp attribute of the object.

In normal operation it should not be necessary to call this method explicitly. It will be implicitly called by the sendmail() when necessary.

SMTP.ehlo(name=’’)

Identify yourself to an ESMTP server using EHLO. The hostname argument defaults to the fully qualified domain name of the local host. Examine the response for ESMTP option and store them for use by has_extn(). Also sets several informational attributes: the message returned by the server is stored as the ehlo_resp attribute, does_esmtp is set to True or False depending on whether the server supports ESMTP, and esmtp_features will be a dictionary containing the names of the SMTP service extensions this server supports, and their parameters (if any).

Unless you wish to use has_extn() before sending mail, it should not be necessary to call this method explicitly. It will be implicitly called by sendmail() when necessary.

SMTP.ehlo_or_helo_if_needed()

This method calls ehlo() and/or helo() if there has been no previous EHLO or HELO command this session. It tries ESMTP EHLO first.

  • SMTPHeloError

    The server didn’t reply properly to the HELO greeting.

SMTP.has_extn(name)

Return True if name is in the set of SMTP service extensions returned by the server, False otherwise. Case is ignored.

SMTP.verify(address)

Check the validity of an address on this server using SMTP VRFY. Returns a tuple consisting of code 250 and a full RFC 822 address (including human name) if the user address is valid. Otherwise returns an SMTP error code of 400 or greater and an error string.

注解

Many sites disable SMTP VRFY in order to foil spammers.

SMTP.login(user, password, **, initial_response_ok=True*)

Log in on an SMTP server that requires authentication. The arguments are the username and the password to authenticate with. If there has been no previous EHLO or HELO command this session, this method tries ESMTP EHLO first. This method will return normally if the authentication was successful, or may raise the following exceptions:

  • SMTPHeloError

    The server didn’t reply properly to the HELO greeting.

    SMTPAuthenticationError

    The server didn’t accept the username/password combination.

    SMTPNotSupportedError

    The AUTH command is not supported by the server.

    SMTPException

    No suitable authentication method was found.

Each of the authentication methods supported by smtplib are tried in turn if they are advertised as supported by the server. See auth() for a list of supported authentication methods. initial_response_ok is passed through to auth().

Optional keyword argument initial_response_ok specifies whether, for authentication methods that support it, an “initial response” as specified in RFC 4954 can be sent along with the AUTH command, rather than requiring a challenge/response.

在 3.5 版更改: SMTPNotSupportedError may be raised, and the initial_response_ok parameter was added.

SMTP.auth(mechanism, authobject, **, initial_response_ok=True*)

Issue an SMTP AUTH command for the specified authentication mechanism, and handle the challenge response via authobject.

mechanism specifies which authentication mechanism is to be used as argument to the AUTH command; the valid values are those listed in the auth element of esmtp_features.

authobject must be a callable object taking an optional single argument:

data = authobject(challenge=None)

If optional keyword argument initial_response_ok is true, authobject() will be called first with no argument. It can return the RFC 4954 “initial response” ASCII str which will be encoded and sent with the AUTH command as below. If the authobject() does not support an initial response (e.g. because it requires a challenge), it should return None when called with challenge=None. If initial_response_ok is false, then authobject() will not be called first with None.

If the initial response check returns None, or if initial_response_ok is false, authobject() will be called to process the server’s challenge response; the challenge argument it is passed will be a bytes. It should return ASCII str data that will be base64 encoded and sent to the server.

The SMTP class provides authobjects for the CRAM-MD5, PLAIN, and LOGIN mechanisms; they are named SMTP.auth_cram_md5, SMTP.auth_plain, and SMTP.auth_login respectively. They all require that the user and password properties of the SMTP instance are set to appropriate values.

User code does not normally need to call auth directly, but can instead call the login() method, which will try each of the above mechanisms in turn, in the order listed. auth is exposed to facilitate the implementation of authentication methods not (or not yet) supported directly by smtplib.

3.5 新版功能.

SMTP.starttls(keyfile=None, certfile=None, context=None)

Put the SMTP connection in TLS (Transport Layer Security) mode. All SMTP commands that follow will be encrypted. You should then call ehlo() again.

If keyfile and certfile are provided, they are used to create an ssl.SSLContext.

Optional context parameter is an ssl.SSLContext object; This is an alternative to using a keyfile and a certfile and if specified both keyfile and certfile should be None.

If there has been no previous EHLO or HELO command this session, this method tries ESMTP EHLO first.

3.6 版后已移除: keyfilecertfile 已弃用并转而推荐 context。 请改用 ssl.SSLContext.load_cert_chain() 或让 ssl.create_default_context() 为你选择系统所信任的 CA 证书。

  • SMTPHeloError

    The server didn’t reply properly to the HELO greeting.

    SMTPNotSupportedError

    The server does not support the STARTTLS extension.

    RuntimeError

    SSL/TLS support is not available to your Python interpreter.

在 3.3 版更改: 增加了 context

在 3.4 版更改: The method now supports hostname check with SSLContext.check_hostname and Server Name Indicator (see HAS_SNI).

在 3.5 版更改: The error raised for lack of STARTTLS support is now the SMTPNotSupportedError subclass instead of the base SMTPException.

SMTP.sendmail(from_addr, to_addrs, msg, mail_options=(), rcpt_options=())

发送邮件。必要参数是一个 RFC 822 发件地址字符串,一个 RFC 822 收件地址字符串列表(裸字符串将被视为含有 1 个地址的列表),以及一个消息字符串。调用者可以将 ESMTP 选项列表(如 8bitmime)作为 mail_options 传入,用于 MAIL FROM 命令。需要与所有 RCPT 命令一起使用的 ESMTP 选项(如 DSN 命令)可以作为 rcpt_options 传入。(如果需要对不同的收件人使用不同的 ESMTP 选项,则必须使用底层的方法来发送消息,如 mail(), rcpt()data()。)

注解

The from_addr and to_addrs parameters are used to construct the message envelope used by the transport agents. sendmail does not modify the message headers in any way.

msg may be a string containing characters in the ASCII range, or a byte string. A string is encoded to bytes using the ascii codec, and lone \r and \n characters are converted to \r\n characters. A byte string is not modified.

If there has been no previous EHLO or HELO command this session, this method tries ESMTP EHLO first. If the server does ESMTP, message size and each of the specified options will be passed to it (if the option is in the feature set the server advertises). If EHLO fails, HELO will be tried and ESMTP options suppressed.

This method will return normally if the mail is accepted for at least one recipient. Otherwise it will raise an exception. That is, if this method does not raise an exception, then someone should get your mail. If this method does not raise an exception, it returns a dictionary, with one entry for each recipient that was refused. Each entry contains a tuple of the SMTP error code and the accompanying error message sent by the server.

If SMTPUTF8 is included in mail_options, and the server supports it, from_addr and to_addrs may contain non-ASCII characters.

This method may raise the following exceptions:

  • SMTPRecipientsRefused

    All recipients were refused. Nobody got the mail. The recipients attribute of the exception object is a dictionary with information about the refused recipients (like the one returned when at least one recipient was accepted).

    SMTPHeloError

    The server didn’t reply properly to the HELO greeting.

    SMTPSenderRefused

    The server didn’t accept the from_addr.

    SMTPDataError

    The server replied with an unexpected error code (other than a refusal of a recipient).

    SMTPNotSupportedError

    SMTPUTF8 was given in the mail_options but is not supported by the server.

Unless otherwise noted, the connection will be open even after an exception is raised.

在 3.2 版更改: msg may be a byte string.

在 3.5 版更改: SMTPUTF8 support added, and SMTPNotSupportedError may be raised if SMTPUTF8 is specified but the server does not support it.

SMTP.send_message(msg, from_addr=None, to_addrs=None, mail_options=(), rcpt_options=())

本方法是一种快捷方法,用于带着消息调用 sendmail(),消息由 email.message.Message 对象表示。参数的含义与 sendmail() 中的相同,除了 msg,它是一个 Message 对象。

如果 from_addrNoneto_addrsNone,那么send_message将根据 RFC 5322,从 msg 头部提取地址填充下列参数:如果头部存在 Sender 字段,则用它填充 from_addr*,不存在则用 *From 字段填充 from_addrto_addrs 组合了 msg 中的 To, CcBcc 字段的值(字段存在的情况下)。如果一组 Resent-** 头部恰好出现在 message 中,那么就忽略常规的头部,改用 Resent-* 头部。如果 message 包含多组 Resent-* 头部,则引发 ValueError,因为无法明确检测出哪一组 Resent-* 头部是最新的。

send_message 使用 BytesGenerator 来序列化 msg,且将 \r\n 作为 *linesep,并调用 sendmail() 来传输序列化后的结果。无论 *from_addrto_addrs 的值为何,send_message 都不会传输 msg 中可能出现的 BccResent-Bcc 头部。如果 from_addrto_addrs 中的某个地址包含非 ASCII 字符,且服务器没有声明支持 SMTPUTF8,则引发 SMTPNotSupported 错误。如果服务器支持,则 Message 将按新克隆的 policy 进行序列化,其中的 utf8 属性被设置为 True,且 SMTPUTF8BODY=8BITMIME 被添加到 mail_options 中。

3.2 新版功能.

3.5 新版功能: Support for internationalized addresses (SMTPUTF8).

SMTP.quit()

Terminate the SMTP session and close the connection. Return the result of the SMTP QUIT command.

Low-level methods corresponding to the standard SMTP/ESMTP commands HELP, RSET, NOOP, MAIL, RCPT, and DATA are also supported. Normally these do not need to be called directly, so they are not documented here. For details, consult the module code.

SMTP Example

This example prompts the user for addresses needed in the message envelope (‘To’ and ‘From’ addresses), and the message to be delivered. Note that the headers to be included with the message must be included in the message as entered; this example doesn’t do any processing of the RFC 822 headers. In particular, the ‘To’ and ‘From’ addresses must be included in the message headers explicitly.

import smtplib
def prompt(prompt):
    return input(prompt).strip()
fromaddr = prompt("From: ")
toaddrs  = prompt("To: ").split()
print("Enter message, end with ^D (Unix) or ^Z (Windows):")
# Add the From: and To: headers at the start!
msg = ("From: %s\r\nTo: %s\r\n\r\n"
       % (fromaddr, ", ".join(toaddrs)))
while True:
    try:
        line = input()
    except EOFError:
        break
    if not line:
        break
    msg = msg + line
print("Message length is", len(msg))
server = smtplib.SMTP('localhost')
server.set_debuglevel(1)
server.sendmail(fromaddr, toaddrs, msg)
server.quit()

注解

In general, you will want to use the email package’s features to construct an email message, which you can then send via send_message(); see email: 示例.

smtpd —- SMTP 服务器

源代码: Lib/smtpd.py


This module offers several classes to implement SMTP (email) servers.

3.6 版后已移除: The aiosmtpd package is a recommended replacement for this module. It is based on asyncio and provides a more straightforward API.

Several server implementations are present; one is a generic do-nothing implementation, which can be overridden, while the other two offer specific mail-sending strategies.

Additionally the SMTPChannel may be extended to implement very specific interaction behaviour with SMTP clients.

The code supports RFC 5321, plus the RFC 1870 SIZE and RFC 6531 SMTPUTF8 extensions.

SMTPServer 对象

class smtpd.SMTPServer(localaddr, remoteaddr, data_size_limit=33554432, map=None, enable_SMTPUTF8=False, decode_data=False)

Create a new SMTPServer object, which binds to local address localaddr. It will treat remoteaddr as an upstream SMTP relayer. Both localaddr and remoteaddr should be a (host, port) tuple. The object inherits from asyncore.dispatcher, and so will insert itself into asyncore‘s event loop on instantiation.

data_size_limit specifies the maximum number of bytes that will be accepted in a DATA command. A value of None or 0 means no limit.

map is the socket map to use for connections (an initially empty dictionary is a suitable value). If not specified the asyncore global socket map is used.

enable_SMTPUTF8 determines whether the SMTPUTF8 extension (as defined in RFC 6531) should be enabled. The default is False. When True, SMTPUTF8 is accepted as a parameter to the MAIL command and when present is passed to process_message() in the kwargs['mail_options'] list. decode_data and enable_SMTPUTF8 cannot be set to True at the same time.

decode_data specifies whether the data portion of the SMTP transaction should be decoded using UTF-8. When decode_data is False (the default), the server advertises the 8BITMIME extension (RFC 6152), accepts the BODY=8BITMIME parameter to the MAIL command, and when present passes it to process_message() in the kwargs['mail_options'] list. decode_data and enable_SMTPUTF8 cannot be set to True at the same time.

  • process_message(peer, mailfrom, rcpttos, data, **kwargs)

    Raise a NotImplementedError exception. Override this in subclasses to do something useful with this message. Whatever was passed in the constructor as remoteaddr will be available as the _remoteaddr attribute. peer is the remote host’s address, mailfrom is the envelope originator, rcpttos are the envelope recipients and data is a string containing the contents of the e-mail (which should be in RFC 5321 format).

    If the decode_data constructor keyword is set to True, the data argument will be a unicode string. If it is set to False, it will be a bytes object.

    kwargs is a dictionary containing additional information. It is empty if decode_data=True was given as an init argument, otherwise it contains the following keys:

    • mail_options:

      a list of all received parameters to the MAIL command (the elements are uppercase strings; example: ['BODY=8BITMIME', 'SMTPUTF8']).

      rcpt_options:

      same as mail_options but for the RCPT command. Currently no RCPT TO options are supported, so for now this will always be an empty list.

    Implementations of process_message should use the **kwargs signature to accept arbitrary keyword arguments, since future feature enhancements may add keys to the kwargs dictionary.

    Return None to request a normal 250 Ok response; otherwise return the desired response string in RFC 5321 format.

  • channel_class

    Override this in subclasses to use a custom SMTPChannel for managing SMTP clients.

3.4 新版功能: The map constructor argument.

在 3.5 版更改: localaddr and remoteaddr may now contain IPv6 addresses.

3.5 新版功能: The decode_data and enable_SMTPUTF8 constructor parameters, and the kwargs parameter to process_message() when decode_data is False.

在 3.6 版更改: decode_data is now False by default.

DebuggingServer 对象

class smtpd.DebuggingServer(localaddr, remoteaddr)

Create a new debugging server. Arguments are as per SMTPServer. Messages will be discarded, and printed on stdout.

PureProxy对象

class smtpd.PureProxy(localaddr, remoteaddr)

Create a new pure proxy server. Arguments are as per SMTPServer. Everything will be relayed to remoteaddr. Note that running this has a good chance to make you into an open relay, so please be careful.

MailmanProxy 对象

class smtpd.MailmanProxy(localaddr, remoteaddr)

Deprecated since version 3.9, will be removed in version 3.11: MailmanProxy is deprecated, it depends on a Mailman module which no longer exists and therefore is already broken.

Create a new pure proxy server. Arguments are as per SMTPServer. Everything will be relayed to remoteaddr, unless local mailman configurations knows about an address, in which case it will be handled via mailman. Note that running this has a good chance to make you into an open relay, so please be careful.

SMTPChannel 对象

class smtpd.SMTPChannel(server, conn, addr, data_size_limit=33554432, map=None, enable_SMTPUTF8=False, decode_data=False)

Create a new SMTPChannel object which manages the communication between the server and a single SMTP client.

conn and addr are as per the instance variables described below.

data_size_limit specifies the maximum number of bytes that will be accepted in a DATA command. A value of None or 0 means no limit.

enable_SMTPUTF8 determines whether the SMTPUTF8 extension (as defined in RFC 6531) should be enabled. The default is False. decode_data and enable_SMTPUTF8 cannot be set to True at the same time.

A dictionary can be specified in map to avoid using a global socket map.

decode_data specifies whether the data portion of the SMTP transaction should be decoded using UTF-8. The default is False. decode_data and enable_SMTPUTF8 cannot be set to True at the same time.

To use a custom SMTPChannel implementation you need to override the SMTPServer.channel_class of your SMTPServer.

在 3.5 版更改: The decode_data and enable_SMTPUTF8 parameters were added.

在 3.6 版更改: decode_data is now False by default.

The SMTPChannel has the following instance variables:

  • smtp_server

    Holds the SMTPServer that spawned this channel.

  • conn

    Holds the socket object connecting to the client.

  • addr

    Holds the address of the client, the second value returned by socket.accept

  • received_lines

    Holds a list of the line strings (decoded using UTF-8) received from the client. The lines have their "\r\n" line ending translated to "\n".

  • smtp_state

    Holds the current state of the channel. This will be either COMMAND initially and then DATA after the client sends a “DATA” line.

  • seen_greeting

    Holds a string containing the greeting sent by the client in its “HELO”.

  • mailfrom

    Holds a string containing the address identified in the “MAIL FROM:” line from the client.

  • rcpttos

    Holds a list of strings containing the addresses identified in the “RCPT TO:” lines from the client.

  • received_data

    Holds a string containing all of the data sent by the client during the DATA state, up to but not including the terminating "\r\n.\r\n".

  • fqdn

    Holds the fully-qualified domain name of the server as returned by socket.getfqdn().

  • peer

    Holds the name of the client peer as returned by conn.getpeername() where conn is conn.

The SMTPChannel operates by invoking methods named smtp_<command> upon reception of a command line from the client. Built into the base SMTPChannel class are methods for handling the following commands (and responding to them appropriately):

命令 所采取的行动
HELO 接受来自客户端的问候语,并将其存储在 seen_greeting 中。将服务器设置为基本命令模式。
EHLO 接受来自客户的问候并将其存储在 seen_greeting 中。将服务器设置为扩展命令模式。
NOOP 不采取任何措施。
QUIT 干净地关闭连接。
MAIL Accepts the “MAIL FROM:” syntax and stores the supplied address as mailfrom. In extended command mode, accepts the RFC 1870 SIZE attribute and responds appropriately based on the value of data_size_limit.
RCPT Accepts the “RCPT TO:” syntax and stores the supplied addresses in the rcpttos list.
RSET 重置 mailfrom, rcpttos, 和 received_data ,但不重置问候语。
DATA Sets the internal state to DATA and stores remaining lines from the client in received_data until the terminator “\r\n.\r\n” is received.
HELP 返回有关命令语法的最少信息
VRFY 返回代码252(服务器不知道该地址是否有效)
EXPN 报告该命令未实现。

telnetlib — Telnet 客户端

源代码: Lib/telnetlib.py


telnetlib 模块提供一个实现Telnet协议的类 Telnet。关于此协议的细节请参见 RFC 854 。此外,它还为协议字符(见下文)和 telnet 选项提供了对应的符号常量。telnet选项对应的符号名遵循 arpa/telnet.h 中的定义,但删除了前缀 TELOPT_。对于不在 arpa/telnet.h 的选项的符号常量名,请参考本模块源码。

telnet命令的符号常量名有: IAC, DONT, DO, WONT, WILL, SE (Subnegotiation End), NOP (No Operation), DM (Data Mark), BRK (Break), IP (Interrupt process), AO (Abort output), AYT (Are You There), EC (Erase Character), EL (Erase Line), GA (Go Ahead), SB (Subnegotiation Begin).

class telnetlib.Telnet(host=None, port=0[, timeout])

Telnet 表示到 Telnet 服务器的连接. 实例初始化后默认不连接;必须使用 open() 方法来建立连接。或者, 可选参数 host 和 port 也可以传递给构造函数,在这种情况下,到服务器的连接将在构造函数返回前建立。可选参数 timeout 为阻塞操作(如连接尝试)指定一个以秒为单位的超时时间(如果没有指定,将使用全局默认设置) 。

不要重新打开一个已经连接的实例。

这个类有很多 read_*() 方法。 请注意,其中一些方法在读取结束时会触发 EOFError 异常,这是由于连接对象可能出于其它原因返回一个空字符串。 请参阅下面的个别描述。

Telnet 对象一个上下文管理器,可以在 with 语句中使用。当 with 块结束,close() 方法会被调用:

>>> from telnetlib import Telnet
>>> with Telnet('localhost', 23) as tn:
...     tn.interact()
...

在 3.6 版更改: 添加了上下文管理器的支持

参见

RFC 854 - Telnet 协议规范

Telnet 协议的定义。

Telnet 对象

Telnet 实例有以下几种方法:

Telnet.read_until(expected, timeout=None)

读取直到遇到给定字节串 expectedtimeout 秒已经过去。

当没有找到匹配时,返回可用的内容,也可能返回空字节。如果连接已关闭且没有可用的熟数据,将触发 EOFError

Telnet.read_all()

读取数据,直到遇到 EOF;连接关闭前都会保持阻塞。

Telnet.read_some()

在达到 EOF 前,读取至少一个字节的熟数据。如果命中 EOF,返回 b''。如果没有立即可用的数据,则阻塞。

Telnet.read_very_eager()

在不阻塞 I/O 的情况下读取所有的内容(eager)。

如果连接已关闭并且没有可用的熟数据,将会触发 EOFError 。如果没有熟数据可用返回 b'' 。除非在一个 IAC 序列的中间,否则不要进行阻塞。

Telnet.read_eager()

读取现成的数据。

如果连接已关闭并且没有可用的熟数据,将会触发 EOFError 。如果没有熟数据可用返回 b'' 。除非在一个 IAC 序列的中间,否则不要进行阻塞。

Telnet.read_lazy()

处理并返回已经在队列中的数据(lazy)。

如果连接已关闭并且没有可用的数据,将会触发 EOFError 。如果没有熟数据可用则返回 b'' 。除非在一个 IAC 序列的中间,否则不要进行阻塞。

Telnet.read_very_lazy()

返回熟数据队列任何可用的数据(very lazy)。

如果连接已关闭并且没有可用的数据,将会触发 EOFError 。如果没有熟数据可用则返回 b'' 。该方法永远不会阻塞。

Telnet.read_sb_data()

返回在 SB/SE 对之间收集的数据(子选项 begin/end)。当使用 SE 命令调用回调函数时,该回调函数应该访问这些数据。该方法永远不会阻塞。

Telnet.open(host, port=0[, timeout])

连接主机。第二个可选参数是端口号,默认为标准 Telnet 端口(23)。可选参数 timeout 指定一个以秒为单位的超时时间用于像连接尝试这样的阻塞操作(如果没有指定,将使用全局默认超时设置)。

不要尝试重新打开一个已经连接的实例。

触发 auditing event telnetlib.Telnet.open ,参数为 selfhostport

Telnet.msg(msg, \args*)

当调试级别 > 0 时打印一条调试信息。如果存在额外参数,则它们会被替换在使用标准字符串格式化操作符的信息中。

Telnet.set_debuglevel(debuglevel)

设置调试级别。debuglevel 的值越高,得到的调试输出越多(在 sys.stdout )。

Telnet.close()

关闭连接对象。

Telnet.get_socket()

返回内部使用的套接字对象。

Telnet.fileno()

返回内部使用的套接字对象的文件描述符。

Telnet.write(buffer)

向套接字写入一个字节字符串,将所有 IAC 字符加倍。如果连接被阻塞,这可能也会阻塞。如果连接关闭可能触发 OSError

触发 auditing event telnetlib.Telnet.write ,参数为 selfbuffer

在 3.3 版更改: 曾经该函数抛出 socket.error,现在这是 OSError 的别名。

Telnet.interact()

交互函数,模拟一个非常笨拙的 Telnet 客户端。

Telnet.mt_interact()

多线程版的 interact().

Telnet.expect(list, timeout=None)

一直读取,直到匹配列表中的某个正则表达式。

第一个参数是一个正则表达式列表,可以是已编译的 (正则表达式对象),也可以是未编译的 (字节串)。 第二个可选参数是超时,单位是秒;默认一直阻塞。

返回一个包含三个元素的元组:列表中的第一个匹配的正则表达式的索引;返回的匹配对象;包括匹配在内的读取过的字节。

如果找到了文件的结尾且没有字节被读取,触发 EOFError。否则,当没有匹配时,返回 (-1, None, data),其中 data 是到目前为止接受到的字节(如果发生超时,则可能是空字节)。

如果一个正则表达式以贪婪匹配结束(例如 .*),或者多个表达式可以匹配同一个输出,则结果是不确定的,可能取决于 I/O 计时。

Telnet.set_option_negotiation_callback(callback)

每次在输入流上读取 telnet 选项时,这个带有如下参数的 callback (如果设置了)会被调用: callback(telnet socket, command (DO/DONT/WILL/WONT), option)。telnetlib 之后不会再执行其它操作。

Telnet 示例

一个简单的说明性典型用法例子:

import getpass
import telnetlib
HOST = "localhost"
user = input("Enter your remote account: ")
password = getpass.getpass()
tn = telnetlib.Telnet(HOST)
tn.read_until(b"login: ")
tn.write(user.encode('ascii') + b"\n")
if password:
    tn.read_until(b"Password: ")
    tn.write(password.encode('ascii') + b"\n")
tn.write(b"ls\n")
tn.write(b"exit\n")
print(tn.read_all().decode('ascii'))

uuid —- UUID objects according to RFC 4122

Source code: Lib/uuid.py


这个模块提供了不可变的 UUID 对象 (UUID 类) 和 uuid1(), uuid3(), uuid4(), uuid5() 等函数用于生成 RFC 4122 所定义的第 1, 3, 4 和 5 版 UUID。

If all you want is a unique ID, you should probably call uuid1() or uuid4(). Note that uuid1() may compromise privacy since it creates a UUID containing the computer’s network address. uuid4() creates a random UUID.

Depending on support from the underlying platform, uuid1() may or may not return a “safe” UUID. A safe UUID is one which is generated using synchronization methods that ensure no two processes can obtain the same UUID. All instances of UUID have an is_safe attribute which relays any information about the UUID’s safety, using this enumeration:

class uuid.SafeUUID

3.7 新版功能.

  • safe

    The UUID was generated by the platform in a multiprocessing-safe way.

  • unsafe

    The UUID was not generated in a multiprocessing-safe way.

  • unknown

    The platform does not provide information on whether the UUID was generated safely or not.

class uuid.UUID(hex=None, bytes=None, bytes_le=None, fields=None, int=None, version=None, **, is_safe=SafeUUID.unknown*)

Create a UUID from either a string of 32 hexadecimal digits, a string of 16 bytes in big-endian order as the bytes argument, a string of 16 bytes in little-endian order as the bytes_le argument, a tuple of six integers (32-bit time_low, 16-bit time_mid, 16-bit time_hi_version, 8-bit clock_seq_hi_variant, 8-bit clock_seq_low, 48-bit node) as the fields argument, or a single 128-bit integer as the int argument. When a string of hex digits is given, curly braces, hyphens, and a URN prefix are all optional. For example, these expressions all yield the same UUID:

UUID('{12345678-1234-5678-1234-567812345678}')
UUID('12345678123456781234567812345678')
UUID('urn:uuid:12345678-1234-5678-1234-567812345678')
UUID(bytes=b'\x12\x34\x56\x78'*4)
UUID(bytes_le=b'\x78\x56\x34\x12\x34\x12\x78\x56' +
              b'\x12\x34\x56\x78\x12\x34\x56\x78')
UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678))
UUID(int=0x12345678123456781234567812345678)

Exactly one of hex, bytes, bytes_le, fields, or int must be given. The version argument is optional; if given, the resulting UUID will have its variant and version number set according to RFC 4122, overriding bits in the given hex, bytes, bytes_le, fields, or int.

Comparison of UUID objects are made by way of comparing their UUID.int attributes. Comparison with a non-UUID object raises a TypeError.

str(uuid) returns a string in the form 12345678-1234-5678-1234-567812345678 where the 32 hexadecimal digits represent the UUID.

UUID instances have these read-only attributes:

UUID.bytes

The UUID as a 16-byte string (containing the six integer fields in big-endian byte order).

UUID.bytes_le

The UUID as a 16-byte string (with time_low, time_mid, and time_hi_version in little-endian byte order).

UUID.fields

以元组形式存放的UUID的6个整数域,有六个单独的属性和两个派生属性:

含意
time_low UUID的前32位
time_mid 接前一域的16位
time_hi_version 接前一域的16位
clock_seq_hi_variant 接前一域的8位
clock_seq_low 接前一域的8位
node UUID的最后48位
time UUID的总长60位的时间戳
clock_seq 14位的序列号
UUID.hex

The UUID as a 32-character hexadecimal string.

UUID.int

The UUID as a 128-bit integer.

UUID.urn

RFC 4122 中定义的 URN 形式的 UUID。

UUID.variant

The UUID variant, which determines the internal layout of the UUID. This will be one of the constants RESERVED_NCS, RFC_4122, RESERVED_MICROSOFT, or RESERVED_FUTURE.

UUID.version

The UUID version number (1 through 5, meaningful only when the variant is RFC_4122).

UUID.is_safe

An enumeration of SafeUUID which indicates whether the platform generated the UUID in a multiprocessing-safe way.

3.7 新版功能.

The uuid module defines the following functions:

uuid.getnode()

Get the hardware address as a 48-bit positive integer. The first time this runs, it may launch a separate program, which could be quite slow. If all attempts to obtain the hardware address fail, we choose a random 48-bit number with the multicast bit (least significant bit of the first octet) set to 1 as recommended in RFC 4122. “Hardware address” means the MAC address of a network interface. On a machine with multiple network interfaces, universally administered MAC addresses (i.e. where the second least significant bit of the first octet is unset) will be preferred over locally administered MAC addresses, but with no other ordering guarantees.

在 3.7 版更改: Universally administered MAC addresses are preferred over locally administered MAC addresses, since the former are guaranteed to be globally unique, while the latter are not.

uuid.uuid1(node=None, clock_seq=None)

Generate a UUID from a host ID, sequence number, and the current time. If node is not given, getnode() is used to obtain the hardware address. If clock_seq is given, it is used as the sequence number; otherwise a random 14-bit sequence number is chosen.

uuid.uuid3(namespace, name)

Generate a UUID based on the MD5 hash of a namespace identifier (which is a UUID) and a name (which is a string).

uuid.uuid4()

Generate a random UUID.

uuid.uuid5(namespace, name)

Generate a UUID based on the SHA-1 hash of a namespace identifier (which is a UUID) and a name (which is a string).

The uuid module defines the following namespace identifiers for use with uuid3() or uuid5().

uuid.NAMESPACE_DNS

When this namespace is specified, the name string is a fully-qualified domain name.

uuid.NAMESPACE_URL

When this namespace is specified, the name string is a URL.

uuid.NAMESPACE_OID

When this namespace is specified, the name string is an ISO OID.

uuid.NAMESPACE_X500

When this namespace is specified, the name string is an X.500 DN in DER or a text output format.

The uuid module defines the following constants for the possible values of the variant attribute:

uuid.RESERVED_NCS

Reserved for NCS compatibility.

uuid.RFC_4122

Specifies the UUID layout given in RFC 4122.

uuid.RESERVED_MICROSOFT

为微软的兼容性保留。

uuid.RESERVED_FUTURE

Reserved for future definition.

参见

RFC 4122 - A Universally Unique IDentifier (UUID) URN Namespace

This specification defines a Uniform Resource Name namespace for UUIDs, the internal format of UUIDs, and methods of generating UUIDs.

示例

Here are some examples of typical usage of the uuid module:

>>> import uuid
>>> # make a UUID based on the host ID and current time
>>> uuid.uuid1()
UUID('a8098c1a-f86e-11da-bd1a-00112444be1e')
>>> # make a UUID using an MD5 hash of a namespace UUID and a name
>>> uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org')
UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e')
>>> # make a random UUID
>>> uuid.uuid4()
UUID('16fd2706-8baf-433b-82eb-8c7fada847da')
>>> # make a UUID using a SHA-1 hash of a namespace UUID and a name
>>> uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org')
UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d')
>>> # make a UUID from a string of hex digits (braces and hyphens ignored)
>>> x = uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}')
>>> # convert a UUID to a string of hex digits in standard form
>>> str(x)
'00010203-0405-0607-0809-0a0b0c0d0e0f'
>>> # get the raw 16 bytes of the UUID
>>> x.bytes
b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f'
>>> # make a UUID from a 16-byte string
>>> uuid.UUID(bytes=x.bytes)
UUID('00010203-0405-0607-0809-0a0b0c0d0e0f')

socketserver —- A framework for network servers

Source code: Lib/socketserver.py


The socketserver module simplifies the task of writing network servers.

There are four basic concrete server classes:

class socketserver.TCPServer(server_address, RequestHandlerClass, bind_and_activate=True)

This uses the internet TCP protocol, which provides for continuous streams of data between the client and server. If bind_and_activate is true, the constructor automatically attempts to invoke server_bind() and server_activate(). The other parameters are passed to the BaseServer base class.

class socketserver.UDPServer(server_address, RequestHandlerClass, bind_and_activate=True)

This uses datagrams, which are discrete packets of information that may arrive out of order or be lost while in transit. The parameters are the same as for TCPServer.

class socketserver.UnixStreamServer(server_address, RequestHandlerClass, bind_and_activate=True)

class socketserver.UnixDatagramServer(server_address, RequestHandlerClass, bind_and_activate=True)

These more infrequently used classes are similar to the TCP and UDP classes, but use Unix domain sockets; they’re not available on non-Unix platforms. The parameters are the same as for TCPServer.

These four classes process requests synchronously; each request must be completed before the next request can be started. This isn’t suitable if each request takes a long time to complete, because it requires a lot of computation, or because it returns a lot of data which the client is slow to process. The solution is to create a separate process or thread to handle each request; the ForkingMixIn and ThreadingMixIn mix-in classes can be used to support asynchronous behaviour.

Creating a server requires several steps. First, you must create a request handler class by subclassing the BaseRequestHandler class and overriding its handle() method; this method will process incoming requests. Second, you must instantiate one of the server classes, passing it the server’s address and the request handler class. It is recommended to use the server in a with statement. Then call the handle_request() or serve_forever() method of the server object to process one or many requests. Finally, call server_close() to close the socket (unless you used a with statement).

When inheriting from ThreadingMixIn for threaded connection behavior, you should explicitly declare how you want your threads to behave on an abrupt shutdown. The ThreadingMixIn class defines an attribute daemon_threads, which indicates whether or not the server should wait for thread termination. You should set the flag explicitly if you would like threads to behave autonomously; the default is False, meaning that Python will not exit until all threads created by ThreadingMixIn have exited.

Server classes have the same external methods and attributes, no matter what network protocol they use.

Server Creation Notes

There are five classes in an inheritance diagram, four of which represent synchronous servers of four types:

+------------+
| BaseServer |
+------------+
      |
      v
+-----------+        +------------------+
| TCPServer |------->| UnixStreamServer |
+-----------+        +------------------+
      |
      v
+-----------+        +--------------------+
| UDPServer |------->| UnixDatagramServer |
+-----------+        +--------------------+

Note that UnixDatagramServer derives from UDPServer, not from UnixStreamServer —- the only difference between an IP and a Unix stream server is the address family, which is simply repeated in both Unix server classes.

class socketserver.ForkingMixIn

class socketserver.ThreadingMixIn

Forking and threading versions of each type of server can be created using these mix-in classes. For instance, ThreadingUDPServer is created as follows:

class ThreadingUDPServer(ThreadingMixIn, UDPServer):
    pass

The mix-in class comes first, since it overrides a method defined in UDPServer. Setting the various attributes also changes the behavior of the underlying server mechanism.

ForkingMixIn and the Forking classes mentioned below are only available on POSIX platforms that support fork().

socketserver.ForkingMixIn.server_close() waits until all child processes complete, except if socketserver.ForkingMixIn.block_on_close attribute is false.

socketserver.ThreadingMixIn.server_close() waits until all non-daemon threads complete, except if socketserver.ThreadingMixIn.block_on_close attribute is false. Use daemonic threads by setting ThreadingMixIn.daemon_threads to True to not wait until threads complete.

在 3.7 版更改: socketserver.ForkingMixIn.server_close() and socketserver.ThreadingMixIn.server_close() now waits until all child processes and non-daemonic threads complete. Add a new socketserver.ForkingMixIn.block_on_close class attribute to opt-in for the pre-3.7 behaviour.

class socketserver.ForkingTCPServer

class socketserver.ForkingUDPServer

class socketserver.ThreadingTCPServer

class socketserver.ThreadingUDPServer

These classes are pre-defined using the mix-in classes.

To implement a service, you must derive a class from BaseRequestHandler and redefine its handle() method. You can then run various versions of the service by combining one of the server classes with your request handler class. The request handler class must be different for datagram or stream services. This can be hidden by using the handler subclasses StreamRequestHandler or DatagramRequestHandler.

Of course, you still have to use your head! For instance, it makes no sense to use a forking server if the service contains state in memory that can be modified by different requests, since the modifications in the child process would never reach the initial state kept in the parent process and passed to each child. In this case, you can use a threading server, but you will probably have to use locks to protect the integrity of the shared data.

On the other hand, if you are building an HTTP server where all data is stored externally (for instance, in the file system), a synchronous class will essentially render the service “deaf” while one request is being handled — which may be for a very long time if a client is slow to receive all the data it has requested. Here a threading or forking server is appropriate.

In some cases, it may be appropriate to process part of a request synchronously, but to finish processing in a forked child depending on the request data. This can be implemented by using a synchronous server and doing an explicit fork in the request handler class handle() method.

Another approach to handling multiple simultaneous requests in an environment that supports neither threads nor fork() (or where these are too expensive or inappropriate for the service) is to maintain an explicit table of partially finished requests and to use selectors to decide which request to work on next (or whether to handle a new incoming request). This is particularly important for stream services where each client can potentially be connected for a long time (if threads or subprocesses cannot be used). See asyncore for another way to manage this.

Server 对象

class socketserver.BaseServer(server_address, RequestHandlerClass)

This is the superclass of all Server objects in the module. It defines the interface, given below, but does not implement most of the methods, which is done in subclasses. The two parameters are stored in the respective server_address and RequestHandlerClass attributes.

  • fileno()

    Return an integer file descriptor for the socket on which the server is listening. This function is most commonly passed to selectors, to allow monitoring multiple servers in the same process.

  • handle_request()

    Process a single request. This function calls the following methods in order: get_request(), verify_request(), and process_request(). If the user-provided handle() method of the handler class raises an exception, the server’s handle_error() method will be called. If no request is received within timeout seconds, handle_timeout() will be called and handle_request() will return.

  • serve_forever(poll_interval=0.5)

    Handle requests until an explicit shutdown() request. Poll for shutdown every poll_interval seconds. Ignores the timeout attribute. It also calls service_actions(), which may be used by a subclass or mixin to provide actions specific to a given service. For example, the ForkingMixIn class uses service_actions() to clean up zombie child processes.

    在 3.3 版更改: Added service_actions call to the serve_forever method.

  • service_actions()

    This is called in the serve_forever() loop. This method can be overridden by subclasses or mixin classes to perform actions specific to a given service, such as cleanup actions.

    3.3 新版功能.

  • shutdown()

    Tell the serve_forever() loop to stop and wait until it does. shutdown() must be called while serve_forever() is running in a different thread otherwise it will deadlock.

  • server_close()

    Clean up the server. May be overridden.

  • address_family

    The family of protocols to which the server’s socket belongs. Common examples are socket.AkF_INET and socket.AF_UNIX.

  • RequestHandlerClass

    The user-provided request handler class; an instance of this class is created for each request.

  • server_address

    The address on which the server is listening. The format of addresses varies depending on the protocol family; see the documentation for the socket module for details. For internet protocols, this is a tuple containing a string giving the address, and an integer port number: ('127.0.0.1', 80), for example.

  • socket

    The socket object on which the server will listen for incoming requests.

The server classes support the following class variables:

  • allow_reuse_address

    Whether the server will allow the reuse of an address. This defaults to False, and can be set in subclasses to change the policy.

  • request_queue_size

    The size of the request queue. If it takes a long time to process a single request, any requests that arrive while the server is busy are placed into a queue, up to request_queue_size requests. Once the queue is full, further requests from clients will get a “Connection denied” error. The default value is usually 5, but this can be overridden by subclasses.

  • socket_type

    The type of socket used by the server; socket.SOCK_STREAM and socket.SOCK_DGRAM are two common values.

  • timeout

    Timeout duration, measured in seconds, or None if no timeout is desired. If handle_request() receives no incoming requests within the timeout period, the handle_timeout() method is called.

There are various server methods that can be overridden by subclasses of base server classes like TCPServer; these methods aren’t useful to external users of the server object.

  • finish_request(request, client_address)

    Actually processes the request by instantiating RequestHandlerClass and calling its handle() method.

  • get_request()

    Must accept a request from the socket, and return a 2-tuple containing the new socket object to be used to communicate with the client, and the client’s address.

  • handle_error(request, client_address)

    This function is called if the handle() method of a RequestHandlerClass instance raises an exception. The default action is to print the traceback to standard error and continue handling further requests.

    在 3.6 版更改: Now only called for exceptions derived from the Exception class.

  • handle_timeout()

    This function is called when the timeout attribute has been set to a value other than None and the timeout period has passed with no requests being received. The default action for forking servers is to collect the status of any child processes that have exited, while in threading servers this method does nothing.

  • process_request(request, client_address)

    Calls finish_request() to create an instance of the RequestHandlerClass. If desired, this function can create a new process or thread to handle the request; the ForkingMixIn and ThreadingMixIn classes do this.

  • server_activate()

    Called by the server’s constructor to activate the server. The default behavior for a TCP server just invokes listen() on the server’s socket. May be overridden.

  • server_bind()

    Called by the server’s constructor to bind the socket to the desired address. May be overridden.

  • verify_request(request, client_address)

    Must return a Boolean value; if the value is True, the request will be processed, and if it’s False, the request will be denied. This function can be overridden to implement access controls for a server. The default implementation always returns True.

在 3.6 版更改: Support for the context manager protocol was added. Exiting the context manager is equivalent to calling server_close().

Request Handler Objects

class socketserver.BaseRequestHandler

This is the superclass of all request handler objects. It defines the interface, given below. A concrete request handler subclass must define a new handle() method, and can override any of the other methods. A new instance of the subclass is created for each request.

  • setup()

    Called before the handle() method to perform any initialization actions required. The default implementation does nothing.

  • handle()

    This function must do all the work required to service a request. The default implementation does nothing. Several instance attributes are available to it; the request is available as self.request; the client address as self.client_address; and the server instance as self.server, in case it needs access to per-server information.

    The type of self.request is different for datagram or stream services. For stream services, self.request is a socket object; for datagram services, self.request is a pair of string and socket.

  • finish()

    Called after the handle() method to perform any clean-up actions required. The default implementation does nothing. If setup() raises an exception, this function will not be called.

class socketserver.StreamRequestHandler

class socketserver.DatagramRequestHandler

These BaseRequestHandler subclasses override the setup() and finish() methods, and provide self.rfile and self.wfile attributes. The self.rfile and self.wfile attributes can be read or written, respectively, to get the request data or return data to the client.

The rfile attributes of both classes support the io.BufferedIOBase readable interface, and DatagramRequestHandler.wfile supports the io.BufferedIOBase writable interface.

在 3.6 版更改: StreamRequestHandler.wfile also supports the io.BufferedIOBase writable interface.

例子

socketserver.TCPServer Example

This is the server side:

import socketserver
class MyTCPHandler(socketserver.BaseRequestHandler):
    """
    The request handler class for our server.
    It is instantiated once per connection to the server, and must
    override the handle() method to implement communication to the
    client.
    """
    def handle(self):
        # self.request is the TCP socket connected to the client
        self.data = self.request.recv(1024).strip()
        print("{} wrote:".format(self.client_address[0]))
        print(self.data)
        # just send back the same data, but upper-cased
        self.request.sendall(self.data.upper())
if __name__ == "__main__":
    HOST, PORT = "localhost", 9999
    # Create the server, binding to localhost on port 9999
    with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
        # Activate the server; this will keep running until you
        # interrupt the program with Ctrl-C
        server.serve_forever()

An alternative request handler class that makes use of streams (file-like objects that simplify communication by providing the standard file interface):

class MyTCPHandler(socketserver.StreamRequestHandler):
    def handle(self):
        # self.rfile is a file-like object created by the handler;
        # we can now use e.g. readline() instead of raw recv() calls
        self.data = self.rfile.readline().strip()
        print("{} wrote:".format(self.client_address[0]))
        print(self.data)
        # Likewise, self.wfile is a file-like object used to write back
        # to the client
        self.wfile.write(self.data.upper())

The difference is that the readline() call in the second handler will call recv() multiple times until it encounters a newline character, while the single recv() call in the first handler will just return what has been sent from the client in one sendall() call.

This is the client side:

import socket
import sys
HOST, PORT = "localhost", 9999
data = " ".join(sys.argv[1:])
# Create a socket (SOCK_STREAM means a TCP socket)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
    # Connect to server and send data
    sock.connect((HOST, PORT))
    sock.sendall(bytes(data + "\n", "utf-8"))
    # Receive data from the server and shut down
    received = str(sock.recv(1024), "utf-8")
print("Sent:     {}".format(data))
print("Received: {}".format(received))

The output of the example should look something like this:

Server:

$ python TCPServer.py
127.0.0.1 wrote:
b'hello world with TCP'
127.0.0.1 wrote:
b'python is nice'

Client:

$ python TCPClient.py hello world with TCP
Sent:     hello world with TCP
Received: HELLO WORLD WITH TCP
$ python TCPClient.py python is nice
Sent:     python is nice
Received: PYTHON IS NICE

socketserver.UDPServer Example

This is the server side:

import socketserver
class MyUDPHandler(socketserver.BaseRequestHandler):
    """
    This class works similar to the TCP handler class, except that
    self.request consists of a pair of data and client socket, and since
    there is no connection the client address must be given explicitly
    when sending data back via sendto().
    """
    def handle(self):
        data = self.request[0].strip()
        socket = self.request[1]
        print("{} wrote:".format(self.client_address[0]))
        print(data)
        socket.sendto(data.upper(), self.client_address)
if __name__ == "__main__":
    HOST, PORT = "localhost", 9999
    with socketserver.UDPServer((HOST, PORT), MyUDPHandler) as server:
        server.serve_forever()

This is the client side:

import socket
import sys
HOST, PORT = "localhost", 9999
data = " ".join(sys.argv[1:])
# SOCK_DGRAM is the socket type to use for UDP sockets
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# As you can see, there is no connect() call; UDP has no connections.
# Instead, data is directly sent to the recipient via sendto().
sock.sendto(bytes(data + "\n", "utf-8"), (HOST, PORT))
received = str(sock.recv(1024), "utf-8")
print("Sent:     {}".format(data))
print("Received: {}".format(received))

The output of the example should look exactly like for the TCP server example.

Asynchronous Mixins

To build asynchronous handlers, use the ThreadingMixIn and ForkingMixIn classes.

An example for the ThreadingMixIn class:

import socket
import threading
import socketserver
class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
    def handle(self):
        data = str(self.request.recv(1024), 'ascii')
        cur_thread = threading.current_thread()
        response = bytes("{}: {}".format(cur_thread.name, data), 'ascii')
        self.request.sendall(response)
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    pass
def client(ip, port, message):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
        sock.connect((ip, port))
        sock.sendall(bytes(message, 'ascii'))
        response = str(sock.recv(1024), 'ascii')
        print("Received: {}".format(response))
if __name__ == "__main__":
    # Port 0 means to select an arbitrary unused port
    HOST, PORT = "localhost", 0
    server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
    with server:
        ip, port = server.server_address
        # Start a thread with the server -- that thread will then start one
        # more thread for each request
        server_thread = threading.Thread(target=server.serve_forever)
        # Exit the server thread when the main thread terminates
        server_thread.daemon = True
        server_thread.start()
        print("Server loop running in thread:", server_thread.name)
        client(ip, port, "Hello World 1")
        client(ip, port, "Hello World 2")
        client(ip, port, "Hello World 3")
        server.shutdown()

The output of the example should look something like this:

$ python ThreadedTCPServer.py
Server loop running in thread: Thread-1
Received: Thread-2: Hello World 1
Received: Thread-3: Hello World 2
Received: Thread-4: Hello World 3

The ForkingMixIn class is used in the same way, except that the server will spawn a new process for each request. Available only on POSIX platforms that support fork().

xmlrpc —- XMLRPC 服务端与客户端模块

XML-RPC 是一种远程过程调用方法,它使用通过 HTTP 传递的 XML 作为载体。 有了它,客户端可以在远程服务器上调用带参数的方法(服务器以 URI 命名)并获取结构化的数据。

xmlrpc 是一个集合了 XML-RPC 服务端与客户端实现模块的包。 这些模块是:

ipaddress —- IPv4/IPv6 操作库

源代码: Lib/ipaddress.py


ipaddress 提供了创建、处理和操作 IPv4 和 IPv6 地址和网络的功能。

该模块中的函数和类可以直接处理与IP地址相关的各种任务,包括检查两个主机是否在同一个子网中,遍历某个子网中的所有主机,检查一个字符串是否是一个有效的IP地址或网络定义等等。

3.3 新版功能.

方便的工厂函数

ipaddress 模块提供来工厂函数来方便地创建 IP 地址,网络和接口:

ipaddress.ip_address(address)

Return an IPv4Address or IPv6Address object depending on the IP address passed as argument. Either IPv4 or IPv6 addresses may be supplied; integers less than 2**32 will be considered to be IPv4 by default. A ValueError is raised if address does not represent a valid IPv4 or IPv6 address.

>>> ipaddress.ip_address('192.168.0.1')
IPv4Address('192.168.0.1')
>>> ipaddress.ip_address('2001:db8::')
IPv6Address('2001:db8::')

ipaddress.ip_network(address, strict=True)

Return an IPv4Network or IPv6Network object depending on the IP address passed as argument. address is a string or integer representing the IP network. Either IPv4 or IPv6 networks may be supplied; integers less than 2**32 will be considered to be IPv4 by default. strict is passed to IPv4Network or IPv6Network constructor. A ValueError is raised if address does not represent a valid IPv4 or IPv6 address, or if the network has host bits set.

>>> ipaddress.ip_network('192.168.0.0/28')
IPv4Network('192.168.0.0/28')

ipaddress.ip_interface(address)

Return an IPv4Interface or IPv6Interface object depending on the IP address passed as argument. address is a string or integer representing the IP address. Either IPv4 or IPv6 addresses may be supplied; integers less than 2**32 will be considered to be IPv4 by default. A ValueError is raised if address does not represent a valid IPv4 or IPv6 address.

这些方便的函数的一个缺点是需要同时处理IPv4和IPv6格式,这意味着提供的错误信息并不精准,因为函数不知道是打算采用IPv4还是IPv6格式。更详细的错误报告可以通过直接调用相应版本的类构造函数来获得。

IP 地址

地址对象

IPv4AddressIPv6Address 对象有很多共同的属性。一些只对IPv6 地址有意义的属性也在 IPv4Address 对象实现,以便更容易编写正确处理两种 IP 版本的代码。地址对象是可哈希的 hashable,所以它们可以作为字典中的键来使用。

class ipaddress.IPv4Address(address)

构造一个 IPv4 地址。 如果 address 不是一个有效的 IPv4 地址,会抛出 AddressValueError

以下是有效的 IPv4 地址:

  1. 以十进制小数点表示的字符串,由四个十进制整数组成,范围为0—255,用点隔开(例如 192.168.0.1 )。每个整数代表地址中的八位(一个字节)。不允许使用前导零,以免与八进制表示产生歧义。
  2. 一个32位可容纳的整数。
  3. 一个长度为 4 的封装在 bytes 对象中的整数(高位优先)。
>>> ipaddress.IPv4Address('192.168.0.1')
IPv4Address('192.168.0.1')
>>> ipaddress.IPv4Address(3232235521)
IPv4Address('192.168.0.1')
>>> ipaddress.IPv4Address(b'\xC0\xA8\x00\x01')
IPv4Address('192.168.0.1')

在 3.8 版更改: 前导零可被接受,即使是在可能与八进制表示混淆的情况下也会被接受。

在 3.10 版更改: 前导零不再被接受,并且会被视作错误。IPv4地址字符串现在严格按照glibc的 inet_pton() 函数进行解析。

在 3.9.5 版更改: 上述变更也在自3.9.5版本起的Python 3.9当中被包含。

在 3.8.12 版更改: The above change was also included in Python 3.8 starting with version 3.8.12.

  • version

    合适的版本号:IPv4为 4 ,IPv6为 6

  • max_prefixlen

    在该版本的地址表示中,比特数的总数:IPv4为 32 ;IPv6为 128

    The prefix defines the number of leading bits in an address that are compared to determine whether or not an address is part of a network.

  • compressed

  • exploded

    The string representation in dotted decimal notation. Leading zeroes are never included in the representation.

    As IPv4 does not define a shorthand notation for addresses with octets set to zero, these two attributes are always the same as str(addr) for IPv4 addresses. Exposing these attributes makes it easier to write display code that can handle both IPv4 and IPv6 addresses.

  • packed

    The binary representation of this address - a bytes object of the appropriate length (most significant octet first). This is 4 bytes for IPv4 and 16 bytes for IPv6.

  • reverse_pointer

    The name of the reverse DNS PTR record for the IP address, e.g.:

    >>> ipaddress.ip_address("127.0.0.1").reverse_pointer
    '1.0.0.127.in-addr.arpa'
    >>> ipaddress.ip_address("2001:db8::1").reverse_pointer
    '1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa'

    This is the name that could be used for performing a PTR lookup, not the resolved hostname itself.

    3.5 新版功能.

  • is_multicast

    如果该地址被保留用作多播用途,返回 True 。关于多播地址,请参见 RFC 3171 (IPv4)和 RFC 2373 (IPv6)。

  • is_private

    如果该地址被分配至私有网络,返回 True 。关于公共网络,请参见 iana-ipv4-special-registry (针对IPv4)和 iana-ipv6-special-registry (针对IPv6)。

  • is_global

    如果该地址被分配至公共网络,返回 True 。关于公共网络,请参见 iana-ipv4-special-registry (针对IPv4)和 iana-ipv6-special-registry (针对IPv6)。

    3.4 新版功能.

  • is_unspecified

    True if the address is unspecified. See RFC 5735 (for IPv4) or RFC 2373 (for IPv6).

  • is_reserved

    如果该地址属于互联网工程任务组(IETF)所规定的其他保留地址,返回 True

  • is_loopback

    如果该地址为一个回环地址,返回 True 。关于回环地址,请见 RFC 3330 (IPv4)和 RFC 2373 (IPv6)。

  • is_link_local

    True if the address is reserved for link-local usage. See RFC 3927.

IPv4Address.__format__(fmt)

Returns a string representation of the IP address, controlled by an explicit format string. fmt can be one of the following: 's', the default option, equivalent to str(), 'b' for a zero-padded binary string, 'X' or 'x' for an uppercase or lowercase hexadecimal representation, or 'n', which is equivalent to 'b' for IPv4 addresses and 'x' for IPv6. For binary and hexadecimal representations, the form specifier '#' and the grouping option '_' are available. __format__ is used by format, str.format and f-strings.

>>> format(ipaddress.IPv4Address('192.168.0.1'))
'192.168.0.1'
>>> '{:#b}'.format(ipaddress.IPv4Address('192.168.0.1'))
'0b11000000101010000000000000000001'
>>> f'{ipaddress.IPv6Address("2001:db8::1000"):s}'
'2001:db8::1000'
>>> format(ipaddress.IPv6Address('2001:db8::1000'), '_X')
'2001_0DB8_0000_0000_0000_0000_0000_1000'
>>> '{:#_n}'.format(ipaddress.IPv6Address('2001:db8::1000'))
'0x2001_0db8_0000_0000_0000_0000_0000_1000'

3.9 新版功能.

class ipaddress.IPv6Address(address)

构造一个 IPv6 地址。 如果 address 不是一个有效的 IPv6 地址,会抛出 AddressValueError

以下是有效的 IPv6 地址:

  1. A string consisting of eight groups of four hexadecimal digits, each group representing 16 bits. The groups are separated by colons. This describes an exploded (longhand) notation. The string can also be compressed (shorthand notation) by various means. See RFC 4291 for details. For example, "0000:0000:0000:0000:0000:0abc:0007:0def" can be compressed to "::abc:7:def".

    Optionally, the string may also have a scope zone ID, expressed with a suffix %scope_id. If present, the scope ID must be non-empty, and may not contain %. See RFC 4007 for details. For example, fe80::1234%1 might identify address fe80::1234 on the first link of the node.

  2. An integer that fits into 128 bits.

  3. An integer packed into a bytes object of length 16, big-endian.

>>> ipaddress.IPv6Address('2001:db8::1000')
IPv6Address('2001:db8::1000')
>>> ipaddress.IPv6Address('ff02::5678%1')
IPv6Address('ff02::5678%1')
  • compressed

The short form of the address representation, with leading zeroes in groups omitted and the longest sequence of groups consisting entirely of zeroes collapsed to a single empty group.

This is also the value returned by str(addr) for IPv6 addresses.

  • exploded

The long form of the address representation, with all leading zeroes and groups consisting entirely of zeroes included.

For the following attributes and methods, see the corresponding documentation of the IPv4Address class:

  • packed

  • reverse_pointer

  • version

  • max_prefixlen

  • is_multicast

  • is_private

  • is_global

  • is_unspecified

  • is_reserved

  • is_loopback

  • is_link_local

    3.4 新版功能: is_global

  • is_site_local

    True if the address is reserved for site-local usage. Note that the site-local address space has been deprecated by RFC 3879. Use is_private to test if this address is in the space of unique local addresses as defined by RFC 4193.

  • ipv4_mapped

    For addresses that appear to be IPv4 mapped addresses (starting with ::FFFF/96), this property will report the embedded IPv4 address. For any other address, this property will be None.

  • scope_id

    For scoped addresses as defined by RFC 4007, this property identifies the particular zone of the address’s scope that the address belongs to, as a string. When no scope zone is specified, this property will be None.

  • sixtofour

    For addresses that appear to be 6to4 addresses (starting with 2002::/16) as defined by RFC 3056, this property will report the embedded IPv4 address. For any other address, this property will be None.

  • teredo

    For addresses that appear to be Teredo addresses (starting with 2001::/32) as defined by RFC 4380, this property will report the embedded (server, client) IP address pair. For any other address, this property will be None.

IPv6Address.__format__(fmt)

Refer to the corresponding method documentation in IPv4Address.

3.9 新版功能.

Conversion to Strings and Integers

To interoperate with networking interfaces such as the socket module, addresses must be converted to strings or integers. This is handled using the str() and int() builtin functions:

>>> str(ipaddress.IPv4Address('192.168.0.1'))
'192.168.0.1'
>>> int(ipaddress.IPv4Address('192.168.0.1'))
3232235521
>>> str(ipaddress.IPv6Address('::1'))
'::1'
>>> int(ipaddress.IPv6Address('::1'))
1

Note that IPv6 scoped addresses are converted to integers without scope zone ID.

运算符

Address objects support some operators. Unless stated otherwise, operators can only be applied between compatible objects (i.e. IPv4 with IPv4, IPv6 with IPv6).

比较运算符

Address objects can be compared with the usual set of comparison operators. Same IPv6 addresses with different scope zone IDs are not equal. Some examples:

>>> IPv4Address('127.0.0.2') > IPv4Address('127.0.0.1')
True
>>> IPv4Address('127.0.0.2') == IPv4Address('127.0.0.1')
False
>>> IPv4Address('127.0.0.2') != IPv4Address('127.0.0.1')
True
>>> IPv6Address('fe80::1234') == IPv6Address('fe80::1234%1')
False
>>> IPv6Address('fe80::1234%1') != IPv6Address('fe80::1234%2')
True
算术运算符

Integers can be added to or subtracted from address objects. Some examples:

>>> IPv4Address('127.0.0.2') + 3
IPv4Address('127.0.0.5')
>>> IPv4Address('127.0.0.2') - 3
IPv4Address('126.255.255.255')
>>> IPv4Address('255.255.255.255') + 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ipaddress.AddressValueError: 4294967296 (>= 2**32) is not permitted as an IPv4 address

IP网络的定义

The IPv4Network and IPv6Network objects provide a mechanism for defining and inspecting IP network definitions. A network definition consists of a mask and a network address, and as such defines a range of IP addresses that equal the network address when masked (binary AND) with the mask. For example, a network definition with the mask 255.255.255.0 and the network address 192.168.1.0 consists of IP addresses in the inclusive range 192.168.1.0 to 192.168.1.255.

Prefix, net mask and host mask

There are several equivalent ways to specify IP network masks. A prefix /<nbits> is a notation that denotes how many high-order bits are set in the network mask. A net mask is an IP address with some number of high-order bits set. Thus the prefix /24 is equivalent to the net mask 255.255.255.0 in IPv4, or ffff:ff00:: in IPv6. In addition, a host mask is the logical inverse of a net mask, and is sometimes used (for example in Cisco access control lists) to denote a network mask. The host mask equivalent to /24 in IPv4 is 0.0.0.255.

Network objects

All attributes implemented by address objects are implemented by network objects as well. In addition, network objects implement additional attributes. All of these are common between IPv4Network and IPv6Network, so to avoid duplication they are only documented for IPv4Network. Network objects are hashable, so they can be used as keys in dictionaries.

class ipaddress.IPv4Network(address, strict=True)

Construct an IPv4 network definition. address can be one of the following:

  1. A string consisting of an IP address and an optional mask, separated by a slash (/). The IP address is the network address, and the mask can be either a single number, which means it’s a prefix, or a string representation of an IPv4 address. If it’s the latter, the mask is interpreted as a net mask if it starts with a non-zero field, or as a host mask if it starts with a zero field, with the single exception of an all-zero mask which is treated as a net mask. If no mask is provided, it’s considered to be /32.

    For example, the following address specifications are equivalent: 192.168.1.0/24, 192.168.1.0/255.255.255.0 and 192.168.1.0/0.0.0.255.

  2. An integer that fits into 32 bits. This is equivalent to a single-address network, with the network address being address and the mask being /32.

  3. An integer packed into a bytes object of length 4, big-endian. The interpretation is similar to an integer address.

  4. A two-tuple of an address description and a netmask, where the address description is either a string, a 32-bits integer, a 4-bytes packed integer, or an existing IPv4Address object; and the netmask is either an integer representing the prefix length (e.g. 24) or a string representing the prefix mask (e.g. 255.255.255.0).

An AddressValueError is raised if address is not a valid IPv4 address. A NetmaskValueError is raised if the mask is not valid for an IPv4 address.

If strict is True and host bits are set in the supplied address, then ValueError is raised. Otherwise, the host bits are masked out to determine the appropriate network address.

Unless stated otherwise, all network methods accepting other network/address objects will raise TypeError if the argument’s IP version is incompatible to self.

在 3.5 版更改: Added the two-tuple form for the address constructor parameter.

  • version

  • max_prefixlen

    Refer to the corresponding attribute documentation in IPv4Address.

  • is_multicast

  • is_private

  • is_unspecified

  • is_reserved

  • is_loopback

  • is_link_local

    These attributes are true for the network as a whole if they are true for both the network address and the broadcast address.

  • network_address

    The network address for the network. The network address and the prefix length together uniquely define a network.

  • broadcast_address

    The broadcast address for the network. Packets sent to the broadcast address should be received by every host on the network.

  • hostmask

    The host mask, as an IPv4Address object.

  • netmask

    The net mask, as an IPv4Address object.

  • with_prefixlen

  • compressed

  • exploded

    A string representation of the network, with the mask in prefix notation.

    with_prefixlen and compressed are always the same as str(network). exploded uses the exploded form the network address.

  • with_netmask

    A string representation of the network, with the mask in net mask notation.

  • with_hostmask

    A string representation of the network, with the mask in host mask notation.

  • num_addresses

    The total number of addresses in the network.

  • prefixlen

    Length of the network prefix, in bits.

  • hosts()

    Returns an iterator over the usable hosts in the network. The usable hosts are all the IP addresses that belong to the network, except the network address itself and the network broadcast address. For networks with a mask length of 31, the network address and network broadcast address are also included in the result. Networks with a mask of 32 will return a list containing the single host address.

    >>> list(ip_network('192.0.2.0/29').hosts())  
    [IPv4Address('192.0.2.1'), IPv4Address('192.0.2.2'),
     IPv4Address('192.0.2.3'), IPv4Address('192.0.2.4'),
     IPv4Address('192.0.2.5'), IPv4Address('192.0.2.6')]
    >>> list(ip_network('192.0.2.0/31').hosts())
    [IPv4Address('192.0.2.0'), IPv4Address('192.0.2.1')]
    >>> list(ip_network('192.0.2.1/32').hosts())
    [IPv4Address('192.0.2.1')]
  • overlaps(other)

    True if this network is partly or wholly contained in other or other is wholly contained in this network.

  • address_exclude(network)

    Computes the network definitions resulting from removing the given network from this one. Returns an iterator of network objects. Raises ValueError if network is not completely contained in this network.

    >>> n1 = ip_network('192.0.2.0/28')
    >>> n2 = ip_network('192.0.2.1/32')
    >>> list(n1.address_exclude(n2))  
    [IPv4Network('192.0.2.8/29'), IPv4Network('192.0.2.4/30'),
     IPv4Network('192.0.2.2/31'), IPv4Network('192.0.2.0/32')]
  • subnets(prefixlen_diff=1, new_prefix=None)

    The subnets that join to make the current network definition, depending on the argument values. prefixlen_diff is the amount our prefix length should be increased by. new_prefix is the desired new prefix of the subnets; it must be larger than our prefix. One and only one of prefixlen_diff and new_prefix must be set. Returns an iterator of network objects.

    >>> list(ip_network('192.0.2.0/24').subnets())
    [IPv4Network('192.0.2.0/25'), IPv4Network('192.0.2.128/25')]
    >>> list(ip_network('192.0.2.0/24').subnets(prefixlen_diff=2))  
    [IPv4Network('192.0.2.0/26'), IPv4Network('192.0.2.64/26'),
     IPv4Network('192.0.2.128/26'), IPv4Network('192.0.2.192/26')]
    >>> list(ip_network('192.0.2.0/24').subnets(new_prefix=26))  
    [IPv4Network('192.0.2.0/26'), IPv4Network('192.0.2.64/26'),
     IPv4Network('192.0.2.128/26'), IPv4Network('192.0.2.192/26')]
    >>> list(ip_network('192.0.2.0/24').subnets(new_prefix=23))
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
        raise ValueError('new prefix must be longer')
    ValueError: new prefix must be longer
    >>> list(ip_network('192.0.2.0/24').subnets(new_prefix=25))
    [IPv4Network('192.0.2.0/25'), IPv4Network('192.0.2.128/25')]
  • supernet(prefixlen_diff=1, new_prefix=None)

    The supernet containing this network definition, depending on the argument values. prefixlen_diff is the amount our prefix length should be decreased by. new_prefix is the desired new prefix of the supernet; it must be smaller than our prefix. One and only one of prefixlen_diff and new_prefix must be set. Returns a single network object.

    >>> ip_network('192.0.2.0/24').supernet()
    IPv4Network('192.0.2.0/23')
    >>> ip_network('192.0.2.0/24').supernet(prefixlen_diff=2)
    IPv4Network('192.0.0.0/22')
    >>> ip_network('192.0.2.0/24').supernet(new_prefix=20)
    IPv4Network('192.0.0.0/20')
  • subnet_of(other)

    Return True if this network is a subnet of other.

    >>> a = ip_network('192.168.1.0/24')
    >>> b = ip_network('192.168.1.128/30')
    >>> b.subnet_of(a)
    True

    3.7 新版功能.

  • supernet_of(other)

    Return True if this network is a supernet of other.

    >>> a = ip_network('192.168.1.0/24')
    >>> b = ip_network('192.168.1.128/30')
    >>> a.supernet_of(b)
    True

    3.7 新版功能.

  • compare_networks(other)

    Compare this network to other. In this comparison only the network addresses are considered; host bits aren’t. Returns either -1, 0 or 1.

    >>> ip_network('192.0.2.1/32').compare_networks(ip_network('192.0.2.2/32'))
    -1
    >>> ip_network('192.0.2.1/32').compare_networks(ip_network('192.0.2.0/32'))
    1
    >>> ip_network('192.0.2.1/32').compare_networks(ip_network('192.0.2.1/32'))
    0

    3.7 版后已移除: It uses the same ordering and comparison algorithm as “<”, “==”, and “>”

class ipaddress.IPv6Network(address, strict=True)

Construct an IPv6 network definition. address can be one of the following:

  1. A string consisting of an IP address and an optional prefix length, separated by a slash (/). The IP address is the network address, and the prefix length must be a single number, the prefix. If no prefix length is provided, it’s considered to be /128.

    Note that currently expanded netmasks are not supported. That means 2001:db00::0/24 is a valid argument while 2001:db00::0/ffff:ff00:: not.

  2. An integer that fits into 128 bits. This is equivalent to a single-address network, with the network address being address and the mask being /128.

  3. An integer packed into a bytes object of length 16, big-endian. The interpretation is similar to an integer address.

  4. A two-tuple of an address description and a netmask, where the address description is either a string, a 128-bits integer, a 16-bytes packed integer, or an existing IPv6Address object; and the netmask is an integer representing the prefix length.

An AddressValueError is raised if address is not a valid IPv6 address. A NetmaskValueError is raised if the mask is not valid for an IPv6 address.

If strict is True and host bits are set in the supplied address, then ValueError is raised. Otherwise, the host bits are masked out to determine the appropriate network address.

在 3.5 版更改: Added the two-tuple form for the address constructor parameter.

  • version

  • max_prefixlen

  • is_multicast

  • is_private

  • is_unspecified

  • is_reserved

  • is_loopback

  • is_link_local

  • network_address

  • broadcast_address

  • hostmask

  • netmask

  • with_prefixlen

  • compressed

  • exploded

  • with_netmask

  • with_hostmask

  • num_addresses

  • prefixlen

  • hosts()

    Returns an iterator over the usable hosts in the network. The usable hosts are all the IP addresses that belong to the network, except the Subnet-Router anycast address. For networks with a mask length of 127, the Subnet-Router anycast address is also included in the result. Networks with a mask of 128 will return a list containing the single host address.

  • overlaps(other)

  • address_exclude(network)

  • subnets(prefixlen_diff=1, new_prefix=None)

  • supernet(prefixlen_diff=1, new_prefix=None)

  • subnet_of(other)

  • supernet_of(other)

  • compare_networks(other)

    Refer to the corresponding attribute documentation in IPv4Network.

  • is_site_local

    These attribute is true for the network as a whole if it is true for both the network address and the broadcast address.

运算符

Network objects support some operators. Unless stated otherwise, operators can only be applied between compatible objects (i.e. IPv4 with IPv4, IPv6 with IPv6).

Logical operators

Network objects can be compared with the usual set of logical operators. Network objects are ordered first by network address, then by net mask.

迭代

Network objects can be iterated to list all the addresses belonging to the network. For iteration, all hosts are returned, including unusable hosts (for usable hosts, use the hosts() method). An example:

>>> for addr in IPv4Network('192.0.2.0/28'):
...     addr
...
IPv4Address('192.0.2.0')
IPv4Address('192.0.2.1')
IPv4Address('192.0.2.2')
IPv4Address('192.0.2.3')
IPv4Address('192.0.2.4')
IPv4Address('192.0.2.5')
IPv4Address('192.0.2.6')
IPv4Address('192.0.2.7')
IPv4Address('192.0.2.8')
IPv4Address('192.0.2.9')
IPv4Address('192.0.2.10')
IPv4Address('192.0.2.11')
IPv4Address('192.0.2.12')
IPv4Address('192.0.2.13')
IPv4Address('192.0.2.14')
IPv4Address('192.0.2.15')
Networks as containers of addresses

Network objects can act as containers of addresses. Some examples:

>>> IPv4Network('192.0.2.0/28')[0]
IPv4Address('192.0.2.0')
>>> IPv4Network('192.0.2.0/28')[15]
IPv4Address('192.0.2.15')
>>> IPv4Address('192.0.2.6') in IPv4Network('192.0.2.0/28')
True
>>> IPv4Address('192.0.3.6') in IPv4Network('192.0.2.0/28')
False

Interface objects

Interface objects are hashable, so they can be used as keys in dictionaries.

class ipaddress.IPv4Interface(address)

Construct an IPv4 interface. The meaning of address is as in the constructor of IPv4Network, except that arbitrary host addresses are always accepted.

IPv4Interface is a subclass of IPv4Address, so it inherits all the attributes from that class. In addition, the following attributes are available:

  • ip

    The address (IPv4Address) without network information.

    >>> interface = IPv4Interface('192.0.2.5/24')
    >>> interface.ip
    IPv4Address('192.0.2.5')
  • network

    The network (IPv4Network) this interface belongs to.

    >>> interface = IPv4Interface('192.0.2.5/24')
    >>> interface.network
    IPv4Network('192.0.2.0/24')
  • with_prefixlen

    A string representation of the interface with the mask in prefix notation.

    >>> interface = IPv4Interface('192.0.2.5/24')
    >>> interface.with_prefixlen
    '192.0.2.5/24'
  • with_netmask

    A string representation of the interface with the network as a net mask.

    >>> interface = IPv4Interface('192.0.2.5/24')
    >>> interface.with_netmask
    '192.0.2.5/255.255.255.0'
  • with_hostmask

    A string representation of the interface with the network as a host mask.

    >>> interface = IPv4Interface('192.0.2.5/24')
    >>> interface.with_hostmask
    '192.0.2.5/0.0.0.255'

class ipaddress.IPv6Interface(address)

Construct an IPv6 interface. The meaning of address is as in the constructor of IPv6Network, except that arbitrary host addresses are always accepted.

IPv6Interface is a subclass of IPv6Address, so it inherits all the attributes from that class. In addition, the following attributes are available:

  • ip

  • network

  • with_prefixlen

  • with_netmask

  • with_hostmask

    Refer to the corresponding attribute documentation in IPv4Interface.

运算符

Interface objects support some operators. Unless stated otherwise, operators can only be applied between compatible objects (i.e. IPv4 with IPv4, IPv6 with IPv6).

Logical operators

Interface objects can be compared with the usual set of logical operators.

For equality comparison (== and !=), both the IP address and network must be the same for the objects to be equal. An interface will not compare equal to any address or network object.

For ordering (<, >, etc) the rules are different. Interface and address objects with the same IP version can be compared, and the address objects will always sort before the interface objects. Two interface objects are first compared by their networks and, if those are the same, then by their IP addresses.

Other Module Level Functions

The module also provides the following module level functions:

ipaddress.v4_int_to_packed(address)

Represent an address as 4 packed bytes in network (big-endian) order. address is an integer representation of an IPv4 IP address. A ValueError is raised if the integer is negative or too large to be an IPv4 IP address.

>>> ipaddress.ip_address(3221225985)
IPv4Address('192.0.2.1')
>>> ipaddress.v4_int_to_packed(3221225985)
b'\xc0\x00\x02\x01'

ipaddress.v6_int_to_packed(address)

Represent an address as 16 packed bytes in network (big-endian) order. address is an integer representation of an IPv6 IP address. A ValueError is raised if the integer is negative or too large to be an IPv6 IP address.

ipaddress.summarize_address_range(first, last)

Return an iterator of the summarized network range given the first and last IP addresses. first is the first IPv4Address or IPv6Address in the range and last is the last IPv4Address or IPv6Address in the range. A TypeError is raised if first or last are not IP addresses or are not of the same version. A ValueError is raised if last is not greater than first or if first address version is not 4 or 6.

>>> [ipaddr for ipaddr in ipaddress.summarize_address_range(
...    ipaddress.IPv4Address('192.0.2.0'),
...    ipaddress.IPv4Address('192.0.2.130'))]
[IPv4Network('192.0.2.0/25'), IPv4Network('192.0.2.128/31'), IPv4Network('192.0.2.130/32')]

ipaddress.collapse_addresses(addresses)

Return an iterator of the collapsed IPv4Network or IPv6Network objects. addresses is an iterator of IPv4Network or IPv6Network objects. A TypeError is raised if addresses contains mixed version objects.

>>> [ipaddr for ipaddr in
... ipaddress.collapse_addresses([ipaddress.IPv4Network('192.0.2.0/25'),
... ipaddress.IPv4Network('192.0.2.128/25')])]
[IPv4Network('192.0.2.0/24')]

ipaddress.get_mixed_type_key(obj)

Return a key suitable for sorting between networks and addresses. Address and Network objects are not sortable by default; they’re fundamentally different, so the expression:

IPv4Address('192.0.2.0') <= IPv4Network('192.0.2.0/24')

doesn’t make sense. There are some times however, where you may wish to have ipaddress sort these anyway. If you need to do this, you can use this function as the key argument to sorted().

obj is either a network or address object.

Custom Exceptions

To support more specific error reporting from class constructors, the module defines the following exceptions:

exception ipaddress.AddressValueError(ValueError)

Any value error related to the address.

exception ipaddress.NetmaskValueError(ValueError)

Any value error related to the net mask.


文章作者: 杰克成
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 杰克成 !
评论
  目录