add more...

This commit is contained in:
CaptainNEO 2022-03-09 21:07:59 +08:00
parent 731c5d5bea
commit c1eb1c06ea
5 changed files with 431 additions and 9 deletions

167
README.md
View File

@ -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

View 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
View 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"},
]

View 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"