or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

actions.mdapi-client.mdapps.mdexceptions.mdhttp-implementations.mdindex.mdrouting.mdsansio.md

sansio.mddocs/

0

# Sans-I/O Functions

1

2

Pure functions for HTTP request/response processing, webhook validation, rate limiting, and URL formatting without performing any I/O operations. This sans-I/O design allows users to choose their preferred HTTP library while gidgethub handles GitHub-specific API details.

3

4

## Capabilities

5

6

### Webhook Validation

7

8

Validate the signature of GitHub webhook events using HMAC with SHA-256 or SHA-1.

9

10

```python { .api }

11

def validate_event(payload: bytes, *, signature: str, secret: str) -> None:

12

"""

13

Validate the signature of a webhook event.

14

15

Parameters:

16

- payload: The raw webhook payload bytes

17

- signature: The signature from X-Hub-Signature or X-Hub-Signature-256 header

18

- secret: The webhook secret configured in GitHub

19

20

Raises:

21

- ValidationFailure: If signature validation fails

22

"""

23

```

24

25

### Request Header Creation

26

27

Create GitHub-specific HTTP headers with proper user agent, API version, and authentication.

28

29

```python { .api }

30

def create_headers(

31

requester: str,

32

*,

33

accept: str = accept_format(),

34

oauth_token: Optional[str] = None,

35

jwt: Optional[str] = None

36

) -> Dict[str, str]:

37

"""

38

Create a dict representing GitHub-specific header fields.

39

40

Parameters:

41

- requester: User agent identifier (username or project name)

42

- accept: Accept header for API version and format

43

- oauth_token: Personal access token for authentication

44

- jwt: JWT bearer token for GitHub App authentication

45

46

Returns:

47

- Dict of lowercased header field names to values

48

49

Raises:

50

- ValueError: If both oauth_token and jwt are provided

51

"""

52

53

def accept_format(

54

*,

55

version: str = "v3",

56

media: Optional[str] = None,

57

json: bool = True

58

) -> str:

59

"""

60

Construct the specification of the format that a request should return.

61

62

Parameters:

63

- version: GitHub API version (default: "v3")

64

- media: Media type for alternative formats

65

- json: Whether to request JSON format

66

67

Returns:

68

- Accept header value

69

"""

70

```

71

72

### Response Processing

73

74

Decode HTTP responses and extract rate limit information and pagination links.

75

76

```python { .api }

77

def decipher_response(

78

status_code: int,

79

headers: Mapping[str, str],

80

body: bytes

81

) -> Tuple[Any, Optional[RateLimit], Optional[str]]:

82

"""

83

Decipher an HTTP response for a GitHub API request.

84

85

Parameters:

86

- status_code: HTTP response status code

87

- headers: HTTP response headers (with lowercase keys)

88

- body: HTTP response body bytes

89

90

Returns:

91

- Tuple of (decoded_body, rate_limit, next_page_url)

92

93

Raises:

94

- HTTPException: For non-success status codes

95

- RateLimitExceeded: When rate limit is exceeded

96

- InvalidField: For 422 responses with field errors

97

- ValidationError: For 422 responses with validation errors

98

"""

99

```

100

101

### URL Formatting

102

103

Construct and expand GitHub API URLs with template variables.

104

105

```python { .api }

106

def format_url(

107

url: str,

108

url_vars: Optional[variable.VariableValueDict],

109

*,

110

base_url: str = DOMAIN

111

) -> str:

112

"""

113

Construct a URL for the GitHub API.

114

115

Parameters:

116

- url: Absolute or relative URL (can be URI template)

117

- url_vars: Variables for URI template expansion

118

- base_url: Base URL for relative URLs (default: https://api.github.com)

119

120

Returns:

121

- Fully-qualified expanded URL

122

"""

123

```

124

125

### Event Processing

126

127

Process GitHub webhook events from HTTP requests.

128

129

```python { .api }

130

class Event:

131

"""Details of a GitHub webhook event."""

132

133

def __init__(self, data: Any, *, event: str, delivery_id: str) -> None:

134

"""

135

Initialize webhook event.

136

137

Parameters:

138

- data: Parsed webhook payload data

139

- event: Event type (e.g., "push", "pull_request")

140

- delivery_id: Unique delivery identifier

141

"""

142

143

@classmethod

144

def from_http(

145

cls,

146

headers: Mapping[str, str],

147

body: bytes,

148

*,

149

secret: Optional[str] = None

150

) -> "Event":

151

"""

152

Construct an event from HTTP headers and JSON body data.

153

154

Parameters:

155

- headers: HTTP headers (with lowercase keys)

156

- body: Raw HTTP body bytes

157

- secret: Webhook secret for validation (optional)

158

159

Returns:

160

- Event instance

161

162

Raises:

163

- BadRequest: For invalid content type

164

- ValidationFailure: For signature validation failures

165

"""

166

167

# Attributes

168

data: Any # Parsed webhook payload

169

event: str # Event type

170

delivery_id: str # Unique delivery ID

171

```

172

173

### Rate Limit Tracking

174

175

Track GitHub API rate limits from HTTP response headers.

176

177

```python { .api }

178

class RateLimit:

179

"""The rate limit imposed upon the requester."""

180

181

def __init__(self, *, limit: int, remaining: int, reset_epoch: float) -> None:

182

"""

183

Instantiate a RateLimit object.

184

185

Parameters:

186

- limit: Rate limit per hour

187

- remaining: Remaining requests in current window

188

- reset_epoch: Reset time in seconds since UTC epoch

189

"""

190

191

def __bool__(self) -> bool:

192

"""True if requests are remaining or the reset datetime has passed."""

193

194

def __str__(self) -> str:

195

"""Provide all details in a reasonable format."""

196

197

@classmethod

198

def from_http(cls, headers: Mapping[str, str]) -> Optional["RateLimit"]:

199

"""

200

Gather rate limit information from HTTP headers.

201

202

Parameters:

203

- headers: HTTP response headers (with lowercase keys)

204

205

Returns:

206

- RateLimit instance or None if headers not found

207

"""

208

209

# Attributes

210

limit: int # Requests per hour limit

211

remaining: int # Remaining requests

212

reset_datetime: datetime.datetime # Reset time (timezone-aware UTC)

213

```

214

215

## Usage Examples

216

217

### Webhook Validation

218

219

```python

220

import gidgethub.sansio

221

222

def handle_webhook(request_headers, request_body, webhook_secret):

223

try:

224

# Validate webhook signature

225

signature = request_headers.get('x-hub-signature-256',

226

request_headers.get('x-hub-signature'))

227

gidgethub.sansio.validate_event(request_body,

228

signature=signature,

229

secret=webhook_secret)

230

231

# Parse webhook event

232

event = gidgethub.sansio.Event.from_http(request_headers,

233

request_body,

234

secret=webhook_secret)

235

236

print(f"Received {event.event} event: {event.delivery_id}")

237

return event

238

239

except gidgethub.ValidationFailure as exc:

240

print(f"Webhook validation failed: {exc}")

241

raise

242

```

243

244

### Manual HTTP Request Processing

245

246

```python

247

import gidgethub.sansio

248

import httpx

249

250

async def make_github_request(url, oauth_token=None):

251

# Create headers

252

headers = gidgethub.sansio.create_headers(

253

"my-app/1.0",

254

oauth_token=oauth_token

255

)

256

257

# Make HTTP request

258

async with httpx.AsyncClient() as client:

259

response = await client.get(url, headers=headers)

260

261

# Process response

262

data, rate_limit, next_page = gidgethub.sansio.decipher_response(

263

response.status_code,

264

dict(response.headers),

265

response.content

266

)

267

268

print(f"Rate limit: {rate_limit}")

269

if next_page:

270

print(f"Next page: {next_page}")

271

272

return data

273

```

274

275

## Constants

276

277

```python { .api }

278

DOMAIN: str = "https://api.github.com" # Default GitHub API base URL

279

```

280

281

## Types

282

283

```python { .api }

284

from typing import Any, Dict, Mapping, Optional, Tuple

285

from uritemplate import variable

286

import datetime

287

288

# Type alias for URI template variables

289

VariableValueDict = variable.VariableValueDict

290

```