It doesn't know, and you don't care!
agithub
is a REST API client tailored to https://api.github.com, with
a transparent syntax which facilitates rapid prototyping. It's code is
lightweight: easy to understand, modify, and integrate. It's most
salient feature is that it doesn't know the Github API — but
that doesn't matter, since it fully supports it anyway.
While browsing the API documentation, you can convert the following
GET /issues/?filter=subscribed
into
g.issues.get(filter='subscribed')
and trust that agithub
will do exactly what you tell it to. It doesn't
second guess you, and it doesn't do anything behind your back. So, you
can read the docs and immediately know how to do the examples via
agithub
— and get on with your life.
-
First, instantiate a
Github
object, passing it your username and password or a token if an authenticated session is desired.>>> from agithub import Github >>> g = Github('user', 'pass')
>>> from agithub import Github >>> g = Github(token='token')
-
When you make a request, the status and response body are passed back as a tuple.
>>> status, data = g.issues.get(filter='subscribed', foobar='llama') >>> data [ list, of, issues ]
Notice the syntax here:
<API-object>.<URL-path>.<request-method>(<GET-parameters>)
-
If you forget the request method,
agithub
will complain that you haven't provided enough information to complete the request.>>> g.issues <class 'agithub.github.RequestBuilder'>: I don't know about /issues
-
Sometimes, it is inconvenient (or impossible) to refer to a URL as a chain of attributes, so indexing syntax is provided as well. It behaves exactly the same.
>>> g.repos.jpaugh.repla.issues[1].get() (200, { 'id': '#blah', ... }) >>> mylogin, myrepo = 'jpaugh', 'braille-converter' >>> g.repos[mylogin][myrepo].milestones.get(state='open', sort='completeness') (200, [ list, of, milestones ])
-
As a weird quirk of the implementation, you may build a partial call to the upstream API, and use it later.
>>> def following(self, user): ... return self.user.following[user].get ... >>> myCall = following(g, 'octocat') >>> if 204 == myCall()[0]: ... print 'You are following octocat' You are following octocat
You may find this useful — or not.
-
Finally,
agithub
knows nothing at all about the Github API, and it won't second-guess you.>>> g.funny.I.donna.remember.that.one.head() (404, {'message': 'Not Found'})
The error message you get is directly from Github's API. This gives you all of the information you need to survey the situation.
-
If you need more information, the response headers of the previous request are available via the
getheaders()
method.>>> g.getheaders() [('status', '404 Not Found'), ('x-ratelimit-remaining', '54'), ... ('server', 'GitHub.com')]
Errors are handled in the most transparent way possible: they are passed on to you for further scrutiny. There are two kinds of errors that can crop up:
-
Networking Exceptions (from the
http
library). Catch these withtry .. catch
blocks, as you otherwise would. -
Github API errors. These means you're doing something wrong with the API, and they are always evident in the response's status. The API considerately returns a helpful error message in the JSON body.
Here's how agithub
works, under the hood:
-
It translates a sequence of attribute look-ups into a URL; The Python method you call at the end of the chain determines the HTTP method to use for the request.
-
The Python method also receives
name=value
arguments, which it interprets as follows:
You can include custom headers as a dictionary supplied to the
headers=
argument. Some headers are provided by default (such as
User-Agent). If these occur in the supplied dictionary, they will be
overridden.
If you're using POST, PUT, or PATCH (post()
, put()
, and
patch()
), then you should include the body as the body=
argument.
The body is serialized to JSON before sending it out on the wire.
Any other arguments to the Python method become GET parameters, and are tacked onto the end of the URL. They are, of course, url-encoded for you.
- When the response is received,
agithub
looks at its content type to determine how to handle it, possibly decoding it from the given char-set to Python's Unicode representation, then converting to an appropriate form, then passed to you along with the response status code. (A JSON object is de-serialized into a Python object.)
agithub
has been written in an extensible way. You can easily:
-
Add new HTTP methods by extending the
Client
class with new Python methods of the same name (and adding them to thehttp_methods
list). -
Add new default headers to the
_default_headers
dictionary. Just make sure that the header names are lower case. -
Add a new media-type (a.k.a. content-type a.k.a mime-type) by inserting a new method into the
Content
class, replacing'-'
and'/'
with'_'
in the name. That method will then be responsible for converting the response body to a usable form — and for callingdecode_body
to do char-set conversion, if required.
And if all else fails, you can strap in, and take 15 minutes to read and become an expert on the code. From there, anything's possible.
Copyright 2012–2014 Jonathan Paugh See [COPYING][LIC] for license details [LIC]: https://github.com/jpaugh/agithub/blob/master/COPYING