port-ocean 0.12.2__py3-none-any.whl → 0.12.2.dev2__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.

@@ -1,12 +1,14 @@
1
+ import asyncio
1
2
  from abc import abstractmethod
3
+ from concurrent.futures import ProcessPoolExecutor
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
- await self.events["on_resync"](resync_args)
72
+ loop = asyncio.get_event_loop()
73
+ with ProcessPoolExecutor() 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)
@@ -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 ProcessPoolExecutor
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
- threading.Thread(target=_wrap_event_loop, args=(self.ocean, logs)).start()
74
+ loop = asyncio.new_event_loop()
75
+ with ProcessPoolExecutor() 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 threading
3
+ from concurrent.futures import ProcessPoolExecutor
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 ProcessPoolExecutor() 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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: port-ocean
3
- Version: 0.12.2
3
+ Version: 0.12.2.dev2
4
4
  Summary: Port Ocean is a CLI tool for managing your Port projects.
5
5
  Home-page: https://app.getport.io
6
6
  Keywords: ocean,port-ocean,port
@@ -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=1Nmpg00OfT2AD2L8eFm4VQEcdG2TClpSWJMhWhAjkEE,2356
68
+ port_ocean/core/event_listener/base.py,sha256=tc7NHg-Vvt8M4iVIBrJvkecVTmTDGE1r-bjMQZpK_ME,2719
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=k9G_Mb4ga2-Jke9irpdlYqj6EYiwv0gEsh4TgyqqOmI,2853
114
+ port_ocean/log/handlers.py,sha256=Br_1aujTmMkT15DbZj1E-mg0sHauvtKQB4E9-SDypmE,2861
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=Oe4H3kKtkj52uNO4Rd_47iY3MBdrTtshXZ_16q7A8bM,5071
118
+ port_ocean/ocean.py,sha256=ZPIRXqBF2WB3-tLsOwRnbFioGzGh2iRadK7YgYP1rpM,5227
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=CPXskYWkhkZtAG-ducEwM8537t3z5usPEqXR9vcivzw,3715
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=CB3srEnScX99emeWn9qiASGQ6hRn2KO2DiCEEi1pMXs,6614
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.dev2.dist-info/LICENSE.md,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
144
+ port_ocean-0.12.2.dev2.dist-info/METADATA,sha256=pFOjjt273h5-4BlEdo99jM90SCsMmMZKUa-waqQIRLc,6619
145
+ port_ocean-0.12.2.dev2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
146
+ port_ocean-0.12.2.dev2.dist-info/entry_points.txt,sha256=F_DNUmGZU2Kme-8NsWM5LLE8piGMafYZygRYhOVtcjA,54
147
+ port_ocean-0.12.2.dev2.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"