使用attrs支持sanic的openapi/swagger

sanic-attrs的Python项目详细描述


SANIC和ATTS支持Swagger 2.0/OpenAPI

使用以下功能增强您的应用程序:

sanicsanic

注意:这是sanic openapi实现的一个分支,来自@channelcat,我非常喜欢它,但它缺少我想要的一些功能(我还使用了第三方库(attrs)作为建模输入/输出模型类的默认值。

pypipypi

超快介绍

给你的sanic api一个ui和openapi文档,一切都是免费的!

example swagger ui

安装

注意:由于这个fork是我的必需品,我想实现的许多功能仍然不可用,因此这个库的状态是pre alpha!另外,不要尝试examples文件夹,它还没有转换!我真丢脸……

pip install sanic-attrs

添加openapi和swagger ui:

fromsanic_attrsimportswagger_blueprint,openapi_blueprintapp.blueprint(openapi_blueprint)app.blueprint(swagger_blueprint)

现在,您将在url/swagger处拥有一个swagger用户界面。你的路线将根据蓝图自动分类。这是默认用法,但可以看到更高级的用法。继续读下去!

注意:swagger_blueprint非常棒,但有时您不希望它以任何原因(安全性等)打开,因此您只能在运行debug=true时才能使用它。我就是这么用的:微笑:

输入

当然,由于sanic attrs是基于attrs的,而python的目标版本是3.5+,因此模型的大多数类型定义都将完全使用python类型,要么是全局类型,要么是来自typing库。此外,还支持枚举!:火花:

以下是支持的类型(到目前为止):

  • int
  • 浮点数
  • str
  • bool
  • 日期
  • 日期时间
  • 字节
  • 键入.any
  • 键入.collection
  • 键入.dict
  • 键入.iterable
  • 键入.list
  • 键入.mapping
  • 键入。可选
  • 键入.sequence
  • 键入.set
  • 键入.union

关于列表dict的说明:请使用typing.list和typing.dict进行此操作。

用法

使用简单的修饰符记录路由

fromsanic_attrsimportdoc@app.get("/user/<user_id:int>")@doc.summary("Fetches a user by ID")@doc.produces(SomeOutputModel)asyncdefget_user(request,user_id):...@app.post("/user")@doc.summary("Creates a user")@doc.consumes(SomeInputModel,location="body")asyncdefcreate_user(request):...

为输入/输出建模

是的,在此版本中,您需要具有描述性:wink:

importtypingfromsanic_attrsimportdocclassCar(doc.Model):make:str=doc.field(description="Who made the car")model:str=doc.field(description="Type of car. This will vary by make")year:int=doc.field(description="4-digit year of the car",required=False)classGarage(doc.Model):spaces:int=doc.field(description="How many cars can fit in the garage")cars:typing.List[Car]=doc.field(description="All cars in the garage")@app.get("/garage")@doc.summary("Gets the whole garage")@doc.produces(Garage)asyncdefget_garage(request):returnjson({"spaces":2,"cars":[{"make":"Nissan","model":"370Z"}]})

高级用法

硅ncedoc.modeldoc.field对于@attr.sdecorator和attr.ib函数来说只是语法糖,您可以使用这些提供的类和方法来表示模型,或者在模型中使用vanillaattr。下面是一个复杂的示例,它显示了一个混合模型:

fromenumimportEnum,IntEnumfromtypingimport(Any,Collection,Dict,Iterable,List,Mapping,Optional,Sequence,Set,Union)importattrfromsanic_attrsimportdocclassPlatformEnum(str,Enum):XBOX1="XBOX1"PLAYSTATION4="PLAYSTATION4"PC="PC"classLanguageEnum(IntEnum):ENGLISH=1JAPANESE=2SPANISH=3GERMAN=4PORTUGUESE=5classSomething(doc.Model):some_name:str=doc.field(description="Something name")@attr.sclassAnotherSomething:another_name:str=attr.ib(metadata={"description":"Another field"})classGame(doc.Model):name:str=doc.field(description="The name of the game")platform:PlatformEnum=doc.field(description="Which platform it runs on")score:float=doc.field(description="The average score of the game")resolution_tested:str=doc.field(description="The resolution which the game was tested")genre:List[str]=doc.field(description="One or more genres this game is part of")genre_extra:Sequence[str]=doc.field(description="One or more genres this game is part of")rating:Dict[str,float]=doc.field(description="Ratings given on each country")rating_outside:Mapping[str,float]=doc.field(description="Ratings given on each country")screenshots:Set[bytes]=doc.field(description="Screenshots of the game")screenshots_extra:Collection[bytes]=doc.field(description="Screenshots of the game")players:Iterable[str]=doc.field(description="Some of the notorious players of this game")review_link:Optional[str]=doc.field(description="The link of the game review (if exists)")junk:Union[str,bytes]=doc.field(description="This should be strange")more_junk:Any=doc.field(description="The more junk field")language:LanguageEnum=doc.field(description="The language of the game")something:List[Something]=doc.field(description="Something to go along the game")another:AnotherSomething=doc.field(description="Another something to go along the game")

关于输入提示或类型参数的说明

您可能已经注意到,在上面的示例中,所有变量都是使用键入提示创建的。虽然这有点有趣,但您可能还想使用type参数,这是attr包提供的,而且sanic attrs完全可以。因此,我们的游戏类更希望如下所示:

classGame(doc.Model):name=doc.field(type=str,description="The name of the game")platform=doc.field(type=PlatformEnum,description="Which platform it runs on")score=doc.field(type=float,description="The average score of the game")resolution_tested=doc.field(type=str,description="The resolution which the game was tested")genre=doc.field(type=List[str],description="One or more genres this game is part of")genre_extra=doc.field(type=Sequence[str],description="One or more genres this game is part of")rating=doc.field(type=Dict[str,float],description="Ratings given on each country")rating_outside=doc.field(type=Mapping[str,float],description="Ratings given on each country")screenshots=doc.field(type=Set[bytes],description="Screenshots of the game")screenshots_extra=doc.field(type=Collection[bytes],description="Screenshots of the game")players=doc.field(type=Iterable[str],description="Some of the notorious players of this game")review_link=doc.field(type=Optional[str],description="The link of the game review (if exists)")junk=doc.field(type=Union[str,bytes],description="This should be strange")more_junk=doc.field(type=Any,description="The more junk field")language=doc.field(type=LanguageEnum,description="The language of the game")something=doc.field(type=List[Something],description="Something to go along the game")another=doc.field(type=AnotherSomething,description="Another something to go along the game")

关于attrs

的许多功能的说明

attrs中有很多特性在声明模型时非常方便,例如验证器、工厂等。对于这个版本,计划对验证器使用一些语法糖(因为大多数规则可以提供给doc.field)。由于没有足够的时间对模型进行实际测试(到目前为止),因此现在不鼓励在声明模型时使用其他功能,如factories(或在本项目的生命周期内,尚未决定):困惑:

动态输入模型解析

在sanic attrs中有一些惊喜。假设您已经声明了您的模型、端点,您还必须接受request.json并将其加载为您的模型?这似乎不对…fortunally编写了一个小型中间件来处理这些情况:wink:

要启用动态输入模型解析,您只需将blueprint添加到您的sanic应用程序中,并直接从请求中使用input-obj关键字访问对象:

fromsanic_attrsimportparser_blueprint# ...app.blueprint(parser_blueprint)# ...@app.post("/game",strict_slashes=True)@doc.summary("Inserts the game data into the database")@doc.response("200","Game inserted successfuly",model=SuccessOutput)@doc.response("403","The user couldn't insert game to application",model=ErrorOutput)@doc.consumes(Game,location="body",content_type="application/json")@doc.produces(SuccessOutput)asyncdefinsert_game(request):my_object=request["input_obj"]assertisinstance(my_object,Game)# your logic here

注意:没有要处理(真正的)损坏数据的验证。如果在填充模型时发生异常,您将发现您的input_obj关键字将是none,以及另一个包含给定异常(如果有)的键input_exc。如果要进一步自定义此行为,以便不必在每个请求中都检查none,则可以在将parser\u blueprint添加到app实例之后添加自己的中间件,如下所示:

fromsanic.responseimportjsonfromsanic_attrsimportparser_blueprint# ...app.blueprint(parser_blueprint)# ...@app.middleware("request")asyncdefcheck_if_input_is_none(request):if"input_obj"inrequest:ifrequest["input_obj"]isNone:# error handling herereturnjson({"error":request["input_exc"].args[0]},500)

动态输出模型序列化

为了简单起见,还可以处理attrs对象的直接返回,而不必创建字典,然后序列化或调用sanic.responses.json,尽管这正是在幕后运行的:

fromsanic_attrsimportresponse# ...@app.get("/game",strict_slashes=True)@doc.summary("Gets the most played game in our database")@doc.response("200","Game data",model=Game)@doc.response("403","The user can't access this endpoint",model=ErrorOutput)@doc.produces(Game)asyncdefget_game(request):game=Game(name="Cities: Skylines",platform="PC",score=9.0,resolution_tested="1920x1080",genre=["Simulators","City Building"],rating={"IGN":8.5,"Gamespot":8.0,"Steam":4.5},players=["Flux","strictoaster"],language=1)returnresponse.model(game)# <--- the game instance, to be further serialized

注意:记住要创建可以将其所有值序列化为json的模型:+1:

配置其他所有内容

pip install sanic-attrs
0

类型尚未可用

这些是当前版本中的键入中没有的类型(带有一些注释,以便我可以记住以后要做什么(如果需要)):

  • 抽象集-是否像集合?
  • anystr-这主要类似于可选的[str]或只是str?
  • 异步上下文管理器-我认为不是变量
  • 异步生成器-我认为不是变量
  • 异步的-我认为不是变量
  • 异步迭代器-我认为不是变量
  • 可等待的-我认为不是变量
  • binaryio-嗯,我不知道……可能是字节?
  • byte string-可以是字节,因为openapi是{"type":"string","format":"byte"}
  • ct_co-我都不知道这是什么…
  • 可调用-不是VARiabel
  • 可调用元-不是变量
  • 链表-不是变量(?)
  • classvar-通用…
  • 容器-通用
  • 上下文管理器-不是变量
  • 协同程序-不是变量
  • 计数器-不是变量
  • defaultdict-也许像dict?
  • 像列表一样?
  • frozenset-a"仅查看列表"?
  • 生成器-不是变量
  • 通用的-不可能-或任何?
  • 可哈希-哈希映射?
  • io-hmmm,来自docs:"textio和binaryio的通用基类",所以…
  • 项目视图-什么是项目?它继承自抽象集…来自文档:"一个集合是一个有限的、可迭代的容器。"
  • 迭代器-不是变量
  • kt-泛型
  • 键视图-dict"readonly"?
  • 映射视图-dict"只读"?
  • 匹配-一般(我认为)
  • 方法描述符类型-不是变量
  • MethodWrapperType-不是变量
  • 可变映射-映射基类,docs:"泛型类型的抽象基类。"
  • 可变序列-同上,但对于序列
  • mutableset-同上,但对于set
  • namedtuple-这里该怎么办?namedtuple只是一个带有变量的对象,变量可以是任何东西
  • namedtuplemeta-namedtuple的基类
  • newtype-不是变量/泛型?
  • noreturn-不是变量
  • 模式-通用
  • 可逆的
  • 大小-一般
  • supportsabs-不是变量
  • 支持字节-不是变量
  • 支持示例-不是变量
  • supportsfloat-不是变量
  • 支持t-不是变量
  • supportsround-不是变量
  • t-通用
  • 键入检查-????
  • t_co-????
  • t_contra-????
  • text-如果创建了str对象,则返回该对象,所以我将使用str还是映射它?
  • textio-缓冲区,如字节…绘制地图?
  • 元组-好吧…类似元组的列表还是类似元组的元组[int,str,float]?
  • 元组-元组的基类
  • 类型-泛型
  • typevar-泛型
  • 键入meta-泛型

如果有任何遗漏或需要,请填写一个问题或提供一个公关。公关是最受欢迎的:笑脸:

待办事项

  • []属性处理必需的字段(在openapi对象模式中)
  • []使用类型提示来记录函数的返回(作为输出模式/模型)
  • []适当的测试
  • []增加用例
  • []了解我是否可以在不调用路由器的情况下获得请求模型
  • []文档

许可证

麻省理工学院,同sanic openapi

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
java过滤会导致JPQL查询   java中的部署控制台   java检索列表内部映射的值   从java堆收集旧对象的垃圾收集   java在运行时向JTable追加新行   很简单的构造函数问题,我就是搞不懂。JAVA   基于IVONA-TTS的java文本语音识别   java将数据保存到数组更高效的代码   java在文件中逐字读取   java在Eclipse中运行两个安卓设备实例   Java兼容性编译模式   java我可以使用SendGrid向多个用户发送电子邮件吗?   java Android:FusedLocationProvider,每隔几秒钟获取一次位置   java在具有并行性限制的惰性流上应用方法   多线程如何使多线程应用程序在java中分布?   java将自定义数据源添加到Jaspersoft Studio   java如何对方法执行post请求返回整数