Metadata-Version: 2.4
Name: aliyun-api-gateway-python
Version: 0.1.0
Summary: Unofficial, tested Python client for Alibaba Cloud API Gateway APP signature authentication
Author: Sampson Luo
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/sampsonlor/aliyun-api-gateway-python3
Project-URL: Documentation, https://github.com/sampsonlor/aliyun-api-gateway-python3#readme
Project-URL: Issues, https://github.com/sampsonlor/aliyun-api-gateway-python3/issues
Project-URL: Changelog, https://github.com/sampsonlor/aliyun-api-gateway-python3/blob/main/CHANGELOG.md
Keywords: aliyun,alibaba-cloud,api-gateway,signature,hmac
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: NOTICE
Provides-Extra: test
Requires-Dist: pytest>=8.4; extra == "test"
Requires-Dist: pytest-cov>=6.2; extra == "test"
Provides-Extra: dev
Requires-Dist: build>=1.3; extra == "dev"
Requires-Dist: mypy>=1.17; extra == "dev"
Requires-Dist: pytest>=8.4; extra == "dev"
Requires-Dist: pytest-cov>=6.2; extra == "dev"
Requires-Dist: ruff>=0.12; extra == "dev"
Requires-Dist: twine>=6.2; extra == "dev"
Dynamic: license-file

# aliyun-api-gateway-python

[![CI](https://github.com/sampsonlor/aliyun-api-gateway-python3/actions/workflows/ci.yml/badge.svg)](https://github.com/sampsonlor/aliyun-api-gateway-python3/actions/workflows/ci.yml)
[![Python](https://img.shields.io/pypi/pyversions/aliyun-api-gateway-python.svg)](https://pypi.org/project/aliyun-api-gateway-python/)
[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)

一个用于阿里云传统 API 网关“阿里云 APP”摘要签名认证的非官方 Python 客户端。

> [!IMPORTANT]
> 本项目不是阿里云官方 SDK，也未获得阿里云背书。阿里云现在已经提供 Python 3.11
> [签名示例](https://github.com/aliyun/api-gateway-demo-sign-python)；本项目的定位是把该签名协议整理为一个可安装、可测试、默认安全、兼容旧代码的社区包。

协议依据：[阿里云《使用摘要签名认证方式调用 API》](https://help.aliyun.com/zh/api-gateway/traditional-api-gateway/user-guide/use-digest-authentication-to-call-an-api)。

## 特性

- 支持 `HmacSHA256`（默认）和 `HmacSHA1`。
- 正确合并并排序 Path、Query 和 Form 参数；重复参数按协议使用第一个值签名。
- Query/Form 先用原文签名，再使用 UTF-8 URL 编码发送，支持中文、空格、空值和重复键。
- JSON、文本和二进制 Body 只序列化一次，`Content-MD5` 始终针对实际发送的字节计算。
- HTTPS 默认使用系统 CA 校验证书；支持自定义 CA 和双向 TLS 客户端证书。
- 网络错误不再被静默吞掉；HTTP 错误可通过 `raise_for_status()` 显式处理。
- `sign()` 与网络 I/O 分离，便于对照网关返回的 `X-Ca-Error-Message` 排查签名。
- 无运行时第三方依赖。
- 支持 Python 3.10–3.14。
- 兼容历史导入路径 `aliyun.api.gateway.sdk...` 和 `com.aliyun.api.gateway.sdk...`。

## 安装

正式发布后：

```bash
python -m pip install aliyun-api-gateway-python
```

从源码开发安装：

```bash
python -m pip install -e ".[dev]"
```

## 快速开始

不要把 APP Secret 写入代码或提交到 Git。下面的示例从环境变量读取凭据：

```python
import os

from aliyun_api_gateway import ApiGatewayClient, ApiGatewayRequest

client = ApiGatewayClient(
    "https://example-cn-hangzhou.alicloudapi.com",
    app_key=os.environ["ALIYUN_API_GATEWAY_APP_KEY"],
    app_secret=os.environ["ALIYUN_API_GATEWAY_APP_SECRET"],
    timeout=30,
)

request = ApiGatewayRequest(
    "POST",
    "/v1/invoices",
    query={"tenant": "华东 区"},
    json_body={"invoiceNo": "INV-001", "amount": 100.5},
)

response = client.send(request)
response.raise_for_status()
print(response.json())
```

### Form 请求

```python
request = ApiGatewayRequest(
    "POST",
    "/v1/token",
    form={"username": "xiaoming", "password": "example-only"},
)
response = client.send(request)
```

### 重复 Query 参数

使用键值对序列可以保留重复键。所有值都会发送，但网关协议只使用第一个值参与签名：

```python
request = ApiGatewayRequest(
    "GET",
    "/v1/search",
    query=(("tag", "first"), ("tag", "second"), ("empty", "")),
)
```

### 自定义签名 Header

所有 `X-Ca-*` Header（签名本身除外）会自动参与签名。普通业务 Header 必须显式列出：

```python
request = ApiGatewayRequest(
    "GET",
    "/v1/orders",
    headers={"X-Business-Id": "42", "X-Ca-Stage": "RELEASE"},
    additional_signed_headers=("X-Business-Id",),
)
```

### 只生成签名，不发送

```python
signed = client.sign(request)
print(signed.string_to_sign)
print(signed.canonical_resource)
```

`StringToSign` 不含 APP Secret，但可能包含业务参数和 Body 摘要；不要把它长期写入生产日志。

## HTTP 与异常处理

`client.send()` 对 4xx/5xx 响应仍返回 `ApiGatewayResponse`，便于读取网关排错 Header：

```python
response = client.send(request)
if not response.ok:
    print(response.get_header("X-Ca-Error-Message"))
response.raise_for_status()
```

DNS、连接、TLS 和超时错误会抛出 `ApiGatewayTransportError`。本包默认不自动重试，因为对非幂等请求的隐式重试可能导致重复写入。

## 旧代码兼容

原 PyPI 包的导入方式可以继续使用：

```python
from aliyun.api.gateway.sdk import client
from aliyun.api.gateway.sdk.common import constant
from aliyun.api.gateway.sdk.http import request

cli = client.DefaultClient(app_key="...", app_secret="...")
req = request.Request(
    host="https://example.com",
    protocol=constant.HTTPS,
    url="/v1/items",
    method=constant.POST,
    time_out=30_000,  # 旧 API 的单位为毫秒
)
req.set_body({"name": "测试"})
req.set_content_type(constant.CONTENT_TYPE_JSON)
status, headers, body = cli.execute(req)
```

你的历史项目如果使用 `from com.aliyun...`，同样可以运行。新项目建议使用顶层 `aliyun_api_gateway` API；兼容层只用于平滑迁移。详细差异见 [MIGRATION.md](MIGRATION.md)。

## 开发

```bash
python -m venv .venv
# Windows: .venv\Scripts\activate
# Linux/macOS: source .venv/bin/activate
python -m pip install -e ".[dev]"
ruff check .
ruff format --check .
mypy
pytest --cov
python -m build
twine check dist/*
```

## 项目来源与许可证

本项目基于阿里云的
[`aliyun/api-gateway-demo-sign-python`](https://github.com/aliyun/api-gateway-demo-sign-python)
及社区打包项目
[`coco369/aliyun-api-gateway-python`](https://github.com/coco369/aliyun-api-gateway-python)
重写和扩展。原始工作及本项目均按 Apache License 2.0 分发，归属与修改说明见
[NOTICE](NOTICE)。

---

## English summary

This is an **unofficial** Python 3 client for Alibaba Cloud Traditional API Gateway APP
signature authentication. It provides deterministic request serialization, HMAC-SHA256/SHA1,
verified TLS by default, typed response/error models, a zero-dependency runtime, comprehensive
tests, and compatibility shims for the historical `aliyun.api...` and `com.aliyun...` imports.
See the Chinese documentation above for the complete API guide.
