Mercurial > hg > Lemuriformes
view lemuriformes/client.py @ 8:59c91bfb6c06
add basic CLI front-end
author | Jeff Hammel <k0scist@gmail.com> |
---|---|
date | Sun, 10 Dec 2017 11:56:51 -0800 |
parents | 92ae11e33f85 |
children |
line wrap: on
line source
""" abstract ReSTful HTTP client """ # imports import requests import sys import time from urlparse import urlparse from .cli import ConfigurationParser def isurl(string): """is `string` a URL?""" return bool(urlparse(string).scheme) def serialize_headers(headers): return '\n'.join(["{key}: {value}".format(key=key, value=value) for key, value in sorted(headers.items(), key=lambda x: x[0]) ]) def serialize_request(request): """serialize a request object to a string""" template = u"""{method} {url} {headers} {body} """ headers = '\n'.join(['{key}: {value}'.format(key=key, value=value) for key, value in request.headers.items()]) retval = template.format(method=request.method, url=request.url, headers=headers, body=request.body or '') return retval def serialize_response(response): """serialize a response object to a string""" template = u"""{url} {status_code} {headers} {text} """ retval = template.format(url=response.url, status_code=response.status_code, headers=serialize_headers(response.headers), text=response.text.strip()) return retval class Endpoint(object): """abstract base class for a RESTful API client""" path = '' endpoints = [] def __init__(self, base_url, session=None, timeout=60.): base_url = base_url.rstrip('/') self.url = '{}/{}'.format(base_url, self.path) self.timeout = timeout if session is None: session = requests.Session() self.session = session for endpoint in self.endpoints: path = endpoint.path setattr(self, path, endpoint(self.url, session=self.session, timeout=timeout)) def __call__(self, method, url, **kwargs): """make an HTTP request""" kwargs.setdefault('timeout', self.timeout) start = time.time() response = self.session.request(method, url, **kwargs) end = time.time() response.duration = end - start try: response.raise_for_status() except requests.HTTPError as e: sys.stderr.write(serialize_response(response) + '\n') sys.stderr.write("=>\n") sys.stderr.write(serialize_request(response.request) + '\n') raise return response def POST(self, data, **kwargs): return self('POST', self.url, data=data, **kwargs) class ClientParser(ConfigurationParser): """abstract argument parser for HTTP client""" client_class = Endpoint def add_arguments(self): self.add_argument('base_url', help="base URL") self.add_argument('--timeout', dest='timeout', type=float, default=60., help='per request timeout in seconds [DEFAULT: %(default)s]') def base_url(self): return self.options.base_url def client(self): """return argument specified requests HTTP client""" if self.options is None: raise Exception("options not yet parsed!") return self.client_class(self.base_url(), timeout=self.options.timeout)