1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
|
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
from datetime import datetime
from pygithub3.core.client import Client
from pygithub3.core.errors import NotFound
from pygithub3.core.result import smart, normal
from pygithub3.requests.base import Factory
from pygithub3.resources.base import GITHUB_DATE_FORMAT
class Service(object):
"""
You can configure each service with this keyword variables:
:param str login: Username to authenticate
:param str password: Username to authenticate
:param str user: Default username in requests
:param str repo: Default repository in requests
:param str token: Token to OAuth
:param int per_page: Items in each page of multiple returns
:param str base_url: To support another github-related API (untested)
:param stream verbose: Stream to write debug logs
You can configure the **authentication** with BasicAuthentication (login
and password) and with `OAuth <http://developer.github.com/v3/oauth/>`_ (
token).
If you include ``login``, ``password`` and ``token`` in config; Oauth has
precedence
Some API requests need ``user`` and/or ``repo`` arguments (e.g
:ref:`repos service <config precedence>`).
You can configure the default value here to avoid repeating
Some API requests return multiple resources with pagination. You can
configure how many items has each page.
You can configure ``verbose`` logging like `requests library <http://docs.
python-requests.org/en/v0.10.6/user/advanced/#verbose-logging>`_
"""
def __init__(self, **config):
self._client = Client(**config)
self.request_builder = Factory()
def _normalize_date(self, key, _dict):
""" If ``key`` comes as ``datetime``, it'll normalize it """
try:
key = str(key)
date = datetime.strftime(_dict.get(key), GITHUB_DATE_FORMAT)
_dict.update({key: date})
except:
pass
@property
def remaining_requests(self):
return Client.remaining_requests
def get_user(self):
return self._client.user
def set_user(self, user):
""" Set user
:param str user: Default username in requests
"""
self._client.user = user
def get_repo(self):
return self._client.repo
def set_repo(self, repo):
""" Set repository
:param str repo: Default repository in requests
"""
self._client.repo = repo
def set_credentials(self, login, password):
""" Set Basic Authentication
:param str login: Username to authenticate
:param str password: Username to authenticate
"""
self._client.set_credentials(login, password)
def set_token(self, token):
""" Set OAuth token
:param str token: Token to OAuth
"""
self._client.set_token(token)
#TODO: Refact as decorator::
"""
Reason: make_request and request_builder ... are confusing names
@precedence('user')
def list(self, sha, user=None):
"""
def make_request(self, request, **kwargs):
if 'user' in kwargs:
kwargs['user'] = kwargs['user'] or self.get_user()
if 'repo' in kwargs:
kwargs['repo'] = kwargs['repo'] or self.get_repo()
return self.request_builder(request, **kwargs)
def _request(self, verb, request, **kwargs):
self._client.request(verb, request, **kwargs)
def _bool(self, request, **kwargs):
try:
self._client.head(request, **kwargs)
return True
except NotFound:
return False
def _patch(self, request, **kwargs):
input_data = request.get_body()
response = self._client.patch(request, data=input_data, **kwargs)
return request.resource.loads(response.content)
def _put(self, request, **kwargs):
""" Bug in Github API? requests library?
I must send data when the specifications' of some PUT request are 'Not
send input data'. If I don't do that and send data as None, the
requests library doesn't send 'Content-length' header and the server
returns 411 - Required Content length (at least 0)
For instance:
- follow-user request doesn't send input data
- merge-pull request send data
For that reason I must do a conditional because I don't want to return
an empty string on follow-user request because it could be confused
Related: https://github.com/github/developer.github.com/pull/52
"""
input_data = request.get_body() or 'PLACEHOLDER'
response = self._client.put(request, data=input_data, **kwargs)
if response.status_code != 204: # != NO_CONTENT
return request.resource.loads(response.content)
def _delete(self, request, **kwargs):
input_data = request.get_body()
self._client.delete(request, data=input_data, **kwargs)
def _post(self, request, **kwargs):
input_data = request.get_body()
response = self._client.post(request, data=input_data, **kwargs)
return request.resource.loads(response.content)
def _get(self, request, **kwargs):
response = self._client.get(request, **kwargs)
return request.resource.loads(response.content)
def _get_result(self, request, **kwargs):
method = smart.Method(self._client.get, request, **kwargs)
return smart.Result(method)
def _get_normal_result(self, request, **kwargs):
method = normal.Method(self._client.get, request, **kwargs)
return normal.Result(method)
# XXX: Refact to set_<type> method
class MimeTypeMixin(object):
"""
Mimetype support to Services
Adds 4 public functions to service:
"""
VERSION = 'beta'
def __set_mimetype(self, mimetype):
self.mimetype = 'application/vnd.github.%s.%s+json' % (
self.VERSION, mimetype)
def set_raw(self):
""" Resource will have ``body`` attribute """
self.__set_mimetype('raw')
def set_text(self):
""" Resource will have ``body_text`` attribute """
self.__set_mimetype('text')
def set_html(self):
""" Resource will have ``body_html`` attribute """
self.__set_mimetype('html')
def set_full(self):
""" Resource will have ``body``, ``body_text`` and ``body_html``
attributes """
self.__set_mimetype('full')
def _get_mimetype_as_header(self):
try:
return {'headers': {'Accept': self.mimetype}}
except AttributeError:
return {}
|