Metadata-Version: 1.2
Name: snug
Version: 0.4.0
Summary: A compact toolkit for wrapping web APIs
Home-page: https://github.com/ariebovenberg/snug
Author: Arie Bovenberg
Author-email: a.c.bovenberg@gmail.com
License: MIT
Description-Content-Type: UNKNOWN
Description: Snug
        ====
        
        .. image:: https://img.shields.io/pypi/v/snug.svg
            :target: https://pypi.python.org/pypi/snug
        
        .. image:: https://img.shields.io/pypi/l/snug.svg
            :target: https://pypi.python.org/pypi/snug
        
        .. image:: https://img.shields.io/pypi/pyversions/snug.svg
            :target: https://pypi.python.org/pypi/snug
        
        .. image:: https://travis-ci.org/ariebovenberg/snug.svg?branch=master
            :target: https://travis-ci.org/ariebovenberg/snug
        
        .. image:: https://coveralls.io/repos/github/ariebovenberg/snug/badge.svg?branch=master
            :target: https://coveralls.io/github/ariebovenberg/snug?branch=master
        
        .. image:: https://readthedocs.org/projects/snug/badge/?version=latest
            :target: http://snug.readthedocs.io/en/latest/?badge=latest
            :alt: Documentation Status
        
        .. image:: https://api.codeclimate.com/v1/badges/00312aa548eb87fe11b4/maintainability
           :target: https://codeclimate.com/github/ariebovenberg/snug/maintainability
           :alt: Maintainability
        
        
        **Snug** is a compact toolkit for wrapping web APIs.
        Architecture agnostic, pluggable, and interchangeably sync/async.
        Write API interactions as regular python code.
        
        Quickstart
        ----------
        
        1. API interactions ("queries") are request/response generators:
        
           .. code-block:: python
        
            import json
            import snug
        
            def repo(name: str, owner: str) -> snug.Query[dict]:
                """a repo lookup by owner and name"""
                request = snug.GET(f'https://api.github.com/repos/{owner}/{name}')
                response = yield request
                return json.loads(response.data)
        
        2. Queries can be executed:
        
           .. code-block:: python
        
            >>> query = repo('Hello-World', owner='octocat')
            >>> snug.execute(query)
            {"description": "My first repository on Github!", ...}
        
        3. That's it
        
        
        Installation
        ------------
        
        There are no required dependencies on python 3.5+. Installation is easy as:
        
        .. code-block:: bash
        
           pip install snug
        
        Although snug includes basic sync and async HTTP clients,
        you may wish to install `requests <http://docs.python-requests.org/>`_
        and/or `aiohttp <http://aiohttp.readthedocs.io/>`_.
        
        .. code-block:: bash
        
           pip install requests
           pip install aiohttp
        
        Features
        --------
        
        1. *Simplicity*. If you understand generators, you understand queries.
           Customizing a query requires no special glue-code.
           For example: add validation logic, or use any serialization method:
        
           .. code-block:: python
        
             from my_types import User, UserSchema
        
             def user(name: str) -> snug.Query[User]:
                 """lookup a user by their username"""
                 if len(name) == 0:
                     raise ValueError('username must have >0 characters')
                 request = snug.GET(f'https://api.github.com/users/{name}')
                 response = yield request
                 return UserSchema().load(json.loads(response.data))
        
        2. *Async out-of-the-box*. The same query can also be executed asynchronously:
        
           .. code-block:: python
        
              query = repo('Hello-World', owner='octocat')
              repo = await snug.execute_async(query)
        
        3. *Pluggable clients*. Queries are fully agnostic of the HTTP client.
           For example, to use ``requests`` instead of the built-in ``urllib``:
        
           .. code-block:: python
        
              >>> import requests
              >>> execute = snug.executor(client=requests.Session())
              >>> execute(repo('Hello-World', owner='octocat'))
              {"description": "My first repository on Github!", ...}
        
        4. *Testable*. Since queries are just generators, we can run them
           just fine without touching the network.
           No need for complex mocks or monkeypatching.
        
           .. code-block:: python
        
              >>> query = iter(epo('Hello-World', owner='octocat'))
              >>> next(query).url.endswith('/repos/octocat/Hello-World')
              True
              >>> query.send(snug.Response(200, ...))
              StopIteration({"description": "My first repository on Github!", ...})
        
        5. *Swappable authentication*. Different credentials can be used to execute
           the same query.
        
           .. code-block:: python
        
              def follow(name: str) -> snug.Query[bool]:
                  """follow another user"""
                  req = snug.PUT('https://api.github.com/user/following/{name}')
                  return (yield req).status_code == 204
        
              exec_as_me = snug.executor(auth=('me', 'password'))
              exec_as_bob = snug.executor(auth=('bob', 'password'))
        
              exec_as_me(follow('octocat'))
              exec_as_bob(follow('octocat'))
        
        6. *Related queries*. Use class-based queries to create a chained API for related objects:
        
           .. code-block:: python
        
              class repo(snug.Query[dict]):
                  """a repo lookup by owner and name"""
                  def __init__(self, name, owner):
                      ...
        
                  def __iter__(self):
                      ...  # query for the repo itself
        
                  def issue(self, num: int) -> snug.Query[dict]:
                      """retrieve an issue in this repository by its number"""
                      req = snug.GET(f'/repos/{self.owner}/{self.name}/issues/{num}')
                      return json.loads((yield req).data)
        
              # the `repo` query works as before
              hello_world_repo = repo('Hello-World', owner='octocat')
              # ...but now we can make a related query
              issue_lookup = hello_world_repo.issue(348)
              snug.execute(issue_lookup)
              # {"title": "Testing comments", ...}
        
        7. *Composable*. If you're comfortable with high-order functions and decorators,
           make use of `gentools <http://gentools.readthedocs.io/>`_ to create generators
           and apply functions to a generator's
           ``yield``, ``send``, and ``return`` values.
        
           .. code-block:: python
        
              from gentools import (map_return, map_yield, map_send,
                                    compose, oneyield)
        
              class Repository:
                  ...
        
              def my_repo_loader(...):
                  ...  # e.g. create a nice `Repository` object
        
              def my_error_checker(...):
                  ...  # e.g. raise descritive errors on HTTP 4xx responses
        
              def my_request_preparer(...):
                  ...  # e.g. add headers, url prefix, etc
        
              basic_interaction = compose(map_send(my_error_checker),
                                          map_yield(my_request_preparer))
        
              @map_return(my_repo_loader)
              @basic_interaction
              @oneyield
              def repo(owner: str, name: str) -> snug.Query[Repository]:
                  """get a repo by owner and name"""
                  return snug.GET(f'/repos/{owner}/{name}')
        
              @basic_interaction
              def follow(name: str) -> snug.Query[bool]:
                  """follow another user"""
                  response = yield snug.PUT(f'/user/following/{name}')
                  return response.status_code == 204
        
           Alternatively, use a class-based approach:
        
           .. code-block:: python
        
              class BaseQuery(snug.Query):
                  """base github query"""
        
                  def prepare(self, request):
                      ...  # e.g. add headers, url prefix, etc
        
                  def __iter__(self):
                      return parse_result((yield self.prepare(self.request)))
        
                  def parse_result(self, result):
                      ...  # e.g. error checking
        
        
              class repo(BaseQuery):
                  """get a repo by owner and name"""
                  def __init__(self, name, owner):
                      self.request = snug.GET(f'/repos/{owner}/{name}')
        
                  def parse_result(self, result):
                      result = super().parse_result(result)
                      return my_repo_loader(result.data)
        
        
              class follow(BaseQuery):
                  """follow another user"""
                  def __init__(self, name):
                      self.request = snug.PUT(f'/user/following/{name}')
        
                  def parse_result(self, result):
                      result = super().parse_result(result)
                      return result.status_code == 204
        
        
        Check the ``examples/`` directory for some samples.
        
        
        Release history
        ---------------
        
        development
        +++++++++++
        
        0.4.0 (2018-01-24)
        ++++++++++++++++++
        
        - removed generator utils and serialization logic (now seperate libraries)
        - improvements to docs
        
        0.3.0 (2018-01-14)
        ++++++++++++++++++
        
        - generator-based queries
        
        0.1.2
        +++++
        
        - fixes to documentation
        
        0.1.1
        +++++
        
        - improvements to versioning info
        
        0.1.0
        +++++
        
        - implement basic resource and simple example
        
Keywords: api,wrapper,rest,http
Platform: UNKNOWN
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Requires-Python: >=3.4
