跳转到主要内容

从WSGI应用生成静态网页

项目描述

freezeyt

由捷克Python社区创建的静态网页生成器。

功能说明

Freezeyt是一个静态网页冻结器。它接受一个Python Web应用程序并将其转换为可以由像GitHub Pages或Python的http.server这样的简单服务器提供文件的集合。

Freezeyt与所有使用通用Web服务器网关接口 (WSGI) 的Python Web框架兼容

安装

Freezeyt需要Python 3.6或更高版本。

强烈建议为该项目创建并激活一个单独的虚拟环境。您可以使用venvvirtualenv、Conda、容器或其他类型的虚拟环境。

可以使用以下方式安装此工具

$ python -m pip install .

用法

要使用freezeyt,您需要一个Python Web应用程序。您可以使用示例Flask应用程序

应用程序和Freezeyt都必须可在您的环境中导入(安装)。

使用应用程序名称和输出目录运行freezeyt。例如

$ python -m freezeyt my_app _build

Freezeyt可能会覆盖构建目录(此处为_build),从中删除所有现有文件。

有关更多信息,请参阅下面的配置。

Python API

Freezeyt还有一个Python API,即freeze函数,它接受一个要冻结的应用程序和一个配置字典

from freezeyt import freeze
freeze(app, config)

配置应该是一个字典,就像从YAML配置文件中读取的那样(请参阅下面的配置)。

从运行在asyncio事件循环中的异步代码中,您可以使用freeze_async而不是freeze

中间件

Freezeyt的一些功能可以作为WSGI中间件使用。要使用它,请将您的应用程序包装在freezeyt.Middleware中。例如

from freezeyt import Middleware

config = {}  # use a configuration dict as for `freeze(app, config)`

app = Middleware(app, config)

配置

虽然常见的选项可以在命令行中给出,但您可以使用YAML配置文件或配置变量来完全控制冻结过程。您可以使用-c/--config选项指定配置文件,例如

$ python -m freezeyt my_app _build -c freezeyt.yaml

配置变量应该是一个字典。要传递配置变量,请使用-C/--import-config选项,例如

$ python -m freezeyt my_app _build -C my_app:freezeyt_config

以下是一个示例配置文件

output: ./_build/   # The website will be saved to this directory
prefix: https://mysite.example.com/subpage/
extra_pages:
    # Let freezeyt know about URLs that are not linked from elsewhere
    /robots.txt
    /easter-egg.html
extra_files:
    # Include additional files in the output:
    # Static files
    static:
        copy_from: static/
    # Web host configuration
    CNAME: mysite.example.com
    ".nojekyll": ''
    googlecc704f0f191eda8f.html:
        copy_from: google-verification.html
status_handlers:
    # If a redirect page (HTTP status 3xx) is found, warn but don't fail
    "3xx": warn

以下选项是可以配置的

应用

包含应用程序的模块必须在命令行中作为第一个参数给出或在配置文件中给出。Freezeyt默认查找变量app。可以使用:指定不同的变量。当模块同时由命令行和配置文件指定时,将引发错误。

示例

Freezeyt默认在模块内部查找变量app

app: app_module

如果app在子模块中,使用点分隔包名

app: folder1.folder2.app_module

可以使用:指定不同的变量名。

app: app_module:wsgi_application

如果变量是某个命名空间属性的属性,请使用点在变量名中使用。

app: app_module:namespace.wsgi_application

当配置作为Python字典给出时,app可以是WSGI应用程序对象,而不是字符串。

输出

要将冻结的网站输出到目录,请指定目录名

output: ./_build/

或使用完整形式 - 使用dir 保存器

output:
    type: dir
    dir: ./_build/

如果配置文件中没有指定输出,则必须在命令行上指定输出目录。有两种方法可以在命令行上指定输出:使用--output (-o)选项或作为第二个位置参数。

必须只通过一种方式指定输出,否则将发生错误。

如果输出目录中存在任何现有内容,freezeyt将删除它(如果内容看起来像之前冻结的网站)或引发错误。最佳实践是在冻结之前删除输出目录。

输出到字典

为了测试,freezeyt可以将输出到字典而不是将文件保存到磁盘。这可以通过以下方式配置

output:
    type: dict

在这种情况下,freeze()函数返回一个包含文件名及其内容的字典。例如,一个包含//second_page//images/smile.png的网站将表示为

{
    'index.html': b'<html>...',
    'second_page': {
        'index.html': b'<html>...',
    },
    'images': {
        'smile.png': b'\x89PNG\r\n\x1a\n\x00...',
    },
}

这在CLI中不是很有用,因为返回值丢失了。

前缀

可以使用以下方式指定应用程序将部署的URL

prefix: http://localhost:8000/

prefix: https://mysite.example.com/subpage/

Freezeyt将冻结它找到的所有以prefix开始的页面。

前缀也可以在命令行上指定,例如:--prefix=http://localhost:8000/。CLI参数优先于配置文件。

额外页面

可以指定为“额外”页面的主页无法通过链接访问的页面的URL,在配置中

extra_pages:
    - /extra/
    - /extra2.html

Freezeyt将像找到它们一样处理这些页面。例如,默认情况下,它将遵循额外页面的链接。

也可以在命令行上指定额外页面,例如--extra-page /extra/ --extra-page /extra2.html。CLI和配置文件中的列表合并在一起。

您还可以使用Python生成器指定额外页面,指定方式如下

extra_pages:
    - generator: my_app:generate_extra_pages

generate_extra_pages函数应该接受应用程序作为参数,并返回一个URL的可迭代对象。

当使用Python API时,可以将额外页面的生成器直接作为Python对象指定,例如

config = {
   ...
   'extra_pages': [{'generator': my_generator_function}],
}
another_config = {
   ...
   'extra_pages': [my_generator_function],
}

附加文件

可以指定要包含在输出中的附加文件及其内容。

这些文件不被视为应用程序的一部分;Freezeyt不会尝试在其中查找链接。

这对于配置您的静态服务器很有用。(对于您网站中的页面,我们建议将它们添加到您的应用程序中,而不是作为附加文件。)

如果您在URL部分中指定反斜杠,freezeyt将它们转换为正斜杠。

例如,以下配置将添加3个文件到输出中

extra_files:
      CNAME: mysite.example.com
      ".nojekyll": ''
      config/xyz: abc

您还可以使用Base64编码或文件路径来指定附加文件,如下所示

extra_files:
    config.dat:
        base64: "YWJjZAASNA=="
    config2.dat:
        copy_from: included/config2.dat

可以使用copy_from递归地复制整个目录,如下所示

extra_files:
    static:
        copy_from: static/

不能在命令行界面中指定附加文件。

清理

如果在“冻结”过程中发生错误,Freezeyt将删除不完整的输出目录。这可以防止,例如,错误地将不完整的结果上传到网络主机。

如果您想保留不完整的目录(例如,帮助调试),可以使用--no-cleanup开关或配置文件中的cleanup键。

cleanup: False

命令行开关优先于配置。使用--no-cleanup来覆盖配置中的cleanup: False

快速失败

快速失败模式会在发生第一个错误时停止应用程序的冻结。

与大多数设置一样,快速失败可以在配置文件和命令行中使用开关设置(命令行总是覆盖配置文件)。快速失败是一个定义为布尔值的选项。

如果您想在配置文件中指定快速失败,请使用fail_fast键,其布尔值为TrueFalse

fail_fast: True

如果您想从命令行指定快速失败,请使用开关--fail-fast(简称-x)或--no-fail-fast来禁用它。

$ freezeyt app -o output -x

Github Pages插件

为了更容易地将冻结的页面上传到(Github Pages服务),您还可以使用--gh-pages开关或配置文件中的gh_pages键,这将在输出目录中创建一个gh-pages git分支。

默认情况下,Github Pages插件不是激活的,但是,如果您已在您的配置中激活了此插件,您可以使用CLI中的--no-gh-pages开关始终覆盖当前配置。

配置示例

gh_pages: True

要部署网站到Github,您可以在输出目录中直接与git仓库一起工作,或者将文件拉入另一个仓库/目录。然后,您可以通过许多方式从新创建的gh-pages git分支中拉取/获取文件,例如

git fetch output_dir gh-pages
git branch --force gh-pages FETCH_HEAD

注意:由于使用了--force开关,这将覆盖gh-pages分支的当前内容。

MIME类型和文件类型比较

Freezeyt会检查其输出中的文件扩展名是否与其由应用程序提供的MIME类型相匹配。如果存在不匹配,freezeyt将失败,因为这意味着服务器无法正确地提供页面。

此功能由freezeyt.Middleware提供。

默认MIME类型

可以指定没有扩展名的文件的MIME类型。例如,如果您的静态页面服务器默认为纯文本文件,请使用

default_mimetype=text/plain

如果YAML配置中没有明确配置默认MIME类型,则freezeyt使用值application/octet-stream

默认mimetype不能在命令行中指定。

从扩展名识别文件类型

可以修改确定文件类型的方式。您可以设置自己的get_mimetype函数。

如果您在配置YAML文件中指定了它,Freezeyt将注册您的函数

get_mimetype=module:your_function

如果配置文件中没有定义get_mimetype,那么freezeyt调用python函数mimetypes.guess_type并使用它返回的mimetype(第一个元素)。

get_mimetype可以定义为

  • 以“模块:函数”形式的字符串,用于指定要调用的函数。
  • Python 函数(如果从 Python 配置 freezeyt,例如作为 dict,而不是 YAML)。

get_mimetype

  • 接收一个参数 - 以 str 格式的 filepath

  • 返回文件 MIME 类型,以 MIME 类型的 list 形式返回(例如 ["text/html"]["audio/wav", "audio/wave"])。

如果 get_mimetype 返回 None,则 freezeyt 将使用配置的 default_mimetype(见上方的 默认 MIME 类型)。

get_mimetype 函数不能在 CLI 上指定。

使用 mime-db 数据库

可以使用来自 jshttp 项目(见 MIME 类型数据库)的 MIME 类型数据库,或者具有相同结构的数据库(这是 GitHub Pages 使用的数据库)。数据库将用于从文件后缀获取文件 MIME 类型。

要使用此数据库,将 JSON 文件的路径添加到 freezeyt 配置中。

mime_db_file=path/to/mime-db.json

这相当于将 get_mimetype 设置为将扩展名映射到文件类型的函数。

mime_db 文件不能在 CLI 上指定。

进度条和日志记录

CLI 选项 --progress 控制在处理页面时 freezeyt 输出什么。

  • --progress=log:将有关每个冻结页面的消息输出到 stdout。
  • --progress=bar:在终端中绘制状态栏。有关每个冻结页面的消息也会打印到 stdout。
  • --progress=none:不做任何操作。

如果 stdout 是终端,则默认为 bar,否则为 log

可以在配置文件中使用插件 freezeyt.progressbar:ProgressBarPluginfreezeyt.progressbar:LogPlugin 来配置此选项。有关如何启用插件,请见下文。

配置版本

为了确保您的配置在 freezeyt 的新版本中不变,您应该在配置中添加当前版本号 1,如下所示:

version: 1

这不是强制性的。如果没有给出版本号,配置可能在 freezeyt 的未来版本中不起作用。

版本参数不接受在命令行上。

插件

可以通过 插件 扩展 freezeyt,这些插件可以是随 freezeyt 一起提供的,也可以是外部的。

插件使用以下配置添加:

plugins:
    - freezeyt.progressbar:ProgressBar
    - mymodule:my_plugin

自定义插件

插件是一个函数,freezeyt 将在开始冻结页面之前调用它。

它将一个 FreezeInfo 对象作为参数传递(见下方的 start 钩子)。通常,插件将调用 freeze_info.add_hook 来注册额外的函数。

钩子

可以注册 钩子,即在冻结过程中发生特定事件时调用的函数。

例如,如果 mymodule 定义了函数 startpage_frozen,则可以使用此配置让 freezeyt 调用它们:

hooks:
    start:
        - mymodule:start
    page_frozen:
        - mymodule:page_frozen

在使用 Python API 时,可以使用函数而不是名称,如 mymodule:start

start

当冻结过程开始时,在调用任何其他钩子之前将调用该函数。

它将一个 FreezeInfo 对象作为参数传递。该对象具有以下属性:

  • add_url(url, reason=None):将 URL 添加到要冻结的页面集合中。如果该 URL 已冻结或位于 prefix 之外,则不执行任何操作。如果您添加一个 reason 字符串,它将用于错误消息中,说明添加的 URL 被处理的原因。
  • add_hook(hook_name, callable):注册一个额外的钩子函数。
  • total_task_count:freezeyt 当前“已知”的页面数量 - 已冻结的页面以及计划冻结的页面。
  • done_task_count:已完成的页面数量(无论是成功冻结还是失败)。
  • failed_task_count:未能冻结的页面数量。

page_frozen

每当处理成功一个页面时,该函数将被调用。它将一个TaskInfo对象作为参数传递。该对象具有以下属性

  • get_a_url():返回包含prefix的页面URL。请注意,页面可能通过多个URL访问;此函数返回任意一个。
  • path:内容保存的相对路径。
  • freeze_info:一个FreezeInfo对象。有关详细信息,请参阅start钩子。
  • exception:对于失败的作业,引发异常;否则为None
  • reasons:解释为什么访问给定页面的字符串列表。(请注意,随着冻结的进行,可能向现有任务添加新的原因。)

page_failed

每当由于异常而导致页面无法保存时,该函数将被调用。它将一个TaskInfo对象作为参数传递(请参阅page_frozen钩子)。

success

在应用成功冻结后,该函数将被调用。它将一个FreezeInfo对象作为参数传递(请参阅start钩子)。

冻结操作

默认情况下,freezeyt将保存它找到的页面。您可以指示它忽略某些页面,或将它们视为错误。这通常用作对某些HTTP状态(例如,将所有404 NOT FOUND页面视为错误)的响应,但也可以独立使用。

要从应用程序(或中间件)内部告诉freezeyt要做什么,请将Freezeyt-Action HTTP头设置为以下值之一

  • 'save'freezeyt将保存页面的主体。
  • 'ignore'freezeyt将不为该页面保存任何内容。
  • 'warn':将保存内容并向stdout发送警告消息
  • 'follow'freezeyt将保存重定向位置的 内容(这需要Location头,通常用于重定向)。不支持重定向到外部页面。
  • 'error':失败;页面将不会保存,并且freeze()将引发异常。

状态处理

如果没有设置Freezeyt-Action头,则freezeyt将根据状态确定要做什么。默认情况下,200 OK页面将被保存,而其他任何页面都将导致错误。可以使用status_handlers设置自定义行为。例如,要忽略状态为404 NOT FOUND的页面,请将404处理程序设置为'ignore'

status_handlers:
    '404': ignore

例如,status_handlers可以这样指定

status_handlers:
    '202': warn
    '301': follow
    '404': ignore
    '418': my_module:custom_action  # see below
    '429': ignore
    '5xx': error

请注意,状态代码必须是一个字符串,因此需要在YAML文件中引用。

可以指定一系列状态,格式为一个数字(例如1-5)后跟小写的xx。(不支持其他“通配符”如50x。)

无法在CLI中指定状态处理程序。

自定义操作

您还可以在status_handlers中定义自定义操作

  • 一个形式为'my_module:custom_action'的字符串,该字符串命名一个要调用的处理程序函数,
  • 一个Python函数(如果从Python而不是从YAML配置freezeyt)。

操作函数接受一个参数,task(TaskInfo):有关冻结任务的详细信息。请参阅TaskInfo钩子以获取说明。Freezeyt的默认操作(如follow)可以从freezeyt.actions导入(例如freezeyt.actions.follow)。自定义操作应调用这些默认操作之一,并从中返回返回值。

URL查找

freezeyt通过URL查找器在应用程序中查找新的链接。URL查找器是旨在查找特定MIME类型URL的函数。freezeyt提供不同的配置选项来使用URL查找器

  • text/htmltext/css(默认)使用预定义的URL查找器。
  • 定义您自己的URL查找器作为您的函数,
  • 关闭一些查找器(下文所述部分)

配置示例

url_finders:
    text/html: get_html_links
    text/css: get_css_links

url_finders字典中的键是MIME类型;

值是URL查找器,可以定义为

  • 形式为"module:function"的字符串,该字符串指定要调用的查找器函数,
  • get_html_links之类的字符串,指定来自freezeyt.url_finders模块的函数,或
  • Python函数(如果从Python而不是YAML配置freezeyt)。

URL查找器获取以下参数

  • 页面内容 BinaryIO
  • 页面的绝对URL,作为string
  • HTTP头,作为元组的列表(WSGI)。

函数应返回一个迭代器,包含在页面内容中找到的所有URL(作为字符串),它们将出现在hrefsrc属性中。具体来说

  • URL可以是相对的。
  • 应包括外部URL(即不以prefix开始的URL)。

查找器函数可能是异步的

  • 函数可以用async def定义(即返回一个协程)。如果是这样,freezeyt将在await之后使用结果。
  • 函数可能是一个异步生成器(使用async def定义并使用yield)。如果是这样,freezeyt将使用异步迭代来处理它。

freezeyt.url_finders模块包括

  • get_html_links,HTML的默认查找器
  • get_css_links,CSS的默认查找器
  • get_html_links_asyncget_css_links_async,上述查找器的异步版本
  • none,一个找不到任何链接的查找器。

URL查找器不能在CLI中指定。

URL查找器头

您可以在Freezeyt-URL-Finder HTTP头中指定查找器。如果指定了,它将覆盖url_finders配置。

默认get_html_links

HTML页面的默认URL查找器在文档中所有标签的srchref属性中查找。它目前不处理其他链接,例如内嵌CSS,但将来可能会改进。

默认get_css_links

CSS的默认URL查找器使用cssutils库在样式表中查找所有链接。

禁用默认URL查找器

如果查找器在配置文件中没有明确指定,freezeyt将使用特定MIME类型的默认值。例如,如果您只指定了text/html: my_custom_finderfreezeyt将使用text/css的默认查找器。

您可以禁用此行为

use_default_url_finders: false

在Link头中查找URL

默认情况下,freezeyt将遵循Link HTTP头中的URL。要禁用此功能,请指定

urls_from_link_headers: false

路径生成

可以使用url_to_path配置键自定义URL保存下的文件名,例如

url_to_path: my_module:url_to_path

值可以是

  • 形式为"module:function"的字符串,该字符串指定要调用的函数(函数可以与冒号一起省略,默认为url_to_path),或
  • Python函数(如果从Python而不是YAML配置freezeyt)。

函数接收要保存的URL的路径,相对于prefix,并应返回一个相对于构建目录的保存文件的路径。

默认函数,可用作freezeyt.url_to_path,如果URL路径以/结尾,则添加index.html

url_to_path不能在CLI中指定。

中间件静态模式

当使用freezeyt中间件时,您可以启用静态模式,该模式模拟应用保存为静态页面后的行为

static_mode: true

当前在静态模式下

  • 不允许除GET和HEAD之外的其他HTTP方法。
  • URL参数已被移除
  • 请求体被丢弃

未来可能会添加其他限制和功能,不考虑向后兼容性。静态模式旨在用于交互式使用——在每次更改后无需冻结所有内容即可测试您的应用程序。

CLI使用示例

$ python -m freezeyt my_app _build/
$ python -m freezeyt my_app _build/ --prefix https://pyladies.cz/
$ python -m freezeyt my_app _build/ -c config.yaml
$ python -m freezeyt my_app _build/ --prefix https://pyladies.cz/ --extra-page /extra1/ --extra-page /extra2/
$ python -m freezeyt my_app _build/ --prefix https://pyladies.cz/ --extra-page /extra1/ --extra-page /extra2/ --config path/to/config.yaml

贡献

您对这个项目感兴趣吗?太棒了!任何想成为这个项目一部分并愿意帮助我们的人都非常受欢迎。您刚开始使用Python吗?好消息!我们主要针对寻求为(理想情况下开源)项目做出贡献并希望成为开源社区成员的新手Python程序员。

所以,如果您已经掌握了一些扎实的Python技能,那会怎么样?首先,总有新东西可以学习,其次,如果您能指导“新手”并传授一些知识给他们,我们将不胜感激。

欢迎贡献、问题和功能请求。如果您想贡献,请随意查看问题页面。

如何贡献

  1. 将此存储库克隆到您的本地计算机
$ git clone https://github.com/encukou/freezeyt
  1. 然后将此存储库分叉到您的GitHub账户
  2. 将您的分叉存储库添加为新的远程到您的本地计算机
$ git remote add <remote_label> https://github.com/<username>/freezeyt
  1. 在您的本地计算机上创建一个新的分支
$ git branch <branch_name>
  1. 切换到您的新的分支
$ git switch <branch_name>
  1. 更新代码
  2. 将更改推送到GitHub上的分叉存储库
$ git push <remote_label> <branch_name>
  1. 最后,从您的GitHub账户向origin发起拉取请求

开发安装

freezeyt可以从当前目录安装

$ python -m pip install -e .

它还有几个额外的依赖组

  • blog用于项目博客
  • dev用于开发和运行测试
  • typecheck用于mypy类型检查

每个组可以单独安装

$ python -m pip install -e ."[typecheck]"

或者您一次可以安装更多组

$ python -m pip install -e ."[blog, dev, typecheck]"

使用开发中的freezeyt副本

  • PYTHONPATH设置为包含freezeyt的目录,例如

    • Unix: export PYTHONPATH="/home/name/freezeyt"
    • Windows: set PYTHONPATH=C:\Users\Name\freezeyt
  • 安装您想要冻结的Web应用程序。例如

    • 如果可能,使用pip安装应用程序
    • 安装应用程序的依赖项,并使用cd进入应用程序目录。
  • 运行freezeyt,例如

    • python -m freezeyt demo_app_url_for _build --prefix http://freezeyt.test/foo/

测试

为了测试项目,需要安装额外的需求

$ python -m pip install .[dev]

要在您的当前环境中运行测试,请使用pytest

$ python -m pytest

如果您安装了多个Python版本,要使用多个Python版本运行测试,请使用python -m pip install tox安装tox并运行它

$ tox

测试环境变量

某些测试场景会将freezeyt的结果与预期的输出进行比较。当尚不存在包含预期输出的文件时,可以通过将环境变量TEST_CREATE_EXPECTED_OUTPUT设置为1来创建这些文件。

Unix

$ export TEST_CREATE_EXPECTED_OUTPUT=1

Windows

> set TEST_CREATE_EXPECTED_OUTPUT=1

如果您将变量设置为其他值或未设置,则不会重新创建文件(如果文件未更新,则测试将失败)。

当输出更改时,您需要首先删除预期输出,通过运行测试并使用TEST_CREATE_EXPECTED_OUTPUT=1来重新生成它,并检查差异是否正确。

使用工具和技术

如何查看进度

不幸的是,我们只能用捷克语查看开发进度。

查看进度

其他通信渠道和信息可以在以下位置找到

Freezeyt博客

我们维护了一个关于Freezeyt开发的博客。它可以在这里找到。

警告:其中一些内容是用捷克语编写的。

博客开发

该博客在Python版本3.8上进行了测试。

该博客是一个Flask应用程序。要运行它,使用python -m pip install .[blog]安装额外的依赖项。然后,将环境变量FLASK_APP设置为博客应用程序的路径。还将FLASK_ENV设置为"开发"以方便调试。然后,运行Flask服务器。

  1. 在Microsoft Windows上
> python -m pip install .[blog]
> set FLASK_APP=freezeyt_blog/app.py
> set FLASK_ENV=development
> flask run
  1. 在UNIX上
$ python -m pip install .[blog]
$ export FLASK_APP=freezeyt_blog/app.py
$ export FLASK_ENV=development
$ flask run

博客运行的URL将在终端上打印出来。

一旦你对博客的外观满意,你可以使用以下方式将其冻结

$ python -m freezeyt freezeyt_blog.app freezeyt_blog/build

向Freezeyt博客添加新文章

文章是用Markdown语言编写的。

文章 - 保存到目录 ../freezeyt/freezeyt_blog/articles

文章中的图片 - 保存到目录 ../freezeyt/freezeyt_blog/static/images

如果文件保存在其他位置,博客将无法正常工作。

历史

项目为什么会开始?

捷克Python社区使用了很多用于社区目的的静态网页,这些网页是从Web应用程序生成的。例如,组织和宣布研讨会、课程或聚会。

到目前为止,社区一直依赖于Frozen Flaskelsa来生成静态网页内容。新的freezer应该与任何任意的Python Web应用程序框架(FlaskDjangoTornado等)一起使用。这样,社区在生成静态页面时就不会再受限于一种Web应用程序技术。

作者

有关所有贡献者的信息,请参阅GitHub历史。

许可证

本项目采用MIT许可。祝您使用愉快。

项目详情


下载文件

下载适合您平台文件的文件。如果您不确定选择哪一个,请了解更多关于安装包的信息。

源分布

freezeyt-1.1.1.tar.gz (81.3 kB 查看哈希值)

上传时间

构建分布

freezeyt-1.1.1-py3-none-any.whl (41.0 kB 查看哈希值)

上传时间 Python 3

由以下机构支持

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误记录 StatusPage StatusPage 状态页面