项目作者: erewok

项目描述 :
Servant client generators for the Python language
高级语言: Haskell
项目地址: git://github.com/erewok/servant-py.git
创建时间: 2017-02-13T00:13:58Z
项目社区:https://github.com/erewok/servant-py

开源协议:Other

下载


servant-py

Build Status

This library lets you derive automatically Python functions that let you query each endpoint of a servant webservice.

Currently, the only supported method for generating requests is via the requests library, which is the recommended way to generate HTTP requests in the Python world (even among Python core devs).

Inspiration

This library is largely inspired by servant-js and by the fantastic work of the Servant team in general. Any good ideas you find in here are from their work (any mistakes are almost entirely mine, however).

Example

There are two different styles of function-return supported here: DangerMode and RawResponse.

The latter returns the raw response from issuing the request and the former calls raise_for_status and then attempts to return resp.json(). You can switch which style you’d like to use by creating a proper CommonGeneratorOptions object.

The default options just chucks it all to the wind and goes for DangerMode (because, seriously, we’re using Haskell to generate Python here…).

Following is an example of using the Servant DSL to describe endpoints and then using servant-py to create Python clients for those endpoints.

Servant DSL API Description

  1. {-# LANGUAGE DataKinds #-}
  2. {-# LANGUAGE DeriveGeneric #-}
  3. {-# LANGUAGE GeneralizedNewtypeDeriving #-}
  4. {-# LANGUAGE TypeOperators #-}
  5. module Main where
  6. import Data.Aeson
  7. import qualified Data.ByteString.Char8 as B
  8. import Data.Proxy
  9. import qualified Data.Text as T
  10. import GHC.Generics
  11. import Servant
  12. import System.FilePath
  13. import Servant.PY
  14. -- * A simple Counter data type
  15. newtype Counter = Counter { value :: Int }
  16. deriving (Generic, Show, Num)
  17. instance ToJSON Counter
  18. data LoginForm = LoginForm
  19. { username :: !T.Text
  20. , password :: !T.Text
  21. , otherMissing :: Maybe T.Text
  22. } deriving (Eq, Show, Generic)
  23. instance ToJSON LoginForm
  24. -- * Our API type
  25. type TestApi = "counter-req-header" :> Post '[JSON] Counter
  26. :<|> "counter-queryparam"
  27. :> QueryParam "sortby" T.Text
  28. :> Header "Some-Header" T.Text :> Get '[JSON] Counter
  29. :<|> "login-queryflag" :> QueryFlag "published" :> Get '[JSON] LoginForm
  30. :<|> "login-params-authors-with-reqBody"
  31. :> QueryParams "authors" T.Text
  32. :> ReqBody '[JSON] LoginForm :> Post '[JSON] LoginForm
  33. :<|> "login-with-path-var-and-header"
  34. :> Capture "id" Int
  35. :> Capture "Name" T.Text
  36. :> Capture "hungrig" Bool
  37. :> ReqBody '[JSON] LoginForm
  38. :> Post '[JSON] (Headers '[Header "test-head" B.ByteString] LoginForm)
  39. testApi :: Proxy TestApi
  40. testApi = Proxy
  41. -- where our static files reside
  42. result :: FilePath
  43. result = "examples"
  44. main :: IO ()
  45. main = writePythonForAPI testApi requests (result </> "api.py")

Generated Python Code

If you build the above and run it, you will get some output that looks like the following:

  1. from urllib import parse
  2. import requests
  3. def post_counterreqheader():
  4. """
  5. POST "counter-req-header"
  6. """
  7. url = "http://localhost:8000/counter-req-header"
  8. resp = requests.post(url)
  9. resp.raise_for_status()
  10. return resp.json()
  11. def get_counterqueryparam(sortby, headerSomeHeader):
  12. """
  13. GET "counter-queryparam"
  14. """
  15. url = "http://localhost:8000/counter-queryparam"
  16. headers = {"Some-Header": headerSomeHeader}
  17. params = {"sortby": sortby}
  18. resp = requests.get(url,
  19. headers=headers,
  20. params=params)
  21. resp.raise_for_status()
  22. return resp.json()
  23. def get_loginqueryflag(published):
  24. """
  25. GET "login-queryflag"
  26. """
  27. url = "http://localhost:8000/login-queryflag"
  28. params = {"published": published}
  29. resp = requests.get(url,
  30. params=params)
  31. resp.raise_for_status()
  32. return resp.json()
  33. def post_loginparamsauthorswithreqBody(authors, data):
  34. """
  35. POST "login-params-authors-with-reqBody"
  36. """
  37. url = "http://localhost:8000/login-params-authors-with-reqBody"
  38. params = {"authors": authors}
  39. resp = requests.post(url,
  40. params=params,
  41. json=data)
  42. resp.raise_for_status()
  43. return resp.json()
  44. def post_loginwithpathvarandheader_by_id_by_Name_by_hungrig(id, Name, hungrig, data):
  45. """
  46. POST "login-with-path-var-and-header/{id}/{Name}/{hungrig}"
  47. Args:
  48. id
  49. Name
  50. hungrig
  51. """
  52. url = "http://localhost:8000/login-with-path-var-and-header/{id}/{Name}/{hungrig}".format(
  53. id=parse.quote(id),
  54. Name=parse.quote(Name),
  55. hungrig=parse.quote(hungrig))
  56. resp = requests.post(url,
  57. json=data)
  58. resp.raise_for_status()
  59. return resp.json()

If you would like to compile and run this example yourself, you can do that like so:

  1. $ stack build --flag servant-py:example
  2. $ stack exec servant-py-exe
  3. $ cat examples/api.py