port-ocean 0.12.2__py3-none-any.whl → 0.12.2.dev1__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.
Potentially problematic release.
This version of port-ocean might be problematic. Click here for more details.
- port_ocean/core/event_listener/base.py +11 -2
- port_ocean/log/handlers.py +8 -7
- port_ocean/ocean.py +12 -11
- port_ocean/utils/async_iterators.py +0 -60
- {port_ocean-0.12.2.dist-info → port_ocean-0.12.2.dev1.dist-info}/METADATA +1 -1
- {port_ocean-0.12.2.dist-info → port_ocean-0.12.2.dev1.dist-info}/RECORD +9 -10
- port_ocean/tests/utils/test_async_iterators.py +0 -45
- {port_ocean-0.12.2.dist-info → port_ocean-0.12.2.dev1.dist-info}/LICENSE.md +0 -0
- {port_ocean-0.12.2.dist-info → port_ocean-0.12.2.dev1.dist-info}/WHEEL +0 -0
- {port_ocean-0.12.2.dist-info → port_ocean-0.12.2.dev1.dist-info}/entry_points.txt +0 -0
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
from abc import abstractmethod
|
|
3
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
2
4
|
from typing import TypedDict, Callable, Any, Awaitable
|
|
3
5
|
|
|
4
6
|
from pydantic import Extra
|
|
5
7
|
|
|
6
8
|
from port_ocean.config.base import BaseOceanModel
|
|
7
|
-
from port_ocean.utils.signal import signal_handler
|
|
8
9
|
from port_ocean.context.ocean import ocean
|
|
9
10
|
from port_ocean.utils.misc import IntegrationStateStatus
|
|
11
|
+
from port_ocean.utils.signal import signal_handler
|
|
10
12
|
|
|
11
13
|
|
|
12
14
|
class EventListenerEvents(TypedDict):
|
|
@@ -67,7 +69,14 @@ class BaseEventListener:
|
|
|
67
69
|
"""
|
|
68
70
|
await self._before_resync()
|
|
69
71
|
try:
|
|
70
|
-
|
|
72
|
+
loop = asyncio.get_event_loop()
|
|
73
|
+
with ThreadPoolExecutor() as executor:
|
|
74
|
+
e = executor.submit(
|
|
75
|
+
lambda: asyncio.run_coroutine_threadsafe(
|
|
76
|
+
self.events["on_resync"](resync_args), loop
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
signal_handler.register(e.cancel)
|
|
71
80
|
await self._after_resync()
|
|
72
81
|
except Exception as e:
|
|
73
82
|
await self._on_resync_failure(e)
|
port_ocean/log/handlers.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import logging
|
|
3
3
|
import sys
|
|
4
|
-
import threading
|
|
5
4
|
import time
|
|
5
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
6
6
|
from datetime import datetime
|
|
7
7
|
from logging.handlers import MemoryHandler
|
|
8
8
|
from typing import Any
|
|
@@ -65,18 +65,19 @@ class HTTPMemoryHandler(MemoryHandler):
|
|
|
65
65
|
if self.ocean is None or not self.buffer:
|
|
66
66
|
return
|
|
67
67
|
|
|
68
|
-
def _wrap_event_loop(_ocean: Ocean, logs_to_send: list[dict[str, Any]]) -> None:
|
|
69
|
-
loop = asyncio.new_event_loop()
|
|
70
|
-
loop.run_until_complete(self.send_logs(_ocean, logs_to_send))
|
|
71
|
-
loop.close()
|
|
72
|
-
|
|
73
68
|
self.acquire()
|
|
74
69
|
logs = list(self._serialized_buffer)
|
|
75
70
|
if logs:
|
|
76
71
|
self.buffer.clear()
|
|
77
72
|
self._serialized_buffer.clear()
|
|
78
73
|
self.last_flush_time = time.time()
|
|
79
|
-
|
|
74
|
+
loop = asyncio.new_event_loop()
|
|
75
|
+
with ThreadPoolExecutor() as executor:
|
|
76
|
+
executor.submit(
|
|
77
|
+
lambda: asyncio.run_coroutine_threadsafe(
|
|
78
|
+
self.send_logs(self.ocean, logs), loop
|
|
79
|
+
)
|
|
80
|
+
)
|
|
80
81
|
self.release()
|
|
81
82
|
|
|
82
83
|
async def send_logs(
|
port_ocean/ocean.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import sys
|
|
3
|
-
import
|
|
3
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
4
4
|
from contextlib import asynccontextmanager
|
|
5
5
|
from typing import Callable, Any, Dict, AsyncIterator, Type
|
|
6
6
|
|
|
@@ -9,8 +9,6 @@ from loguru import logger
|
|
|
9
9
|
from pydantic import BaseModel
|
|
10
10
|
from starlette.types import Scope, Receive, Send
|
|
11
11
|
|
|
12
|
-
from port_ocean.core.handlers.resync_state_updater import ResyncStateUpdater
|
|
13
|
-
from port_ocean.core.models import Runtime
|
|
14
12
|
from port_ocean.clients.port.client import PortClient
|
|
15
13
|
from port_ocean.config.settings import (
|
|
16
14
|
IntegrationConfiguration,
|
|
@@ -20,13 +18,15 @@ from port_ocean.context.ocean import (
|
|
|
20
18
|
ocean,
|
|
21
19
|
initialize_port_ocean_context,
|
|
22
20
|
)
|
|
21
|
+
from port_ocean.core.handlers.resync_state_updater import ResyncStateUpdater
|
|
23
22
|
from port_ocean.core.integrations.base import BaseIntegration
|
|
23
|
+
from port_ocean.core.models import Runtime
|
|
24
24
|
from port_ocean.log.sensetive import sensitive_log_filter
|
|
25
25
|
from port_ocean.middlewares import request_handler
|
|
26
|
+
from port_ocean.utils.misc import IntegrationStateStatus
|
|
26
27
|
from port_ocean.utils.repeat import repeat_every
|
|
27
28
|
from port_ocean.utils.signal import signal_handler
|
|
28
29
|
from port_ocean.version import __integration_version__
|
|
29
|
-
from port_ocean.utils.misc import IntegrationStateStatus
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
class Ocean:
|
|
@@ -92,6 +92,13 @@ class Ocean:
|
|
|
92
92
|
)
|
|
93
93
|
raise e
|
|
94
94
|
|
|
95
|
+
def pool_executor_wrapper(event_loop) -> None:
|
|
96
|
+
with ThreadPoolExecutor() as executor:
|
|
97
|
+
e = executor.submit(lambda: asyncio.run_coroutine_threadsafe(
|
|
98
|
+
execute_resync_all(), event_loop
|
|
99
|
+
))
|
|
100
|
+
signal_handler.register(e.cancel)
|
|
101
|
+
|
|
95
102
|
interval = self.config.scheduled_resync_interval
|
|
96
103
|
loop = asyncio.get_event_loop()
|
|
97
104
|
if interval is not None:
|
|
@@ -103,13 +110,7 @@ class Ocean:
|
|
|
103
110
|
seconds=interval * 60,
|
|
104
111
|
# Not running the resync immediately because the event listener should run resync on startup
|
|
105
112
|
wait_first=True,
|
|
106
|
-
)(
|
|
107
|
-
lambda: threading.Thread(
|
|
108
|
-
target=lambda: asyncio.run_coroutine_threadsafe(
|
|
109
|
-
execute_resync_all(), loop
|
|
110
|
-
)
|
|
111
|
-
).start()
|
|
112
|
-
)
|
|
113
|
+
)(lambda _: pool_executor_wrapper(loop))
|
|
113
114
|
await repeated_function()
|
|
114
115
|
|
|
115
116
|
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
|
|
@@ -2,9 +2,6 @@ import typing
|
|
|
2
2
|
|
|
3
3
|
import aiostream
|
|
4
4
|
|
|
5
|
-
if typing.TYPE_CHECKING:
|
|
6
|
-
from asyncio import Semaphore
|
|
7
|
-
|
|
8
5
|
|
|
9
6
|
async def stream_async_iterators_tasks(
|
|
10
7
|
*tasks: typing.AsyncIterable[typing.Any],
|
|
@@ -50,60 +47,3 @@ async def stream_async_iterators_tasks(
|
|
|
50
47
|
async with combine.stream() as streamer:
|
|
51
48
|
async for batch_items in streamer:
|
|
52
49
|
yield batch_items
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
async def semaphore_async_iterator(
|
|
56
|
-
semaphore: "Semaphore",
|
|
57
|
-
function: typing.Callable[[], typing.AsyncIterator[typing.Any]],
|
|
58
|
-
) -> typing.AsyncIterator[typing.Any]:
|
|
59
|
-
"""
|
|
60
|
-
Executes an asynchronous iterator function under a semaphore to limit concurrency.
|
|
61
|
-
|
|
62
|
-
This function ensures that the provided asynchronous iterator function is executed
|
|
63
|
-
while respecting the concurrency limit imposed by the semaphore. It acquires the
|
|
64
|
-
semaphore before executing the function and releases it after the function completes,
|
|
65
|
-
thus controlling the number of concurrent executions.
|
|
66
|
-
|
|
67
|
-
Parameters:
|
|
68
|
-
semaphore (asyncio.Semaphore | asyncio.BoundedSemaphore): The semaphore used to limit concurrency.
|
|
69
|
-
function (Callable[[], AsyncIterator[Any]]): A nullary asynchronous function, - apply arguments with `functools.partial` or an anonymous function (lambda)
|
|
70
|
-
that returns an asynchronous iterator. This function is executed under the semaphore.
|
|
71
|
-
|
|
72
|
-
Yields:
|
|
73
|
-
Any: The items yielded by the asynchronous iterator function.
|
|
74
|
-
|
|
75
|
-
Usage:
|
|
76
|
-
```python
|
|
77
|
-
import asyncio
|
|
78
|
-
|
|
79
|
-
async def async_iterator_function(param1, param2):
|
|
80
|
-
# Your async code here
|
|
81
|
-
yield ...
|
|
82
|
-
|
|
83
|
-
async def async_generator_function():
|
|
84
|
-
# Your async code to retrieve items
|
|
85
|
-
param1 = "your_param1"
|
|
86
|
-
yield param1
|
|
87
|
-
|
|
88
|
-
async def main():
|
|
89
|
-
semaphore = asyncio.BoundedSemaphore(50)
|
|
90
|
-
param2 = "your_param2"
|
|
91
|
-
|
|
92
|
-
tasks = [
|
|
93
|
-
semaphore_async_iterator(
|
|
94
|
-
semaphore,
|
|
95
|
-
lambda: async_iterator_function(param1, param2) # functools.partial(async_iterator_function, param1, param2)
|
|
96
|
-
)
|
|
97
|
-
async for param1 in async_generator_function()
|
|
98
|
-
]
|
|
99
|
-
|
|
100
|
-
async for batch in stream_async_iterators_tasks(*tasks):
|
|
101
|
-
# Process each batch
|
|
102
|
-
pass
|
|
103
|
-
|
|
104
|
-
asyncio.run(main())
|
|
105
|
-
```
|
|
106
|
-
"""
|
|
107
|
-
async with semaphore:
|
|
108
|
-
async for result in function():
|
|
109
|
-
yield result
|
|
@@ -65,7 +65,7 @@ port_ocean/core/defaults/clean.py,sha256=S3UAfca-oU89WJKIB4OgGjGjPr0vxBQ2aRZsLTZ
|
|
|
65
65
|
port_ocean/core/defaults/common.py,sha256=uVUg6VEn4RqtXQwLwMNGfkmT5zYRN_h5USfKw3poVyo,3561
|
|
66
66
|
port_ocean/core/defaults/initialize.py,sha256=qg4JLIWjp0c5-9w09X99muHRYX4k1cGZ_77vYo-tnpU,8422
|
|
67
67
|
port_ocean/core/event_listener/__init__.py,sha256=mzJ33wRq0kh60fpVdOHVmvMTUQIvz3vxmifyBgwDn0E,889
|
|
68
|
-
port_ocean/core/event_listener/base.py,sha256=
|
|
68
|
+
port_ocean/core/event_listener/base.py,sha256=meO-SidN-7-KmwUhicUzXm1NHVC9-LyDXGhnPVnzIZw,2717
|
|
69
69
|
port_ocean/core/event_listener/factory.py,sha256=AYYfSHPAF7P5H-uQECXT0JVJjKDHrYkWJJBSL4mGkg8,3697
|
|
70
70
|
port_ocean/core/event_listener/http.py,sha256=N8HrfFqR3KGKz96pWdp_pP-m30jGtcz_1CkijovkBN8,2565
|
|
71
71
|
port_ocean/core/event_listener/kafka.py,sha256=ulidnp4sz-chXwHsbH9JayVjcxy_mG6ts_Im3YKmLpI,6983
|
|
@@ -111,11 +111,11 @@ port_ocean/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
|
|
|
111
111
|
port_ocean/helpers/async_client.py,sha256=SRlP6o7_FCSY3UHnRlZdezppePVxxOzZ0z861vE3K40,1783
|
|
112
112
|
port_ocean/helpers/retry.py,sha256=IQ0RfQ2T5o6uoZh2WW2nrFH5TT6K_k3y2Im0HDp5j9Y,15059
|
|
113
113
|
port_ocean/log/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
114
|
-
port_ocean/log/handlers.py,sha256=
|
|
114
|
+
port_ocean/log/handlers.py,sha256=afTTD4c_wlv5Ugp5dDrdBV6D91_a8SbeD6QjvNuLDYk,2859
|
|
115
115
|
port_ocean/log/logger_setup.py,sha256=BaXt-mh9CVXhneh37H46d04lqOdIBixG1pFyGfotuZs,2328
|
|
116
116
|
port_ocean/log/sensetive.py,sha256=lVKiZH6b7TkrZAMmhEJRhcl67HNM94e56x12DwFgCQk,2920
|
|
117
117
|
port_ocean/middlewares.py,sha256=9wYCdyzRZGK1vjEJ28FY_DkfwDNENmXp504UKPf5NaQ,2727
|
|
118
|
-
port_ocean/ocean.py,sha256=
|
|
118
|
+
port_ocean/ocean.py,sha256=5_TWyZ4qnTvJ90gx9NGir9aLkt4UzYYHvUN4kTjS1ao,5225
|
|
119
119
|
port_ocean/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
120
120
|
port_ocean/run.py,sha256=rTxBlrQd4yyrtgErCFJCHCEHs7d1OXrRiJehUYmIbN0,2212
|
|
121
121
|
port_ocean/sonar-project.properties,sha256=X_wLzDOkEVmpGLRMb2fg9Rb0DxWwUFSvESId8qpvrPI,73
|
|
@@ -130,10 +130,9 @@ port_ocean/tests/helpers/ocean_app.py,sha256=Dp1bwEDhWsx_G-KVxOfJX1eVIS4168ajLu3
|
|
|
130
130
|
port_ocean/tests/helpers/port_client.py,sha256=5d6GNr8vNNSOkrz1AdOhxBUKuusr_-UPDP7AVpHasQw,599
|
|
131
131
|
port_ocean/tests/helpers/smoke_test.py,sha256=_9aJJFRfuGJEg2D2YQJVJRmpreS6gEPHHQq8Q01x4aQ,2697
|
|
132
132
|
port_ocean/tests/test_smoke.py,sha256=uix2uIg_yOm8BHDgHw2hTFPy1fiIyxBGW3ENU_KoFlo,2557
|
|
133
|
-
port_ocean/tests/utils/test_async_iterators.py,sha256=3PLk1emEXekb8LcC5GgVh3OicaX15i5WyaJT_eFnu_4,1336
|
|
134
133
|
port_ocean/utils/__init__.py,sha256=KMGnCPXZJbNwtgxtyMycapkDz8tpSyw23MSYT3iVeHs,91
|
|
135
134
|
port_ocean/utils/async_http.py,sha256=arnH458TExn2Dju_Sy6pHas_vF5RMWnOp-jBz5WAAcE,1226
|
|
136
|
-
port_ocean/utils/async_iterators.py,sha256=
|
|
135
|
+
port_ocean/utils/async_iterators.py,sha256=iw3cUHxfQm3zUSPdw2FmSXDU8E1Ppnys4TGhswNuQ8s,1569
|
|
137
136
|
port_ocean/utils/cache.py,sha256=3KItZDE2yVrbVDr-hoM8lNna8s2dlpxhP4ICdLjH4LQ,2231
|
|
138
137
|
port_ocean/utils/misc.py,sha256=0q2cJ5psqxn_5u_56pT7vOVQ3shDM02iC1lzyWQ_zl0,2098
|
|
139
138
|
port_ocean/utils/queue_utils.py,sha256=KWWl8YVnG-glcfIHhM6nefY-2sou_C6DVP1VynQwzB4,2762
|
|
@@ -141,8 +140,8 @@ port_ocean/utils/repeat.py,sha256=0EFWM9d8lLXAhZmAyczY20LAnijw6UbIECf5lpGbOas,32
|
|
|
141
140
|
port_ocean/utils/signal.py,sha256=K-6kKFQTltcmKDhtyZAcn0IMa3sUpOHGOAUdWKgx0_E,1369
|
|
142
141
|
port_ocean/utils/time.py,sha256=pufAOH5ZQI7gXvOvJoQXZXZJV-Dqktoj9Qp9eiRwmJ4,1939
|
|
143
142
|
port_ocean/version.py,sha256=UsuJdvdQlazzKGD3Hd5-U7N69STh8Dq9ggJzQFnu9fU,177
|
|
144
|
-
port_ocean-0.12.2.dist-info/LICENSE.md,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
|
145
|
-
port_ocean-0.12.2.dist-info/METADATA,sha256=
|
|
146
|
-
port_ocean-0.12.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
147
|
-
port_ocean-0.12.2.dist-info/entry_points.txt,sha256=F_DNUmGZU2Kme-8NsWM5LLE8piGMafYZygRYhOVtcjA,54
|
|
148
|
-
port_ocean-0.12.2.dist-info/RECORD,,
|
|
143
|
+
port_ocean-0.12.2.dev1.dist-info/LICENSE.md,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
|
144
|
+
port_ocean-0.12.2.dev1.dist-info/METADATA,sha256=B4fcje8vn0RFBIoR7DGLB3wkqsFCNkY_FqjL-MV0Q9Y,6619
|
|
145
|
+
port_ocean-0.12.2.dev1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
146
|
+
port_ocean-0.12.2.dev1.dist-info/entry_points.txt,sha256=F_DNUmGZU2Kme-8NsWM5LLE8piGMafYZygRYhOVtcjA,54
|
|
147
|
+
port_ocean-0.12.2.dev1.dist-info/RECORD,,
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
from typing import Any, AsyncGenerator
|
|
2
|
-
import asyncio
|
|
3
|
-
from port_ocean.utils.async_iterators import semaphore_async_iterator
|
|
4
|
-
import pytest
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
@pytest.mark.asyncio
|
|
8
|
-
async def test_semaphore_async_iterator() -> None:
|
|
9
|
-
max_concurrency = 5
|
|
10
|
-
semaphore = asyncio.BoundedSemaphore(max_concurrency)
|
|
11
|
-
|
|
12
|
-
concurrent_tasks = 0
|
|
13
|
-
max_concurrent_tasks = 0
|
|
14
|
-
lock = asyncio.Lock() # Protect shared variables
|
|
15
|
-
|
|
16
|
-
num_tasks = 20
|
|
17
|
-
|
|
18
|
-
async def mock_function() -> AsyncGenerator[str, None]:
|
|
19
|
-
nonlocal concurrent_tasks, max_concurrent_tasks
|
|
20
|
-
|
|
21
|
-
async with lock:
|
|
22
|
-
concurrent_tasks += 1
|
|
23
|
-
if concurrent_tasks > max_concurrent_tasks:
|
|
24
|
-
max_concurrent_tasks = concurrent_tasks
|
|
25
|
-
|
|
26
|
-
await asyncio.sleep(0.1)
|
|
27
|
-
yield "result"
|
|
28
|
-
|
|
29
|
-
async with lock:
|
|
30
|
-
concurrent_tasks -= 1
|
|
31
|
-
|
|
32
|
-
async def consume_iterator(async_iterator: Any) -> None:
|
|
33
|
-
async for _ in async_iterator:
|
|
34
|
-
pass
|
|
35
|
-
|
|
36
|
-
tasks = [
|
|
37
|
-
consume_iterator(semaphore_async_iterator(semaphore, mock_function))
|
|
38
|
-
for _ in range(num_tasks)
|
|
39
|
-
]
|
|
40
|
-
await asyncio.gather(*tasks)
|
|
41
|
-
|
|
42
|
-
assert (
|
|
43
|
-
max_concurrent_tasks <= max_concurrency
|
|
44
|
-
), f"Max concurrent tasks {max_concurrent_tasks} exceeded semaphore limit {max_concurrency}"
|
|
45
|
-
assert concurrent_tasks == 0, "Not all tasks have completed"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|