読者です 読者をやめる 読者になる 読者になる

pyramid-swagger-routerと一緒にtoyboxのswaggerのvalidationを使ってみる

python pyramid toybox swagger

相も変わらずtoyboxというリポジトリで作業していた。昨日試しに作ってみたswaggerのinput,outputのvalidationを行うものを昔作っていた pyramid-swagger-router と一緒に使ってみるようにしてみた。ココまで来るとそろそろswaggerに対するnormalizer + iteratorみたいなものが欲しくなってくる。

動くサンプルは ここ

使い方

だいたい以下のようなMakefileを使うという感じで察して欲しい(yapfのコメントアウトはASTのmergeに使うredbaronというpackageのバグを踏んでしまっていたので。yapfはgofmtのpython版みたいなもの)。

gen: spec router schema

# fmt:
#  yapf -r -i --style='{based_on_style: chromium, indent_width: 4}' app

spec:
  cp ../swagger/swagger.yml .
  gsed -i 's/operationId: /operationId: views./g' swagger.yml

router:
  pyramid-swagger-router --driver=./driver.py:Driver --logging=DEBUG ./swagger.yml app

schema:
  swagger-marshmallow-codegen --full --logging=DEBUG ./swagger.yml > app/schema.py

run:
  PYTHONPATH=. python app/__init__.py

このようにするとだいたい以下のようなコードが生成される。 使うswagger specは 昨日のものと同じもの

routes.py

def includeme_swagger_router(config):
    config.add_route('views', '/')
    config.add_route('views1', '/add')
    config.add_route('views2', '/dateadd')
    config.scan('.views')


def includeme(config):
    config.include(includeme_swagger_router)

views.py

from pyramid.view import (
    view_config
)
from . import (
    schema
)
from toybox.swagger import (
    withswagger
)


@view_config(decorator=withswagger(schema.Input, schema.Output), renderer='vjson', request_method='GET', route_name='views')
def hello(context, request):
    """

    request.GET:

        * 'name'  -  `{"type": "string", "example": "Ada", "default": "Friend"}`
    """
    return {}


@view_config(decorator=withswagger(schema.AddInput, schema.AddOutput), renderer='vjson', request_method='POST', route_name='views1')
def add(context, request):
    """


    request.json_body:

    ```
        {
          "type": "object",
          "properties": {
            "x": {
              "type": "integer"
            },
            "y": {
              "type": "integer"
            }
          },
          "required": [
            "x",
            "y"
          ]
        }
    ```
    """
    return {}


@view_config(decorator=withswagger(schema.DateaddInput, schema.DateaddOutput), renderer='vjson', request_method='POST', route_name='views2')
def dateadd(context, request):
    """


    request.json_body:

    ```
        {
          "type": "object",
          "properties": {
            "value": {
              "type": "string",
              "format": "date"
            },
            "addend": {
              "minimum": 1,
              "type": "integer"
            },
            "unit": {
              "type": "string",
              "default": "days",
              "enum": [
                "days",
                "minutes"
              ]
            }
          },
          "required": [
            "addend"
          ]
        }
    ```
    """
    return {}

init.pyは自分で書かなければダメ。

# -*- coding:utf-8 -*-
from toybox.simpleapi import run


def includeme(config):
    config.include("toybox.swagger")
    config.include("app.routes")
    config.scan("app.views")


if __name__ == "__main__":
    run.include(includeme)
    run(port=5001)

以下の事が自動で行われる

  • routingの定義
  • viewの内部のformat validation

もちろんroutingができるだけなので内部のviewは自分で実装してあげないとダメ。

diff --git a/examples/swagger2/app/views.py b/examples/swagger2/app/views.py
index a531b8c..85b5a96 100644
--- a/examples/swagger2/app/views.py
+++ b/examples/swagger2/app/views.py
@@ -1,3 +1,4 @@
+from datetime import datetime, timedelta
 from pyramid.view import (
     view_config
 )
@@ -17,7 +18,7 @@ def hello(context, request):
 
         * 'name'  -  `{"type": "string", "example": "Ada", "default": "Friend"}`
     """
-    return {}
+    return {'message': 'Welcome, {}!'.format(request.GET["name"])}
 
 
 @view_config(decorator=withswagger(schema.AddInput, schema.AddOutput), renderer='vjson', request_method='POST', route_name='views1')
@@ -45,7 +46,9 @@ def add(context, request):
         }
     ```
     """
-    return {}
+    x = request.json["x"]
+    y = request.json["y"]
+    return {"result": x + y}
 
 
 @view_config(decorator=withswagger(schema.DateaddInput, schema.DateaddOutput), renderer='vjson', request_method='POST', route_name='views2')
@@ -82,4 +85,13 @@ def dateadd(context, request):
         }
     ```
     """
-    return {}
\ No newline at end of file
+    value = request.json["value"]
+    addend = request.json["addend"]
+    unit = request.json["unit"]
+    value = value or datetime.utcnow()
+    if unit == 'minutes':
+        delta = timedelta(minutes=addend)
+    else:
+        delta = timedelta(days=addend)
+    result = value + delta
+    return {'result': result}

pyramid-swagger-routerについて

昔記事書いていた

http://pod.hatenablog.com/entry/2017/01/03/202551