apiron¶
apiron
(“API” + “iron”) is a Python wrapper for interacting with RESTful APIs
Tip
Read the upgrade guide to make sure you’re prepared for the latest version of apiron.
Welcome to the documentation for apiron
!
You can use this documentation to orient yourself to the codebase,
search broadly for existing functionality,
or look up the expectations for specific classes and methods.
As with all documentation, this is a living, breathing beast. If you notice something undocumented or misdocumented, feel free to change it in a pull request!
Getting started¶
The goal of apiron
is to get you up and running quickly,
consuming a service with little initial configuration
while allowing for granular customization.
The declarative nature of this setup makes the shape of services and their endpoints more obvious
than placing those details in one-off calls.
The minimum possible configuration requires a bit of information about the service.
Defining a service¶
A service definition requires a domain and one or more endpoints with which to interact:
from apiron import JsonEndpoint, Service
class GitHub(Service):
domain = 'https://api.github.com'
user = JsonEndpoint(path='/users/{username}')
repo = JsonEndpoint(path='/repos/{org}/{repo}')
Interacting with a service¶
Once your service definition is in place, you can interact with its endpoints in an SDK-like manner:
response = GitHub.user(username='defunkt')
# {"name": "Chris Wanstrath", ...}
response = GitHub.repo(org='github', repo='hub')
# {"description": "hub helps you win at git.", ...}
Next steps¶
Now that you’re a seasoned pro, explore the advanced usage!
Advanced usage¶
httpbin.org is a great testing tool
for various situations you may run into when interacting with RESTful services.
Let’s define a Service
that points at httpbin.org
and hook up a few interesting endpoints.
Service and endpoints¶
# test_service.py
from apiron import Endpoint, JsonEndpoint, Service, StreamingEndpoint
class HttpBin(Service):
domain = 'https://httpbin.org'
getter = JsonEndpoint(path='/get')
poster = JsonEndpoint(path='/post', default_method='POST')
status = Endpoint(path='/status/{status_code}/')
anything = JsonEndpoint(path='/anything/{anything}')
slow = JsonEndpoint(path='/delay/5')
streamer = StreamingEndpoint(path='/stream/{num_lines}')
Using all the features¶
import requests
from apiron import Timeout
from test_service import HttpBin
# A normal old GET call
HttpBin.getter(params={'foo': 'bar'})
# A normal old POST call
HttpBin.poster(data={'foo': 'bar'})
# A GET call with parameters formatted into the path
HttpBin.anything(anything=42)
# A GET call with a 500 response, raises RetryError since we successfully tried but got a bad response
try:
HttpBin.status(status_code=500)
except requests.exceptions.RetryError:
pass
# A GET call to a slow endpoint, raises ConnectionError since our connection failed
try:
HttpBin.slow()
except requests.exceptions.ConnectionError:
pass
# A GET call to a slow endpoint with a longer timeout
HttpBin.slow(
timeout_spec=Timeout(connection_timeout=1, read_timeout=6)
)
# A streaming response
response = HttpBin.streamer(num_lines=20)
for chunk in response:
print(chunk)
Service discovery¶
You may want to interact with a service whose name is known but whose hosts are resolved via another application.
Here is an example where the resolver application always resolves to https://www.google.com
for the host.
from apiron import DiscoverableService
class Eureka:
@staticmethod
def resolve(service_name):
hosts = ... # get host list from Eureka
class AuthenticationService(DiscoverableService):
service_name = 'authentication-service'
host_resolver_class = Eureka
auth = Endpoint(path='/auth')
response = AuthenticationService.auth(data={'user': 'Gandalf', 'password': 'Mellon'})
An application may wish to use a load balancer application or a more complex service discovery mechanism (like Netflix’s Eureka) to resolve the hostnames of a given service.
Workflow consistency¶
It’s common to have an existing requests.Session
object you’d like to use to make additional requests.
This is enabled in apiron
with the session
argument to an endpoint call.
The passed in session object will be used to send the request.
This is useful for workflows where cookies or other information need to persist across multiple calls.
It’s often more useful in logs to know which module initiated the code doing the logging.
apiron
allows for an existing logger object to be passed to an endpoint call using the logger
argument
so that logs will indicate the caller module rather than apiron.client
.
Upgrade guide¶
This document will guide you through upgrading from apiron 2.X versions to apiron 3.X.
Replacing ServiceCaller
usage¶
As of version 2.3.0, instantiating a service class and passing it to ServiceCaller.call
is no longer necessary.
In 3.X, ServiceCaller
has been removed altogether (though its behaviors are still in the apiron.client
module).
Replace calls to ServiceCaller.call
with the more semantic SomeService.some_endpoint
:
# Before
from apiron.client import ServiceCaller
from apiron.service.base import Service
from apiron.endpoint import JsonEndpoint
class GitHub(Service):
domain = 'https://api.github.com'
user = JsonEndpoint(path='/users/{username}')
repo = JsonEndpoint(path='/repos/{org}/{repo}')
GITHUB = GitHub()
defunkt = ServiceCaller.call(GITHUB, GITHUB.user, path_kwargs={'username', 'defunkt'})
# After
from apiron.service.base import Service
from apiron.endpoint import JsonEndpoint
class GitHub(Service):
domain = 'https://api.github.com'
user = JsonEndpoint(path='/users/{username}')
repo = JsonEndpoint(path='/repos/{org}/{repo}')
defunkt = GitHub.user(path_kwargs={'username', 'defunkt'})
Simplifying imports¶
As of version 2.4.0, most classes are available as top-level imports from the apiron
package:
# Before
from apiron.service.base import Service
from apiron.endpoint import JsonEndpoint
# After
from apiron import Service, JsonEndpoint
Simplifying endpoint path placeholders¶
As of version 2.5.0, path_kwargs
is no longer necessary; just pass path fillers as additional keyword arguments:
# Before
defunkt = GitHub.user(path_kwargs={'username', 'defunkt'})
# After
defunkt = GitHub.user(username='defunkt')
Summary¶
Prepare for apiron 3.X by installing apiron 2.5+ and doing the following:
Replace
ServiceCaller.call
with the more directSomeService.some_endpoint
Import classes from
apiron
directlyReplace
path_kwargs
with direct keyword arguments
API reference¶
Services¶
- class apiron.service.base.Service¶
Bases:
ServiceBase
A base class for low-level services.
A service has a domain off of which one or more endpoints stem.
- class apiron.service.discoverable.DiscoverableService¶
Bases:
ServiceBase
A Service whose hosts are determined via a host resolver. A host resolver is any class with a
resolve()
method that takes a service name as its sole argument and returns a list of host names that correspond to that service.
Endpoints¶
- class apiron.endpoint.Endpoint(path: str = '/', default_method: str = 'GET', default_params: dict[str, Any] | None = None, required_params: Iterable[str] | None = None, return_raw_response_object: bool = False, timeout_spec: Timeout | None = None, retry_spec: Retry | None = None)¶
Bases:
object
A basic service endpoint that responds with the default
Content-Type
for that endpoint- Parameters:
path (str) – The URL path for this endpoint, without the protocol or domain
default_method (str) – (Default
'GET'
) The default method to use when calling this endpoint.default_params (dict) – The default parameters to use when calling this endpoint. Useful when an endpoint always or most often needs a base set of parameters supplied.
required_params – An iterable of required parameter names. Calling an endpoint without its required parameters raises an exception.
return_raw_response_object (bool) – Whether to return a
requests.Response
object or callformat_response()
on it first. This can be overridden when calling the endpoint. (DefaultFalse
)timeout_spec (Timeout) – (optional) An override of the timeout behavior for calls to this endpoint. (default
None
)retry_spec (urllib3.util.retry.Retry) – (optional) An override of the retry behavior for calls to this endpoint. (default
None
)
- format_response(response: Response) str | dict[str, Any] | Iterable[bytes] ¶
Extracts the appropriate type of response data from a
requests.Response
object- Parameters:
response (requests.Response) – The original response from
requests
- Returns:
The response’s text content
- Return type:
- get_formatted_path(**kwargs) str ¶
Format this endpoint’s path with the supplied keyword arguments
- Returns:
The fully-formatted path
- Return type:
- get_merged_params(supplied_params: dict[str, Any] | None = None) dict[str, Any] ¶
Merge this endpoint’s default parameters with the supplied parameters
- Parameters:
supplied_params (dict) – A dictionary of query parameter, value pairs
- Returns:
A dictionary of this endpoint’s default parameters, merged with the supplied parameters. Any default parameters which have a value supplied are overridden.
- Return type:
- Raises:
apiron.exceptions.UnfulfilledParameterException – When a required parameter for this endpoint is not a default param and is not supplied by the caller
- class apiron.endpoint.JsonEndpoint(*args, path: str = '/', default_method: str = 'GET', default_params: Dict[str, Any] | None = None, required_params: Iterable[str] | None = None, preserve_order: bool = False)¶
Bases:
Endpoint
An endpoint that returns application/json
- Parameters:
path (str) – The URL path for this endpoint, without the protocol or domain
default_method (str) – (Default
'GET'
) The default method to use when calling this endpoint.default_params (dict) – The default parameters to use when calling this endpoint. Useful when an endpoint always or most often needs a base set of parameters supplied.
required_params – An iterable of required parameter names. Calling an endpoint without its required parameters raises an exception.
return_raw_response_object (bool) – Whether to return a
requests.Response
object or callformat_response()
on it first. This can be overridden when calling the endpoint. (DefaultFalse
)timeout_spec (Timeout) – (optional) An override of the timeout behavior for calls to this endpoint. (default
None
)retry_spec (urllib3.util.retry.Retry) – (optional) An override of the retry behavior for calls to this endpoint. (default
None
)
- format_response(response) Dict[str, Any] ¶
Extracts JSON data from the response
- Parameters:
response (requests.Response) – The original response from
requests
- Returns:
The response’s JSON content
- Return type:
dict
ifpreserve_order
isFalse
- Return type:
collections.OrderedDict
ifpreserve_order
isTrue
- class apiron.endpoint.StreamingEndpoint(path: str = '/', default_method: str = 'GET', default_params: dict[str, Any] | None = None, required_params: Iterable[str] | None = None, return_raw_response_object: bool = False, timeout_spec: Timeout | None = None, retry_spec: Retry | None = None)¶
Bases:
Endpoint
An endpoint that streams data incrementally
- Parameters:
path (str) – The URL path for this endpoint, without the protocol or domain
default_method (str) – (Default
'GET'
) The default method to use when calling this endpoint.default_params (dict) – The default parameters to use when calling this endpoint. Useful when an endpoint always or most often needs a base set of parameters supplied.
required_params – An iterable of required parameter names. Calling an endpoint without its required parameters raises an exception.
return_raw_response_object (bool) – Whether to return a
requests.Response
object or callformat_response()
on it first. This can be overridden when calling the endpoint. (DefaultFalse
)timeout_spec (Timeout) – (optional) An override of the timeout behavior for calls to this endpoint. (default
None
)retry_spec (urllib3.util.retry.Retry) – (optional) An override of the retry behavior for calls to this endpoint. (default
None
)
- format_response(response) Iterable[bytes] ¶
Stream response in chunks
- Parameters:
response (requests.Response) – The original response from
requests
- Returns:
The response’s content
- Return type:
generator
- class apiron.endpoint.StubEndpoint(stub_response: Any | None = None, **kwargs)¶
Bases:
Endpoint
A stub endpoint designed to return a pre-baked response
The intent is to allow for a service to be implemented before the endpoint is complete.
- Parameters:
stub_response –
A pre-baked response or response-determining function. Pre-baked response example:
'stub response'
or{'stub': 'response'}
A response-determining function may operate on any arguments provided to the client’scall
method. Example of a response-determining function:def stub_response(**kwargs): if kwargs.get('params') and kwargs['params'].get('param_key') == 'param_value': return {'stub response': 'for param_key=param_value'} else: return {'default': 'response'}
**kwargs – Arbitrary parameters that can match the intended real endpoint. These don’t do anything for the stub but streamline the interface.
Service client¶
- class apiron.client.Timeout(connection_timeout, read_timeout)¶
Bases:
tuple
Create new instance of Timeout(connection_timeout, read_timeout)
- connection_timeout¶
Alias for field number 0
- read_timeout¶
Alias for field number 1
- apiron.client.call(service: apiron.Service, endpoint: apiron.Endpoint, method: str | None = None, session: requests.Session | None = None, params: dict[str, Any] | None = None, data: dict[str, Any] | None = None, files: dict[str, str] | None = None, json: dict[str, Any] | None = None, headers: dict[str, Any] | None = None, cookies: dict[str, Any] | None = None, auth: Any | None = None, encoding: str | None = None, retry_spec: retry.Retry | None = None, timeout_spec: Timeout | None = None, logger: logging.Logger | None = None, allow_redirects: bool = True, return_raw_response_object: bool | None = None, **kwargs)¶
- Parameters:
service (Service) – The service that hosts the endpoint being called
endpoint (Endpoint) – The endpoint being called
method (str) – The HTTP method to use for the call
session (requests.Session) – (optional) An existing session, useful for making many calls in a single session (default
None
)params (dict) – (optional)
GET
parameters to send to the endpoint (defaultNone
)data (dict) – (optional)
POST
data to send to the endpoint. Adict
will be form-encoded, while astr
will be sent raw (defaultNone
)files (dict) – (optional) Dictionary of
'filename': file-like-objects
for multipart encoding upload. (defaultNone
)json (dict) – (optional) A JSON-serializable dictionary that will be sent as the
POST
body (defaultNone
)headers (dict) – HTTP Headers to send to the endpoint (default
None
)cookies (dict) – Cookies to send to the endpoint (default
None
)auth – An object suitable for the
requests.Request
object’sauth
argumentencoding (str) – The codec to use when decoding the response. Default behavior is to have
requests
guess the codec. (defaultNone
)retry_spec (urllib3.util.retry.Retry) – (optional) An override of the retry behavior for this call. (default
None
)timeout_spec (Timeout) – (optional) An override of the timeout behavior for this call. (default
None
)logger (logging.Logger) – (optional) An existing logger for logging from the proper caller for better correlation
allow_redirects (bool) – (optional) Enable/disable GET/OPTIONS/POST/PUT/PATCH/DELETE/HEAD redirection (default
True
)return_raw_response_object (bool) – Whether to return a
requests.Response
object or callformat_response()
on it first. (DefaultFalse
)**kwargs – Arguments to be formatted into the
endpoint
argument’spath
attribute
- Returns:
The result of
endpoint
’sformat_response()
- Return type:
The type returned by
endpoint
’sformat_response()
- Raises:
requests.RetryError – if retry threshold exceeded due to bad HTTP codes (default 500 range)
requests.ConnectionError – if retry threshold exceeded due to connection or request timeouts
Writing documentation¶
Learn about developing these docs themselves.
Indices¶
You can always find these features in the navigation menu.
Developing the apiron documentation¶
These docs are built using sphinx.
Developing¶
If you have tox installed, you may build these docs by running tox -e docs. Otherwise, you can follow the instructions below to manually install dependencies and build the docs.
Automated¶
You can use the docs
environment for tox
to build the documentation.
With tox
installed, run tox -e docs
to build the HTML documentation.
After the documentation is built, you can serve the build directory, which will be located at .tox/docs/tmp/docs
.
Manual¶
You can also manage the documentation manually if you want more control.
Installation¶
Use your favorite method to create a virtual environment and install the package with its extras for documentation:
$ cd /path/to/apiron/
$ pyenv virtualenv 3.8.0 apiron # pick your favorite virtual environment tool
$ pyenv local apiron
(apiron) $ pip install -e .[docs]
Building¶
You can build or rebuild the static documentation using make
:
$ cd /path/to/apiron/docs/
(apiron) $ make html
Running Sphinx v1.7.4
...
build succeeded.
The HTML pages are in _build/html.
If you’d instead like to have the docs rebuilt as you’re changing them, you can watch for changes:
$ cd /path/to/apiron/docs/
(apiron) $ make watch
[...] Serving on http://127.0.0.1:8000
[...] Start watching changes
[...] Start detecting changes