add more...
This commit is contained in:
parent
731c5d5bea
commit
c1eb1c06ea
167
README.md
167
README.md
@ -340,54 +340,203 @@ grp.GET("", []fizz.OperationOption{
|
||||
}, tonic.Handler(ListFruits, 200))
|
||||
```
|
||||
|
||||
##### 4. main函数将以上逻辑串起来
|
||||
|
||||
```go
|
||||
func main() {
|
||||
router, err := NewRouter()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
srv := &http.Server{
|
||||
Addr: ":4242",
|
||||
Handler: router,
|
||||
}
|
||||
_ = srv.ListenAndServe()
|
||||
}
|
||||
|
||||
// NewRouter returns a new router for the
|
||||
// Pet Store.
|
||||
func NewRouter() (*fizz.Fizz, error) {
|
||||
engine := gin.New()
|
||||
engine.Use(cors.Default())
|
||||
|
||||
fizz := fizz.NewFromEngine(engine)
|
||||
|
||||
// Override type names.
|
||||
// fizz.Generator().OverrideTypeName(reflect.TypeOf(Fruit{}), "SweetFruit")
|
||||
|
||||
// Initialize the informations of
|
||||
// the API that will be served with
|
||||
// the specification.
|
||||
infos := &openapi.Info{
|
||||
Title: "Fruits Market",
|
||||
Description: `This is a sample Fruits market server.`,
|
||||
Version: "1.0.0",
|
||||
}
|
||||
// Create a new route that serve the OpenAPI spec.
|
||||
fizz.GET("/openapi.json", nil, fizz.OpenAPI(infos, "json"))
|
||||
fizz.GET("/doc", nil, func(ctx *gin.Context) {
|
||||
ctx.Status(200)
|
||||
ctx.Header("Content-Type", "text/html")
|
||||
ctx.String(200, rapidoc)
|
||||
})
|
||||
|
||||
// Setup routes.
|
||||
routes(fizz.Group("/market", "market", "Your daily dose of freshness"))
|
||||
|
||||
if len(fizz.Errors()) != 0 {
|
||||
return nil, fmt.Errorf("fizz errors: %v", fizz.Errors())
|
||||
}
|
||||
return fizz, nil
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. OAS的实现方式
|
||||
|
||||
比较核心的地方有两个
|
||||
|
||||
1. tonic.Handler 该方法使用reflect动态判断到传入的第一个参数,即第二步描述的Handler,根绝参数长度判断有无入参,根据返回值的长度判断有无返回值。如果有,则继续用reflect解析传入的struct,根据struct的tag描述出来各数据模型。如此的一个过程则基本上已经把一个接口的入参、返回能描述清楚。[关键代码](https://github.com/loopfz/gadgeto/blob/c4f8b2f64586099b9b281cbe99aa2f8b05e7d8b0/tonic/handler.go#L46)
|
||||
1. tonic.Handler 该方法使用reflect动态判断到传入的第一个参数,即第二步描述的Handler,根绝参数长度判断有无入参,根据返回值的长度判断有无返回值。如果有,则继续用reflect解析传入的struct,根据struct的tag描述出来各数据模型。如此的一个过程则基本上已经把一个接口的入参、返回能描述清楚。[关键代码](https://github.com/loopfz/gadgeto/blob/c4f8b2f64586099b9b281cbe99aa2f8b05e7d8b0/tonic/handler.go#L308)
|
||||
|
||||
另外,由于已经拿到了Handler的所有信息,包括入参的具体类型信息。所以还为我们提供了一个非常有用的额外能力:通过tag的描述,直接把各个位置的Parameters和RequestBody的值取到,然后把取到的Value设置到函数入参的指针里。所以我们可以在Handler中直接使用第二个参数中的值,无需自己再另行反序列化或者绑定操作。[关键代码](https://github.com/loopfz/gadgeto/blob/c4f8b2f64586099b9b281cbe99aa2f8b05e7d8b0/tonic/handler.go#L50)
|
||||
2. fizz.OperationOption 库提供了一系列的OperationOption,可以为第一步的信息增加更多的对OpenAPI的支持。再配合上二次封装的Router,即可获取到整个OAS请求的全部信息。
|
||||
|
||||
#### 3. 优点
|
||||
|
||||
WIP
|
||||
1. 开发流程很直观,比起纯粹的Gin项目开发额外需要改造的东西并不多,提供的有限的几个API上手也比较快
|
||||
2. 可以完全利用Gin提供的所有API,不失灵活性。
|
||||
3. 提供了 go-validator 和 一部分json_schema规则的校验。业务逻辑里不必再写取参数、参数校验部分的冗余代码,可以直接处理业务逻辑,且和接口文档严格保持一致。
|
||||
|
||||
#### 4. 缺点
|
||||
|
||||
WIP
|
||||
1. 知名度低,小众
|
||||
2. 因为struct中字段的各种描述信息只能在tag中描述,会导致tag写的很长,比较丑
|
||||
3. 参考代码信息,json_schema部分的支持不够完全,有些部分与go-validator共用一些字段可能会导致混乱
|
||||
4. 框架绑定死了,基本不能解耦
|
||||
|
||||
### 5. Django REST Framework
|
||||
|
||||
Django项目是Python语言中最知名的Web框架,在2005年被开源以来,用户量非常庞大,目前Django社区依然非常活跃。
|
||||
|
||||
Django REST Framework(以下简称DRF)是著名开源组织[encode](https://www.encode.io)在2011年专门面向Django用户开发REST API的需求而诞生的一套工具。
|
||||
|
||||
#### 1. 开发过程
|
||||
|
||||
创建python虚拟环境、Django项目初始化相关的流程由于比较冗长,涉及到的上下文信息比较多,这里就不再赘述,感兴趣可以去看[Django官方文档](https://docs.djangoproject.com/en/4.0/)和[Django REST Framework官方文档](https://www.django-rest-framework.org),我们现在只关注他是怎么描述Schema的。
|
||||
|
||||
在DRF中,描述数据model的结构叫做Serializer。官方提供了非常多的内置的序列化方法,包括各种可接受的数据类型,然后用户在定义Serializer的时候使用kwargs为各个参数字段添加约束信息。serializer.Serializer类本身提供了 request → serializer → response 数据流的转化能力。用户只需要继承这些能力即可。例如
|
||||
|
||||
```python
|
||||
class SnippetSerializer(serializers.Serializer):
|
||||
# int类型、只读、默认required=True
|
||||
id = serializers.IntegerField(read_only=True)
|
||||
# str类型、允许为空、最大长度限定100
|
||||
title = serializers.CharField(required=False, allow_blank=True, max_length=100)
|
||||
# bool类型、非必填字段
|
||||
linenos = serializers.BooleanField(required=False)
|
||||
# 限定入参的值只能是提供的枚举中的值、并指定了默认值
|
||||
language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')
|
||||
```
|
||||
|
||||
也提供了直接继承Django Model的能力
|
||||
|
||||
```python
|
||||
class Snippet(models.Model):
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
title = models.CharField(max_length=100, blank=True, default='')
|
||||
code = models.TextField()
|
||||
linenos = models.BooleanField(default=False)
|
||||
language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
|
||||
style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)
|
||||
|
||||
class Meta:
|
||||
ordering = ['created']
|
||||
|
||||
|
||||
class SnippetSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Snippet
|
||||
fields = "__all__"
|
||||
```
|
||||
|
||||
#### 2. OAS的实现方式
|
||||
|
||||
WIP
|
||||
如上面的例子,基于Serializer的描述可以获取到各种类型的request body信息,加上django-filter和django的router可以得到完整的API描述信息。
|
||||
|
||||
#### 3. 优点
|
||||
|
||||
WIP
|
||||
1. 因为Serializer 与Django-ORM 的Model 可以结合在一起,大部分情况下,数据模型只需要定义一次,所有的地方都能得到约束,行为统一
|
||||
|
||||
如:上文Snippet类的title字段设置为max_length=100,那么接口入参的时候会校验长度,生成的文档中该参数也有对应的最大值,还有默认情况下数据库定义也会是varchar(20)
|
||||
2. 经过了10年以上的考验,基本上碰得到的问题都能够找得到答案。本身也提供了各种各样丰富的API让用户可以完成各种场景的需求。
|
||||
|
||||
光论开发效率的话,可能是笔者知道的开发效率最高的web框架了。简单场景下,从零到一实现一张表的所有增删改查接口,一共只需要不到十行代码。
|
||||
|
||||
#### 4. 缺点
|
||||
|
||||
WIP
|
||||
1. Python语言本身导致的性能低下。虽然Django提供了几乎整个WEB周期所需要的全部工具,开发体验一流,但是仍然不适用于要求性能的场景,Django的主战场可能还是CMS或者是用于一些产品的MVP版本快速试错。
|
||||
|
||||
2. 隐晦性的声明太多,行为不够直观。历史包袱过重,未来发展的空间不大。
|
||||
|
||||
### 6. FastAPI
|
||||
|
||||
[FastAPI](https://fastapi.tiangolo.com/)可以算是近两年python web框架中最炙手可热的明星项目了,上线仅3年多就已收获40000多star。得益于python3.5版本以来带来的async await异步关键字,和各种高性能的asyncio runtime的出现,性能上已经比传统的Flask等类似的微框架的表现有了极大的改观。
|
||||
|
||||
#### 1. 开发过程
|
||||
|
||||
##### 1. 编写业务逻辑main.py
|
||||
|
||||
```python
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
id: Optional[int] = None
|
||||
name: str = Field(description="The name of the item", regex="^[a-zA-Z0-9]*$")
|
||||
price: float
|
||||
is_offer: Optional[bool] = None
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"Hello": "World"}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: Optional[str] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
|
||||
|
||||
@app.post("/items")
|
||||
def create_item(item: Item) -> Item:
|
||||
return item
|
||||
|
||||
```
|
||||
|
||||
##### 2. 使用uvicorn启动服务
|
||||
|
||||
```bash
|
||||
uvicorn main:app
|
||||
```
|
||||
|
||||
#### 2. OAS的实现方式
|
||||
|
||||
WIP
|
||||
通过Python提供的类型注解功能,程序在启动的时候,就能够得到所有入参内容、和返回结果,包括他们的类型信息。开发体验更像是直接写好处理逻辑的函数,然后框架把这些函数按照一定的规则用HTTP的形式实现。
|
||||
|
||||
#### 3. 优点
|
||||
|
||||
WIP
|
||||
1. 所见即所得,开发接口的时候非常直观,效率很高,而且提供了十分丰富的其他功能使得参数可以复用。
|
||||
2. 设计清晰易懂,用户层面不需要知道太多细节,不强制添加对应的类型注解(当然对应的也就无法得到足够的文档信息)
|
||||
3. 可以直接利用python语言提供的强大生态资源。
|
||||
|
||||
#### 4. 缺点
|
||||
|
||||
WIP
|
||||
1. 除了语言与公司技术栈不太相符外,找不到明显的缺点。
|
||||
|
||||
### 7. Poem
|
||||
|
||||
|
BIN
python/fastapi_example/__pycache__/main.cpython-310.pyc
Normal file
BIN
python/fastapi_example/__pycache__/main.cpython-310.pyc
Normal file
Binary file not shown.
28
python/fastapi_example/main.py
Normal file
28
python/fastapi_example/main.py
Normal file
@ -0,0 +1,28 @@
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
id: Optional[int] = None
|
||||
name: str = Field(description="The name of the item", regex="^[a-zA-Z0-9]*$")
|
||||
price: float
|
||||
is_offer: Optional[bool] = None
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"Hello": "World"}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: Optional[str] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
|
||||
|
||||
@app.post("/items")
|
||||
def create_item(item: Item) -> Item:
|
||||
return item
|
229
python/fastapi_example/poetry.lock
generated
Normal file
229
python/fastapi_example/poetry.lock
generated
Normal file
@ -0,0 +1,229 @@
|
||||
[[package]]
|
||||
name = "anyio"
|
||||
version = "3.5.0"
|
||||
description = "High level compatibility layer for multiple asynchronous event loop implementations"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6.2"
|
||||
|
||||
[package.dependencies]
|
||||
idna = ">=2.8"
|
||||
sniffio = ">=1.1"
|
||||
|
||||
[package.extras]
|
||||
doc = ["packaging", "sphinx-rtd-theme", "sphinx-autodoc-typehints (>=1.2.0)"]
|
||||
test = ["coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "pytest (>=6.0)", "pytest-mock (>=3.6.1)", "trustme", "contextlib2", "uvloop (<0.15)", "mock (>=4)", "uvloop (>=0.15)"]
|
||||
trio = ["trio (>=0.16)"]
|
||||
|
||||
[[package]]
|
||||
name = "asgiref"
|
||||
version = "3.5.0"
|
||||
description = "ASGI specs, helper code, and adapters"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.extras]
|
||||
tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.0.4"
|
||||
description = "Composable command line interface toolkit"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.4"
|
||||
description = "Cross-platform colored terminal text."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "fastapi"
|
||||
version = "0.75.0"
|
||||
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6.1"
|
||||
|
||||
[package.dependencies]
|
||||
pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0"
|
||||
starlette = "0.17.1"
|
||||
|
||||
[package.extras]
|
||||
all = ["requests (>=2.24.0,<3.0.0)", "jinja2 (>=2.11.2,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "itsdangerous (>=1.1.0,<3.0.0)", "pyyaml (>=5.3.1,<6.0.0)", "ujson (>=4.0.1,<5.0.0)", "orjson (>=3.2.1,<4.0.0)", "email_validator (>=1.1.1,<2.0.0)", "uvicorn[standard] (>=0.12.0,<0.16.0)"]
|
||||
dev = ["python-jose[cryptography] (>=3.3.0,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "autoflake (>=1.4.0,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.16.0)"]
|
||||
doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "typer-cli (>=0.0.12,<0.0.13)", "pyyaml (>=5.3.1,<6.0.0)"]
|
||||
test = ["pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "mypy (==0.910)", "flake8 (>=3.8.3,<4.0.0)", "black (==21.9b0)", "isort (>=5.0.6,<6.0.0)", "requests (>=2.24.0,<3.0.0)", "httpx (>=0.14.0,<0.19.0)", "email_validator (>=1.1.1,<2.0.0)", "sqlalchemy (>=1.3.18,<1.5.0)", "peewee (>=3.13.3,<4.0.0)", "databases[sqlite] (>=0.3.2,<0.6.0)", "orjson (>=3.2.1,<4.0.0)", "ujson (>=4.0.1,<5.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "flask (>=1.1.2,<3.0.0)", "anyio[trio] (>=3.2.1,<4.0.0)", "types-ujson (==0.1.1)", "types-orjson (==3.6.0)", "types-dataclasses (==0.1.7)"]
|
||||
|
||||
[[package]]
|
||||
name = "h11"
|
||||
version = "0.13.0"
|
||||
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.3"
|
||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "1.9.0"
|
||||
description = "Data validation and settings management using python 3.6 type hinting"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6.1"
|
||||
|
||||
[package.dependencies]
|
||||
typing-extensions = ">=3.7.4.3"
|
||||
|
||||
[package.extras]
|
||||
dotenv = ["python-dotenv (>=0.10.4)"]
|
||||
email = ["email-validator (>=1.0.3)"]
|
||||
|
||||
[[package]]
|
||||
name = "sniffio"
|
||||
version = "1.2.0"
|
||||
description = "Sniff out which async library your code is running under"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[[package]]
|
||||
name = "starlette"
|
||||
version = "0.17.1"
|
||||
description = "The little ASGI library that shines."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
anyio = ">=3.0.0,<4"
|
||||
|
||||
[package.extras]
|
||||
full = ["itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.1.1"
|
||||
description = "Backported and Experimental Type Hints for Python 3.6+"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "uvicorn"
|
||||
version = "0.17.5"
|
||||
description = "The lightning-fast ASGI server."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
asgiref = ">=3.4.0"
|
||||
click = ">=7.0"
|
||||
h11 = ">=0.8"
|
||||
|
||||
[package.extras]
|
||||
standard = ["websockets (>=10.0)", "httptools (>=0.2.0,<0.4.0)", "watchgod (>=0.6)", "python-dotenv (>=0.13)", "PyYAML (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "colorama (>=0.4)"]
|
||||
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "37101b26a19ded2c6be0c1faa5eb281b94f6f6a32e3039b15c16f23f2c285956"
|
||||
|
||||
[metadata.files]
|
||||
anyio = [
|
||||
{file = "anyio-3.5.0-py3-none-any.whl", hash = "sha256:b5fa16c5ff93fa1046f2eeb5bbff2dad4d3514d6cda61d02816dba34fa8c3c2e"},
|
||||
{file = "anyio-3.5.0.tar.gz", hash = "sha256:a0aeffe2fb1fdf374a8e4b471444f0f3ac4fb9f5a5b542b48824475e0042a5a6"},
|
||||
]
|
||||
asgiref = [
|
||||
{file = "asgiref-3.5.0-py3-none-any.whl", hash = "sha256:88d59c13d634dcffe0510be048210188edd79aeccb6a6c9028cdad6f31d730a9"},
|
||||
{file = "asgiref-3.5.0.tar.gz", hash = "sha256:2f8abc20f7248433085eda803936d98992f1343ddb022065779f37c5da0181d0"},
|
||||
]
|
||||
click = [
|
||||
{file = "click-8.0.4-py3-none-any.whl", hash = "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1"},
|
||||
{file = "click-8.0.4.tar.gz", hash = "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"},
|
||||
]
|
||||
colorama = [
|
||||
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
|
||||
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
|
||||
]
|
||||
fastapi = [
|
||||
{file = "fastapi-0.75.0-py3-none-any.whl", hash = "sha256:43d12891b78fc497a50623e9c7c24640c569489f060acd9ce2c4902080487a93"},
|
||||
{file = "fastapi-0.75.0.tar.gz", hash = "sha256:124774ce4cb3322841965f559669b233a0b8d343ea24fdd8b293253c077220d7"},
|
||||
]
|
||||
h11 = [
|
||||
{file = "h11-0.13.0-py3-none-any.whl", hash = "sha256:8ddd78563b633ca55346c8cd41ec0af27d3c79931828beffb46ce70a379e7442"},
|
||||
{file = "h11-0.13.0.tar.gz", hash = "sha256:70813c1135087a248a4d38cc0e1a0181ffab2188141a93eaf567940c3957ff06"},
|
||||
]
|
||||
idna = [
|
||||
{file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
|
||||
{file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
|
||||
]
|
||||
pydantic = [
|
||||
{file = "pydantic-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cb23bcc093697cdea2708baae4f9ba0e972960a835af22560f6ae4e7e47d33f5"},
|
||||
{file = "pydantic-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1d5278bd9f0eee04a44c712982343103bba63507480bfd2fc2790fa70cd64cf4"},
|
||||
{file = "pydantic-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab624700dc145aa809e6f3ec93fb8e7d0f99d9023b713f6a953637429b437d37"},
|
||||
{file = "pydantic-1.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c8d7da6f1c1049eefb718d43d99ad73100c958a5367d30b9321b092771e96c25"},
|
||||
{file = "pydantic-1.9.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3c3b035103bd4e2e4a28da9da7ef2fa47b00ee4a9cf4f1a735214c1bcd05e0f6"},
|
||||
{file = "pydantic-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3011b975c973819883842c5ab925a4e4298dffccf7782c55ec3580ed17dc464c"},
|
||||
{file = "pydantic-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:086254884d10d3ba16da0588604ffdc5aab3f7f09557b998373e885c690dd398"},
|
||||
{file = "pydantic-1.9.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0fe476769acaa7fcddd17cadd172b156b53546ec3614a4d880e5d29ea5fbce65"},
|
||||
{file = "pydantic-1.9.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8e9dcf1ac499679aceedac7e7ca6d8641f0193c591a2d090282aaf8e9445a46"},
|
||||
{file = "pydantic-1.9.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1e4c28f30e767fd07f2ddc6f74f41f034d1dd6bc526cd59e63a82fe8bb9ef4c"},
|
||||
{file = "pydantic-1.9.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:c86229333cabaaa8c51cf971496f10318c4734cf7b641f08af0a6fbf17ca3054"},
|
||||
{file = "pydantic-1.9.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:c0727bda6e38144d464daec31dff936a82917f431d9c39c39c60a26567eae3ed"},
|
||||
{file = "pydantic-1.9.0-cp36-cp36m-win_amd64.whl", hash = "sha256:dee5ef83a76ac31ab0c78c10bd7d5437bfdb6358c95b91f1ba7ff7b76f9996a1"},
|
||||
{file = "pydantic-1.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9c9bdb3af48e242838f9f6e6127de9be7063aad17b32215ccc36a09c5cf1070"},
|
||||
{file = "pydantic-1.9.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ee7e3209db1e468341ef41fe263eb655f67f5c5a76c924044314e139a1103a2"},
|
||||
{file = "pydantic-1.9.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b6037175234850ffd094ca77bf60fb54b08b5b22bc85865331dd3bda7a02fa1"},
|
||||
{file = "pydantic-1.9.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b2571db88c636d862b35090ccf92bf24004393f85c8870a37f42d9f23d13e032"},
|
||||
{file = "pydantic-1.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8b5ac0f1c83d31b324e57a273da59197c83d1bb18171e512908fe5dc7278a1d6"},
|
||||
{file = "pydantic-1.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:bbbc94d0c94dd80b3340fc4f04fd4d701f4b038ebad72c39693c794fd3bc2d9d"},
|
||||
{file = "pydantic-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e0896200b6a40197405af18828da49f067c2fa1f821491bc8f5bde241ef3f7d7"},
|
||||
{file = "pydantic-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7bdfdadb5994b44bd5579cfa7c9b0e1b0e540c952d56f627eb227851cda9db77"},
|
||||
{file = "pydantic-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:574936363cd4b9eed8acdd6b80d0143162f2eb654d96cb3a8ee91d3e64bf4cf9"},
|
||||
{file = "pydantic-1.9.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c556695b699f648c58373b542534308922c46a1cda06ea47bc9ca45ef5b39ae6"},
|
||||
{file = "pydantic-1.9.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f947352c3434e8b937e3aa8f96f47bdfe6d92779e44bb3f41e4c213ba6a32145"},
|
||||
{file = "pydantic-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5e48ef4a8b8c066c4a31409d91d7ca372a774d0212da2787c0d32f8045b1e034"},
|
||||
{file = "pydantic-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:96f240bce182ca7fe045c76bcebfa0b0534a1bf402ed05914a6f1dadff91877f"},
|
||||
{file = "pydantic-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:815ddebb2792efd4bba5488bc8fde09c29e8ca3227d27cf1c6990fc830fd292b"},
|
||||
{file = "pydantic-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6c5b77947b9e85a54848343928b597b4f74fc364b70926b3c4441ff52620640c"},
|
||||
{file = "pydantic-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c68c3bc88dbda2a6805e9a142ce84782d3930f8fdd9655430d8576315ad97ce"},
|
||||
{file = "pydantic-1.9.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a79330f8571faf71bf93667d3ee054609816f10a259a109a0738dac983b23c3"},
|
||||
{file = "pydantic-1.9.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f5a64b64ddf4c99fe201ac2724daada8595ada0d102ab96d019c1555c2d6441d"},
|
||||
{file = "pydantic-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a733965f1a2b4090a5238d40d983dcd78f3ecea221c7af1497b845a9709c1721"},
|
||||
{file = "pydantic-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cc6a4cb8a118ffec2ca5fcb47afbacb4f16d0ab8b7350ddea5e8ef7bcc53a16"},
|
||||
{file = "pydantic-1.9.0-py3-none-any.whl", hash = "sha256:085ca1de245782e9b46cefcf99deecc67d418737a1fd3f6a4f511344b613a5b3"},
|
||||
{file = "pydantic-1.9.0.tar.gz", hash = "sha256:742645059757a56ecd886faf4ed2441b9c0cd406079c2b4bee51bcc3fbcd510a"},
|
||||
]
|
||||
sniffio = [
|
||||
{file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"},
|
||||
{file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"},
|
||||
]
|
||||
starlette = [
|
||||
{file = "starlette-0.17.1-py3-none-any.whl", hash = "sha256:26a18cbda5e6b651c964c12c88b36d9898481cd428ed6e063f5f29c418f73050"},
|
||||
{file = "starlette-0.17.1.tar.gz", hash = "sha256:57eab3cc975a28af62f6faec94d355a410634940f10b30d68d31cb5ec1b44ae8"},
|
||||
]
|
||||
typing-extensions = [
|
||||
{file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"},
|
||||
{file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"},
|
||||
]
|
||||
uvicorn = [
|
||||
{file = "uvicorn-0.17.5-py3-none-any.whl", hash = "sha256:8adddf629b79857b48b999ae1b14d6c92c95d4d7840bd86461f09bee75f1653e"},
|
||||
{file = "uvicorn-0.17.5.tar.gz", hash = "sha256:c04a9c069111489c324f427501b3840d306c6b91a77b00affc136a840a3f45f1"},
|
||||
]
|
16
python/fastapi_example/pyproject.toml
Normal file
16
python/fastapi_example/pyproject.toml
Normal file
@ -0,0 +1,16 @@
|
||||
[tool.poetry]
|
||||
name = "fastapi_example"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
authors = ["CaptainNEO <tianpengfei@rcrai.com>"]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.10"
|
||||
fastapi = "^0.75.0"
|
||||
uvicorn = "^0.17.5"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
Loading…
x
Reference in New Issue
Block a user