pyrestkit 0.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. pyrestkit/__init__.py +35 -0
  2. pyrestkit/ai/__init__.py +17 -0
  3. pyrestkit/ai/analyzer.py +137 -0
  4. pyrestkit/ai/client.py +101 -0
  5. pyrestkit/ai/config/__init__.py +5 -0
  6. pyrestkit/ai/config/ai_config.py +200 -0
  7. pyrestkit/ai/exceptions.py +22 -0
  8. pyrestkit/ai/generators/__init__.py +0 -0
  9. pyrestkit/ai/models.py +44 -0
  10. pyrestkit/ai/parsers/__init__.py +0 -0
  11. pyrestkit/ai/prompts/failure_analysis.md +21 -0
  12. pyrestkit/ai/provider.py +58 -0
  13. pyrestkit/ai/providers/__init__.py +21 -0
  14. pyrestkit/ai/providers/anthropic.py +85 -0
  15. pyrestkit/ai/providers/azure_openai.py +84 -0
  16. pyrestkit/ai/providers/base.py +39 -0
  17. pyrestkit/ai/providers/bedrock.py +70 -0
  18. pyrestkit/ai/providers/cohere.py +82 -0
  19. pyrestkit/ai/providers/gemini.py +113 -0
  20. pyrestkit/ai/providers/groq.py +81 -0
  21. pyrestkit/ai/providers/mistral.py +88 -0
  22. pyrestkit/ai/providers/ollama.py +82 -0
  23. pyrestkit/ai/providers/openai.py +124 -0
  24. pyrestkit/ai/utils/__init__.py +0 -0
  25. pyrestkit/ai/utils/prompt_loader.py +52 -0
  26. pyrestkit/assertions/__init__.py +7 -0
  27. pyrestkit/assertions/assertion_exception.py +4 -0
  28. pyrestkit/assertions/response_assertions.py +181 -0
  29. pyrestkit/auth/__init__.py +11 -0
  30. pyrestkit/auth/auth_strategy.py +14 -0
  31. pyrestkit/auth/authentication_manager.py +35 -0
  32. pyrestkit/auth/strategies/__init__.py +5 -0
  33. pyrestkit/auth/strategies/api_key_auth.py +18 -0
  34. pyrestkit/auth/strategies/basic_auth.py +24 -0
  35. pyrestkit/auth/strategies/bearer_auth.py +17 -0
  36. pyrestkit/auth/token_cache.py +44 -0
  37. pyrestkit/auth/token_manager.py +32 -0
  38. pyrestkit/auth/token_provider.py +12 -0
  39. pyrestkit/auth/token_response.py +13 -0
  40. pyrestkit/builder/__init__.py +5 -0
  41. pyrestkit/builder/fluent_request_builder.py +167 -0
  42. pyrestkit/clients/__init__.py +7 -0
  43. pyrestkit/clients/base_client.py +68 -0
  44. pyrestkit/clients/user_client.py +66 -0
  45. pyrestkit/config/__init__.py +5 -0
  46. pyrestkit/config/config.py +97 -0
  47. pyrestkit/constants/__init__.py +0 -0
  48. pyrestkit/constants/content_types.py +0 -0
  49. pyrestkit/constants/headers.py +0 -0
  50. pyrestkit/constants/status_codes.py +0 -0
  51. pyrestkit/core/__init__.py +13 -0
  52. pyrestkit/core/api_client.py +129 -0
  53. pyrestkit/core/logger.py +41 -0
  54. pyrestkit/core/request_builder.py +45 -0
  55. pyrestkit/core/request_executor.py +64 -0
  56. pyrestkit/core/request_logger.py +0 -0
  57. pyrestkit/core/response_logger.py +0 -0
  58. pyrestkit/core/session_manager.py +19 -0
  59. pyrestkit/database/__init__.py +0 -0
  60. pyrestkit/endpoints/__init__.py +5 -0
  61. pyrestkit/endpoints/base_endpoints.py +32 -0
  62. pyrestkit/endpoints/order_endpoints.py +9 -0
  63. pyrestkit/endpoints/payment_endpoints.py +5 -0
  64. pyrestkit/endpoints/user_endpoints.py +48 -0
  65. pyrestkit/exceptions/__init__.py +21 -0
  66. pyrestkit/exceptions/api_exception.py +8 -0
  67. pyrestkit/exceptions/authentication_exception.py +10 -0
  68. pyrestkit/exceptions/configuration_exception.py +10 -0
  69. pyrestkit/exceptions/exception_mapper.py +32 -0
  70. pyrestkit/exceptions/network_exception.py +10 -0
  71. pyrestkit/exceptions/response_exception.py +10 -0
  72. pyrestkit/exceptions/serialization_exception.py +10 -0
  73. pyrestkit/exceptions/validation_exception.py +10 -0
  74. pyrestkit/factories/__init__.py +5 -0
  75. pyrestkit/factories/base_factory.py +25 -0
  76. pyrestkit/factories/user_factory.py +37 -0
  77. pyrestkit/hooks/__init__.py +5 -0
  78. pyrestkit/hooks/hook.py +27 -0
  79. pyrestkit/hooks/hook_manager.py +39 -0
  80. pyrestkit/hooks/request_hook.py +18 -0
  81. pyrestkit/hooks/response_hook.py +17 -0
  82. pyrestkit/hooks/timing_hook.py +32 -0
  83. pyrestkit/models/__init__.py +8 -0
  84. pyrestkit/models/base_response.py +11 -0
  85. pyrestkit/models/request/__init__.py +7 -0
  86. pyrestkit/models/request/create_user_request.py +11 -0
  87. pyrestkit/models/request/update_user_request.py +10 -0
  88. pyrestkit/models/response/__init__.py +5 -0
  89. pyrestkit/models/response/create_user_response.py +26 -0
  90. pyrestkit/models/response/get_user_response.py +28 -0
  91. pyrestkit/models/response/user_response.py +12 -0
  92. pyrestkit/pipeline/__init__.py +5 -0
  93. pyrestkit/pipeline/middleware.py +18 -0
  94. pyrestkit/pipeline/middleware_chain.py +11 -0
  95. pyrestkit/pipeline/pipeline.py +27 -0
  96. pyrestkit/pipeline/request_context.py +26 -0
  97. pyrestkit/response/__init__.py +8 -0
  98. pyrestkit/response/framework_response.py +271 -0
  99. pyrestkit/response/response_body.py +124 -0
  100. pyrestkit/retry/__init__.py +5 -0
  101. pyrestkit/retry/backoff.py +32 -0
  102. pyrestkit/retry/retry_handler.py +52 -0
  103. pyrestkit/retry/retry_policy.py +33 -0
  104. pyrestkit/serializers/__init__.py +0 -0
  105. pyrestkit/serializers/response_mapper.py +25 -0
  106. pyrestkit/types/__init__.py +0 -0
  107. pyrestkit/types/model_protocol.py +17 -0
  108. pyrestkit/utils/__init__.py +0 -0
  109. pyrestkit/validators/__init__.py +7 -0
  110. pyrestkit/validators/response_validator.py +57 -0
  111. pyrestkit/validators/schema_validator.py +33 -0
  112. pyrestkit-0.0.0.dist-info/METADATA +741 -0
  113. pyrestkit-0.0.0.dist-info/RECORD +115 -0
  114. pyrestkit-0.0.0.dist-info/WHEEL +5 -0
  115. pyrestkit-0.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,5 @@
1
+ from .fluent_request_builder import FluentRequestBuilder
2
+
3
+ __all__ = [
4
+ "FluentRequestBuilder",
5
+ ]
@@ -0,0 +1,167 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from pyrestkit.core.api_client import APIClient
6
+ from pyrestkit.response.framework_response import FrameworkResponse
7
+
8
+
9
+ class FluentRequestBuilder:
10
+ """
11
+ Fluent API for building and sending HTTP requests.
12
+
13
+ Example:
14
+
15
+ response = (
16
+ api.request()
17
+ .get("/users")
18
+ .query(page=2)
19
+ .header("Authorization", token)
20
+ .send()
21
+ )
22
+ """
23
+
24
+ def __init__(
25
+ self,
26
+ client: APIClient,
27
+ ) -> None:
28
+ self._client = client
29
+
30
+ self._method = ""
31
+ self._endpoint = ""
32
+
33
+ self._kwargs: dict[str, Any] = {}
34
+
35
+ def get(
36
+ self,
37
+ endpoint: str,
38
+ ) -> FluentRequestBuilder:
39
+ self._method = "GET"
40
+ self._endpoint = endpoint
41
+ return self
42
+
43
+ def post(
44
+ self,
45
+ endpoint: str,
46
+ ) -> FluentRequestBuilder:
47
+ self._method = "POST"
48
+ self._endpoint = endpoint
49
+ return self
50
+
51
+ def put(
52
+ self,
53
+ endpoint: str,
54
+ ) -> FluentRequestBuilder:
55
+ self._method = "PUT"
56
+ self._endpoint = endpoint
57
+ return self
58
+
59
+ def patch(
60
+ self,
61
+ endpoint: str,
62
+ ) -> FluentRequestBuilder:
63
+ self._method = "PATCH"
64
+ self._endpoint = endpoint
65
+ return self
66
+
67
+ def delete(
68
+ self,
69
+ endpoint: str,
70
+ ) -> FluentRequestBuilder:
71
+ self._method = "DELETE"
72
+ self._endpoint = endpoint
73
+ return self
74
+
75
+ def query(
76
+ self,
77
+ **params: Any,
78
+ ) -> FluentRequestBuilder:
79
+ self._kwargs["params"] = params
80
+ return self
81
+
82
+ def header(
83
+ self,
84
+ name: str,
85
+ value: str,
86
+ ) -> FluentRequestBuilder:
87
+ headers = self._kwargs.setdefault(
88
+ "headers",
89
+ {},
90
+ )
91
+
92
+ headers[name] = value
93
+
94
+ return self
95
+
96
+ def headers(
97
+ self,
98
+ headers: dict[str, str],
99
+ ) -> FluentRequestBuilder:
100
+ current = self._kwargs.setdefault(
101
+ "headers",
102
+ {},
103
+ )
104
+
105
+ current.update(headers)
106
+
107
+ return self
108
+
109
+ def json(
110
+ self,
111
+ body: Any,
112
+ ) -> FluentRequestBuilder:
113
+ self._kwargs["json"] = body
114
+ return self
115
+
116
+ def data(
117
+ self,
118
+ body: Any,
119
+ ) -> FluentRequestBuilder:
120
+ self._kwargs["data"] = body
121
+ return self
122
+
123
+ def timeout(
124
+ self,
125
+ seconds: int,
126
+ ) -> FluentRequestBuilder:
127
+ self._kwargs["timeout"] = seconds
128
+ return self
129
+
130
+ def send(
131
+ self,
132
+ ) -> FrameworkResponse:
133
+ method = self._method.upper()
134
+
135
+ if method == "GET":
136
+ return self._client.get(
137
+ self._endpoint,
138
+ **self._kwargs,
139
+ )
140
+
141
+ if method == "POST":
142
+ return self._client.post(
143
+ self._endpoint,
144
+ **self._kwargs,
145
+ )
146
+
147
+ if method == "PUT":
148
+ return self._client.put(
149
+ self._endpoint,
150
+ **self._kwargs,
151
+ )
152
+
153
+ if method == "PATCH":
154
+ return self._client.patch(
155
+ self._endpoint,
156
+ **self._kwargs,
157
+ )
158
+
159
+ if method == "DELETE":
160
+ return self._client.delete(
161
+ self._endpoint,
162
+ **self._kwargs,
163
+ )
164
+
165
+ raise ValueError(
166
+ "HTTP method not selected.",
167
+ )
@@ -0,0 +1,7 @@
1
+ from .base_client import BaseClient
2
+ from .user_client import UserClient
3
+
4
+ __all__ = [
5
+ "BaseClient",
6
+ "UserClient",
7
+ ]
@@ -0,0 +1,68 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from pyrestkit.core.api_client import APIClient
6
+ from pyrestkit.response.framework_response import FrameworkResponse
7
+
8
+
9
+ class BaseClient:
10
+ """
11
+ Base class for all endpoint clients.
12
+ """
13
+
14
+ def __init__(
15
+ self,
16
+ api_client: APIClient,
17
+ ) -> None:
18
+ self._api_client = api_client
19
+
20
+ def get(
21
+ self,
22
+ endpoint: str,
23
+ **kwargs: Any,
24
+ ) -> FrameworkResponse:
25
+ return self._api_client.get(
26
+ endpoint,
27
+ **kwargs,
28
+ )
29
+
30
+ def post(
31
+ self,
32
+ endpoint: str,
33
+ **kwargs: Any,
34
+ ) -> FrameworkResponse:
35
+ return self._api_client.post(
36
+ endpoint,
37
+ **kwargs,
38
+ )
39
+
40
+ def put(
41
+ self,
42
+ endpoint: str,
43
+ **kwargs: Any,
44
+ ) -> FrameworkResponse:
45
+ return self._api_client.put(
46
+ endpoint,
47
+ **kwargs,
48
+ )
49
+
50
+ def patch(
51
+ self,
52
+ endpoint: str,
53
+ **kwargs: Any,
54
+ ) -> FrameworkResponse:
55
+ return self._api_client.patch(
56
+ endpoint,
57
+ **kwargs,
58
+ )
59
+
60
+ def delete(
61
+ self,
62
+ endpoint: str,
63
+ **kwargs: Any,
64
+ ) -> FrameworkResponse:
65
+ return self._api_client.delete(
66
+ endpoint,
67
+ **kwargs,
68
+ )
@@ -0,0 +1,66 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import asdict
4
+
5
+ from pyrestkit.clients.base_client import BaseClient
6
+ from pyrestkit.core.api_client import APIClient
7
+ from pyrestkit.endpoints.user_endpoints import UserEndpoints
8
+ from pyrestkit.models.request.create_user_request import CreateUserRequest
9
+ from pyrestkit.models.request.update_user_request import UpdateUserRequest
10
+ from pyrestkit.response.framework_response import FrameworkResponse
11
+
12
+
13
+ class UserClient(BaseClient):
14
+ """
15
+ Business client responsible for User APIs.
16
+
17
+ This client delegates HTTP operations to BaseClient.
18
+ """
19
+
20
+ def __init__(
21
+ self,
22
+ api_client: APIClient,
23
+ ) -> None:
24
+ super().__init__(api_client)
25
+
26
+ def list_users(
27
+ self,
28
+ ) -> FrameworkResponse:
29
+ return self.get(
30
+ UserEndpoints.list_users(),
31
+ )
32
+
33
+ def get_user(
34
+ self,
35
+ user_id: int,
36
+ ) -> FrameworkResponse:
37
+ return self.get(
38
+ UserEndpoints.get_user(user_id),
39
+ )
40
+
41
+ def create_user(
42
+ self,
43
+ request: CreateUserRequest,
44
+ ) -> FrameworkResponse:
45
+ return self.post(
46
+ UserEndpoints.create_user(),
47
+ json=asdict(request),
48
+ )
49
+
50
+ def update_user(
51
+ self,
52
+ user_id: int,
53
+ request: UpdateUserRequest,
54
+ ) -> FrameworkResponse:
55
+ return self.put(
56
+ UserEndpoints.update_user(user_id),
57
+ json=asdict(request),
58
+ )
59
+
60
+ def delete_user(
61
+ self,
62
+ user_id: int,
63
+ ) -> FrameworkResponse:
64
+ return self.delete(
65
+ UserEndpoints.delete_user(user_id),
66
+ )
@@ -0,0 +1,5 @@
1
+ from .config import ConfigManager
2
+
3
+ __all__ = [
4
+ "ConfigManager",
5
+ ]
@@ -0,0 +1,97 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from pathlib import Path
5
+ from typing import Any, cast
6
+
7
+ from pyrestkit.exceptions.configuration_exception import ConfigurationException
8
+
9
+
10
+ class ConfigManager:
11
+ """
12
+ Loads environment configuration from JSON files.
13
+
14
+ Why Path(__file__).parent?
15
+
16
+ Path(__file__).parent locates the directory containing
17
+ config.py and appends the selected environment JSON
18
+ file. This keeps the implementation portable across
19
+ operating systems.
20
+
21
+ Why use @property?
22
+
23
+ It allows callers to access configuration values like
24
+ attributes while keeping the flexibility to validate or
25
+ compute values internally in the future.
26
+
27
+ Why validate the file?
28
+
29
+ If a user runs:
30
+
31
+ pytest --env=production
32
+
33
+ and production.json does not exist, the framework fails
34
+ immediately with a clear error message.
35
+ """
36
+
37
+ def __init__(
38
+ self,
39
+ environment: str = "dev",
40
+ ) -> None:
41
+ self.environment = environment.lower()
42
+
43
+ config_file = Path(__file__).parent / f"{self.environment}.json"
44
+
45
+ if not config_file.exists():
46
+ raise ConfigurationException(
47
+ f"Configuration file '{self.environment}' not found.",
48
+ )
49
+
50
+ with config_file.open(
51
+ encoding="utf-8",
52
+ ) as file:
53
+ self.config: dict[str, Any] = json.load(file)
54
+
55
+ @property
56
+ def base_url(self) -> str:
57
+ return cast(
58
+ str,
59
+ self.config["base_url"],
60
+ )
61
+
62
+ @property
63
+ def timeout(self) -> int:
64
+ return cast(
65
+ int,
66
+ self.config.get(
67
+ "timeout",
68
+ 30,
69
+ ),
70
+ )
71
+
72
+ @property
73
+ def headers(self) -> dict[str, str]:
74
+ return cast(
75
+ dict[str, str],
76
+ self.config.get(
77
+ "headers",
78
+ {},
79
+ ),
80
+ )
81
+
82
+ @property
83
+ def auto_raise_exceptions(self) -> bool:
84
+ return cast(
85
+ bool,
86
+ self.config.get(
87
+ "auto_raise_exceptions",
88
+ True,
89
+ ),
90
+ )
91
+
92
+ @property
93
+ def environment_name(self) -> str:
94
+ return cast(
95
+ str,
96
+ self.config["environment"],
97
+ )
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,13 @@
1
+ from .api_client import APIClient
2
+ from .logger import FrameworkLogger
3
+ from .request_builder import RequestBuilder
4
+ from .request_executor import RequestExecutor
5
+ from .session_manager import SessionManager
6
+
7
+ __all__ = [
8
+ "APIClient",
9
+ "FrameworkLogger",
10
+ "RequestBuilder",
11
+ "RequestExecutor",
12
+ "SessionManager",
13
+ ]
@@ -0,0 +1,129 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any
4
+
5
+ from pyrestkit.auth.auth_strategy import AuthenticationStrategy
6
+ from pyrestkit.config.config import ConfigManager
7
+ from pyrestkit.core.request_builder import RequestBuilder
8
+ from pyrestkit.core.request_executor import RequestExecutor
9
+ from pyrestkit.core.session_manager import SessionManager
10
+ from pyrestkit.hooks.hook_manager import HookManager
11
+ from pyrestkit.response.framework_response import FrameworkResponse
12
+
13
+ if TYPE_CHECKING:
14
+ from pyrestkit.builder.fluent_request_builder import FluentRequestBuilder
15
+
16
+
17
+ class APIClient:
18
+ """
19
+ Generic HTTP client for the automation framework.
20
+ """
21
+
22
+ def __init__(
23
+ self,
24
+ config: ConfigManager,
25
+ session_manager: SessionManager,
26
+ auth_strategy: AuthenticationStrategy | None = None,
27
+ hook_manager: HookManager | None = None,
28
+ ) -> None:
29
+ self._builder = RequestBuilder(
30
+ config=config,
31
+ auth_strategy=auth_strategy,
32
+ )
33
+
34
+ self._executor = RequestExecutor(
35
+ config=config,
36
+ session_manager=session_manager,
37
+ hook_manager=hook_manager,
38
+ )
39
+
40
+ def request(self) -> FluentRequestBuilder:
41
+ """
42
+ Creates a fluent request builder.
43
+
44
+ Example:
45
+
46
+ response = (
47
+ api.request()
48
+ .get("/users")
49
+ .query(page=2)
50
+ .send()
51
+ )
52
+ """
53
+ from pyrestkit.builder.fluent_request_builder import (
54
+ FluentRequestBuilder,
55
+ )
56
+
57
+ return FluentRequestBuilder(self)
58
+
59
+ def _send_request(
60
+ self,
61
+ method: str,
62
+ endpoint: str,
63
+ **kwargs: Any,
64
+ ) -> FrameworkResponse:
65
+ url, kwargs = self._builder.build(
66
+ endpoint,
67
+ **kwargs,
68
+ )
69
+
70
+ return self._executor.execute(
71
+ method=method,
72
+ url=url,
73
+ **kwargs,
74
+ )
75
+
76
+ def get(
77
+ self,
78
+ endpoint: str,
79
+ **kwargs: Any,
80
+ ) -> FrameworkResponse:
81
+ return self._send_request(
82
+ "GET",
83
+ endpoint,
84
+ **kwargs,
85
+ )
86
+
87
+ def post(
88
+ self,
89
+ endpoint: str,
90
+ **kwargs: Any,
91
+ ) -> FrameworkResponse:
92
+ return self._send_request(
93
+ "POST",
94
+ endpoint,
95
+ **kwargs,
96
+ )
97
+
98
+ def put(
99
+ self,
100
+ endpoint: str,
101
+ **kwargs: Any,
102
+ ) -> FrameworkResponse:
103
+ return self._send_request(
104
+ "PUT",
105
+ endpoint,
106
+ **kwargs,
107
+ )
108
+
109
+ def patch(
110
+ self,
111
+ endpoint: str,
112
+ **kwargs: Any,
113
+ ) -> FrameworkResponse:
114
+ return self._send_request(
115
+ "PATCH",
116
+ endpoint,
117
+ **kwargs,
118
+ )
119
+
120
+ def delete(
121
+ self,
122
+ endpoint: str,
123
+ **kwargs: Any,
124
+ ) -> FrameworkResponse:
125
+ return self._send_request(
126
+ "DELETE",
127
+ endpoint,
128
+ **kwargs,
129
+ )
@@ -0,0 +1,41 @@
1
+ import logging
2
+ from pathlib import Path
3
+
4
+
5
+ class FrameworkLogger:
6
+ """
7
+ Singleton logger for the automation framework.
8
+ """
9
+
10
+ _logger = None
11
+
12
+ @classmethod
13
+ def get_logger(cls) -> logging.Logger:
14
+ if cls._logger:
15
+ return cls._logger
16
+
17
+ log_directory = Path("logs")
18
+ log_directory.mkdir(exist_ok=True)
19
+
20
+ log_file = log_directory / "framework.log"
21
+
22
+ logger = logging.getLogger("api-framework")
23
+ logger.setLevel(logging.INFO)
24
+
25
+ if logger.handlers:
26
+ cls._logger = logger
27
+ return logger
28
+
29
+ formatter = logging.Formatter("%(asctime)s | %(levelname)-8s | %(message)s")
30
+
31
+ file_handler = logging.FileHandler(log_file)
32
+ file_handler.setFormatter(formatter)
33
+
34
+ console_handler = logging.StreamHandler()
35
+ console_handler.setFormatter(formatter)
36
+
37
+ logger.addHandler(file_handler)
38
+ logger.addHandler(console_handler)
39
+
40
+ cls._logger = logger
41
+ return logger
@@ -0,0 +1,45 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from pyrestkit.auth.auth_strategy import AuthenticationStrategy
6
+ from pyrestkit.config.config import ConfigManager
7
+
8
+
9
+ class RequestBuilder:
10
+ """
11
+ Builds request arguments for the API client.
12
+ """
13
+
14
+ def __init__(
15
+ self,
16
+ config: ConfigManager,
17
+ auth_strategy: AuthenticationStrategy | None = None,
18
+ ) -> None:
19
+ self._config = config
20
+ self._auth_strategy = auth_strategy
21
+
22
+ def build(
23
+ self,
24
+ endpoint: str,
25
+ **kwargs: Any,
26
+ ) -> tuple[str, dict[str, Any]]:
27
+ url = f"{self._config.base_url}{endpoint}"
28
+
29
+ headers = self._config.headers.copy()
30
+
31
+ if self._auth_strategy is not None:
32
+ headers.update(self._auth_strategy.get_headers())
33
+
34
+ request_headers = kwargs.pop("headers", {})
35
+ headers.update(request_headers)
36
+
37
+ timeout = kwargs.pop(
38
+ "timeout",
39
+ self._config.timeout,
40
+ )
41
+
42
+ kwargs["headers"] = headers
43
+ kwargs["timeout"] = timeout
44
+
45
+ return url, kwargs