0
# Request and Response Models
1
2
Core data structures for representing HTTP requests and responses. These classes provide access to all HTTP components including headers, body content, status codes, and metadata. They form the foundation of all HTTP operations in niquests.
3
4
## Capabilities
5
6
### Request Class
7
8
A user-created Request object used to represent an HTTP request before it's prepared for transmission.
9
10
```python { .api }
11
class Request:
12
"""
13
A user-created Request object.
14
15
Used to prepare a PreparedRequest, which is sent to the server.
16
Contains the high-level request information provided by the user.
17
"""
18
19
def __init__(
20
self,
21
method: HttpMethodType,
22
url: str,
23
*,
24
headers: HeadersType | None = None,
25
files: MultiPartFilesType | MultiPartFilesAltType | None = None,
26
data: BodyType | None = None,
27
json: Any | None = None,
28
params: QueryParameterType | None = None,
29
auth: HttpAuthenticationType | None = None,
30
cookies: CookiesType | None = None,
31
hooks: HookType[PreparedRequest | Response] | None = None,
32
):
33
"""
34
Initialize a new Request object.
35
36
Args:
37
method: HTTP method (GET, POST, PUT, etc.)
38
url: Target URL
39
headers: HTTP headers dictionary
40
files: Files for multipart upload
41
data: Request body data
42
json: JSON data to serialize
43
params: Query parameters
44
auth: Authentication credentials
45
cookies: Cookies to include
46
hooks: Request/response hooks
47
"""
48
49
def prepare(self) -> PreparedRequest:
50
"""
51
Prepare the Request for transmission.
52
53
Returns:
54
PreparedRequest ready for sending
55
"""
56
57
# Instance attributes
58
method: HttpMethodType
59
url: str
60
headers: HeadersType | None
61
files: MultiPartFilesType | MultiPartFilesAltType | None
62
data: BodyType | None
63
json: Any | None
64
params: QueryParameterType | None
65
auth: HttpAuthenticationType | None
66
cookies: CookiesType | None
67
hooks: HookType | None
68
```
69
70
### PreparedRequest Class
71
72
The fully prepared request object containing the exact bytes that will be sent to the server.
73
74
```python { .api }
75
class PreparedRequest:
76
"""
77
The fully mutable PreparedRequest object.
78
79
Contains the exact bytes that will be sent to the server.
80
Generated from a Request object and should not be instantiated manually.
81
"""
82
83
def __init__(self):
84
"""Initialize a new PreparedRequest."""
85
86
def prepare(
87
self,
88
method: HttpMethodType | None = None,
89
url: str | None = None,
90
files: MultiPartFilesType | MultiPartFilesAltType | None = None,
91
data: BodyType | None = None,
92
json: Any | None = None,
93
headers: HeadersType | None = None,
94
params: QueryParameterType | None = None,
95
auth: HttpAuthenticationType | None = None,
96
cookies: CookiesType | None = None,
97
hooks: HookType[PreparedRequest | Response] | None = None,
98
):
99
"""
100
Prepare all aspects of the request.
101
102
Args:
103
method: HTTP method
104
url: Target URL
105
files: Files for upload
106
data: Request body
107
json: JSON data
108
headers: HTTP headers
109
params: Query parameters
110
auth: Authentication
111
cookies: Cookies
112
hooks: Lifecycle hooks
113
"""
114
115
def copy(self) -> PreparedRequest:
116
"""
117
Copy the PreparedRequest.
118
119
Returns:
120
New PreparedRequest instance with same data
121
"""
122
123
# Instance attributes
124
method: HttpMethodType | None
125
url: str | None
126
headers: HeadersType | None
127
body: bytes | None
128
hooks: HookType | None
129
path_url: str | None
130
```
131
132
### Response Class
133
134
The Response object containing a server's response to an HTTP request.
135
136
```python { .api }
137
class Response:
138
"""
139
The Response object containing a server's response to an HTTP request.
140
141
Provides access to response data, headers, status code, and metadata.
142
"""
143
144
def __init__(self):
145
"""Initialize a new Response object."""
146
147
def __enter__(self) -> Response:
148
"""Enter context manager for response."""
149
return self
150
151
def __exit__(self, *args):
152
"""Exit context manager and close response."""
153
self.close()
154
155
@property
156
def content(self) -> bytes:
157
"""
158
Content of the response, in bytes.
159
160
Returns:
161
Response body as bytes
162
"""
163
164
@property
165
def text(self) -> str:
166
"""
167
Content of the response, in unicode.
168
169
If Response.encoding is None, encoding will be guessed using
170
charset-normalizer or chardet.
171
172
Returns:
173
Response body as string
174
"""
175
176
@property
177
def encoding(self) -> str | None:
178
"""
179
Encoding to decode with when accessing text.
180
181
Returns:
182
Character encoding name or None
183
"""
184
185
@encoding.setter
186
def encoding(self, value: str):
187
"""Set the encoding for text decoding."""
188
189
def json(self, **kwargs) -> Any:
190
"""
191
Return the json-encoded content of a response, if any.
192
193
Args:
194
**kwargs: Optional arguments to pass to json.loads()
195
196
Returns:
197
JSON-decoded response content
198
199
Raises:
200
JSONDecodeError: If response content is not valid JSON
201
"""
202
203
@property
204
def links(self) -> dict:
205
"""
206
Returns the parsed header links of the response, if any.
207
208
Returns:
209
Dictionary of link relations to links
210
"""
211
212
@property
213
def ok(self) -> bool:
214
"""
215
Returns True if status_code is less than 400, False otherwise.
216
217
Returns:
218
True for successful responses (status < 400)
219
"""
220
221
@property
222
def is_redirect(self) -> bool:
223
"""
224
True if this Response is a well-formed HTTP redirect.
225
226
Returns:
227
True if response is a redirect
228
"""
229
230
@property
231
def is_permanent_redirect(self) -> bool:
232
"""
233
True if this Response is a permanent redirect.
234
235
Returns:
236
True if response is a permanent redirect (301, 308)
237
"""
238
239
def iter_content(self, chunk_size: int = 1, decode_unicode: bool = False):
240
"""
241
Iterate over the response data in chunks.
242
243
Args:
244
chunk_size: Size of each chunk in bytes
245
decode_unicode: Whether to decode chunks to unicode
246
247
Yields:
248
Raw bytes or decoded strings if decode_unicode=True
249
"""
250
251
def iter_lines(self, chunk_size: int = 512, decode_unicode: bool = False, delimiter: str | None = None):
252
"""
253
Iterate over the response data one line at a time.
254
255
Args:
256
chunk_size: Size of chunks to read
257
decode_unicode: Whether to decode lines to unicode
258
delimiter: Line delimiter to use
259
260
Yields:
261
Lines from the response content
262
"""
263
264
def raise_for_status(self):
265
"""
266
Raise HTTPError if one occurred.
267
268
Raises:
269
HTTPError: If the response status indicates an error
270
"""
271
272
def close(self):
273
"""
274
Release the connection back to the pool.
275
276
Once this method has been called the underlying raw object
277
must not be accessed again.
278
"""
279
280
# Instance attributes
281
status_code: int # HTTP status code
282
headers: HeadersType # Response headers
283
raw: BaseHTTPResponse | None # Raw response object
284
url: str # Final URL after redirects
285
encoding: str | None # Character encoding
286
history: list[Response] # List of redirect responses
287
reason: str # HTTP status reason phrase
288
cookies: RequestsCookieJar # Response cookies
289
elapsed: datetime.timedelta # Time taken for request
290
request: PreparedRequest # The request that generated this response
291
connection: ConnectionInfo # Connection information
292
extensions: dict # Protocol extensions (WebSocket, etc.)
293
```
294
295
### AsyncResponse Class
296
297
Async version of the Response class for asynchronous HTTP operations.
298
299
```python { .api }
300
class AsyncResponse(Response):
301
"""
302
The AsyncResponse object for asynchronous HTTP requests.
303
304
Inherits from Response and provides async methods for content access.
305
"""
306
307
async def aclose(self):
308
"""
309
Asynchronously release the connection back to the pool.
310
"""
311
312
def close(self):
313
"""
314
Synchronously close the response (calls aclose internally).
315
"""
316
317
async def json(self, **kwargs) -> Any:
318
"""
319
Asynchronously return the json-encoded content of the response.
320
321
Args:
322
**kwargs: Optional arguments to pass to json.loads()
323
324
Returns:
325
JSON-decoded response content
326
327
Raises:
328
JSONDecodeError: If response content is not valid JSON
329
"""
330
331
@property
332
async def content(self) -> bytes:
333
"""
334
Asynchronously access content of the response, in bytes.
335
336
Returns:
337
Response body as bytes
338
"""
339
340
@property
341
async def text(self) -> str:
342
"""
343
Asynchronously access content of the response, in unicode.
344
345
Returns:
346
Response body as string
347
"""
348
349
# Async context manager support
350
async def __aenter__(self) -> AsyncResponse:
351
"""Enter async context manager."""
352
return self
353
354
async def __aexit__(self, *args):
355
"""Exit async context manager and close response."""
356
await self.aclose()
357
```
358
359
## Usage Examples
360
361
### Creating and Using Requests
362
363
```python
364
import niquests
365
366
# Create a Request object
367
request = niquests.Request(
368
method='POST',
369
url='https://api.example.com/users',
370
json={'name': 'John Doe', 'email': 'john@example.com'},
371
headers={'Content-Type': 'application/json'}
372
)
373
374
# Prepare the request
375
prepared = request.prepare()
376
377
# Send using a session
378
with niquests.Session() as session:
379
response = session.send(prepared)
380
print(response.status_code)
381
```
382
383
### Working with Responses
384
385
```python
386
import niquests
387
388
# Make a request and get response
389
response = niquests.get('https://api.example.com/users')
390
391
# Access response properties
392
print(f"Status Code: {response.status_code}")
393
print(f"Status Reason: {response.reason}")
394
print(f"Content Type: {response.headers.get('content-type')}")
395
print(f"Response Size: {len(response.content)} bytes")
396
397
# Check if request was successful
398
if response.status_code == 200:
399
# Parse JSON response
400
users = response.json()
401
print(f"Found {len(users)} users")
402
else:
403
print(f"Request failed: {response.status_code}")
404
405
# Raise exception for HTTP errors
406
try:
407
response.raise_for_status()
408
data = response.json()
409
except niquests.HTTPError as e:
410
print(f"HTTP Error: {e}")
411
except niquests.JSONDecodeError as e:
412
print(f"JSON Decode Error: {e}")
413
```
414
415
### Response Content Handling
416
417
```python
418
# Text content with automatic encoding detection
419
response = niquests.get('https://example.com/page.html')
420
html_content = response.text
421
422
# Binary content
423
response = niquests.get('https://example.com/image.jpg')
424
image_bytes = response.content
425
426
# Save binary content to file
427
with open('downloaded_image.jpg', 'wb') as f:
428
f.write(response.content)
429
430
# Streaming large responses
431
response = niquests.get('https://example.com/large-file.zip', stream=True)
432
with open('large-file.zip', 'wb') as f:
433
for chunk in response.iter_content(chunk_size=8192):
434
f.write(chunk)
435
```
436
437
### Async Response Handling
438
439
```python
440
import asyncio
441
import niquests
442
443
async def fetch_and_process():
444
response = await niquests.aget('https://api.example.com/data')
445
446
# Async context manager automatically closes response
447
async with response:
448
# Async content access
449
json_data = await response.json()
450
text_content = await response.text
451
binary_content = await response.content
452
453
return json_data
454
455
# Run async function
456
data = asyncio.run(fetch_and_process())
457
```
458
459
### Response Metadata
460
461
```python
462
response = niquests.get('https://httpbin.org/get')
463
464
# Connection information
465
print(f"HTTP Version: {response.connection.http_version}")
466
print(f"Remote Address: {response.connection.destination_address}")
467
468
# Timing information
469
print(f"Request took: {response.elapsed.total_seconds()} seconds")
470
471
# Redirect history
472
if response.history:
473
print(f"Request was redirected {len(response.history)} times")
474
for i, redirect in enumerate(response.history):
475
print(f" Redirect {i+1}: {redirect.status_code} -> {redirect.url}")
476
477
# Cookie handling
478
if response.cookies:
479
print("Response cookies:")
480
for cookie in response.cookies:
481
print(f" {cookie.name}: {cookie.value}")
482
483
# Link headers (for pagination, etc.)
484
if response.links:
485
print("Link headers:")
486
for rel, link in response.links.items():
487
print(f" {rel}: {link['url']}")
488
```
489
490
### Error Handling with Models
491
492
```python
493
import niquests
494
495
try:
496
response = niquests.get('https://api.example.com/protected', timeout=5.0)
497
response.raise_for_status() # Raise HTTPError for bad status codes
498
499
data = response.json()
500
print("Success:", data)
501
502
except niquests.ConnectionError:
503
print("Failed to connect to the server")
504
except niquests.Timeout:
505
print("Request timed out")
506
except niquests.HTTPError as e:
507
print(f"HTTP error {e.response.status_code}: {e.response.reason}")
508
# Can still access response data even for errors
509
if e.response.headers.get('content-type') == 'application/json':
510
error_details = e.response.json()
511
print(f"Error details: {error_details}")
512
except niquests.JSONDecodeError:
513
print("Response is not valid JSON")
514
print("Raw content:", response.text)
515
```
516
517
## Advanced Features
518
519
### Custom Response Processing
520
521
```python
522
class CustomResponse(niquests.Response):
523
"""Custom response class with additional methods."""
524
525
def is_json(self) -> bool:
526
"""Check if response content type is JSON."""
527
content_type = self.headers.get('content-type', '')
528
return 'application/json' in content_type.lower()
529
530
def safe_json(self, default=None):
531
"""Safely parse JSON, returning default if parsing fails."""
532
if self.is_json():
533
try:
534
return self.json()
535
except niquests.JSONDecodeError:
536
pass
537
return default
538
539
# Note: Custom response classes would need to be used with custom adapters
540
```
541
542
### Response Streaming
543
544
```python
545
def download_large_file(url, filename):
546
"""Download large file with progress tracking."""
547
response = niquests.get(url, stream=True)
548
response.raise_for_status()
549
550
total_size = int(response.headers.get('content-length', 0))
551
downloaded = 0
552
553
with open(filename, 'wb') as f:
554
for chunk in response.iter_content(chunk_size=8192):
555
if chunk: # Filter out keep-alive chunks
556
f.write(chunk)
557
downloaded += len(chunk)
558
559
if total_size > 0:
560
progress = (downloaded / total_size) * 100
561
print(f"Downloaded: {progress:.1f}%")
562
563
print(f"Download complete: {filename}")
564
```
565
566
## Type Definitions
567
568
```python { .api }
569
# Base HTTP types
570
HttpMethodType = Literal["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]
571
HeadersType = Union[dict, Headers, CaseInsensitiveDict]
572
CookiesType = Union[dict, RequestsCookieJar]
573
574
# Request body types
575
BodyType = Union[str, bytes, dict, list, IOBase]
576
AsyncBodyType = Union[str, bytes, dict, list, IOBase, AsyncIterable[bytes]]
577
578
# Multipart file types
579
MultiPartFilesType = dict
580
MultiPartFilesAltType = dict
581
582
# Query parameters
583
QueryParameterType = Union[dict, list, bytes]
584
585
# Authentication types
586
HttpAuthenticationType = Union[Tuple[str, str], HTTPBasicAuth, BearerTokenAuth]
587
AsyncHttpAuthenticationType = Union[HttpAuthenticationType, Callable]
588
589
# Hook types
590
HookType = dict
591
592
# Connection info
593
ConnectionInfo = object # Detailed connection metadata
594
```