GithubHelp home page GithubHelp logo

agithub's Introduction

The Agnostic GitHub API

It doesn't know, and you don't care!

agithub is a REST API client with transparent syntax which facilitates rapid prototyping — on any REST API!

Originally tailored to the GitHub REST API, AGitHub has grown up to support many other REST APIs:

  • DigitalOcean
  • Facebook
  • GitHub
  • OpenWeatherMap
  • SalesForce

Additionally, you can add full support for another REST API with very little new code! To see how, check out the Facebook client, which has about 30 lines of code.

This works because AGithub knows everything it needs to about protocol (REST, HTTP, TCP), but assumes nothing about your upstream API.

Use

The most striking quality of AGitHub is how closely its syntax emulates HTTP. In fact, you might find it even more convenient than HTTP, and almost as general (as far as REST APIs go, anyway). The examples below tend to use the GitHub API as a reference point, but it is no less easy to use agithub with, say, the Facebook Graph.

Create a client

from agithub.GitHub import GitHub
client = GitHub()

GET

Here's how to do a GET request, with properly-encoded url parameters:

client.issues.get(filter='subscribed')

That is equivalent to the following:

GET /issues/?filter=subscribed

POST

Here's how to send a request body along with your request:

some_object = {'foo': 'bar'}
client.video.upload.post(body=some_object, tags="social devcon")

This will send the following request, with some_object serialized as the request body:*

POST /video/upload?tags=social+devcon

{"foo": "bar"}

The body parameter is reserved and is used to define the request body to be POSTed. tags is an example query parameter, showing that you can pass both an object to send as the request body as well as query parameters.

* For now, the request body is limited to JSON data; but we plan to add support for other types as well

Parameters

headers

Pass custom http headers in your ruquest with the reserved parameter headers.

from agithub.GitHub import GitHub
g = GitHub()
headers = {'Accept': 'application/vnd.github.symmetra-preview+json'}
status, data = g.search.labels.get(headers=headers, repository_id=401025, q='¯\_(ツ)_/¯')
print(data['items'][0])
{u'default': False, u'name': u'\xaf\\_(\u30c4)_/\xaf', u'url': u'https://api.github.com/repos/github/hub/labels/%C2%AF%5C_(%E3%83%84)_/%C2%AF', u'color': u'008672', u'node_id': u'MDU6TGFiZWwxMTcwNjYzNTM=', u'score': 43.937515, u'id': 117066353, u'description': u''}

body

If you're using POST, PUT, or PATCH (post(), put(), or patch()), then you should include the body as the body= argument. The body is serialized to JSON before sending it out on the wire.

from agithub.GitHub import GitHub
g = GitHub()
# This Content-Type header is only required in this example due to a GitHub 
# requirement for this specific markdown.raw API endpoint
headers={'Content-Type': 'text/plain'}  
body = '# This should be my header'
status, data = g.markdown.raw.post(body=body, headers=headers)
print(data)
<h1>
<a id="user-content-this-should-be-my-header" class="anchor" href="#this-should-be-my-header" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>This should be my header</h1>

Example App

  1. First, instantiate a GitHub object.

    from agithub.GitHub import GitHub
    g = GitHub()
  2. When you make a request, the status and response body are passed back as a tuple.

    status, data = g.users.octocat.get()
    print(data['name'])
    print(status)
    The Octocat
    200
    
  3. If you forget the request method, agithub will complain that you haven't provided enough information to complete the request.

    g.users
    <class 'agithub.github.IncompleteRequest'>: /users
    
  4. 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. In these examples we use indexing syntax because you can't have a python function name

    • starting with a digit : 1
    • containing a dash (-) character : Spoon-Knife
    g.repos.github.hub.issues[1].get()
    g.repos.octocat['Spoon-Knife'].branches.get()
    (200, { 'id': '#blah', ... })
    (200, [ list, of, branches ])
    
    
  5. You can also pass query parameter to the API as function parameters to the method function (e.g. get).

    status, data = g.repos.octocat['Spoon-Knife'].issues.get(
        state='all', creator='octocat')
    print(data[0].keys())
    print(status)
    [u'labels', u'number', … , u'assignees']
    200
    

    Notice the syntax here: <API-object>.<URL-path>.<request-method>(<query-parameters>)

    • API-object : g
    • URL-path : repos.octocat['Spoon-Knife'].issues
    • request-method : get
    • query-parameters : state='all', creator='octocat'
  6. 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.

  7. 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.

  8. 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')]
    

    Note that the headers are standardized to all lower case. So though, in this example, GitHub returns a header of X-RateLimit-Remaining the header is returned from getheaders as x-ratelimit-remaining

Error handling

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:

  1. Networking Exceptions (from the http library). Catch these with try .. catch blocks, as you otherwise would.

  2. GitHub API errors. These mean 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.

Specific REST APIs

agithub includes a handful of implementations for specific REST APIs. The example above uses the GitHub API but only for demonstration purposes. It doesn't include any GitHub specific functionality (for example, authentication).

Here is a summary of additional functionality available for each distinct REST API with support included in agithub. Keep in mind, agithub is designed to be extended to any REST API and these are just an initial collection of APIs.

GitHub Authentication

To initiate an authenticated GitHub object, pass it your username and password or a token.

from agithub.GitHub import GitHub
g = GitHub('user', 'pass')
from agithub.GitHub import GitHub
g = GitHub(token='token')

GitHub Pagination

When calling the GitHub API with a query that returns many results, GitHub will paginate the response, requiring you to request each page of results with separate API calls. If you'd like to automatically fetch all pages, you can enable pagination in the GitHub object by setting paginate to True.

from agithub.GitHub import GitHub
g = GitHub(paginate=True)
status, data = g.repos.octocat['Spoon-Knife'].issues.get()

status, data = g.users.octocat.repos.get(per_page=1)
print(len(data))
8

(added in v2.2.0)

GitHub Rate Limiting

By default, if GitHub returns a response indicating that a request was refused due to rate limiting, agithub will wait until the point in time when the rate limit is lifted and attempt the call again.

If you'd like to disable this behavior and instead just return the error response from GitHub set sleep_on_ratelimit to False.

from agithub.GitHub import GitHub
g = GitHub(sleep_on_ratelimit=False)
status, data = g.repos.octocat['Spoon-Knife'].issues.get()
print(status)
print(data['message'])
403
API rate limit exceeded for 203.0.113.2. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)

(added in v2.2.0)

GitHub Logging

To see log messages related to GitHub specific features like pagination and rate limiting, you can use a root logger from the Python logging module.

import logging
logging.basicConfig()
logger = logging.getLogger()  # The root logger
logger.setLevel(logging.DEBUG)
from agithub.GitHub import GitHub
g = GitHub(paginate=True)
status, data = g.repos.octocat['Spoon-Knife'].issues.get()
DEBUG:agithub.GitHub:No GitHub ratelimit remaining. Sleeping for 676 seconds until 14:22:43 before trying API call again.
DEBUG:agithub.GitHub:Fetching an additional paginated GitHub response page at https://api.github.com/repositories/1300192/issues?page=2
DEBUG:agithub.GitHub:Fetching an additional paginated GitHub response page at https://api.github.com/repositories/1300192/issues?page=3
…

Semantics

Here's how agithub works, under the hood:

  1. 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.
  2. The Python method also receives name=value arguments, which it interprets as follows:
    • headers=
      • 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, the default value will be overridden.

        headers = {'Accept': 'application/vnd.github.loki-preview+json'}
    • body=
      • 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.
    • GET Parameters
      • 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.
  3. 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.)

Extensibility

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 the http_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 ResponseBody class, replacing '-' and '/' with '_' in the method name. That method will then be responsible for converting the response body to a usable form — and for calling decode_body to do char-set conversion, if required. For example to create a handler for the content-type application/xml you'd extend ResponseBody and create a new method like this

    import xml.etree.ElementTree as ET
    
    class CustomResponseBody(ResponseBody):
        def __init__(self):
            super(ChildB, self).__init__()
        
        def application_xml(self):
            # Handles Content-Type of "application/xml"
            return ET.fromstring(self.body)

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.

License

Copyright 2012–2016 Jonathan Paugh and contributors See COPYING for license details

agithub's People

Contributors

ala-ableton avatar ben avatar cmarqu avatar gene1wood avatar gserra-olx avatar harry239 avatar jaredhobbs avatar jenstimmerman avatar jpaugh avatar korfuri avatar marcoslhc avatar schimfim avatar tedwards avatar toolforger avatar topic2k avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

agithub's Issues

How does this compare to slumber

I just found slumber. How does agithub compare to slumber?

  • It looks like slumber was also created in 2012 like agithub
  • Not a lot of activity in slumber recently. 2 days of commiits between 2015 and now. agithub for comparison (discounting the recent activity) has few but regular commits over the past 3 years
  • 28 outstanding issues and 17 PRs in slumber so it feels a bit unmaintained
  • slumber has 5 times the stars and 4 times the forks of agithub

If there's equal functionality and quality in slumber compared to agithub it might make sense to think about deprecating agithub in favor of it since it's far more popular. That would depend on how truly unmaintained slumber is. Those outstanding issues and PRs are worrisome.

Any nice way to specify the path as a variable?

The URL is passed in a daisy chain, e.g.
status, data = g.repos.hamishwillee.docs.readme.get(headers= {'Accept':'application/vnd.github.3.raw+json'} )
Is there any way to pass in the repo (e.g. "hamishwillee.docs" as a variable (e.g. options.repo).

As an aside, while it is often useful to have a "python-esque" API, having access to everything in a more or less unbreakable way is very clever :-)

Trying to get in touch regarding a security issue

Hi there,

I couldn't find a SECURITY.md in your repository and am not sure how to best contact you privately to disclose a security issue.

Can you add a SECURITY.md file with an e-mail to your repository, so that our system can send you the vulnerability details? GitHub suggests that a security policy is the best way to make sure security issues are responsibly disclosed.

Once you've done that, you should receive an e-mail within the next hour with more info.

Thanks! (cc @huntr-helper)

Needs a new maintainer

AGithub has been a fun project, and I'm thrilled about the interest and fixes that have come from the open source community since it has been released. Thank you all for using and improving AGithub!

Right now, I'm looking for a new maintainer to replace me. If you are interested in the future of this project and have the time, consider nominating yourself as a potential maintainer in a comment to this issue. If you know of someone else who might be interested (preferably someone who has shown some activity with the project), mention them in a comment.

Additionally, if you support the nomination of a particular candidate, speak up! No open-source project can survive without community involvement. I will choose the person who seems most eager, and most able to shepherd the project going forward. I will give that person commit access, and point all new-comers to their repo, using whatever tools GitHub has made available for doing so. This person should then talk to @jaredhobbs about gaining access to the Pypi repo. I will support the transition however I can.

Why?

Regretfully, I no longer find it personally gratifying to work on it. Any future work on AGithub has thus-far been impeded by my unresponsiveness to issues and pull requests. Worse than that, I see no future direction for the project, viewing it as largely complete, as my need for a Python REST client is limited to the occasional one-off. I don't foresee this changing in the future, as I'm primarily focusing on Haskell, Java and C# development, and have been for the past few years.

Wiki changes

FYI: The following changes were made to this repository's wiki:

These were made as the result of a recent automated defacement of publically writeable wikis.

POST/PUT/PATCH Requests Override Content-Type Header

Currently, the POST, PUT, and PATCH methods automatically set the content-type header of any request (that doesn't explicitly supply content-type in the headers keyword argument) to application/json. This happens even if you provide extra_headers on the ConnectionProperties object that specify the content type.

This means that if you are sending POST/PATCH/PUT requests to a non-JSON API, your requests will be sent with application/json as the content-type without your knowledge. This can be confusing for users and cause needless head scratching.

CODE_OF_CONDUCT.md file missing

As of January 1 2019, Mozilla requires that all GitHub projects include this CODE_OF_CONDUCT.md file in the project root. The file has two parts:

  1. Required Text - All text under the headings Community Participation Guidelines and How to Report, are required, and should not be altered.
  2. Optional Text - The Project Specific Etiquette heading provides a space to speak more specifically about ways people can work effectively and inclusively together. Some examples of those can be found on the Firefox Debugger project, and Common Voice. (The optional part is commented out in the raw template file, and will not be visible until you modify and uncomment that part.)

If you have any questions about this file, or Code of Conduct policies and procedures, please reach out to [email protected].

(Message COC001)

Consider what if any planned work should still be done and capture it in issues

The Changelog says

  • The todo list for the project is in the changelog
  • upcoming releases are at the top
  • the tip of the maint branch is the latest stable branch

I'm planning to

  • change the Changelog format to the Keepachangelog standard
  • Use an Unreleased section at the top but get rid of Unscheduled, Upcoming and version numbers that don't yet exist.

Here's what the Upcoming section contains, please create issues for each of these plans if we want to pursue them.

Upcoming

Unscheduled

  • Create a script to pack the basic module and any given set of
    service-specific classes as one file. I still like the idea that a
    given API (e.g. Facebook) could be packed into a single file, and
    dropped into another project as a unit.

  • Actually support OAuth

  • Use a real, venerable test framework — maybe unittest

  • Support Request/Response compression. Here's a great tutorial

  • Get total coverage in the test suite, with the possible exception of
    actually sending a request across the wire

  • Support reusing TCP connections, and "pipelining" of requests, a la
    RFC 2068, Sect 8.1, L2377

    • The user must ask for pipelining, and supply a callback function
      to be called after a response is received.
    • Rename Client.request() -> Client.addRequest() (or such)
    • Have Client.addRequest() check whether a persistent connection is
      wanted, and save the entire request in a Client.pendingRequests
      list in that case
    • Create a new Client.sendRequests() method which the user may call
      to send all requests up the pipeline. (It should work even if the
      server does not support pipelining)
    • Call the user-supplied callback whenever a request is received.
      There are some concurrency issues here, and we may elect to call
      the callback only after all requests are received.

v3.0

  • Unbreak the test suite
  • Be consistently camelCase (Exception: the media-type converters in
    Content (e.g. application_json) should stay the same.)

How to handle Api Endpoint dynamically changes

Hi there,

How agithub handle the endpoint which is dynamically changed as example below:
api.com/rooms/{room_id}/members
the room_id is a parameter which is dynamically changed.

Thank you

Username must be supplied to trick agithub to use the token

Hi,

I'm not sure if it's intentional, but with github, a token is all that is needed to make an authorized request. However, according to agithub's logic, an username must be provided in order for it to add the authorization header that includes the token.

It's not a big deal though, one can always just supply a dummy username or add the authorization header for each request.

Thanks

Support Spotify API

Are there any plans to add Spotify API Client Support.

Another question that I have is, which APIs are being considered into adding to this Repository?

Support Bitbucket API

I love the syntax of the code "produced" by agithub. In addition to GitHub, I'd like to use it for accessing Bitbucket.

Are there any plans to implement a dedicated module for that?

Can't create new files - problems parsing JSON

What I want to do is create a file in my repository at an arbitrary path. So for example "fred/mytext.txt"

Unfortunately I either get an error 400 for "Problems parsing JSON" or "not found".

   filetext1 = 'This is my test file text. It has to be something, why not this.'
   encoded_content = base64.b64encode(filetext1)    #must be Base64 encoded.
   print 'encoded content: %s' % encoded_content
   #Creating a file

The documentation reads like I should do it like this:

status, data = g.repos.hamishwillee.ardupilot_wordpress_sources.contents.fred/mytestfile1.txt.put(message= 'Adding a new file', content=encoded_content )

but I get an error "NameError: name 'mytestfile1' is not defined" because of the period in .txt. So then I tried putting this information in a path variable:

status, data = g.repos.hamishwillee.ardupilot_wordpress_sources.contents.put(path="fred/mytestfile1.txt",message= 'Adding a new file', content=encoded_content )

With the URL/debug:

  PUT
  /repos/hamishwillee/ardupilot_wordpress_sources/contents?  content=VGhpcyBpcyBteSB0ZXN0IGZpbGUgdGV4dC4gSXQgaGFzIHRvIGJlIHNvbWV0aGluZywgd2h5IG5vdCB0aGlzLg%3D%3D&path=fred%2Fmytestfile1.txt&message=Adding+a+new+file

  404
  {u'documentation_url': u'https://developer.github.com/v3', u'message': u'Not Found'}

I also tried without the "fred" path.

I appreciate this might be an error in my usage, but would be great to have an example of this approach working.

getheaders() returns uppercase headers in python3 and lowercase in python2

The case of the headers returned differs between Python2 and Python3. This may be due to differences between http.client and httplib

Python 2

$ python
Python 2.7.15+ (default, Jul  9 2019, 16:51:35) 
[GCC 7.4.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from agithub.GitHub import GitHub
>>> g = GitHub()
>>> status, data = g.users.octocat.get()
>>> print([x[0] for x in g.getheaders()])
['content-length', 'vary', 'x-xss-protection', 'x-content-type-options', 'etag', 'cache-control', 'referrer-policy', 'status', 'x-ratelimit-remaining', 'x-github-media-type', 'access-control-expose-headers', 'x-github-request-id', 'last-modified', 'date', 'access-control-allow-origin', 'content-security-policy', 'strict-transport-security', 'server', 'x-ratelimit-limit', 'x-frame-options', 'content-type', 'x-ratelimit-reset']

Python 3

$ python3
Python 3.6.8 (default, Aug 20 2019, 17:12:48) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from agithub.GitHub import GitHub
>>> g = GitHub()
>>> status, data = g.users.octocat.get()
>>> print([x[0] for x in g.getheaders()])
['Date', 'Content-Type', 'Content-Length', 'Server', 'Status', 'X-RateLimit-Limit', 'X-RateLimit-Remaining', 'X-RateLimit-Reset', 'Cache-Control', 'Vary', 'ETag', 'Last-Modified', 'X-GitHub-Media-Type', 'Access-Control-Expose-Headers', 'Access-Control-Allow-Origin', 'Strict-Transport-Security', 'X-Frame-Options', 'X-Content-Type-Options', 'X-XSS-Protection', 'Referrer-Policy', 'Content-Security-Policy', 'Vary', 'X-GitHub-Request-Id']

Support GitLab API

I love the syntax of the code "produced" by agithub. In addition to GitHub, I'd like to use it for accessing GitLab.

Are there any plans to implement a dedicated module for that?

Cleanup old branches

Look over the old branches, make PRs of those that you can to examine them. Those that can't examine them in situ and decide what to do with them.

Reaching a rate limit with pagination causes error

If you set paginate to True and leaving sleep_on_ratelimit as the default, you can get this error message.

Traceback (most recent call last):
  File "/path/to/__init__.py", line 53, in <module>
    status, data['refs_map'][repo['name']] = g.repos[org_name][repo['name']].git.refs.get()
  File "/path/to/.venv/lib/python3.6/site-packages/agithub/base.py", line 157, in get
    return self.request('GET', url, None, headers)
  File "/path/to/.venv/lib/python3.6/site-packages/agithub/GitHub.py", line 115, in request
    data.extend(self.get_additional_pages(method, bodyData, headers))
  File "/path/to/.venv/lib/python3.6/site-packages/agithub/GitHub.py", line 127, in get_additional_pages
    status, data = self.request(method, url, bodyData, headers)
  File "/path/to/.venv/lib/python3.6/site-packages/agithub/GitHub.py", line 115, in request
    data.extend(self.get_additional_pages(method, bodyData, headers))
  File "/path/to/.venv/lib/python3.6/site-packages/agithub/GitHub.py", line 127, in get_additional_pages
    status, data = self.request(method, url, bodyData, headers)
...
  File "/path/to/.venv/lib/python3.6/site-packages/agithub/GitHub.py", line 115, in request
    data.extend(self.get_additional_pages(method, bodyData, headers))
  File "/path/to/.venv/lib/python3.6/site-packages/agithub/GitHub.py", line 140, in get_additional_pages
    'was returned with status {}: {}'.format(status, data))
TypeError: While fetching a paginated GitHub response page, a non-list was returned with status 403: {'message': "API rate limit exceeded for 1.2.3.4. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)", 'documentation_url': 'https://developer.github.com/v3/#rate-limiting'}

add a patch method

 +    def patch(self, url, body=None, headers={}, **params):
 +        """
 +        Do a http patch request on the given url with given body, headers and parameters
 +        Parameters is a dictionary that will will be urlencoded
 +        """
 +        url += self.urlencode(params)
 +        return self.request(self.PATCH, url, json.dumps(body), headers)

URL params doesn't accept character '.' as a parameter

Hello there i've been using the tool for a while now and i encountered an issue recently.
I have the following code line:
status_search_customer, data_search_customer = cms.customers.search.get(f_users.login = csv_dicc["login"])
As far as i can see the issue here is that the parameter is taken by python as a method due to the . character in it. Is there a way around this problem?

Error requesting to custom endpoint

Hi there!

FIrst of all thanks for the lib, it looks really good and usable.

I'm trying to connect to an enterprise endpoint, like:

gh = GitHub(token=token, api_url=base_url + '/api/v3/')

But after that, any request (eg. gh.issues.get()) I try fails with this exception:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/agithub/base.py", line 157, in get
    return self.request('GET', url, None, headers)
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/agithub/base.py", line 207, in request
    conn.request(method, url, requestBody.process(), headers)
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 1057, in request
    self._send_request(method, url, body, headers)
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 1097, in _send_request
    self.endheaders(body)
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 1053, in endheaders
    self._send_output(message_body)
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 897, in _send_output
    self.send(msg)
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 859, in send
    self.connect()
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 1270, in connect
    HTTPConnection.connect(self)
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 836, in connect
    self.timeout, self.source_address)
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py", line 557, in create_connection
    for res in getaddrinfo(host, port, 0, SOCK_STREAM):
socket.gaierror: [Errno 8] nodename nor servname provided, or not known

I tried to run this locally to see if it was a dns/connection trouble:

socket.gethostbyname(base_url)

But it returns me the right IP address.

Any ideas? Thanks in advance!

Straw poll: more encompassing name (ends April 1 23:59)

This project is no longer limited to just the Github REST API, so a more encompassing name is appropriate. Please submit suggestions below and vote below.

  • One suggestion per comment: shortname/Long Name
  • Vote with thumbs up or down (others ignored)
  • Suggestion window ends April 1 23:59 AoE
  • Final decision (tie-breaking, valid entry, veto, etc) by @gene1wood

github pagination support

GitHub returns a maximum of 100 items, the rest of the response can be gotten by using one of the links in the headers, especially the link to the next page, with the rest of the results.
https://developer.github.com/guides/traversing-with-pagination/

It would be nice if there was some sort of general way of getting all results from a call to the github api (or an option to specifiy this) so that we could get all results instead of the first X results, and no headers on how to get to the rest.

I was thinking of a PaginatedClient class?

Rate limit sleep is off-by-one

From a run, with agithub logging at DEBUG level (INFO level from my script):

agithub==2.2.2

2019-12-18 04:29:32,495 INFO: Running as moz-hwine
2019-12-18 04:29:32,495 INFO: Gathering branch protection data. (calls remaining 942).
No GitHub ratelimit remaining. Sleeping for 402 seconds until 04:45:14 before trying API call again.
2019-12-18 04:38:32,008 DEBUG: No GitHub ratelimit remaining. Sleeping for 402 seconds until 04:45:14 before trying API call again.
No GitHub ratelimit remaining. Sleeping for 1 seconds until 04:45:15 before trying API call again.
2019-12-18 04:45:14,247 DEBUG: No GitHub ratelimit remaining. Sleeping for 1 seconds until 04:45:15 before trying API call again.

Github pagination does not operate if the response is not a list, such as in the list-workflow-runs API

The List workflow runs GitHub API returns an object with a nested list. It also returns the standard GitHub link headers to support pagination.

With the following code, no pagination occurs:

    github_client = GitHub(token=GITHUB_TOKEN, paginate=True)
    status, data = github_client.repos.falkonai.server.actions.workflows[
        "<workflow_name>"
    ].runs.get()

I believe the issue is this check in the Github request method:

if self.paginate and type(data) == list:

Since data is a dict here. I don't know why Github chose to make this API return an object instead of a list.

immutable IncompleteRequest.__getattr__

I've been using this client in one of my projects[1] and I'm loving that I don't need to do anything to incorporate new API additions but I've run into a lot of issues trying to reuse partial constructions because IncompleteRequest.__getattr__ modifies self rather than returning a new request.

For example, if I want to get the branch protection details for every branch in a repo, I would assume that I could just do something along the lines of the below (actual url mutation in comments):

import os
import sys

from agithub.GitHub import GitHub

def all_branch_protection_data(username, repo_name):
    client = GitHub(token=os.environ["GITHUB_TOKEN"], paginate=True)

    repo = getattr(getattr(client.repos, username), repo_name)
    # repo.url == /repos/:owner/:repo
    print(repo.url)

    status, data = repo.branches.get()
    # repo.url == /repos/:owner/:repo/branches
    print(repo.url)

    # BASELINE == /repos/:owner/:repo/branches
    # LOOP_BASELINE = BASELINE
    for branch in data:
        branch = getattr(repo.branches, branch["name"])
        # branch is repo; repo.url == LOOP_BASELINE/branches/:branch
        print(repo.url)
        status, data = branch.protection.get()
        # branch is repo; repo.url == LOOP_BASELINE/branches/:branch/protection
        print(repo.url)
        yield data
        # LOOP_BASELINE = LOOP_BASELINE/branches/:branch/protection

if __name__ == "__main__":
    list(all_branch_protection_data(sys.argv[1], sys.argv[2]))

However, if I were to try this, it would not do what I would think it should, because every call to __getattr__ mutates repo.url.

/repos/mattsb42/rhodes
/repos/mattsb42/rhodes/branches
/repos/mattsb42/rhodes/branches/branches/development
/repos/mattsb42/rhodes/branches/branches/development/protection
/repos/mattsb42/rhodes/branches/branches/development/protection/branches/master
/repos/mattsb42/rhodes/branches/branches/development/protection/branches/master/protection
/repos/mattsb42/rhodes/branches/branches/development/protection/branches/master/protection/branches/wat
/repos/mattsb42/rhodes/branches/branches/development/protection/branches/master/protection/branches/wat/protection

I can work around this by resetting repo.url at the start of every iteration of the loop, but this seems to me like a common enough general use-case that I suspect that a large percentage of users are also running into this.

The fix for this is fairly simple, but would be a potentially significant breaking change to the client behavior. I'll go ahead and send up a PR for this because the change is so small, but I'm also happy to discuss the problem more here if you like.

[1] https://github.com/mattsb42/repo-manager

Overwrite default encoding for json

Class Body in method parseContentType is assuming (basing on RFC 2068) that ISO-8859-1 is default charset in case none is provided... while RFC-4627 (https://tools.ietf.org/html/rfc4627) says "JSON text SHALL be encoded in Unicode. The default encoding is UTF-8."

Possible resolutions are to change default encoding for JSON or to allow custom encoding using env variables or some parameter

Cannot pass 'body' or 'headers' key as a POST parameter

The below code is from base.py

def post(self, url, body=None, headers=None, **params):
    headers = headers or {}
    url += self.urlencode(params)
    if 'content-type' not in headers:
        headers['content-type'] = 'application/json'
    return self.request('POST', url, body, headers)

the params dictionary cannot contain body key or a headers key. As the below example, I want to pass body='body of message' as a param to the Api

some_object = {'foo': 'bar'}
client.rooms.message.post(body=some_object, tags="social devcon", body="body of message")

as the body is referenced as body of POST method. Is there any way to pass body as a parameter

Feature request : Support 301 redirects on API endpoints

I want to love this library, but I have an API that I'm trying to extend a class for this. It SHOULD be working, but it's stuck in a 301 response and nothing continues. How can I get redirects to be handled? It looks like they're stuck...

params vs body in PUT request

I had to switch the usage of PUT /teams/:id/repos/:org/:repo

this way...

- status, data = gh.teams[infra_central_id].repos[org['login']][repo_name].put(permission="push")
- if status != 204:
-    print colored('X', 'red'),

+ payload={"permission": "push"}
+ status, data = gh.teams[infra_central_id].repos[org['login']][repo_name].put(body=payload)
+ if status != 204:
+    print colored('X', 'red'),

either else it would have not worked

is it intended, or a bug worth looking at?

upload asset to github release?

How do i upload an asset to a release?
With the following code i get socket.error: [Errno 10054] (connection closed by remote).

import base64
from agithub import GitHub

with open('MyApp.exe', 'rb') as f:
    body = f.read()

gh = Github(token=token)
rc, data = gh.repos[user][repo].releases[releaseId].assets.post(
    url= uploadUrl,
    name='MyApp.exe',
    body=base64.encodestring(body),
    headers={'Content-Type': 'application/octet-stream'}
    )

If i don't encode to base64 i get an UnicodeDecodeError.

What is the correct way to do it?
I'm using Python 2.7.9 Stackless 3.1b3 060516 (default, Feb 21 2015, 11:54:09) [MSC v.1500 32 bit (Intel)] on win32 and release 2.0 of agithub.

Add to PyPi?

Just went to install this, and discovered it's not on PyPi.

Any chance you can add it there, to keep things easy for potential users? 😄

Single file

Since this api implementation is so small it would be handy to have it on just one single file, so it can easily be redistributed in other projects.

how to use full URL as string

in many api's, github returns full URL's to the client.
Am i able to use these URL's without having to strip the whole http://api.github.com part?

i would like for my code to not break at the slightest change github makes.
any ideas on how i can accomplish this?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.