☰
Hug 扩展开发实战指南:从命名规范到源码级实现原理(基于 hug 的 EXTENDING.md)
2026/10/8 2:05:43 网站建设 项目流程
  • 后端

【免费下载链接】hug

Embrace the APIs of the future. Hug aims to make developing APIs as simple as possible, but no simpler.

项目地址:https://gitcode.com/gh_mirrors/hu/hug
点击查看免费下载

本指南以 hug 仓库的 EXTENDING.md 为核心骨架,讲解如何为 hug 构建、命名、测试并注册扩展(extension)。无论你想为 hug 新增一种类型(type)、接入一种新的认证方式(authentication)、定制输入/输出格式(input/output format),还是实现全局校验器、变换器或中间件,本文都会给出从工程组织到发布注册的完整路径,并结合 hug/types.py、hug/authentication.py、hug/output_format.py、hug/input_format.py、hug/transform.py、hug/validate.py、hug/middleware.py 等核心源码,剖析每种扩展能力背后的实现机制,让读者既能照着写,也能真正理解其工作原理。

一、hug 扩展的本质:先是一个普通的 Python 包

EXTENDING.md 开宗明义:hug 扩展应当按照任何普通 Python 项目的方式构建,并发布到 PyPI。它之所以能被称为 "hug 扩展",决定性因素只有两点:

  1. 命名:包名以hug_前缀开头,方便在 PyPI 上被检索发现;
  2. 内容:包内包含用于扩展 hug 能力的工具类与函数(utilities and classes that extend hug's capabilities)。

换句话说,hug 对扩展几乎没有侵入式要求——你不需要继承某个框架基类,也不需要遵循特殊目录约定,只需发布一个普通 Python 包,并在包内正确利用 hug 暴露的扩展点。hug 之所以能做到这一点,与它把各扩展能力模块独立组织的设计密切相关:在 hug/init.py 中,types、authentication、input_format、output_format、transform、validate、middleware、directives等模块被逐一导入并作为公共 API 暴露,这正是外部扩展可以按模块各取所需的入口。

二、命名规范:让用户在 PyPI 上"一眼看到能力"

扩展的包名直接决定它的可发现性。EXTENDING.md 规定:所有 hug 扩展必须以hug_开头,在此基础上,可以按功能选用更精确的前缀来指引用户理解扩展的用途。

下表完整继承自 EXTENDING.md,并补充了对应的源码模块参考:

前缀适用场景文档示例对应 hug 源码模块
hug_types_主要为 hug 新增类型hug_types_numpyhug/types.py
hug_authentication_主要为 hug 新增认证方式hug_authentication_oath2hug/authentication.py
hug_output_format_主要为 hug 新增输出格式hug_output_format_svghug/output_format.py
hug_input_format_主要为 hug 新增输入格式hug_input_format_htmlhug/input_format.py
hug_validate_主要为 hug 新增整体校验器hug_validate_no_nullhug/validate.py
hug_transform_主要为 hug 新增变换器hug_transform_add_timehug/transform.py
hug_middleware_为 hug 新增中间件hug_middleware_redis_sessionhug/middleware.py

对于更复杂或通用的场景——比如同时涉及多种能力——直接使用hug_前缀即可。EXTENDING.md 以hug_geo为例:一个地理相关的扩展可以同时提供 types、input formats 和 output formats,此时简单的hug_前缀比强行套用单一能力前缀更合适。

注意:前缀只是建议性的命名约定,并不强制校验。它解决的是"用户如何在海量 PyPI 包中快速判断一个包是不是 hug 扩展、能做什么"的问题。

三、七类扩展能力的源码级实现要点

命名只是第一步。要让扩展真正生效,关键在于正确使用 hug 各模块暴露的扩展点。下面按前缀分类,逐一给出源码依据与最小可用的实现思路。

3.1 新增类型:hug_types_/hug.type与hug.types

hug 的类型系统以"可调用对象"为核心。在 hug/types.py 中,一切类型的基类是Type(第 47-60 行),它约定:

  • 用函数注解(annotation)标记参数类型;
  • 子类必须重写__call__方法,在其中完成值转换 + 校验;
  • 校验失败时应抛出异常(通常是ValueError),hug 会将其转换为 API 层的错误响应。

为了降低扩展门槛,hug 提供了两个工厂方法:

  • hug.types.create(...)(hug/types.py):基于装饰器语义创建新类型,支持doc、error_text、exception_handlers、chain、accept_context等参数,并会自动生成带异常兜底与错误文案的__call__实现;
  • hug.types.accept(kind, ...)(hug/types.py):最轻量的方式——直接把任意 Python 类型转换函数包装为可用的 hug 类型注解。

内置类型的定义就是现成的范式,例如 hug/types.py:

number = accept(int, "A whole number", "Invalid whole number provided") float_number = accept(float, "A float number", "Invalid float number provided") boolean = accept(bool, "Providing any value will set this to true", "Invalid boolean value provided") uuid = accept(native_uuid.UUID, "A Universally Unique IDentifier", "Invalid UUID provided")

一个自定义类型的典型写法如下(例如假设要做一个hug_types_风格的扩展,新增"版本号"类型):

# 包内模块(示例) from hug import type @type(error_text="Invalid version string provided") def version_string(value): """A semantic version string, e.g. 1.2.3""" parts = value.split(".") if not all(part.isdigit() for part in parts): raise ValueError(value) return tuple(int(part) for part in parts)

若需支持"参数化类型"(如types.Multiple[types.text]、DelimitedList),源码中通过SubTyped元类实现下标语法(hug/types.py),扩展同样可以借鉴__getitem__的写法为自定义类型提供泛型能力。此外,Multi(任一类型通过即可)、OneOf/Mapping(枚举取值)、InRange/LessThan/GreaterThan/Length(数值与长度约束)、JSON(解析 JSON 数据)等内置类型都集中在 hug/types.py,可作为功能对照清单。

3.2 新增认证方式:hug_authentication_与authenticator

认证扩展的核心是hug.authentication模块的authenticator装饰器(hug/authentication.py。其约定非常清晰:

  • 被@authenticator包裹的认证函数接收request、response,并通过**kwargs获得verify_user回调;
  • verify_user接受凭证(如 API key),认证成功返回用户对象,失败返回 falsy 值;
  • authenticator包装后,成功时会把用户对象写入request.context["user"];失败时根据result is None(缺凭证)或result is False(凭证无效)抛出HTTPUnauthorized,并附带challenges(挑战头)。

内置的两种认证方式即标准范例:

  • basic(hug/authentication.py):解析Authorization: Basic ...头,base64 解码出user_id:key,调用verify_user(user_id, key);
  • api_key(hug/authentication.py):读取X-Api-Key请求头,调用verify_user(api_key)。

文档示例hug_authentication_oath2即指实现 OAuth2 类型的认证函数。自建认证扩展的骨架可以这样组织:

# 示例:包内 authentication.py from hug.authentication import authenticator @authenticator def token_auth(request, response, verify_user, **kwargs): """Bearer Token Authentication""" token = request.get_header("Authorization") if token and token.lower().startswith("bearer "): token = token.split(" ", 1)[1] user = verify_user(token) if user: return user return False return None # 无凭证 -> HTTPUnauthorized

使用时,把认证函数传给路由装饰器的requires参数即可,例如hug.get(requires=token_auth)。

3.3 新增输出格式:hug_output_format_与output_format

输出格式是"把 API 返回值转换为响应体"的函数。内置实现集中在 hug/output_format.py,包括json、text、html、pretty_json、json_camelcase、图片/视频处理器、file等。其中两个机制对扩展至关重要:

  • content_type装饰器(来自 hug/format.py,在 hug/output_format.py 中被大量使用):为输出函数声明 MIME 类型,例如@content_type("application/json; charset=utf-8");
  • on_valid(content_type, on_invalid=json)(hug/output_format.py):当数据结构中出现errors键时自动切换到错误格式,用于"校验失败返回错误格式"的语义。

文档示例hug_output_format_svg可以借助image工厂快速实现——事实上 hug/output_format.py 就是用循环为IMAGE_TYPES中每种格式动态生成xxx_image处理器的,svg_image正源于此。一个输出格式扩展的最小形态:

# 示例:包内 output_format.py from hug.format import content_type @content_type("application/x-yaml; charset=utf-8") def yaml(content, **kwargs): """YAML (Yet Another Markup Language)""" import yaml as yaml_lib return yaml_lib.safe_dump(content).encode("utf8")

更进阶的用法包括:用on_content_type(handlers, default=...)按请求 Content-Type 选择处理器(hug/output_format.py),用output_format.accept(handlers)/suffix/prefix按客户端可接受类型或 URL 后缀/前缀分发(hug/output_format.py)。若想让自定义对象在 JSON 序列化时自动转换,可以用@json_convert(kind)注册全局转换器(hug/output_format.py,内置的 numpy 系列转换即为范例)。

3.4 新增输入格式:hug_input_format_与input_format

输入格式负责把请求体解析为 Python 对象。内置实现位于 hug/input_format.py:text(按 charset 解码)、json(解析 JSON 为原生对象)、json_underscore(JSON 键名转下划线风格)、urlencoded(解析查询字符串)、multipart(解析 multipart 表单)。它们统一使用content_type装饰器声明适用的请求媒体类型。

文档示例hug_input_format_html的扩展思路即:注册一个解析 HTML 请求体的处理器。骨架如下:

# 示例:包内 input_format.py from hug.format import content_type @content_type("text/html") def html(body, charset="utf-8", **kwargs): """Takes HTML formatted data""" from bs4 import BeautifulSoup return BeautifulSoup(body.read().decode(charset), "html.parser")

3.5 新增整体校验器:hug_validate_与validate

校验器与类型不同:类型针对单个参数值,而校验器针对整个请求字段集。内置实现位于 hug/validate.py,包括:

  • validate.all(*validators):全部通过才成功(hug/validate.py);
  • validate.any(*validators):任一通过即成功(hug/validate.py);
  • validate.contains_one_of(*fields):保证多个可选字段中至少有一个被设置(hug/validate.py)。

文档示例hug_validate_no_null对应的实现模式是:接收字段字典fields,对其中值为空的字段返回{字段名: 错误信息}结构。一个自建校验器的示例:

# 示例:包内 validate.py def no_null(fields): """Ensures no passed in field is null""" errors = {} for field, value in fields.items(): if value is None or value == "": errors[field] = "must not be null" return errors

使用时通过路由装饰器的validate=参数传入(可配合hug.validate.all(...)组合多个校验器)。

3.6 新增变换器:hug_transform_与transform

变换器(transformer)用于在输出前后对数据进行统一处理。内置实现位于 hug/transform.py,提供:

  • transform.content_type(transformers, default=None):按请求 Content-Type 选择变换器(hug/transform.py);
  • transform.suffix(...)/transform.prefix(...):按 URL 后缀/前缀选择(hug/transform.py);
  • transform.all(*transformers):依次应用全部变换器(hug/transform.py)。

文档示例hug_transform_add_time即"给数据追加时间戳"这类通用变换。骨架示例:

# 示例:包内 transform.py from datetime import datetime def add_time(data, request=None, response=None): """Adds a timestamp to the output data""" if isinstance(data, dict): data["served_at"] = datetime.utcnow().isoformat() return data

3.7 新增中间件:hug_middleware_与middleware

中间件用于在请求处理生命周期中注入横切逻辑。内置实现 hug/middleware.py 中的SessionMiddleware是一个完整范本:其构造参数(store、context_name、cookie_name及一系列cookie_*参数)定义了会话中间件的可配置面,并通过process_request(加载会话注入request.context)与process_response(保存会话并下发 cookie)两个钩子接入请求生命周期。

文档示例hug_middleware_redis_session即用 Redis 存储实现该会话中间件的get/exists/set三个方法(hug/middleware.py 的文档明确约定了 store 对象必须实现的接口)。自建中间件只需定义这两个钩子:

# 示例:包内 middleware.py class TimingMiddleware(object): """Adds a simple response header recording request processing time.""" def process_request(self, request, response): import time request.context["_start"] = time.time() def process_response(self, request, response, resource, req_succeeded): import time start = request.context.get("_start") if start is not None: response.set_header("X-Processing-Time", str(time.time() - start))

注册方式为hug.middleware_class(TimingMiddleware)(该装饰器在 hug/init.py 中作为公共 API 导出)。

四、构建建议:参照 hug 自身的工程标准

EXTENDING.md 建议扩展尽可能按 hug 自身的方式构建,并给出四项工程标准:

  1. 100% 测试覆盖率(pytest):hug 本身就是高度依赖测试驱动的项目,仓库tests/目录包含 test_types.py、test_authentication.py、test_output_format.py、test_validate.py、test_transform.py 等对应模块的完整测试套件——扩展开发者可以参照这些测试文件为各自能力编写等价覆盖;
  2. 不错的性能(decent performance):类型转换、输出格式化处于每次请求的热路径上,应避免引入无谓的开销;
  3. PEP8 合规:保证代码风格统一、可读;
  4. 可选地内置 Cython 编译:hug 自身支持 Cython 加速,扩展若同样提供可编译路径,性能敏感用户会更有信心。

需要强调:这四项均非强制要求,它们的价值在于给用户"这个扩展不会拖慢我的服务、不会意外破坏环境"的信心。

五、注册与发布:让扩展被整个生态发现

开发并测试完毕后,扩展需要被注册以便他人发现。EXTENDING.md 给出两个注册层级:

  1. 发布到 PyPI:与任何普通 Python 包无异。结合本仓库的 setup.py、setup.cfg、pyproject.toml 与 requirements、tox.ini 等打包/构建配置,可以推断出标准流程大致为:配置好包元数据与依赖 → 本地构建(python -m build)→ 上传发布(twine upload dist/*);
  2. 登记到 hug 官方扩展清单:将扩展补充到 hug 项目在 GitHub Wiki 上维护的Hug-Extensions列表中,进一步提高被检索与发现的机会。

六、生态共建:扩展是 hug 长期演进的基石

EXTENDING.md 在结尾向每一位扩展作者表达了诚挚的感谢:每一个用心开发并注册的扩展,都在让 hug 的生态更加完整,也为未来 API 基础设施打牢地基。这也是本项目设计哲学的延伸——正如 hug/init.py 所述的设计目标:让 API 开发"像写定义一样简洁",并"拥抱最新技术"。扩展机制正是这一哲学的落地通道:hug 核心保持精简,把能力的横向扩张交给开放、标准的扩展协议。


阅读延伸:本文对应的官方指南原文位于 EXTENDING.md;各能力模块的实现细节可继续阅读 hug/types.py、hug/authentication.py、hug/input_format.py、hug/output_format.py、hug/transform.py、hug/validate.py、hug/middleware.py;配套测试用例集中在 tests 目录;此外,仓库中的 documentation/AUTHENTICATION.md、documentation/TYPE_ANNOTATIONS.md、documentation/OUTPUT_FORMATS.md、documentation/CUSTOM_CONTEXT.md 分别对认证、类型注解、输出格式与中间件上下文做了深入讲解,可配合本指南交叉阅读。

  • 后端

【免费下载链接】hug

Embrace the APIs of the future. Hug aims to make developing APIs as simple as possible, but no simpler.

项目地址:https://gitcode.com/gh_mirrors/hu/hug
点击查看免费下载

相关推荐

上一篇:AWS SAM 安全 Lambda 部署(Safe Lambda Deployments)完全指南:基于 CodeDeploy 的渐进式流量切换与自动回滚
下一篇:VR-reversal 3D转2D播放器踩坑实录:硬件加速失效、播放卡顿与画质调节的终极解决方案

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询