import gzip import json as json_module import re from json.decoder import JSONDecodeError from typing import Any from typing import Callable from typing import List from typing import Mapping from typing import MutableMapping from typing import Optional from typing import Pattern from typing import Tuple from typing import Union from urllib.parse import parse_qsl from urllib.parse import urlparse
from requests import PreparedRequest from urllib3.util.url import parse_url
def _filter_dict_recursively(
dict1: Mapping[Any, Any], dict2: Mapping[Any, Any]
) -> Mapping[Any, Any]:
filtered_dict = {} for k, val in dict1.items(): if k in dict2: if isinstance(val, dict):
val = _filter_dict_recursively(val, dict2[k])
filtered_dict[k] = val
Parameters
----------
params : dict or list
JSON object provided to 'json' arg of request or a part of it if used in
conjunction with ``strict_match=False``.
strict_match : bool, default=True
Applied only when JSON object is a dictionary. If set to ``True``, validates that all keys of JSON object match. If set to ``False``, original request may contain additional keys.
if ( not strict_match and isinstance(json_body, dict) and isinstance(json_params, dict)
): # filter down to just the params specified in the matcher
json_body = _filter_dict_recursively(json_body, json_params)
ifnot valid:
reason = ( "URL fragment identifier is different: "# type: ignore[str-bytes-safe]
f"{identifier} doesn't match {url_fragment}"
)
return valid, reason
return match
def query_param_matcher(
params: Optional[MutableMapping[str, Any]], *, strict_match: bool = True
) -> Callable[..., Any]: """Matcher to match 'params' argument in request.
Parameters
----------
params : dict
The same as provided to request or a part of it if used in
conjunction with ``strict_match=False``.
strict_match : bool, default=True If set to ``True``, validates that all parameters match. If set to ``False``, original request may contain additional parameters.
Returns
-------
Callable
Matcher function.
"""
params_dict = params or {}
for k, v in params_dict.items(): if isinstance(v, (int, float)):
params_dict[k] = str(v)
ifnot strict_match: # filter down to just the params specified in the matcher
request_params_dict = {
k: v for k, v in request_params_dict.items() if k in params_dict
}
ifnot valid:
reason = f"Parameters do not match. {request_params_dict} doesn't match {params_dict}" ifnot strict_match:
reason += ( "\nYou can use `strict_match=True` to do a strict parameters check."
)
return valid, reason
return match
def query_string_matcher(query: Optional[str]) -> Callable[..., Any]: """
Matcher to match query string part of request
:param query: (str), same as constructed by request
:return: (func) matcher """
def request_kwargs_matcher(kwargs: Optional[Mapping[str, Any]]) -> Callable[..., Any]: """
Matcher to match keyword arguments provided to request
:param kwargs: (dict), keyword arguments, same as provided to request
:return: (func) matcher """
def match(request: PreparedRequest) -> Tuple[bool, str]:
reason = ""
kwargs_dict = kwargs or {} # validate only kwargs that were requested for comparison, skip defaults
req_kwargs = request.req_kwargs # type: ignore[attr-defined]
request_kwargs = {k: v for k, v in req_kwargs.items() if k in kwargs_dict}
def multipart_matcher(
files: Mapping[str, Any], data: Optional[Mapping[str, str]] = None
) -> Callable[..., Any]: """
Matcher to match 'multipart/form-data' content-type.
This function constructs request body and headers from provided 'data'and'files'
arguments and compares to actual request
:param files: (dict), same as provided to request
:param data: (dict), same as provided to request
:return: (func) matcher """ ifnot files: raise TypeError("files argument cannot be empty")
# replace boundary value in header and in body, since by default # urllib3.filepost.encode_multipart_formdata dynamically calculates # random boundary alphanumeric value
request_content_type = request.headers["Content-Type"]
prepared_content_type = prepared.headers["Content-Type"].replace(
prepared_boundary, request_boundary
)
if isinstance(prepared_body, bytes): # since headers always come as str, need to convert to bytes
prepared_boundary = prepared_boundary.encode("utf-8") # type: ignore[assignment]
request_boundary = request_boundary.encode("utf-8") # type: ignore[assignment]
def header_matcher(
headers: Mapping[str, Union[str, Pattern[str]]], strict_match: bool = False
) -> Callable[..., Any]: """
Matcher to match 'headers' argument in request using the responses library.
Because ``requests`` will send several standard headers in addition to what
was specified by your code, request headers that are additional to the ones
passed to the matcher are ignored by default. You can change this behaviour
by passing ``strict_match=True``.
:param headers: (dict), same as provided to request
:param strict_match: (bool), whether headers in addition to those specified in the matcher should cause the match to fail.
:return: (func) matcher """
def _compare_with_regex(request_headers: Union[Mapping[Any, Any], Any]) -> bool: if strict_match and len(request_headers) != len(headers): returnFalse
for k, v in headers.items(): if request_headers.get(k) isnotNone: if isinstance(v, re.Pattern): if re.match(v, request_headers[k]) isNone: returnFalse else: ifnot v == request_headers[k]: returnFalse else: returnFalse
ifnot strict_match: # filter down to just the headers specified in the matcher
request_headers = {k: v for k, v in request_headers.items() if k in headers}
valid = _compare_with_regex(request_headers)
ifnot valid: return ( False,
f"Headers do not match: {request_headers} doesn't match {headers}",
)
return valid, ""
return match
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet am 2026-08-25)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.