/furnish

Create HTTP API clients from Python.

Primary LanguagePython

furnish

Create HTTP API clients from Python. Inspired by Retrofit.

>>> import furnish

>>> class FakeService:
...
...     @furnish.get("/posts")
...     def all_posts(user_id: furnish.Query(Optional[int], "userId")=None): pass
...
...     @furnish.post("/posts")
...     def create_post(post: furnish.Body(dict)): pass
...
...     @furnish.post("/posts")
...     def create_post_json(post: furnish.Json()):
...         """
...         Create a new post. Encode request data as JSON.
...         """
...
...     @furnish.get("/posts/{id}")
...     def get_post(id: furnish.Path(int)): pass
...
...     @furnish.put("/posts/{id}")
...     def replace_post(id: furnish.Path(int),
...                      post: furnish.Body(dict)): pass
...
...     @furnish.patch("/posts/{id}")
...     def update_post(id: furnish.Path(int),
...                     post: furnish.Body(dict)): pass
...
...     @furnish.delete("/posts/{id}")
...     def remove_post(id: furnish.Path(int)): pass
...
...     @furnish.get("/comments")
...     def comments(post_id: furnish.Query(int, "postId")): pass
...

>>> service = furnish.create(FakeService, "https://jsonplaceholder.typicode.com")

>>> service.all_posts(user_id=2).json()
[{'userId': 2,
  'body': 'delectus reiciendis molestiae occaecati non minima eveniet qui voluptatibus\naccusamus in eum beatae sit\nvel qui neque voluptates ut commodi qui incidunt\nut animi commodi',
  'title': 'et ea vero quia laudantium autem',
  'id': 11},
  ...

>>> service.update_post(1, { 'title': 'New title' }).json()
{'title': 'New title',
 'id': 1,
 'userId': 1,
 'body': 'quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto'}

Supports simple deserialization of response bodies to Python objects:

>>> import furnish

>>> class Post:
...     def __item__(self, *, id, title, body, userId):
...         self.id = id
...         self.title = title
...         self.body = body
...         self.userId = userId
...

>>> class FakeService:
...     @furnish.get("/posts/{id}")
...     def get_post(id: furnish.Path(int)) -> furnish.Response[Post]: pass
...

>>> service = furnish.create(FakeService, "https://jsonplaceholder.typicode.com")

>>> service.get_post(1).body()
<__main__.Post object at 0x7f01ef00b208>

>>> _.title
'sunt aut facere repellat provident occaecati excepturi optio reprehenderit'

TODO

  • Serialize according to annotations

License

This package is licensed under the OSI MIT License.