cloudquery-plugin-sdk 0.1.55__tar.gz → 0.1.57__tar.gz
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.
- {cloudquery_plugin_sdk-0.1.55/cloudquery_plugin_sdk.egg-info → cloudquery_plugin_sdk-0.1.57}/PKG-INFO +2 -1
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/internal/checkpoint.py +61 -8
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scheduler/scheduler.py +50 -35
- cloudquery_plugin_sdk-0.1.57/cloudquery/sdk/stateclient/__init__.py +2 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/stateclient/stateclient.py +31 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57/cloudquery_plugin_sdk.egg-info}/PKG-INFO +2 -1
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery_plugin_sdk.egg-info/requires.txt +1 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/pyproject.toml +3 -2
- cloudquery_plugin_sdk-0.1.55/cloudquery/sdk/stateclient/__init__.py +0 -1
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/LICENSE +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/README.md +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/__init__.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/internal/__init__.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/internal/memdb/__init__.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/internal/memdb/memdb.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/internal/servers/__init__.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/internal/servers/discovery_v1/__init__.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/internal/servers/discovery_v1/discovery.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/internal/servers/plugin_v3/__init__.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/internal/servers/plugin_v3/plugin.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/message/__init__.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/message/read.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/message/sync.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/message/write.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/plugin/__init__.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/plugin/plugin.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/py.typed +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scalar/__init__.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scalar/binary.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scalar/bool.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scalar/date32.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scalar/date64.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scalar/float.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scalar/int.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scalar/json.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scalar/list.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scalar/scalar.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scalar/scalar_factory.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scalar/string.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scalar/timestamp.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scalar/uint.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scalar/uuid.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scalar/vector.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scheduler/__init__.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scheduler/table_resolver.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/schema/__init__.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/schema/arrow.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/schema/column.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/schema/resource.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/schema/table.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/serve/__init__.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/serve/plugin.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/stateclient/keys.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/transformers/__init__.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/transformers/openapi.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/transformers/transformers.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/types/__init__.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/types/json.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/types/uuid.py +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery_plugin_sdk.egg-info/SOURCES.txt +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery_plugin_sdk.egg-info/dependency_links.txt +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery_plugin_sdk.egg-info/top_level.txt +0 -0
- {cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cloudquery-plugin-sdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.57
|
|
4
4
|
Summary: CloudQuery Plugin SDK for Python
|
|
5
5
|
Author-email: CloudQuery LTD <pypi-packages@cloudquery.io>
|
|
6
6
|
License-Expression: MPL-2.0
|
|
@@ -16,6 +16,7 @@ Classifier: Topic :: Internet
|
|
|
16
16
|
Requires-Python: >=3.11
|
|
17
17
|
Description-Content-Type: text/markdown
|
|
18
18
|
License-File: LICENSE
|
|
19
|
+
Requires-Dist: certifi>=2026.10.19
|
|
19
20
|
Requires-Dist: cloudquery-plugin-pb>=0.0.55
|
|
20
21
|
Requires-Dist: grpcio>=1.78.0
|
|
21
22
|
Requires-Dist: grpcio-tools>=1.78.0
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/internal/checkpoint.py
RENAMED
|
@@ -22,6 +22,14 @@ from typing import Dict, Iterable, Iterator, List, Optional, Set, Tuple
|
|
|
22
22
|
from cloudquery.sdk.message import SyncCheckpointMessage
|
|
23
23
|
|
|
24
24
|
|
|
25
|
+
class SyncNotRunningError(RuntimeError):
|
|
26
|
+
"""
|
|
27
|
+
Raised when state checkpoints are flushed while their sync is not running, so they cannot be
|
|
28
|
+
sent: before or after Scheduler.sync, or from a resolver of an ended sync. Flush state that
|
|
29
|
+
summarizes the whole sync in the scheduler's post-sync hook instead.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
|
|
25
33
|
class Scope:
|
|
26
34
|
"""
|
|
27
35
|
A table resolver call and the relations its resources spawn.
|
|
@@ -39,6 +47,10 @@ class Scope:
|
|
|
39
47
|
self.resolving = True
|
|
40
48
|
# checkpoints waiting for this scope to finish
|
|
41
49
|
self.waiters: List[_Pending] = []
|
|
50
|
+
# set while a resource is resolved, until it is on the queue and its relations are submitted
|
|
51
|
+
self.resource_in_flight = False
|
|
52
|
+
# checkpoints waiting for that resource and its relations
|
|
53
|
+
self.after_resource: List[_Pending] = []
|
|
42
54
|
|
|
43
55
|
def finished(self) -> bool:
|
|
44
56
|
return not self.resolving and not self.open_children
|
|
@@ -112,6 +124,37 @@ class Checkpoints:
|
|
|
112
124
|
"""Returns the scope of the table resolver running in this context, or None outside table resolvers."""
|
|
113
125
|
return self._scope.get()
|
|
114
126
|
|
|
127
|
+
@contextmanager
|
|
128
|
+
def resource(self, scope: Scope) -> Iterator[None]:
|
|
129
|
+
"""
|
|
130
|
+
Marks a resource of scope as in flight until it is on the queue and its relations are
|
|
131
|
+
submitted, so that checkpoints flushed meanwhile, e.g. from its resource hooks, follow
|
|
132
|
+
the resource and its relations.
|
|
133
|
+
"""
|
|
134
|
+
with self._lock:
|
|
135
|
+
scope.resource_in_flight = True
|
|
136
|
+
try:
|
|
137
|
+
yield
|
|
138
|
+
finally:
|
|
139
|
+
with self._lock:
|
|
140
|
+
scope.resource_in_flight = False
|
|
141
|
+
ready: List[_Pending] = []
|
|
142
|
+
for pending in scope.after_resource:
|
|
143
|
+
pending.blockers -= 1
|
|
144
|
+
self._wait_for_children(pending, scope)
|
|
145
|
+
if pending.blockers == 0:
|
|
146
|
+
ready.append(pending)
|
|
147
|
+
scope.after_resource = []
|
|
148
|
+
self._send(ready)
|
|
149
|
+
|
|
150
|
+
def superseded(self, scope: Optional[Scope]) -> bool:
|
|
151
|
+
"""Returns True if scope belongs to a sync that ended while another sync runs now."""
|
|
152
|
+
return (
|
|
153
|
+
scope is not None
|
|
154
|
+
and self._sync is not None
|
|
155
|
+
and scope.sync is not self._sync
|
|
156
|
+
)
|
|
157
|
+
|
|
115
158
|
def resolver_finished(self, scope: Scope) -> None:
|
|
116
159
|
"""
|
|
117
160
|
Marks the resolver of scope as returned, once every resource it produced is on the queue.
|
|
@@ -143,17 +186,17 @@ class Checkpoints:
|
|
|
143
186
|
wait for nothing. Schedules all of them or, if it raises, none of them.
|
|
144
187
|
|
|
145
188
|
Raises:
|
|
146
|
-
|
|
189
|
+
SyncNotRunningError: if the sync of the checkpoints is not running.
|
|
147
190
|
"""
|
|
148
191
|
with self._lock:
|
|
149
192
|
if self._res is None:
|
|
150
|
-
raise
|
|
193
|
+
raise SyncNotRunningError(
|
|
151
194
|
"state checkpoints can only be flushed while the scheduler syncs, "
|
|
152
195
|
"e.g. from a table resolver or the post-sync hook"
|
|
153
196
|
)
|
|
154
197
|
for scopes, _, _ in checkpoints:
|
|
155
|
-
if any(
|
|
156
|
-
raise
|
|
198
|
+
if any(self.superseded(s) for s in scopes):
|
|
199
|
+
raise SyncNotRunningError(
|
|
157
200
|
"state checkpoints of a table resolver can only be flushed during its own sync"
|
|
158
201
|
)
|
|
159
202
|
for scopes, keys, msg in checkpoints:
|
|
@@ -167,11 +210,11 @@ class Checkpoints:
|
|
|
167
210
|
) -> None:
|
|
168
211
|
pending = _Pending(msg, keys, self._sync)
|
|
169
212
|
for scope in {s for s in scopes if s is not None}:
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
for child in scope.open_children:
|
|
173
|
-
child.waiters.append(pending)
|
|
213
|
+
if scope.resource_in_flight:
|
|
214
|
+
scope.after_resource.append(pending)
|
|
174
215
|
pending.blockers += 1
|
|
216
|
+
else:
|
|
217
|
+
self._wait_for_children(pending, scope)
|
|
175
218
|
earlier = set()
|
|
176
219
|
for key in keys:
|
|
177
220
|
previous = self._unsent.get(key)
|
|
@@ -183,6 +226,16 @@ class Checkpoints:
|
|
|
183
226
|
if pending.blockers == 0:
|
|
184
227
|
self._send([pending])
|
|
185
228
|
|
|
229
|
+
@staticmethod
|
|
230
|
+
def _wait_for_children(pending: _Pending, scope: Scope) -> None:
|
|
231
|
+
"""
|
|
232
|
+
Makes pending wait for the relations of scope's resources. Its resources are on the queue
|
|
233
|
+
already, as none is in flight. Called with the lock held.
|
|
234
|
+
"""
|
|
235
|
+
for child in scope.open_children:
|
|
236
|
+
child.waiters.append(pending)
|
|
237
|
+
pending.blockers += 1
|
|
238
|
+
|
|
186
239
|
def _send(self, ready: List[_Pending]) -> None:
|
|
187
240
|
"""Puts ready checkpoints on the queue, then the ones that only waited for them. Called with the lock held."""
|
|
188
241
|
while ready:
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scheduler/scheduler.py
RENAMED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import queue
|
|
2
|
+
import threading
|
|
2
3
|
from concurrent import futures
|
|
3
4
|
from typing import List, Generator, Any, Optional
|
|
4
5
|
|
|
@@ -19,9 +20,22 @@ QUEUE_PER_WORKER = 100
|
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
class ThreadPoolExecutorWithQueueSizeLimit(futures.ThreadPoolExecutor):
|
|
22
|
-
def __init__(self, maxsize, *args, **kwargs):
|
|
23
|
-
super().__init__(*args, **kwargs)
|
|
24
|
-
self.
|
|
23
|
+
def __init__(self, maxsize: int, max_workers: int, *args, **kwargs):
|
|
24
|
+
super().__init__(max_workers, *args, **kwargs)
|
|
25
|
+
self.maxsize = maxsize
|
|
26
|
+
# submit() puts on the work queue while holding a lock that every pool shares, so a
|
|
27
|
+
# full bounded queue would block the other pools' workers too: limit it before submit
|
|
28
|
+
self._slots = threading.BoundedSemaphore(maxsize + max_workers)
|
|
29
|
+
|
|
30
|
+
def submit(self, fn, /, *args, **kwargs):
|
|
31
|
+
self._slots.acquire()
|
|
32
|
+
try:
|
|
33
|
+
future = super().submit(fn, *args, **kwargs)
|
|
34
|
+
except BaseException:
|
|
35
|
+
self._slots.release()
|
|
36
|
+
raise
|
|
37
|
+
future.add_done_callback(lambda _: self._slots.release())
|
|
38
|
+
return future
|
|
25
39
|
|
|
26
40
|
|
|
27
41
|
class TableResolverStarted:
|
|
@@ -61,7 +75,7 @@ class Scheduler:
|
|
|
61
75
|
)
|
|
62
76
|
self._pools: List[ThreadPoolExecutorWithQueueSizeLimit] = []
|
|
63
77
|
current_depth_concurrency = concurrency
|
|
64
|
-
current_depth_queue_size =
|
|
78
|
+
current_depth_queue_size = self._queue_size
|
|
65
79
|
for _ in range(max_depth + 1):
|
|
66
80
|
self._pools.append(
|
|
67
81
|
ThreadPoolExecutorWithQueueSizeLimit(
|
|
@@ -134,37 +148,38 @@ class Scheduler:
|
|
|
134
148
|
depth=depth,
|
|
135
149
|
)
|
|
136
150
|
for item in resolver.resolve(client, parent_item):
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
self.
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
151
|
+
with self.checkpoints.resource(scope):
|
|
152
|
+
try:
|
|
153
|
+
resource = self.resolve_resource(
|
|
154
|
+
resolver, client, parent_item, item
|
|
155
|
+
)
|
|
156
|
+
except Exception as e:
|
|
157
|
+
self._logger.error(
|
|
158
|
+
"failed to resolve resource",
|
|
159
|
+
client_id=client.id(),
|
|
160
|
+
table=resolver.table.name,
|
|
161
|
+
depth=depth,
|
|
162
|
+
exc_info=e,
|
|
163
|
+
)
|
|
164
|
+
self._logger.debug(
|
|
165
|
+
"details about resource that failed to resolve",
|
|
166
|
+
client_id=client.id(),
|
|
167
|
+
table=resolver.table.name,
|
|
168
|
+
resource=item,
|
|
169
|
+
)
|
|
170
|
+
continue
|
|
171
|
+
res.put(SyncInsertMessage(resource.to_arrow_record()))
|
|
172
|
+
for child_resolvers in resolver.child_resolvers:
|
|
173
|
+
res.put(TableResolverStarted())
|
|
174
|
+
self._pools[depth + 1].submit(
|
|
175
|
+
self.resolve_table,
|
|
176
|
+
child_resolvers,
|
|
177
|
+
depth + 1,
|
|
178
|
+
client,
|
|
179
|
+
resource,
|
|
180
|
+
res,
|
|
181
|
+
self.checkpoints.new_scope(scope),
|
|
182
|
+
)
|
|
168
183
|
total_resources += 1
|
|
169
184
|
if depth == 0:
|
|
170
185
|
self._logger.info(
|
|
@@ -79,14 +79,22 @@ class StateClient(ABC):
|
|
|
79
79
|
|
|
80
80
|
self.migrate_state_table()
|
|
81
81
|
self.read_all_state()
|
|
82
|
+
self._flushed = dict(self.mem)
|
|
82
83
|
|
|
83
84
|
def get_key(self, key: str) -> Optional[str]:
|
|
84
85
|
with self._lock:
|
|
86
|
+
self._forget_changes_of_ended_syncs(key)
|
|
85
87
|
return self.mem.get(key)
|
|
86
88
|
|
|
87
89
|
def set_key(self, key: str, value: str) -> None:
|
|
88
90
|
scope = self.checkpoints.current_scope() if self.checkpoints else None
|
|
91
|
+
if self.checkpoints and self.checkpoints.superseded(scope):
|
|
92
|
+
return
|
|
89
93
|
with self._lock:
|
|
94
|
+
# with checkpoints, the value also covers the caller's resources, so later checkpoints
|
|
95
|
+
# of the key must still follow them even if it is unchanged
|
|
96
|
+
if self.checkpoints is None and self.mem.get(key) == value:
|
|
97
|
+
return
|
|
90
98
|
self.mem[key] = value
|
|
91
99
|
self.changes.setdefault(key, set()).add(scope)
|
|
92
100
|
|
|
@@ -95,9 +103,17 @@ class StateClient(ABC):
|
|
|
95
103
|
Persists the keys changed since the last flush: writes them to the backend, or, with
|
|
96
104
|
checkpoints, sends them on the sync stream after the resources they cover.
|
|
97
105
|
If that fails, the keys stay changed, so a later flush retries them.
|
|
106
|
+
Changes made by table resolvers of an ended sync are dropped once another sync runs,
|
|
107
|
+
as the resources they cover may never have been committed.
|
|
108
|
+
|
|
109
|
+
Raises:
|
|
110
|
+
SyncNotRunningError: with checkpoints, if the sync is not running, e.g. once
|
|
111
|
+
Scheduler.sync returned. Flush in the scheduler's post-sync hook instead.
|
|
98
112
|
"""
|
|
99
113
|
with self._flush_lock:
|
|
100
114
|
with self._lock:
|
|
115
|
+
for key in list(self.changes):
|
|
116
|
+
self._forget_changes_of_ended_syncs(key)
|
|
101
117
|
if not self.changes:
|
|
102
118
|
return
|
|
103
119
|
changes, self.changes = self.changes, {}
|
|
@@ -112,6 +128,21 @@ class StateClient(ABC):
|
|
|
112
128
|
for key, scopes in changes.items():
|
|
113
129
|
self.changes.setdefault(key, set()).update(scopes)
|
|
114
130
|
raise
|
|
131
|
+
with self._lock:
|
|
132
|
+
self._flushed.update(values)
|
|
133
|
+
|
|
134
|
+
def _forget_changes_of_ended_syncs(self, key: str) -> None:
|
|
135
|
+
if self.checkpoints is None or key not in self.changes:
|
|
136
|
+
return
|
|
137
|
+
scopes = {s for s in self.changes[key] if not self.checkpoints.superseded(s)}
|
|
138
|
+
if scopes:
|
|
139
|
+
self.changes[key] = scopes
|
|
140
|
+
return
|
|
141
|
+
del self.changes[key]
|
|
142
|
+
if key in self._flushed:
|
|
143
|
+
self.mem[key] = self._flushed[key]
|
|
144
|
+
else:
|
|
145
|
+
self.mem.pop(key, None)
|
|
115
146
|
|
|
116
147
|
def _send_checkpoints(
|
|
117
148
|
self, changes: Dict[str, Set[Optional[Scope]]], values: Dict[str, str]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cloudquery-plugin-sdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.57
|
|
4
4
|
Summary: CloudQuery Plugin SDK for Python
|
|
5
5
|
Author-email: CloudQuery LTD <pypi-packages@cloudquery.io>
|
|
6
6
|
License-Expression: MPL-2.0
|
|
@@ -16,6 +16,7 @@ Classifier: Topic :: Internet
|
|
|
16
16
|
Requires-Python: >=3.11
|
|
17
17
|
Description-Content-Type: text/markdown
|
|
18
18
|
License-File: LICENSE
|
|
19
|
+
Requires-Dist: certifi>=2026.10.19
|
|
19
20
|
Requires-Dist: cloudquery-plugin-pb>=0.0.55
|
|
20
21
|
Requires-Dist: grpcio>=1.78.0
|
|
21
22
|
Requires-Dist: grpcio-tools>=1.78.0
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "cloudquery-plugin-sdk"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.57"
|
|
8
8
|
description = "CloudQuery Plugin SDK for Python"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MPL-2.0"
|
|
@@ -23,6 +23,7 @@ classifiers = [
|
|
|
23
23
|
"Topic :: Internet",
|
|
24
24
|
]
|
|
25
25
|
dependencies = [
|
|
26
|
+
"certifi>=2026.10.19",
|
|
26
27
|
"cloudquery-plugin-pb>=0.0.55",
|
|
27
28
|
"grpcio>=1.78.0",
|
|
28
29
|
"grpcio-tools>=1.78.0",
|
|
@@ -61,4 +62,4 @@ python_files = "tests/*.py"
|
|
|
61
62
|
|
|
62
63
|
[tool.uv]
|
|
63
64
|
exclude-newer = "1 week"
|
|
64
|
-
exclude-newer-package = { cloudquery-plugin-pb = false }
|
|
65
|
+
exclude-newer-package = { cloudquery-plugin-pb = false, certifi = false }
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .keys import state_key
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/internal/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/internal/memdb/memdb.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/message/__init__.py
RENAMED
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/message/read.py
RENAMED
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/message/sync.py
RENAMED
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/message/write.py
RENAMED
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/plugin/__init__.py
RENAMED
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/plugin/plugin.py
RENAMED
|
File without changes
|
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scalar/__init__.py
RENAMED
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scalar/binary.py
RENAMED
|
File without changes
|
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scalar/date32.py
RENAMED
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scalar/date64.py
RENAMED
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scalar/float.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scalar/scalar.py
RENAMED
|
File without changes
|
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scalar/string.py
RENAMED
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scalar/timestamp.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scalar/vector.py
RENAMED
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/scheduler/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/schema/__init__.py
RENAMED
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/schema/arrow.py
RENAMED
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/schema/column.py
RENAMED
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/schema/resource.py
RENAMED
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/schema/table.py
RENAMED
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/serve/__init__.py
RENAMED
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/serve/plugin.py
RENAMED
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/stateclient/keys.py
RENAMED
|
File without changes
|
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/transformers/openapi.py
RENAMED
|
File without changes
|
|
File without changes
|
{cloudquery_plugin_sdk-0.1.55 → cloudquery_plugin_sdk-0.1.57}/cloudquery/sdk/types/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|