Skip to content
This repository was archived by the owner on Dec 17, 2019. It is now read-only.

Commit 4539f80

Browse files
author
Nat Williams
committed
add pull request API
there are a few little issues remaining. Mostly regarding handling meaningful non-20x response codes
1 parent 17649c9 commit 4539f80

File tree

10 files changed

+540
-3
lines changed

10 files changed

+540
-3
lines changed

pygithub3/core/client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def request(self, verb, request, **kwargs):
8181

8282
def get(self, request, **kwargs):
8383
response = self.request('get', request, **kwargs)
84-
assert response.status_code == 200
84+
# there are valid GET responses that != 200
8585
return response
8686

8787
def post(self, request, **kwargs):

pygithub3/exceptions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,6 @@ class UnprocessableEntity(Exception):
3737
class NotFound(Exception):
3838
""" Raised when server response is 404
3939
40-
Catched with a pygithub3-exception to `services.base.Service._bool` method
40+
Caught with a pygithub3-exception to `services.base.Service._bool` method
4141
"""
4242
pass

pygithub3/github.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ def __init__(self, **config):
1717
from pygithub3.services.users import User
1818
from pygithub3.services.repos import Repo
1919
from pygithub3.services.gists import Gist
20+
from pygithub3.services.pull_requests import PullRequests
2021
self._users = User(**config)
2122
self._repos = Repo(**config)
2223
self._gists = Gist(**config)
24+
self._pull_requests = PullRequests(**config)
2325

2426
@property
2527
def remaining_requests(self):
@@ -47,3 +49,10 @@ def gists(self):
4749
:ref:`Gists service <Gists service>`
4850
"""
4951
return self._gists
52+
53+
@property
54+
def pull_requests(self):
55+
"""
56+
:ref:`Pull Requests service <Pull Requests service`
57+
"""
58+
return self._pull_requests
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
from pygithub3.requests.base import Request, ValidationError
2+
from pygithub3.resources.base import Raw
3+
from pygithub3.resources.pull_requests import PullRequest, File
4+
from pygithub3.resources.repos import Commit
5+
6+
7+
class List(Request):
8+
uri = 'repos/{user}/{repo}/pulls'
9+
resource = PullRequest
10+
11+
12+
class Get(Request):
13+
uri = 'repos/{user}/{repo}/pulls/{number}'
14+
resource = PullRequest
15+
16+
17+
class Create(Request):
18+
uri = 'repos/{user}/{repo}/pulls'
19+
resource = PullRequest
20+
body_schema = {
21+
'schema': ('title', 'body', 'base', 'head', 'issue'),
22+
'required': ('base', 'head'),
23+
}
24+
25+
def validate_body(self, parsed):
26+
if (not ('title' in parsed and 'body' in parsed) and
27+
not 'issue' in parsed):
28+
raise ValidationError('pull request creation requires either an '
29+
'issue number or a title and body')
30+
31+
class Update(Request):
32+
uri = 'repos/{user}/{repo}/pulls/{number}'
33+
resource = PullRequest
34+
body_schema = {
35+
'schema': ('title', 'body', 'state'),
36+
'required': (),
37+
}
38+
39+
def validate_body(self, body):
40+
if 'state' in body and body['state'] not in ['open', 'closed']:
41+
raise ValidationError('If a state is specified, it must be one '
42+
'of "open" or "closed"')
43+
44+
45+
class List_commits(Request):
46+
uri = 'repos/{user}/{repo}/pulls/{number}/commits'
47+
resource = Commit
48+
49+
50+
class List_files(Request):
51+
uri = 'repos/{user}/{repo}/pulls/{number}/files'
52+
resource = File
53+
54+
55+
class Merge_status(Request):
56+
uri = 'repos/{user}/{repo}/pulls/{number}/merge'
57+
resource = Raw
58+
59+
60+
class Merge(Request):
61+
uri = 'repos/{user}/{repo}/pulls/{number}/merge'
62+
resource = Raw
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from pygithub3.requests.base import Request
2+
from pygithub3.resources.pull_requests import Comment
3+
4+
5+
class List(Request):
6+
uri = 'repos/{user}/{repo}/pulls/{number}/comments'
7+
resource = Comment
8+
9+
10+
class Get(Request):
11+
uri = 'repos/{user}/{repo}/pulls/comments/{number}'
12+
resource = Comment
13+
14+
15+
class Create(Request):
16+
uri = 'repos/{user}/{repo}/pulls/{number}/comments'
17+
resource = Comment
18+
body_schema = {
19+
'schema': ('body', 'commit_id', 'path', 'position', 'in_reply_to'),
20+
'required': ('body',),
21+
}
22+
23+
def validate_body(self, body):
24+
if (not ('commit_id' in body and
25+
'path' in body and
26+
'position' in body) and
27+
not 'in_reply_to' in body):
28+
raise ValidationError('supply either in_reply_to or commit_id, '
29+
'path, and position')
30+
31+
32+
class Edit(Request):
33+
uri = 'repos/{user}/{repo}/pulls/comments/{number}'
34+
resource = Comment
35+
body_schema = {
36+
'schema': ('body',),
37+
'required': ('body',),
38+
}
39+
40+
41+
class Delete(Request):
42+
uri = 'repos/{user}/{repo}/pulls/comments/{number}'
43+
resource = Comment
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from .base import Resource
2+
3+
4+
class PullRequest(Resource):
5+
_dates = ('created_at', 'updated_at', 'closed_at', 'merged_at')
6+
7+
def __str__(self):
8+
return '<PullRequest (%s)>' % getattr(self, 'title', '')
9+
10+
11+
class File(Resource):
12+
def __str__(self):
13+
return '<File (%s)>' % getattr(self, 'filename', '')
14+
15+
16+
class Comment(Resource):
17+
_dates = ('created_at', 'updated_at')
18+
19+
def __str__(self):
20+
return '<Comment (#%s)>' % getattr(self, 'id', '')
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
from pygithub3.exceptions import BadRequest, NotFound
2+
from pygithub3.services.base import Service, MimeTypeMixin
3+
from .comments import Comments
4+
5+
6+
class PullRequests(Service, MimeTypeMixin):
7+
"""Consume `Pull Request API <http://developer.github.com/v3/pulls/>`_"""
8+
9+
def __init__(self, **config):
10+
self.comments = Comments(**config)
11+
super(PullRequests, self).__init__(**config)
12+
13+
def list(self, user=None, repo=None):
14+
"""List all of the pull requests for a repo
15+
16+
:param str user: Username
17+
:param str repo: Repository
18+
19+
"""
20+
return self._get_result(
21+
self.make_request('pull_requests.list', user=user, repo=repo)
22+
)
23+
24+
def get(self, number, user=None, repo=None):
25+
"""Get a single pull request
26+
27+
:param str number: The number of the pull request to get
28+
:param str user: Username
29+
:param str repo: Repository
30+
31+
"""
32+
return self._get(
33+
self.make_request('pull_requests.get', number=number, user=user,
34+
repo=repo)
35+
)
36+
37+
def create(self, body, user=None, repo=None):
38+
"""Create a pull request
39+
40+
:param dict body: Data for the new pull request
41+
:param str user: Username
42+
:param str repo: Repository
43+
44+
"""
45+
return self._post(
46+
self.make_request('pull_requests.create', body=body, user=user,
47+
repo=repo)
48+
)
49+
50+
def update(self, number, body, user=None, repo=None):
51+
"""Update a pull request
52+
53+
:param str number: The number of the the pull request to update
54+
:param dict body: The data to update the pull request with
55+
:param str user: Username
56+
:param str repo: Repository
57+
58+
"""
59+
return self._patch(
60+
self.make_request('pull_requests.update', number=number,
61+
body=body, user=user, repo=repo)
62+
)
63+
64+
def list_commits(self, number, user=None, repo=None):
65+
"""List the commits for a pull request
66+
67+
:param str number: The number of the pull request to list commits for
68+
:param str user: Username
69+
:param str repo: Repository
70+
71+
"""
72+
return self._get_result(
73+
self.make_request('pull_requests.list_commits', number=number,
74+
user=user, repo=repo)
75+
)
76+
77+
def list_files(self, number, user=None, repo=None):
78+
"""List the files for a pull request
79+
80+
:param str number: The number of the pull request to list files for
81+
:param str user: Username
82+
:param str repo: Repository
83+
84+
"""
85+
return self._get_result(
86+
self.make_request('pull_requests.list_files', number=number,
87+
user=user, repo=repo)
88+
)
89+
90+
def merge_status(self, number, user=None, repo=None):
91+
"""Gets whether a pull request has been merged or not.
92+
93+
:param str number: The pull request to check
94+
:param str user: Username
95+
:param str repo: Repository
96+
97+
"""
98+
# for this to work with a proper Resource, we would need to pass the
99+
# response's status code to the Resource constructor, and that's kind
100+
# of scary
101+
try:
102+
resp = self._client.get(
103+
self.make_request('pull_requests.merge_status', number=number,
104+
user=user, repo=repo)
105+
)
106+
except NotFound:
107+
return False
108+
code = resp.status_code
109+
if code == 204:
110+
return True
111+
# TODO: more flexible way to return arbitrary objects based on
112+
# response. Probably something on Request
113+
raise BadRequest('got code %s: %s' % (code, resp.content))
114+
# again, I'm sorry.
115+
116+
def merge(self, number, message='', user=None, repo=None):
117+
"""Merge a pull request.
118+
119+
:param str number: The pull request to merge
120+
:param str user: Username
121+
:param str repo: Repository
122+
123+
"""
124+
# so, the API docs don't actually say what the status code will be in
125+
# the case of a merge failure. I hope it's not a 404.
126+
return self._put(
127+
self.make_request('pull_requests.merge', number=number,
128+
message=message, user=user, repo=repo)
129+
)
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
from pygithub3.services.base import Service, MimeTypeMixin
2+
3+
4+
class Comments(Service, MimeTypeMixin):
5+
"""Consume `Review Comments API
6+
<http://developer.github.com/v3/pulls/comments/>`_
7+
8+
"""
9+
10+
def list(self, number, user=None, repo=None):
11+
"""List all the comments for a pull request
12+
13+
:param str number: The number of the pull request
14+
:param str user: Username
15+
:param str repo: Repository
16+
17+
"""
18+
return self._get_result(
19+
self.make_request('pull_requests.comments.list', number=number,
20+
user=user, repo=repo)
21+
)
22+
23+
def get(self, number, user=None, repo=None):
24+
"""Get a single comment
25+
26+
:param str number: The comment to get
27+
:param str user: Username
28+
:param str repo: Repository
29+
30+
"""
31+
return self._get(
32+
self.make_request('pull_requests.comments.get', number=number,
33+
user=user, repo=repo)
34+
)
35+
36+
def create(self, number, body, user=None, repo=None):
37+
"""Create a comment
38+
39+
:param str number: the pull request to comment on
40+
:param str user: Username
41+
:param str repo: Repository
42+
43+
"""
44+
return self._post(
45+
self.make_request('pull_requests.comments.create', number=number,
46+
body=body, user=user, repo=repo)
47+
)
48+
49+
def edit(self, number, body, user=None, repo=None):
50+
"""Edit a comment
51+
52+
:param str number: The id of the comment to edit
53+
:param str user: Username
54+
:param str repo: Repository
55+
56+
"""
57+
return self._patch(
58+
self.make_request('pull_requests.comments.edit', number=number,
59+
body=body, user=user, repo=repo)
60+
)
61+
62+
def delete(self, number, user=None, repo=None):
63+
"""Delete a comment
64+
65+
:param str number: The comment to delete
66+
:param str user: Username
67+
:param str repo: Repository
68+
69+
"""
70+
return self._delete(
71+
self.make_request('pull_requests.comments.delete', number=number,
72+
user=user, repo=repo)
73+
)

0 commit comments

Comments
 (0)