koheesio 0.7.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 (85) hide show
  1. koheesio/__about__.py +48 -0
  2. koheesio/__init__.py +37 -0
  3. koheesio/asyncio/__init__.py +107 -0
  4. koheesio/asyncio/http.py +383 -0
  5. koheesio/context.py +480 -0
  6. koheesio/integrations/__init__.py +3 -0
  7. koheesio/integrations/box.py +664 -0
  8. koheesio/integrations/spark/__init__.py +0 -0
  9. koheesio/integrations/spark/dq/__init__.py +0 -0
  10. koheesio/integrations/spark/dq/spark_expectations.py +166 -0
  11. koheesio/integrations/spark/sftp.py +477 -0
  12. koheesio/logger.py +267 -0
  13. koheesio/models/__init__.py +668 -0
  14. koheesio/models/sql.py +68 -0
  15. koheesio/notifications/__init__.py +26 -0
  16. koheesio/notifications/slack.py +122 -0
  17. koheesio/secrets/__init__.py +72 -0
  18. koheesio/secrets/cerberus.py +89 -0
  19. koheesio/spark/__init__.py +53 -0
  20. koheesio/spark/delta.py +311 -0
  21. koheesio/spark/etl_task.py +141 -0
  22. koheesio/spark/readers/__init__.py +56 -0
  23. koheesio/spark/readers/databricks/__init__.py +0 -0
  24. koheesio/spark/readers/databricks/autoloader.py +116 -0
  25. koheesio/spark/readers/delta.py +326 -0
  26. koheesio/spark/readers/dummy.py +44 -0
  27. koheesio/spark/readers/file_loader.py +233 -0
  28. koheesio/spark/readers/hana.py +66 -0
  29. koheesio/spark/readers/jdbc.py +109 -0
  30. koheesio/spark/readers/kafka.py +138 -0
  31. koheesio/spark/readers/memory.py +113 -0
  32. koheesio/spark/readers/metastore.py +21 -0
  33. koheesio/spark/readers/rest_api.py +126 -0
  34. koheesio/spark/readers/snowflake.py +33 -0
  35. koheesio/spark/readers/spark_sql_reader.py +62 -0
  36. koheesio/spark/readers/teradata.py +72 -0
  37. koheesio/spark/snowflake.py +1343 -0
  38. koheesio/spark/transformations/__init__.py +552 -0
  39. koheesio/spark/transformations/arrays.py +509 -0
  40. koheesio/spark/transformations/camel_to_snake.py +82 -0
  41. koheesio/spark/transformations/cast_to_datatype.py +880 -0
  42. koheesio/spark/transformations/date_time/__init__.py +277 -0
  43. koheesio/spark/transformations/date_time/interval.py +345 -0
  44. koheesio/spark/transformations/drop_column.py +50 -0
  45. koheesio/spark/transformations/dummy.py +38 -0
  46. koheesio/spark/transformations/get_item.py +138 -0
  47. koheesio/spark/transformations/hash.py +103 -0
  48. koheesio/spark/transformations/lookup.py +174 -0
  49. koheesio/spark/transformations/repartition.py +66 -0
  50. koheesio/spark/transformations/replace.py +121 -0
  51. koheesio/spark/transformations/row_number_dedup.py +142 -0
  52. koheesio/spark/transformations/sql_transform.py +37 -0
  53. koheesio/spark/transformations/strings/__init__.py +66 -0
  54. koheesio/spark/transformations/strings/change_case.py +181 -0
  55. koheesio/spark/transformations/strings/concat.py +129 -0
  56. koheesio/spark/transformations/strings/pad.py +99 -0
  57. koheesio/spark/transformations/strings/regexp.py +158 -0
  58. koheesio/spark/transformations/strings/replace.py +108 -0
  59. koheesio/spark/transformations/strings/split.py +135 -0
  60. koheesio/spark/transformations/strings/substring.py +80 -0
  61. koheesio/spark/transformations/strings/trim.py +133 -0
  62. koheesio/spark/transformations/transform.py +104 -0
  63. koheesio/spark/transformations/uuid5.py +166 -0
  64. koheesio/spark/utils.py +179 -0
  65. koheesio/spark/writers/__init__.py +79 -0
  66. koheesio/spark/writers/buffer.py +486 -0
  67. koheesio/spark/writers/delta/__init__.py +15 -0
  68. koheesio/spark/writers/delta/batch.py +366 -0
  69. koheesio/spark/writers/delta/scd.py +577 -0
  70. koheesio/spark/writers/delta/stream.py +36 -0
  71. koheesio/spark/writers/delta/utils.py +68 -0
  72. koheesio/spark/writers/dummy.py +84 -0
  73. koheesio/spark/writers/kafka.py +180 -0
  74. koheesio/spark/writers/snowflake.py +5 -0
  75. koheesio/spark/writers/stream.py +358 -0
  76. koheesio/sso/__init__.py +0 -0
  77. koheesio/sso/okta.py +108 -0
  78. koheesio/steps/__init__.py +685 -0
  79. koheesio/steps/dummy.py +43 -0
  80. koheesio/steps/http.py +447 -0
  81. koheesio/utils.py +102 -0
  82. koheesio-0.7.0.dist-info/METADATA +238 -0
  83. koheesio-0.7.0.dist-info/RECORD +85 -0
  84. koheesio-0.7.0.dist-info/WHEEL +4 -0
  85. koheesio-0.7.0.dist-info/licenses/LICENSE.txt +201 -0
koheesio/__about__.py ADDED
@@ -0,0 +1,48 @@
1
+ """A type-safe Python framework for building efficient data pipelines
2
+
3
+ Koheesio is a Python framework for building type-safe and efficient data pipelines, particularly useful for
4
+ large-scale data processing. It promotes modularity, composability, and collaboration, allowing for the creation of
5
+ complex pipelines from simple, reusable components.
6
+
7
+ Leveraging Pydantic, it ensures predictable pipeline execution and structured configurations within pipeline components.
8
+
9
+ With its simple and straightforward API, Koheesio makes it easy for developers to build and manage data pipelines,
10
+ enhancing productivity and code maintainability.
11
+ """
12
+
13
+ LICENSE_INFO = "Licensed as Apache 2.0"
14
+ SOURCE = "https://github.com/Nike-Inc/koheesio"
15
+ __version__ = "0.7.0"
16
+ __logo__ = 75, (
17
+ b"\x1f\x8b\x08\x00TiGf\x02\xff}\x91\xbb\r\xc30\x0cD{Nq\x1bh\n\x01\x16R\xa4pK@\x8bh\xf8\xe8\xf8\x89\xe9\x04\xf0\x15"
18
+ b"\xc4\x91\x10\x9f(J`z\xbd4B\xea8J\xf2\xa01T\x02\x01,\x0b\x85Q\x92\x07\xe9\x9cK\x92\xd1,\xe0mRBL\x9c\xa6\x9b\xee"
19
+ b"\xeet)\x07Av\xc9/\x0b\x98\x93\xb4=\xd1v\xa4\xf5NG\xc6\xe5\xce\x93nk\x8d\x81\xf5\xed\x92\x80AmC\xbb\xde,.\x7f\x1fc"
20
+ b"\x0fU\xa79\x19\x82\x16]\x1248\x8f\xa5\x7f\x1c|\x92\xe2\xb8\xa59\xfd\xa5\x86\x8b.I\x9a\xf3\xd4W\x80\x8a\xd3\x9e"
21
+ b"\xfb\xba\\\xecm\x9f#\xee\xea\x92}M+\xffb\xb7\xb2\xc4\xc4K\x88Zui\xda\xedD\xfb\x00\xcfU6\xd3_\x02\x00\x00"
22
+ )
23
+ __short_description__ = __doc__.split("\n", maxsplit=1)[0]
24
+ __about__ = f"""Koheesio - v{__version__}
25
+ {__short_description__}
26
+ {LICENSE_INFO}
27
+ Source: {SOURCE}
28
+ """
29
+
30
+
31
+ # fmt: off
32
+ def _about(): # pragma: no cover
33
+ """Return the Koheesio logo and version/about information as a string
34
+ Note: this code is not meant to be readable, instead it is written to be as compact as possible
35
+ """
36
+ import gzip
37
+ _w, _logo = __logo__
38
+ _h, _v, _n = "\u2500", "\u2502", "\n"
39
+ return gzip.decompress(_logo).decode() + (
40
+ _n + '\u256d' + _h * _w + "\u256e" + _n +
41
+ f"{_n.join([f'{_v}{_l:^{_w}}{_v}' for _l in __about__.split(_n)[:-1]])}" +
42
+ _n + "\u2570" + _h * _w + "\u256f"
43
+ )
44
+ # fmt: on
45
+
46
+
47
+ if __name__ == "__main__":
48
+ print(_about())
koheesio/__init__.py ADDED
@@ -0,0 +1,37 @@
1
+ # pragma: no cover
2
+ from os import environ
3
+
4
+ from koheesio.__about__ import __version__, _about
5
+ from koheesio.context import Context
6
+ from koheesio.logger import LoggingFactory
7
+ from koheesio.models import BaseModel, ExtraParamsMixin
8
+ from koheesio.steps import Step, StepOutput
9
+ from koheesio.utils import convert_str_to_bool
10
+
11
+ _koheesio_print_logo = convert_str_to_bool(environ.get("KOHEESIO__PRINT_LOGO", "True"))
12
+ _logo_printed = False
13
+ ABOUT = _about()
14
+ VERSION = __version__
15
+
16
+ __all__ = [
17
+ "ABOUT",
18
+ "BaseModel",
19
+ "Context",
20
+ "ExtraParamsMixin",
21
+ "LoggingFactory",
22
+ "Step",
23
+ "StepOutput",
24
+ "VERSION",
25
+ ]
26
+
27
+
28
+ def print_logo():
29
+ global _logo_printed
30
+ global _koheesio_print_logo
31
+
32
+ if not _logo_printed and _koheesio_print_logo:
33
+ print(ABOUT)
34
+ _logo_printed = True
35
+
36
+
37
+ print_logo()
@@ -0,0 +1,107 @@
1
+ """
2
+ This module provides classes for asynchronous steps in the koheesio package.
3
+ """
4
+
5
+ from typing import Dict, Union
6
+ from abc import ABC
7
+ from asyncio import iscoroutine
8
+
9
+ from koheesio.steps import Step, StepMetaClass, StepOutput
10
+
11
+
12
+ class AsyncStepMetaClass(StepMetaClass):
13
+ """Metaclass for asynchronous steps.
14
+
15
+ This metaclass is used to define asynchronous steps in the Koheesio framework.
16
+ It inherits from the StepMetaClass and provides additional functionality for
17
+ executing asynchronous steps.
18
+
19
+ Attributes:
20
+ None
21
+
22
+ Methods:
23
+ _execute_wrapper: Wrapper method for executing asynchronous steps.
24
+
25
+ """
26
+
27
+ def _execute_wrapper(cls, *args, **kwargs):
28
+ """Wrapper method for executing asynchronous steps.
29
+
30
+ This method is called when an asynchronous step is executed. It wraps the
31
+ execution of the step with additional functionality.
32
+
33
+ Parameters
34
+ ----------
35
+ *args: Variable length argument list.
36
+ **kwargs: Arbitrary keyword arguments.
37
+
38
+ Returns
39
+ -------
40
+ The result of executing the asynchronous step.
41
+
42
+ """
43
+
44
+ return super()._execute_wrapper(*args, **kwargs)
45
+
46
+
47
+ class AsyncStepOutput(Step.Output):
48
+ """
49
+ Represents the output of an asynchronous step.
50
+
51
+ This class extends the base `Step.Output` class and provides additional functionality for merging key-value maps.
52
+
53
+ Attributes
54
+ ----------
55
+ ...
56
+
57
+ Methods
58
+ -------
59
+ merge(other: Union[Dict, StepOutput])
60
+ Merge key-value map with self.
61
+ """
62
+
63
+ def merge(self, other: Union[Dict, StepOutput]):
64
+ """Merge key,value map with self
65
+
66
+ Examples
67
+ --------
68
+ ```python
69
+ step_output = StepOutput(foo="bar")
70
+ step_output.merge({"lorem": "ipsum"}) # step_output will now contain {'foo': 'bar', 'lorem': 'ipsum'}
71
+ ```
72
+
73
+ Functionally similar to adding two dicts together; like running `{**dict_a, **dict_b}`.
74
+
75
+ Parameters
76
+ ----------
77
+ other: Union[Dict, StepOutput]
78
+ Dict or another instance of a StepOutputs class that will be added to self
79
+ """
80
+ if isinstance(other, StepOutput):
81
+ other = other.model_dump() # ensures we really have a dict
82
+
83
+ if not iscoroutine(other):
84
+ for k, v in other.items():
85
+ self.set(k, v)
86
+
87
+ return self
88
+
89
+
90
+ class AsyncStep(Step, ABC, metaclass=AsyncStepMetaClass):
91
+ """
92
+ Asynchronous step class that inherits from Step and uses the AsyncStepMetaClass metaclass.
93
+
94
+ Attributes
95
+ ----------
96
+ Output : AsyncStepOutput
97
+ The output class for the asynchronous step.
98
+ """
99
+
100
+ class Output(AsyncStepOutput):
101
+ """
102
+ Output class for asyncio step.
103
+
104
+ This class represents the output of the asyncio step. It inherits from the AsyncStepOutput class.
105
+ """
106
+
107
+ __output__: Output
@@ -0,0 +1,383 @@
1
+ """
2
+ This module contains async implementation of HTTP step.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import asyncio
8
+ import warnings
9
+ from typing import Any, Dict, List, Optional, Tuple, Union
10
+
11
+ import nest_asyncio
12
+ import yarl
13
+ from aiohttp import BaseConnector, ClientSession, TCPConnector
14
+ from aiohttp_retry import ExponentialRetry, RetryClient, RetryOptionsBase
15
+
16
+ from pydantic import Field, SecretStr, field_validator, model_validator
17
+
18
+ from koheesio.asyncio import AsyncStep, AsyncStepOutput
19
+ from koheesio.models import ExtraParamsMixin
20
+ from koheesio.steps.http import HttpMethod
21
+
22
+
23
+ class AsyncHttpStep(AsyncStep, ExtraParamsMixin):
24
+ """
25
+ Asynchronous HTTP step for making HTTP requests using aiohttp.
26
+
27
+ Parameters
28
+ ----------
29
+ client_session : Optional[ClientSession]
30
+ Aiohttp ClientSession.
31
+ url : List[yarl.URL]
32
+ List of yarl.URL.
33
+ retry_options : Optional[RetryOptionsBase]
34
+ Retry options for the request.
35
+ connector : Optional[BaseConnector]
36
+ Connector for the aiohttp request.
37
+ headers : Optional[Dict[str, Union[str, SecretStr]]]
38
+ Request headers.
39
+
40
+ Output
41
+ ------
42
+ responses_urls : Optional[List[Tuple[Dict[str, Any], yarl.URL]]]
43
+ List of responses from the API and request URL.
44
+
45
+ Examples
46
+ --------
47
+ ```python
48
+ >>> import asyncio
49
+ >>> from aiohttp import ClientSession
50
+ >>> from aiohttp.connector import TCPConnector
51
+ >>> from aiohttp_retry import ExponentialRetry
52
+ >>> from koheesio.steps.async.http import AsyncHttpStep
53
+ >>> from yarl import URL
54
+ >>> from typing import Dict, Any, Union, List, Tuple
55
+ >>>
56
+ >>> # Initialize the AsyncHttpStep
57
+ >>> async def main():
58
+ >>> session = ClientSession()
59
+ >>> urls = [URL('https://example.com/api/1'), URL('https://example.com/api/2')]
60
+ >>> retry_options = ExponentialRetry()
61
+ >>> connector = TCPConnector(limit=10)
62
+ >>> headers = {'Content-Type': 'application/json'}
63
+ >>> step = AsyncHttpStep(
64
+ >>> client_session=session,
65
+ >>> url=urls,
66
+ >>> retry_options=retry_options,
67
+ >>> connector=connector,
68
+ >>> headers=headers
69
+ >>> )
70
+ >>>
71
+ >>> # Execute the step
72
+ >>> responses_urls= await step.get()
73
+ >>>
74
+ >>> return responses_urls
75
+ >>>
76
+ >>> # Run the main function
77
+ >>> responses_urls = asyncio.run(main())
78
+ ```
79
+ """
80
+
81
+ client_session: Optional[ClientSession] = Field(default=None, description="Aiohttp ClientSession", exclude=True)
82
+ url: List[yarl.URL] = Field(
83
+ default=None,
84
+ alias="urls",
85
+ description="""Expecting list, as there is no value in executing async request for one value.
86
+ yarl.URL is preferable, because params/data can be injected into URL instance""",
87
+ exclude=True,
88
+ )
89
+ retry_options: Optional[RetryOptionsBase] = Field(
90
+ default=None, description="Retry options for the request", exclude=True
91
+ )
92
+ connector: Optional[BaseConnector] = Field(
93
+ default=None, description="Connector for the aiohttp request", exclude=True
94
+ )
95
+ headers: Dict[str, Union[str, SecretStr]] = Field(
96
+ default_factory=dict,
97
+ description="Request headers",
98
+ alias="header",
99
+ exclude=True,
100
+ )
101
+
102
+ timeout: None = Field(default=None, description="[Optional] Request timeout")
103
+
104
+ method: Union[str, HttpMethod] = Field(
105
+ default=HttpMethod.GET,
106
+ description="What type of Http call to perform. One of 'get', 'post', 'put', 'delete'. Defaults to 'get'.",
107
+ )
108
+
109
+ class Output(AsyncStepOutput):
110
+ """Output class for Step"""
111
+
112
+ responses_urls: Optional[List[Tuple[Dict[str, Any], yarl.URL]]] = Field(
113
+ default=None, description="List of responses from the API and request URL", repr=False
114
+ )
115
+
116
+ def __tasks_generator(self, method) -> List[asyncio.Task]:
117
+ """
118
+ Generate a list of tasks for making HTTP requests.
119
+
120
+ Parameters
121
+ ----------
122
+ method : HttpMethod
123
+ The HTTP method to use for the requests.
124
+
125
+ Returns
126
+ -------
127
+ List[Task]
128
+ A list of asyncio tasks for making HTTP requests.
129
+ """
130
+ tasks = [
131
+ asyncio.create_task(
132
+ self.request(
133
+ method=method,
134
+ url=u,
135
+ headers=self.get_headers(),
136
+ )
137
+ )
138
+ for u in self.url
139
+ ]
140
+
141
+ return tasks
142
+
143
+ @model_validator(mode="after")
144
+ def _move_extra_params_to_params(self):
145
+ """
146
+ Move extra_params to params dict.
147
+
148
+ Returns
149
+ -------
150
+ AsyncHttpStep
151
+ The updated AsyncHttpStep instance.
152
+ """
153
+ return self
154
+
155
+ async def _execute(self, tasks: List[asyncio.Task]) -> List[Tuple[Dict[str, Any], yarl.URL]]:
156
+ """
157
+ Execute the HTTP requests asynchronously.
158
+
159
+ Parameters
160
+ ----------
161
+ tasks : List[Task]
162
+ A list of asyncio tasks for making HTTP requests.
163
+
164
+ Returns
165
+ -------
166
+ List[Tuple[Dict[str, Any], yarl.URL]]
167
+ A list of response data and corresponding request URLs.
168
+ """
169
+ self._init_session()
170
+ try:
171
+ responses_urls = await asyncio.gather(*tasks)
172
+ finally:
173
+ await self.client_session.close()
174
+ await self.__retry_client.close()
175
+
176
+ return responses_urls
177
+
178
+ def _init_session(self):
179
+ """
180
+ Initialize the aiohttp session and retry client.
181
+ """
182
+ self.connector = self.connector or TCPConnector(limit=10)
183
+ self.client_session = self.client_session or ClientSession(connector=self.connector)
184
+ self.retry_options = self.retry_options or ExponentialRetry()
185
+ # Disable pylint warning: attribute is not initialized in __init__
186
+ # pylint: disable=W0201
187
+ self.__retry_client = RetryClient(
188
+ client_session=self.client_session, logger=self.log, retry_options=self.retry_options, raise_for_status=True
189
+ )
190
+
191
+ @field_validator("timeout")
192
+ def validate_timeout(cls, timeout):
193
+ """
194
+ Validate the 'data' field.
195
+
196
+ Parameters
197
+ ----------
198
+ data : Any
199
+ The value of the 'timeout' field.
200
+
201
+ Raises
202
+ ------
203
+ ValueError
204
+ If 'data' is not allowed in AsyncHttpStep.
205
+ """
206
+ if timeout:
207
+ raise ValueError("timeout is not allowed in AsyncHttpStep. Provide timeout through retry_options.")
208
+
209
+ def get_headers(self):
210
+ """
211
+ Get the request headers.
212
+
213
+ Returns
214
+ -------
215
+ Optional[Dict[str, Union[str, SecretStr]]]
216
+ The request headers.
217
+ """
218
+ _headers = None
219
+
220
+ if self.headers:
221
+ _headers = {k: v.get_secret_value() if isinstance(v, SecretStr) else v for k, v in self.headers.items()}
222
+
223
+ for k, v in self.headers.items():
224
+ if isinstance(v, SecretStr):
225
+ self.headers[k] = v.get_secret_value()
226
+
227
+ return _headers or self.headers
228
+
229
+ def set_outputs(self, response):
230
+ """
231
+ Set the outputs of the step.
232
+
233
+ Parameters
234
+ ----------
235
+ response : Any
236
+ The response data.
237
+ """
238
+ warnings.warn("set outputs is not implemented in AsyncHttpStep.")
239
+
240
+ def get_options(self):
241
+ """
242
+ Get the options of the step.
243
+ """
244
+ warnings.warn("get_options is not implemented in AsyncHttpStep.")
245
+
246
+ # Disable pylint warning: method was expected to be 'non-async'
247
+ # pylint: disable=W0236
248
+ async def request(
249
+ self,
250
+ method: HttpMethod,
251
+ url: yarl.URL,
252
+ **kwargs,
253
+ ) -> Tuple[Dict[str, Any], yarl.URL]:
254
+ """
255
+ Make an HTTP request.
256
+
257
+ Parameters
258
+ ----------
259
+ method : HttpMethod
260
+ The HTTP method to use for the request.
261
+ url : yarl.URL
262
+ The URL to make the request to.
263
+ kwargs : Any
264
+ Additional keyword arguments to pass to the request.
265
+
266
+ Returns
267
+ -------
268
+ Tuple[Dict[str, Any], yarl.URL]
269
+ A tuple containing the response data and the request URL.
270
+ """
271
+ async with self.__retry_client.request(method=method, url=url, **kwargs) as response:
272
+ res = await response.json()
273
+
274
+ return (res, response.request_info.url)
275
+
276
+ # Disable pylint warning: method was expected to be 'non-async'
277
+ # pylint: disable=W0236
278
+ async def get(self) -> List[Tuple[Dict[str, Any], yarl.URL]]:
279
+ """
280
+ Make GET requests.
281
+
282
+ Returns
283
+ -------
284
+ List[Tuple[Dict[str, Any], yarl.URL]]
285
+ A list of response data and corresponding request URLs.
286
+ """
287
+ tasks = self.__tasks_generator(method=HttpMethod.GET)
288
+ responses_urls = await self._execute(tasks=tasks)
289
+
290
+ return responses_urls
291
+
292
+ # Disable pylint warning: method was expected to be 'non-async'
293
+ # pylint: disable=W0236
294
+ async def post(self) -> List[Tuple[Dict[str, Any], yarl.URL]]:
295
+ """
296
+ Make POST requests.
297
+
298
+ Returns
299
+ -------
300
+ List[Tuple[Dict[str, Any], yarl.URL]]
301
+ A list of response data and corresponding request URLs.
302
+ """
303
+ tasks = self.__tasks_generator(method=HttpMethod.POST)
304
+ responses_urls = await self._execute(tasks=tasks)
305
+
306
+ return responses_urls
307
+
308
+ # Disable pylint warning: method was expected to be 'non-async'
309
+ # pylint: disable=W0236
310
+ async def put(self) -> List[Tuple[Dict[str, Any], yarl.URL]]:
311
+ """
312
+ Make PUT requests.
313
+
314
+ Returns
315
+ -------
316
+ List[Tuple[Dict[str, Any], yarl.URL]]
317
+ A list of response data and corresponding request URLs.
318
+ """
319
+ tasks = self.__tasks_generator(method=HttpMethod.PUT)
320
+ responses_urls = await self._execute(tasks=tasks)
321
+
322
+ return responses_urls
323
+
324
+ # Disable pylint warning: method was expected to be 'non-async'
325
+ # pylint: disable=W0236
326
+ async def delete(self) -> List[Tuple[Dict[str, Any], yarl.URL]]:
327
+ """
328
+ Make DELETE requests.
329
+
330
+ Returns
331
+ -------
332
+ List[Tuple[Dict[str, Any], yarl.URL]]
333
+ A list of response data and corresponding request URLs.
334
+ """
335
+ tasks = self.__tasks_generator(method=HttpMethod.DELETE)
336
+ responses_urls = await self._execute(tasks=tasks)
337
+
338
+ return responses_urls
339
+
340
+ def execute(self) -> AsyncHttpStep.Output:
341
+ """
342
+ Execute the step.
343
+
344
+ Raises
345
+ ------
346
+ ValueError
347
+ If the specified HTTP method is not implemented in AsyncHttpStep.
348
+ """
349
+ # By design asyncio does not allow its event loop to be nested. This presents a practical problem:
350
+ # When in an environment where the event loop is already running
351
+ # it’s impossible to run tasks and wait for the result.
352
+ # Trying to do so will give the error “RuntimeError: This event loop is already running”.
353
+ # The issue pops up in various environments, such as web servers, GUI applications and in
354
+ # Jupyter/DataBricks notebooks.
355
+ nest_asyncio.apply()
356
+
357
+ map_method_func = {
358
+ HttpMethod.GET: self.get,
359
+ HttpMethod.POST: self.post,
360
+ HttpMethod.PUT: self.put,
361
+ HttpMethod.DELETE: self.delete,
362
+ }
363
+
364
+ if self.method not in map_method_func:
365
+ raise ValueError(f"Method {self.method} not implemented in AsyncHttpStep.")
366
+
367
+ self.output.responses_urls = asyncio.run(map_method_func[self.method]())
368
+
369
+ return self.output
370
+
371
+
372
+ class AsyncHttpGetStep(AsyncHttpStep):
373
+ """
374
+ Represents an asynchronous HTTP GET step.
375
+
376
+ This class inherits from the AsyncHttpStep class and specifies the HTTP method as GET.
377
+
378
+ Attributes:
379
+ method (HttpMethod): The HTTP method for the step, set to HttpMethod.GET.
380
+
381
+ """
382
+
383
+ method: HttpMethod = HttpMethod.GET