1- """This module provides the main client and helper classes for interacting with the Mailjet API .
1+ """Mailjet API v3, v3.1, and v1 Python wrapper .
22
3- The `mailjet_rest.client` module includes the core `Client` class for managing
4- API requests, configuration, and error handling, as well as utility functions
5- and classes for building URLs and managing endpoints .
3+ This module provides the main client and helper classes for interacting
4+ with the Mailjet API. It handles authentication, secure URL construction,
5+ dynamic endpoint resolution, and request execution .
66"""
77
88from __future__ import annotations
6060
6161
6262def prepare_url (match : Any ) -> str :
63- """Replace capital letters in the input string with a dash prefix and converts them to lowercase.
63+ """Replace capital letters in the input string with a dash prefix and convert to lowercase.
6464
6565 Args:
66- match (Any): A regex match object.
66+ match (Any): A regex match object containing a capital letter .
6767
6868 Returns:
69- str: A formatted URL string fragment.
69+ str: A formatted URL string fragment (e.g., '_m') .
7070 """
7171 return f"_{ match .group (0 ).lower ()} "
7272
@@ -151,7 +151,15 @@ def logging_handler(response: requests.Response) -> None: # noqa: ARG001
151151
152152@dataclass
153153class Config :
154- """Configuration settings for interacting with the Mailjet API."""
154+ """Configuration settings for interacting with the Mailjet API.
155+
156+ Attributes:
157+ ALLOWED_ROOT_DOMAIN (ClassVar[str]): The permitted root domain to prevent SSRF.
158+ version (str): The API version to use (e.g., 'v3', 'v3.1', 'v1').
159+ api_url (str): The base URL for the Mailjet API.
160+ user_agent (str): The User-Agent string sent with API requests.
161+ timeout (int | float | tuple[float, float] | None): Request timeout in seconds.
162+ """
155163
156164 ALLOWED_ROOT_DOMAIN : ClassVar [str ] = "mailjet.com"
157165
@@ -161,7 +169,11 @@ class Config:
161169 timeout : int | float | tuple [float , float ] | None = 60
162170
163171 def __post_init__ (self ) -> None :
164- """Validate configuration for secure transport and resource limits (OWASP Input Validation)."""
172+ """Validate configuration for secure transport and resource limits (OWASP Input Validation).
173+
174+ Raises:
175+ ValueError: If the URL scheme is insecure or timeout bounds are violated.
176+ """
165177 SecurityGuard .validate_config_url (self .api_url , allowed_root_domain = self .ALLOWED_ROOT_DOMAIN )
166178 if not self .api_url .endswith ("/" ):
167179 self .api_url += "/"
@@ -183,13 +195,13 @@ def _validate_timeout(t: float) -> None:
183195 _validate_timeout (cast ("float" , self .timeout ))
184196
185197 def __getitem__ (self , key : str ) -> tuple [str , dict [str , str ]]:
186- """Retrieve the API endpoint URL and headers for a given key.
198+ """Retrieve the base API endpoint URL and default headers for a given key.
187199
188200 Args:
189- key (str): The endpoint key name.
201+ key (str): The raw endpoint key name.
190202
191203 Returns:
192- tuple[str, dict[str, str]]: The constructed URL and headers dictionary.
204+ tuple[str, dict[str, str]]: A tuple containing the base URL and the headers dictionary.
193205 """
194206 action = key .split ("_" , maxsplit = 1 )[0 ]
195207 name_lower = key .lower ()
@@ -212,14 +224,18 @@ def __getitem__(self, key: str) -> tuple[str, dict[str, str]]:
212224
213225
214226class Endpoint :
215- """A class representing a specific Mailjet API endpoint."""
227+ """A class representing a specific Mailjet API endpoint.
228+
229+ This class provides methods to execute standard HTTP operations (GET, POST, PUT, DELETE)
230+ dynamically based on the requested resource.
231+ """
216232
217233 def __init__ (self , client : Client , name : str ) -> None :
218234 """Initialize a new Endpoint instance.
219235
220236 Args:
221- client (Client): The core client instance .
222- name (str): The endpoint resource name.
237+ client (Client): The active API client managing the session .
238+ name (str): The resource name (e.g., 'contact', 'send', 'contactslist_csvdata') .
223239 """
224240 self .client = client
225241 self .name = name
@@ -327,19 +343,19 @@ def __call__(
327343 """Execute the API call directly.
328344
329345 Args:
330- method (Literal["GET", "POST", "PUT", "DELETE"]): The HTTP method.
331- filters (dict[str, Any] | None): Query parameters.
332- data (dict[str, Any] | list[Any] | str | None): Request payload.
333- headers (dict[str, str] | None): Custom headers.
334- id (int | str | None): Primary resource ID.
335- action_id (int | str | None): Sub-action ID .
336- timeout (int | float | tuple[float, float] | None): Request timeout.
337- ensure_ascii (bool | None): Ensure ASCII serialization (Deprecated) .
338- data_encoding (str | None): Data encoding string (Deprecated) .
339- **kwargs (Any): Additional arguments .
346+ method (Literal["GET", "POST", "PUT", "DELETE"], optional ): The HTTP method. Defaults to "GET" .
347+ filters (dict[str, Any] | None, optional ): Query parameters to append to the URL .
348+ data (dict[str, Any] | list[Any] | str | None, optional ): The payload for the request body .
349+ headers (dict[str, str] | None, optional ): Additional HTTP headers to send .
350+ id (int | str | None, optional ): The primary resource ID.
351+ action_id (int | str | None, optional ): The secondary ID or action string for nested resources .
352+ timeout (int | float | tuple[float, float] | None, optional ): Custom timeout for this request .
353+ ensure_ascii (bool | None, optional ): Deprecated. Ensure ASCII serialization.
354+ data_encoding (str | None, optional ): Deprecated. Target encoding string for the payload .
355+ **kwargs (Any): Additional parameters passed to `requests.Session.request` .
340356
341357 Returns:
342- requests.Response: The HTTP response from the API.
358+ requests.Response: The HTTP response from the Mailjet API.
343359 """
344360 if id is None and action_id is not None :
345361 id = action_id # noqa: A001
@@ -473,15 +489,36 @@ def delete(self, id: int | str, action_id: int | str | None = None, **kwargs: An
473489
474490
475491class Client :
476- """A client for interacting with the Mailjet API."""
492+ """The primary client for interacting with the Mailjet API.
493+
494+ Handles authentication, session management, configuration, and dynamic
495+ endpoint resolution via magic methods (`__getattr__`).
496+
497+ Examples:
498+ >>> client = Client(auth=(API_KEY, API_SECRET), version='v3.1')
499+ >>> response = client.send.create(data=payload)
500+ """
477501
478502 def __init__ (
479503 self ,
480504 auth : tuple [str , str ] | str | None = None ,
481505 config : Config | None = None ,
482506 ** kwargs : Any ,
483507 ) -> None :
484- """Initialize a new Client instance."""
508+ """Initialize a new Mailjet API Client instance.
509+
510+ Args:
511+ auth (tuple[str, str] | str | None, optional): Authentication credentials.
512+ Use a tuple `(API_KEY, API_SECRET)` for Basic Auth (Email API).
513+ Use a string `TOKEN` for Bearer Auth (Content API v1).
514+ config (Config | None, optional): A pre-configured `Config` instance.
515+ **kwargs (Any): Configuration overrides if `config` is not provided
516+ (e.g., `version='v3.1'`, `timeout=10`).
517+
518+ Raises:
519+ ValueError: If the provided `auth` credentials are invalid or empty.
520+ TypeError: If the `auth` type is neither a tuple nor a string.
521+ """
485522 self .config = config or Config (** kwargs )
486523 self .session = requests .Session ()
487524
@@ -680,26 +717,29 @@ def api_call(
680717 data_encoding : str | None = None ,
681718 ** kwargs : Any ,
682719 ) -> requests .Response :
683- """Perform the actual network request using the persistent session.
720+ """Perform the actual network request using the persistent HTTP session.
721+
722+ This method acts as the core orchestrator, handling telemetry extraction,
723+ payload serialization, security guardrails, and centralized logging.
684724
685725 Args:
686726 method (Literal["GET", "POST", "PUT", "DELETE"]): The HTTP method.
687- url (str): The fully constructed URL.
688- filters (dict[str, Any] | None): Query parameters.
689- data (dict[str, Any] | list[Any] | str | None): Request payload.
690- headers (dict[str, str] | None): HTTP headers.
691- timeout (int | float | tuple[float, float] | None): Request timeout.
692- ensure_ascii (bool | None): Ensure ASCII encoding (deprecated) .
693- data_encoding (str | None): Data encoding (deprecated) .
694- **kwargs (Any): Additional arguments.
727+ url (str): The fully constructed API URL.
728+ filters (dict[str, Any] | None, optional ): Query parameters.
729+ data (dict[str, Any] | list[Any] | str | None, optional ): Request payload.
730+ headers (dict[str, str] | None, optional): Custom HTTP headers.
731+ timeout (int | float | tuple[float, float] | None, optional ): Request timeout.
732+ ensure_ascii (bool | None, optional ): Deprecated. Ensure ASCII encoding.
733+ data_encoding (str | None, optional ): Deprecated. Data encoding string .
734+ **kwargs (Any): Additional arguments passed to `requests.Session.request` .
695735
696736 Returns:
697- requests.Response: The HTTP response from the API.
737+ requests.Response: The HTTP response from the Mailjet API.
698738
699739 Raises:
700740 TimeoutError: If the API request times out.
701- CriticalApiError: If there is a connection failure.
702- ApiError: For other unhandled request exceptions.
741+ CriticalApiError: If a connection failure occurs .
742+ ApiError: For other unhandled network exceptions.
703743 """
704744 request_data = self ._prepare_payload (data , ensure_ascii , data_encoding )
705745 timeout_val = timeout if timeout is not None else self .config .timeout
0 commit comments