Niquests is a simple, yet elegant, HTTP library that is a drop-in replacement for Requests, which is under feature freeze.
—
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.
A user-created Request object used to represent an HTTP request before it's prepared for transmission.
class Request:
"""
A user-created Request object.
Used to prepare a PreparedRequest, which is sent to the server.
Contains the high-level request information provided by the user.
"""
def __init__(
self,
method: HttpMethodType,
url: str,
*,
headers: HeadersType | None = None,
files: MultiPartFilesType | MultiPartFilesAltType | None = None,
data: BodyType | None = None,
json: Any | None = None,
params: QueryParameterType | None = None,
auth: HttpAuthenticationType | None = None,
cookies: CookiesType | None = None,
hooks: HookType[PreparedRequest | Response] | None = None,
):
"""
Initialize a new Request object.
Args:
method: HTTP method (GET, POST, PUT, etc.)
url: Target URL
headers: HTTP headers dictionary
files: Files for multipart upload
data: Request body data
json: JSON data to serialize
params: Query parameters
auth: Authentication credentials
cookies: Cookies to include
hooks: Request/response hooks
"""
def prepare(self) -> PreparedRequest:
"""
Prepare the Request for transmission.
Returns:
PreparedRequest ready for sending
"""
# Instance attributes
method: HttpMethodType
url: str
headers: HeadersType | None
files: MultiPartFilesType | MultiPartFilesAltType | None
data: BodyType | None
json: Any | None
params: QueryParameterType | None
auth: HttpAuthenticationType | None
cookies: CookiesType | None
hooks: HookType | NoneThe fully prepared request object containing the exact bytes that will be sent to the server.
class PreparedRequest:
"""
The fully mutable PreparedRequest object.
Contains the exact bytes that will be sent to the server.
Generated from a Request object and should not be instantiated manually.
"""
def __init__(self):
"""Initialize a new PreparedRequest."""
def prepare(
self,
method: HttpMethodType | None = None,
url: str | None = None,
files: MultiPartFilesType | MultiPartFilesAltType | None = None,
data: BodyType | None = None,
json: Any | None = None,
headers: HeadersType | None = None,
params: QueryParameterType | None = None,
auth: HttpAuthenticationType | None = None,
cookies: CookiesType | None = None,
hooks: HookType[PreparedRequest | Response] | None = None,
):
"""
Prepare all aspects of the request.
Args:
method: HTTP method
url: Target URL
files: Files for upload
data: Request body
json: JSON data
headers: HTTP headers
params: Query parameters
auth: Authentication
cookies: Cookies
hooks: Lifecycle hooks
"""
def copy(self) -> PreparedRequest:
"""
Copy the PreparedRequest.
Returns:
New PreparedRequest instance with same data
"""
# Instance attributes
method: HttpMethodType | None
url: str | None
headers: HeadersType | None
body: bytes | None
hooks: HookType | None
path_url: str | NoneThe Response object containing a server's response to an HTTP request.
class Response:
"""
The Response object containing a server's response to an HTTP request.
Provides access to response data, headers, status code, and metadata.
"""
def __init__(self):
"""Initialize a new Response object."""
def __enter__(self) -> Response:
"""Enter context manager for response."""
return self
def __exit__(self, *args):
"""Exit context manager and close response."""
self.close()
@property
def content(self) -> bytes:
"""
Content of the response, in bytes.
Returns:
Response body as bytes
"""
@property
def text(self) -> str:
"""
Content of the response, in unicode.
If Response.encoding is None, encoding will be guessed using
charset-normalizer or chardet.
Returns:
Response body as string
"""
@property
def encoding(self) -> str | None:
"""
Encoding to decode with when accessing text.
Returns:
Character encoding name or None
"""
@encoding.setter
def encoding(self, value: str):
"""Set the encoding for text decoding."""
def json(self, **kwargs) -> Any:
"""
Return the json-encoded content of a response, if any.
Args:
**kwargs: Optional arguments to pass to json.loads()
Returns:
JSON-decoded response content
Raises:
JSONDecodeError: If response content is not valid JSON
"""
@property
def links(self) -> dict:
"""
Returns the parsed header links of the response, if any.
Returns:
Dictionary of link relations to links
"""
@property
def ok(self) -> bool:
"""
Returns True if status_code is less than 400, False otherwise.
Returns:
True for successful responses (status < 400)
"""
@property
def is_redirect(self) -> bool:
"""
True if this Response is a well-formed HTTP redirect.
Returns:
True if response is a redirect
"""
@property
def is_permanent_redirect(self) -> bool:
"""
True if this Response is a permanent redirect.
Returns:
True if response is a permanent redirect (301, 308)
"""
def iter_content(self, chunk_size: int = 1, decode_unicode: bool = False):
"""
Iterate over the response data in chunks.
Args:
chunk_size: Size of each chunk in bytes
decode_unicode: Whether to decode chunks to unicode
Yields:
Raw bytes or decoded strings if decode_unicode=True
"""
def iter_lines(self, chunk_size: int = 512, decode_unicode: bool = False, delimiter: str | None = None):
"""
Iterate over the response data one line at a time.
Args:
chunk_size: Size of chunks to read
decode_unicode: Whether to decode lines to unicode
delimiter: Line delimiter to use
Yields:
Lines from the response content
"""
def raise_for_status(self):
"""
Raise HTTPError if one occurred.
Raises:
HTTPError: If the response status indicates an error
"""
def close(self):
"""
Release the connection back to the pool.
Once this method has been called the underlying raw object
must not be accessed again.
"""
# Instance attributes
status_code: int # HTTP status code
headers: HeadersType # Response headers
raw: BaseHTTPResponse | None # Raw response object
url: str # Final URL after redirects
encoding: str | None # Character encoding
history: list[Response] # List of redirect responses
reason: str # HTTP status reason phrase
cookies: RequestsCookieJar # Response cookies
elapsed: datetime.timedelta # Time taken for request
request: PreparedRequest # The request that generated this response
connection: ConnectionInfo # Connection information
extensions: dict # Protocol extensions (WebSocket, etc.)Async version of the Response class for asynchronous HTTP operations.
class AsyncResponse(Response):
"""
The AsyncResponse object for asynchronous HTTP requests.
Inherits from Response and provides async methods for content access.
"""
async def aclose(self):
"""
Asynchronously release the connection back to the pool.
"""
def close(self):
"""
Synchronously close the response (calls aclose internally).
"""
async def json(self, **kwargs) -> Any:
"""
Asynchronously return the json-encoded content of the response.
Args:
**kwargs: Optional arguments to pass to json.loads()
Returns:
JSON-decoded response content
Raises:
JSONDecodeError: If response content is not valid JSON
"""
@property
async def content(self) -> bytes:
"""
Asynchronously access content of the response, in bytes.
Returns:
Response body as bytes
"""
@property
async def text(self) -> str:
"""
Asynchronously access content of the response, in unicode.
Returns:
Response body as string
"""
# Async context manager support
async def __aenter__(self) -> AsyncResponse:
"""Enter async context manager."""
return self
async def __aexit__(self, *args):
"""Exit async context manager and close response."""
await self.aclose()import niquests
# Create a Request object
request = niquests.Request(
method='POST',
url='https://api.example.com/users',
json={'name': 'John Doe', 'email': 'john@example.com'},
headers={'Content-Type': 'application/json'}
)
# Prepare the request
prepared = request.prepare()
# Send using a session
with niquests.Session() as session:
response = session.send(prepared)
print(response.status_code)import niquests
# Make a request and get response
response = niquests.get('https://api.example.com/users')
# Access response properties
print(f"Status Code: {response.status_code}")
print(f"Status Reason: {response.reason}")
print(f"Content Type: {response.headers.get('content-type')}")
print(f"Response Size: {len(response.content)} bytes")
# Check if request was successful
if response.status_code == 200:
# Parse JSON response
users = response.json()
print(f"Found {len(users)} users")
else:
print(f"Request failed: {response.status_code}")
# Raise exception for HTTP errors
try:
response.raise_for_status()
data = response.json()
except niquests.HTTPError as e:
print(f"HTTP Error: {e}")
except niquests.JSONDecodeError as e:
print(f"JSON Decode Error: {e}")# Text content with automatic encoding detection
response = niquests.get('https://example.com/page.html')
html_content = response.text
# Binary content
response = niquests.get('https://example.com/image.jpg')
image_bytes = response.content
# Save binary content to file
with open('downloaded_image.jpg', 'wb') as f:
f.write(response.content)
# Streaming large responses
response = niquests.get('https://example.com/large-file.zip', stream=True)
with open('large-file.zip', 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)import asyncio
import niquests
async def fetch_and_process():
response = await niquests.aget('https://api.example.com/data')
# Async context manager automatically closes response
async with response:
# Async content access
json_data = await response.json()
text_content = await response.text
binary_content = await response.content
return json_data
# Run async function
data = asyncio.run(fetch_and_process())response = niquests.get('https://httpbin.org/get')
# Connection information
print(f"HTTP Version: {response.connection.http_version}")
print(f"Remote Address: {response.connection.destination_address}")
# Timing information
print(f"Request took: {response.elapsed.total_seconds()} seconds")
# Redirect history
if response.history:
print(f"Request was redirected {len(response.history)} times")
for i, redirect in enumerate(response.history):
print(f" Redirect {i+1}: {redirect.status_code} -> {redirect.url}")
# Cookie handling
if response.cookies:
print("Response cookies:")
for cookie in response.cookies:
print(f" {cookie.name}: {cookie.value}")
# Link headers (for pagination, etc.)
if response.links:
print("Link headers:")
for rel, link in response.links.items():
print(f" {rel}: {link['url']}")import niquests
try:
response = niquests.get('https://api.example.com/protected', timeout=5.0)
response.raise_for_status() # Raise HTTPError for bad status codes
data = response.json()
print("Success:", data)
except niquests.ConnectionError:
print("Failed to connect to the server")
except niquests.Timeout:
print("Request timed out")
except niquests.HTTPError as e:
print(f"HTTP error {e.response.status_code}: {e.response.reason}")
# Can still access response data even for errors
if e.response.headers.get('content-type') == 'application/json':
error_details = e.response.json()
print(f"Error details: {error_details}")
except niquests.JSONDecodeError:
print("Response is not valid JSON")
print("Raw content:", response.text)class CustomResponse(niquests.Response):
"""Custom response class with additional methods."""
def is_json(self) -> bool:
"""Check if response content type is JSON."""
content_type = self.headers.get('content-type', '')
return 'application/json' in content_type.lower()
def safe_json(self, default=None):
"""Safely parse JSON, returning default if parsing fails."""
if self.is_json():
try:
return self.json()
except niquests.JSONDecodeError:
pass
return default
# Note: Custom response classes would need to be used with custom adaptersdef download_large_file(url, filename):
"""Download large file with progress tracking."""
response = niquests.get(url, stream=True)
response.raise_for_status()
total_size = int(response.headers.get('content-length', 0))
downloaded = 0
with open(filename, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk: # Filter out keep-alive chunks
f.write(chunk)
downloaded += len(chunk)
if total_size > 0:
progress = (downloaded / total_size) * 100
print(f"Downloaded: {progress:.1f}%")
print(f"Download complete: {filename}")# Base HTTP types
HttpMethodType = Literal["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]
HeadersType = Union[dict, Headers, CaseInsensitiveDict]
CookiesType = Union[dict, RequestsCookieJar]
# Request body types
BodyType = Union[str, bytes, dict, list, IOBase]
AsyncBodyType = Union[str, bytes, dict, list, IOBase, AsyncIterable[bytes]]
# Multipart file types
MultiPartFilesType = dict
MultiPartFilesAltType = dict
# Query parameters
QueryParameterType = Union[dict, list, bytes]
# Authentication types
HttpAuthenticationType = Union[Tuple[str, str], HTTPBasicAuth, BearerTokenAuth]
AsyncHttpAuthenticationType = Union[HttpAuthenticationType, Callable]
# Hook types
HookType = dict
# Connection info
ConnectionInfo = object # Detailed connection metadataInstall with Tessl CLI
npx tessl i tessl/pypi-niquests