0
# API Client
1
2
High-level GitHub API interface providing HTTP method implementations (GET, POST, PUT, PATCH, DELETE) with authentication, caching, and GraphQL support. The abstract base class defines the interface that concrete HTTP implementations must fulfill.
3
4
## Capabilities
5
6
### Abstract Base Class
7
8
The core abstract class that defines the GitHub API interface.
9
10
```python { .api }
11
import abc
12
from typing import Any, AsyncGenerator, Dict, Mapping, MutableMapping, Optional, Tuple
13
from uritemplate import variable
14
from gidgethub.sansio import RateLimit
15
import gidgethub.sansio as sansio
16
17
# Cache type definition
18
CACHE_TYPE = MutableMapping[str, Tuple[Optional[str], Optional[str], Any, Optional[str]]]
19
20
class GitHubAPI(abc.ABC):
21
"""Provide an idiomatic API for making calls to GitHub's API."""
22
23
def __init__(
24
self,
25
requester: str,
26
*,
27
oauth_token: Optional[str] = None,
28
cache: Optional[CACHE_TYPE] = None,
29
base_url: str = "https://api.github.com"
30
) -> None:
31
"""
32
Initialize GitHub API client.
33
34
Parameters:
35
- requester: User agent identifier (username or project name)
36
- oauth_token: Personal access token for authentication
37
- cache: Optional cache for GET requests (etag/last-modified based)
38
- base_url: GitHub API base URL
39
"""
40
41
# Abstract methods that implementations must provide
42
@abc.abstractmethod
43
async def _request(
44
self,
45
method: str,
46
url: str,
47
headers: Mapping[str, str],
48
body: bytes = b""
49
) -> Tuple[int, Mapping[str, str], bytes]:
50
"""Make an HTTP request."""
51
52
@abc.abstractmethod
53
async def sleep(self, seconds: float) -> None:
54
"""Sleep for the specified number of seconds."""
55
56
# Attributes
57
requester: str
58
oauth_token: Optional[str]
59
rate_limit: Optional[RateLimit]
60
base_url: str
61
```
62
63
### GET Methods
64
65
Methods for retrieving data from the GitHub API.
66
67
```python { .api }
68
async def getitem(
69
self,
70
url: str,
71
url_vars: Optional[variable.VariableValueDict] = {},
72
*,
73
accept: str = sansio.accept_format(),
74
jwt: Optional[str] = None,
75
oauth_token: Optional[str] = None,
76
extra_headers: Optional[Dict[str, str]] = None,
77
) -> Any:
78
"""
79
Send a GET request for a single item to the specified endpoint.
80
81
Parameters:
82
- url: API endpoint URL (absolute or relative)
83
- url_vars: Variables for URI template expansion
84
- accept: Accept header value for API version/format
85
- jwt: JWT token for GitHub App authentication
86
- oauth_token: OAuth token (overrides instance token)
87
- extra_headers: Additional headers to include
88
89
Returns:
90
- Parsed JSON response data
91
"""
92
93
async def getstatus(
94
self,
95
url: str,
96
url_vars: Optional[variable.VariableValueDict] = {},
97
*,
98
accept: str = sansio.accept_format(),
99
jwt: Optional[str] = None,
100
oauth_token: Optional[str] = None,
101
) -> int:
102
"""
103
Send a GET request and return only the status code.
104
105
Parameters:
106
- url: API endpoint URL
107
- url_vars: Variables for URI template expansion
108
- accept: Accept header value
109
- jwt: JWT token for GitHub App authentication
110
- oauth_token: OAuth token (overrides instance token)
111
112
Returns:
113
- HTTP status code (even for error responses)
114
"""
115
116
async def getiter(
117
self,
118
url: str,
119
url_vars: Optional[variable.VariableValueDict] = {},
120
*,
121
accept: str = sansio.accept_format(),
122
jwt: Optional[str] = None,
123
oauth_token: Optional[str] = None,
124
extra_headers: Optional[Dict[str, str]] = None,
125
iterable_key: Optional[str] = "items",
126
) -> AsyncGenerator[Any, None]:
127
"""
128
Return an async iterable for all items at a specified endpoint.
129
Automatically handles pagination using Link headers.
130
131
Parameters:
132
- url: API endpoint URL
133
- url_vars: Variables for URI template expansion
134
- accept: Accept header value
135
- jwt: JWT token for GitHub App authentication
136
- oauth_token: OAuth token (overrides instance token)
137
- extra_headers: Additional headers to include
138
- iterable_key: Key containing items in paginated responses
139
140
Yields:
141
- Individual items from paginated API responses
142
"""
143
```
144
145
### Mutation Methods
146
147
Methods for creating, updating, and deleting data via the GitHub API.
148
149
```python { .api }
150
async def post(
151
self,
152
url: str,
153
url_vars: Optional[variable.VariableValueDict] = {},
154
*,
155
data: Any,
156
accept: str = sansio.accept_format(),
157
jwt: Optional[str] = None,
158
oauth_token: Optional[str] = None,
159
extra_headers: Optional[Dict[str, str]] = None,
160
content_type: str = "application/json",
161
) -> Any:
162
"""
163
Send a POST request to create resources.
164
165
Parameters:
166
- url: API endpoint URL
167
- url_vars: Variables for URI template expansion
168
- data: Request body data (JSON serializable or bytes)
169
- accept: Accept header value
170
- jwt: JWT token for GitHub App authentication
171
- oauth_token: OAuth token (overrides instance token)
172
- extra_headers: Additional headers to include
173
- content_type: Content-Type header value
174
175
Returns:
176
- Parsed JSON response data
177
"""
178
179
async def patch(
180
self,
181
url: str,
182
url_vars: Optional[variable.VariableValueDict] = {},
183
*,
184
data: Any,
185
accept: str = sansio.accept_format(),
186
jwt: Optional[str] = None,
187
oauth_token: Optional[str] = None,
188
extra_headers: Optional[Dict[str, str]] = None,
189
) -> Any:
190
"""
191
Send a PATCH request to update resources.
192
193
Parameters:
194
- url: API endpoint URL
195
- url_vars: Variables for URI template expansion
196
- data: Request body data (JSON serializable)
197
- accept: Accept header value
198
- jwt: JWT token for GitHub App authentication
199
- oauth_token: OAuth token (overrides instance token)
200
- extra_headers: Additional headers to include
201
202
Returns:
203
- Parsed JSON response data
204
"""
205
206
async def put(
207
self,
208
url: str,
209
url_vars: Optional[variable.VariableValueDict] = {},
210
*,
211
data: Any = b"",
212
accept: str = sansio.accept_format(),
213
jwt: Optional[str] = None,
214
oauth_token: Optional[str] = None,
215
extra_headers: Optional[Dict[str, str]] = None,
216
) -> Any:
217
"""
218
Send a PUT request to create or replace resources.
219
220
Parameters:
221
- url: API endpoint URL
222
- url_vars: Variables for URI template expansion
223
- data: Request body data (default: empty)
224
- accept: Accept header value
225
- jwt: JWT token for GitHub App authentication
226
- oauth_token: OAuth token (overrides instance token)
227
- extra_headers: Additional headers to include
228
229
Returns:
230
- Parsed JSON response data
231
"""
232
233
async def delete(
234
self,
235
url: str,
236
url_vars: Optional[variable.VariableValueDict] = {},
237
*,
238
data: Any = b"",
239
accept: str = sansio.accept_format(),
240
jwt: Optional[str] = None,
241
oauth_token: Optional[str] = None,
242
extra_headers: Optional[Dict[str, str]] = None,
243
) -> None:
244
"""
245
Send a DELETE request to remove resources.
246
247
Parameters:
248
- url: API endpoint URL
249
- url_vars: Variables for URI template expansion
250
- data: Request body data (default: empty)
251
- accept: Accept header value
252
- jwt: JWT token for GitHub App authentication
253
- oauth_token: OAuth token (overrides instance token)
254
- extra_headers: Additional headers to include
255
256
Returns:
257
- None (DELETE responses typically have no body)
258
"""
259
```
260
261
### GraphQL Support
262
263
Methods for querying GitHub's GraphQL v4 API.
264
265
```python { .api }
266
async def graphql(
267
self,
268
query: str,
269
*,
270
endpoint: str = "https://api.github.com/graphql",
271
**variables: Any,
272
) -> Any:
273
"""
274
Query the GraphQL v4 API.
275
276
Parameters:
277
- query: GraphQL query string
278
- endpoint: GraphQL endpoint URL
279
- **variables: Query variables as keyword arguments
280
281
Returns:
282
- GraphQL response data
283
284
Raises:
285
- GraphQLException: For malformed responses
286
- GraphQLAuthorizationFailure: For 401 responses
287
- BadGraphQLRequest: For 4XX responses
288
- QueryError: For GraphQL query errors
289
- GitHubBroken: For 5XX responses
290
"""
291
```
292
293
## Usage Examples
294
295
### Basic Repository Information
296
297
```python
298
import asyncio
299
from gidgethub.aiohttp import GitHubAPI
300
import aiohttp
301
302
async def get_repo_info():
303
async with aiohttp.ClientSession() as session:
304
gh = GitHubAPI(session, "my-app/1.0")
305
306
# Get single repository
307
repo = await gh.getitem("/repos/octocat/Hello-World")
308
print(f"Repository: {repo['name']}")
309
print(f"Stars: {repo['stargazers_count']}")
310
311
# Check if repository exists (status only)
312
status = await gh.getstatus("/repos/octocat/Hello-World")
313
if status == 200:
314
print("Repository exists")
315
316
asyncio.run(get_repo_info())
317
```
318
319
### Pagination with Async Iteration
320
321
```python
322
async def list_all_issues():
323
async with aiohttp.ClientSession() as session:
324
gh = GitHubAPI(session, "my-app/1.0", oauth_token="your_token")
325
326
# Iterate through all issues across all pages
327
async for issue in gh.getiter("/repos/owner/repo/issues"):
328
print(f"Issue #{issue['number']}: {issue['title']}")
329
```
330
331
### Creating and Updating Resources
332
333
```python
334
async def manage_issues():
335
async with aiohttp.ClientSession() as session:
336
gh = GitHubAPI(session, "my-app/1.0", oauth_token="your_token")
337
338
# Create a new issue
339
new_issue = await gh.post(
340
"/repos/owner/repo/issues",
341
data={
342
"title": "Bug report",
343
"body": "Description of the bug",
344
"labels": ["bug", "urgent"]
345
}
346
)
347
issue_number = new_issue['number']
348
349
# Update the issue
350
updated_issue = await gh.patch(
351
f"/repos/owner/repo/issues/{issue_number}",
352
data={"state": "closed"}
353
)
354
355
# Add a comment
356
await gh.post(
357
f"/repos/owner/repo/issues/{issue_number}/comments",
358
data={"body": "Fixed in latest release"}
359
)
360
```
361
362
### GraphQL Queries
363
364
```python
365
async def graphql_example():
366
async with aiohttp.ClientSession() as session:
367
gh = GitHubAPI(session, "my-app/1.0", oauth_token="your_token")
368
369
query = """
370
query($owner: String!, $name: String!) {
371
repository(owner: $owner, name: $name) {
372
name
373
description
374
stargazerCount
375
forkCount
376
issues(first: 10) {
377
nodes {
378
title
379
state
380
}
381
}
382
}
383
}
384
"""
385
386
result = await gh.graphql(query, owner="octocat", name="Hello-World")
387
repo = result['repository']
388
print(f"Repository: {repo['name']}")
389
print(f"Issues: {len(repo['issues']['nodes'])}")
390
```
391
392
### URL Template Variables
393
394
```python
395
async def template_variables():
396
async with aiohttp.ClientSession() as session:
397
gh = GitHubAPI(session, "my-app/1.0")
398
399
# Use template variables for dynamic URLs
400
repo_data = await gh.getitem(
401
"/repos/{owner}/{repo}",
402
{"owner": "octocat", "repo": "Hello-World"}
403
)
404
405
# Get specific issue
406
issue = await gh.getitem(
407
"/repos/{owner}/{repo}/issues/{issue_number}",
408
{
409
"owner": "octocat",
410
"repo": "Hello-World",
411
"issue_number": 1
412
}
413
)
414
```
415
416
## Constants
417
418
```python { .api }
419
JSON_CONTENT_TYPE: str = "application/json"
420
UTF_8_CHARSET: str = "utf-8"
421
JSON_UTF_8_CHARSET: str = "application/json; charset=utf-8"
422
ITERABLE_KEY: str = "items"
423
```
424
425
## Types
426
427
```python { .api }
428
from typing import Any, AsyncGenerator, Dict, Mapping, MutableMapping, Optional, Tuple
429
from uritemplate import variable
430
431
# Cache type for HTTP caching
432
CACHE_TYPE = MutableMapping[str, Tuple[Optional[str], Optional[str], Any, Optional[str]]]
433
434
# From sansio module
435
RateLimit = gidgethub.sansio.RateLimit
436
```