genericclient
A generic client for RESTful APIs based on aiohttp. Python 3.5+ only.
Installation
$ pip install genericclient-aiohttp
Quickstart
import asyncio
from genericclient_aiohttp import GenericClient
async def main():
myclient = GenericClient(api_url)
myresource = await myclient.resources.get(id=1)
actives = await myclient.posts.filter(active=True)
# or you can make multiple HTTP sharing the same session
async with myclient as session:
myresource = await session.resources.get(id=1)
actives = await session.posts.filter(active=True)
loop = asyncio.get_event_loop()
loop.run_until_complete(main(loop))
Usage
Instantiation
myclient = GenericClient(url, auth=None, session=None, trailing_slash=False, retries=3, autopaginate=None)
Arguments:
url: The root URL of your API
auth: The auth for your API. You can pass anything that aiohttp.ClientSession can accept as auth.
session: Pass a session instance to have aiohttp use that session. If None (the default), it will instantiate an instance of aiohttp.ClientSession for you.
trailing_slash: You can set this to True if your API’s URLs end with a /
retries: How many times should the client retry the http call after a ClientConnectionError
autopaginate: You can set this to a coroutine to fetch all pages resulting from a request. Currently, the only coroutine included is genericclient_aiohttp.pagination.link_header, which supports [RFC5988](https://tools.ietf.org/html/rfc5988).
Endpoints
Endpoints are available as properties or items on the main instance:
await myclient.posts.all() # GET /posts/
await myclient["active-users"].all() # GET /active-users/
.all()
Retrieves all resources (essentially a simple GET on the endpoint):
await myclient.posts.all() # GET /posts/
.filter()
.filter(**kwargs) calls a GET with kwargs as querystring values:
await myclient.posts.filter(blog=12, status=1) # GET /posts/?blog=12&status=1
.get(**kwargs)
A special case of .filter().
If kwargs contains id, pk, slug or username, that value will be used in the URL path, in that order.
Otherwise, it calls a GET with kwargs as querystring values.
If the returned list is empty, will raise ResourceNotFound.
If the returned list contains more than 1 resource, will raise MultipleResourcesFound
Note that .get() will return a Resource, not a list of Resource s
await myclient.posts.filter(blog=12, status=1) # GET /posts/?blog=12&status=1
await myclient.posts.filter(id=12) # GET /posts/12/
await myclient.posts.filter(slug='12-ways-clickbait') # GET /posts/12-ways-clickbait/
.create(payload)
Will result in a POST, with payload (a dict) as the request’s body, returning a new Resource:
post = await myclient.posts.create({'blog': 12, 'status': 1}) # POST /posts/
.get_or_create(defaults, **kwargs)
Issues a GET to fetch the resource. If the resource is not found, issues a POST to create the resource:
# Assuming it doesn't exist
post = await myclient.posts.get_or_update(slug='my-post', defaults={'status': 1}) # GET /posts/my-post/, then POST /posts/
.create_or_update(payload)
If payload contains a key called 'id', will issue a PUT. If the server returns a 400 error, a PATCH request will be re-issued. If payload` does not contains 'id', it will issue a POST:
post = await myclient.posts.create_or_update({'status': 1}) # POST /posts/
post = await myclient.posts.create_or_update({'id': 1234, 'status': 1}) # PUT /posts/1234/
post = await myclient.posts.create_or_update({'id': 1234}) # PUT /posts/1234/
# <- server returns 400
# -> PATCH /posts/1234/
.delete(pk)
Will issue a DELETE, and will use pk as part of the URL:
await myclient.posts.delete(24) # DELETE /posts/24/
Resources
All endpoints methods (with the exception of .delete()) return either a Resource or a list of Resource s.
A Resource is just a wrapping class for a dict, where keys can be accessed as properties.
Additionally, Resource s have a special property called .payload, which contains the original payload received from the server.
Resource s have the following methods:
Resource.delete() will result in a DELETE, with Resource.id as par of the URL:
blog = await myclient.posts.create({'blog': 12, 'status': 1}) # POST /posts/
await blog.delete() # DELETE /blog/345/ -- the ID 345 was returned by the server in the previous response
Resource.save() will result in a PUT, with Resource.id as par of the URL. If the server returns a 400 error, a PATCH request will be re-issued:
post = await myclient.posts.create({'blog': 12, 'status': 1}) # POST /posts/
post.status = 2
await post.save() # PUT /posts/345/
post = Resource(id=345, status=1)
await post.save() # PUT /posts/345/
# <- server returns 400
# -> PATCH /posts/345/
ResourceSets
Whenever a method returns a list of Resources, they list will be wrapped in a ResultSet.
A ResultSet is a just a list object, with the addition of a .response containing the original response from the server.
Customizing Endpoints and Resources
Resources can be customized by subclassing genericclient_aiohttp.Resource.
The most common reason is specifying the name of the primary key:
from genericclient_aiohttp import Resource
class PostResource(Resource):
pk_name = 'slug'
Endpoints can be customized by subclassing genericclient_aiohttp.Endpoint:
form genericclient_aiohttp import Endpoint
class PostEndpoint(Endpoint):
resource_class = PostResource
You can then subclass genericclient_aiohttp.GenericClient to tell the client which endpoint classes to use on each endpoint:
from genericclient_aiohttp import GenericClient
class Client(GenericClient):
endpoint_classes = {
'posts': PostEndpoint,
}
Routes
If your API has some non-RESTful calls within the main endpoints (sometimes referred as detail_route and list_route), you can use genericclient to call them:
await myclient.posts(id=123).publish(date=tomorrow)
await myclient.blogs().ping()
Routes http calls use POST by default, but you can specify something else by using the _method argument:
await myclient.posts(_method='get', id=123).pingbacks()
await myclient.blogs(_method='get').visits()
Note that this calls will return an instance of genericclient.ParsedResponse, instead of instances of genericclient.Resource,
License
Licensed under the MIT License.
Metadata
Release files for genericclient-aiohttp 1.4.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| genericclient-aiohttp-1.4.2.tar.gz | 10.7 kB | Without provenance | Details for genericclient-aiohttp-1.4.2.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| genericclient_aiohttp-1.4.2-py2.py3-none-any.whl | Python 3, Python 2 | none | any | Details for genericclient_aiohttp-1.4.2-py2.py3-none-any.whl |
Total release size: 23.6 kB
Release files / genericclient-aiohttp-1.4.2.tar.gz
| Download URL | genericclient-aiohttp-1.4.2.tar.gz |
|---|---|
| Size | 10.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5ec77a32e28cc5419f45ddd79f1bc00fc77adb918d651428f2adf45ca248e006
|
|
BLAKE2b-256 checksum How to use checksums |
e0b72f3642c934a7970649ff6f9ffb2f2ac93461a19a0d601f4e0419fcdf9ee0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/50.3.1 requests-toolbelt/0.9.1 tqdm/4.50.2 CPython/3.7.4
|
Release files / genericclient_aiohttp-1.4.2-py2.py3-none-any.whl
| Download URL | genericclient_aiohttp-1.4.2-py2.py3-none-any.whl |
|---|---|
| Size | 12.9 kB |
| Tags | Python 2 Python 3 |
|
SHA-256 checksum How to use checksums |
d8997a022b16a87702b8aaf542962e8bc00721ee930ecb97557683660cb4aca7
|
|
BLAKE2b-256 checksum How to use checksums |
d75691b9c887749e0da45c902f8dead0e72b33270ddfe6e7f80ffbd5915971b4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/50.3.1 requests-toolbelt/0.9.1 tqdm/4.50.2 CPython/3.7.4
|
Security for genericclient-aiohttp 1.4.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.