Skip to content

Commit 4cb91b3

Browse files
authored
feat(python): Add support for synchronous callbacks (#407)
1 parent 0f7559c commit 4cb91b3

File tree

6 files changed

+147
-57
lines changed

6 files changed

+147
-57
lines changed

packages/jsii-python-runtime/bin/generate-calc

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,10 @@ subprocess.run(
2323
"install",
2424
"--force-reinstall",
2525
"--upgrade",
26-
"--find-links",
27-
os.path.abspath("."),
28-
"--find-links",
29-
os.path.abspath(".env/jsii-calc/python"),
30-
"jsii-calc",
31-
],
26+
]
27+
+
28+
[x for x in os.listdir(".") if x.endswith(".whl")]
29+
+
30+
[os.path.join('.env/jsii-calc/python', x) for x in os.listdir(".env/jsii-calc/python") if x.endswith(".whl")],
3231
check=True,
3332
)

packages/jsii-python-runtime/src/jsii/_kernel/__init__.py

Lines changed: 88 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,25 @@
22
import inspect
33
import itertools
44

5-
from typing import Any, List, Optional, Type
5+
from typing import Any, List, Optional, Type, Union
66

77
import functools
88

99
import attr
1010

11+
from jsii.errors import JSIIError
1112
from jsii import _reference_map
1213
from jsii._utils import Singleton
1314
from jsii._kernel.providers import BaseProvider, ProcessProvider
1415
from jsii._kernel.types import JSClass, Referenceable
16+
from jsii._kernel.types import Callback
1517
from jsii._kernel.types import (
1618
EnumRef,
1719
LoadRequest,
1820
BeginRequest,
1921
CallbacksRequest,
2022
CreateRequest,
23+
CreateResponse,
2124
CompleteRequest,
2225
DeleteRequest,
2326
EndRequest,
@@ -30,6 +33,13 @@
3033
StatsRequest,
3134
ObjRef,
3235
Override,
36+
CompleteRequest,
37+
CompleteResponse,
38+
GetResponse,
39+
SetResponse,
40+
InvokeResponse,
41+
KernelResponse,
42+
BeginResponse
3343
)
3444

3545

@@ -40,12 +50,6 @@ class Object:
4050
__jsii_type__ = "Object"
4151

4252

43-
def _handle_callback(kernel, callback):
44-
obj = _reference_map.resolve_id(callback.invoke.objref.ref)
45-
method = getattr(obj, callback.cookie)
46-
return method(*callback.invoke.args)
47-
48-
4953
def _get_overides(klass: JSClass, obj: Any) -> List[Override]:
5054
overrides = []
5155

@@ -120,6 +124,39 @@ def _make_reference_for_native(kernel, d):
120124
return d
121125

122126

127+
def _handle_callback(kernel, callback):
128+
# need to handle get, set requests here as well as invoke requests
129+
if callback.invoke:
130+
obj = _reference_map.resolve_id(callback.invoke.objref.ref)
131+
method = getattr(obj, callback.cookie)
132+
return method(*callback.invoke.args)
133+
elif callback.get:
134+
obj = _reference_map.resolve_id(callback.get.objref.ref)
135+
return getattr(obj, callback.cookie)
136+
elif callback.set:
137+
obj = _reference_map.resolve_id(callback.set.objref.ref)
138+
return setattr(obj, callback.cookie, callback.set.value)
139+
else:
140+
raise JSIIError("Callback does not contain invoke|get|set")
141+
142+
143+
def _callback_till_result(kernel, response: Callback, response_type: Type[KernelResponse]) -> Any:
144+
while isinstance(response, Callback):
145+
try:
146+
result = _handle_callback(kernel, response)
147+
except Exception as exc:
148+
response = kernel.sync_complete(response.cbid, str(exc), None, response_type)
149+
else:
150+
response = kernel.sync_complete(response.cbid, None, result, response_type)
151+
152+
if isinstance(response, InvokeResponse):
153+
return response.result
154+
elif isinstance(response, GetResponse):
155+
return response.value
156+
else:
157+
return response
158+
159+
123160
@attr.s(auto_attribs=True, frozen=True, slots=True)
124161
class Statistics:
125162

@@ -155,33 +192,42 @@ def create(
155192

156193
overrides = _get_overides(klass, obj)
157194

158-
obj.__jsii_ref__ = self.provider.create(
195+
response = self.provider.create(
159196
CreateRequest(
160197
fqn=klass.__jsii_type__,
161198
args=_make_reference_for_native(self, args),
162199
overrides=overrides,
163200
)
164201
)
165-
202+
if isinstance(response, Callback):
203+
obj.__jsii_ref__ = _callback_till_result(self, response, CreateResponse)
204+
else:
205+
obj.__jsii_ref__ = response
166206
return obj.__jsii_ref__
167207

168208
def delete(self, ref: ObjRef) -> None:
169209
self.provider.delete(DeleteRequest(objref=ref))
170210

171211
@_dereferenced
172212
def get(self, obj: Referenceable, property: str) -> Any:
173-
return self.provider.get(
213+
response = self.provider.get(
174214
GetRequest(objref=obj.__jsii_ref__, property=property)
175-
).value
215+
)
216+
if isinstance(response, Callback):
217+
return _callback_till_result(self, response, GetResponse)
218+
else:
219+
return response.value
176220

177221
def set(self, obj: Referenceable, property: str, value: Any) -> None:
178-
self.provider.set(
222+
response = self.provider.set(
179223
SetRequest(
180224
objref=obj.__jsii_ref__,
181225
property=property,
182226
value=_make_reference_for_native(self, value),
183227
)
184228
)
229+
if isinstance(response, Callback):
230+
_callback_till_result(self, response, SetResponse)
185231

186232
@_dereferenced
187233
def sget(self, klass: JSClass, property: str) -> Any:
@@ -205,13 +251,17 @@ def invoke(
205251
if args is None:
206252
args = []
207253

208-
return self.provider.invoke(
254+
response = self.provider.invoke(
209255
InvokeRequest(
210256
objref=obj.__jsii_ref__,
211257
method=method,
212258
args=_make_reference_for_native(self, args),
213259
)
214-
).result
260+
)
261+
if isinstance(response, Callback):
262+
return _callback_till_result(self, response, InvokeResponse)
263+
else:
264+
return response.result
215265

216266
@_dereferenced
217267
def sinvoke(
@@ -229,6 +279,28 @@ def sinvoke(
229279
).result
230280

231281
@_dereferenced
282+
def complete(
283+
self, cbid: str, err: Optional[str], result: Any
284+
) -> Any:
285+
return self.provider.complete(
286+
CompleteRequest(
287+
cbid=cbid,
288+
err=err,
289+
result=result
290+
)
291+
)
292+
293+
def sync_complete(
294+
self, cbid: str, err: Optional[str], result: Any, response_type: Type[KernelResponse]
295+
) -> Any:
296+
return self.provider.sync_complete(
297+
CompleteRequest(
298+
cbid=cbid,
299+
err=err,
300+
result=result),
301+
response_type=response_type
302+
)
303+
232304
def ainvoke(
233305
self, obj: Referenceable, method: str, args: Optional[List[Any]] = None
234306
) -> Any:
@@ -242,6 +314,8 @@ def ainvoke(
242314
args=_make_reference_for_native(self, args),
243315
)
244316
)
317+
if isinstance(promise, Callback):
318+
promise = _callback_till_result(self, promise, BeginResponse)
245319

246320
callbacks = self.provider.callbacks(CallbacksRequest()).callbacks
247321
while callbacks:

packages/jsii-python-runtime/src/jsii/_kernel/providers/base.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import abc
22

3-
from typing import Optional
3+
from typing import Optional, Union, Type
44

55
from jsii._kernel.types import (
66
LoadRequest,
@@ -28,6 +28,9 @@
2828
CompleteResponse,
2929
StatsRequest,
3030
StatsResponse,
31+
Callback,
32+
CompleteRequest,
33+
KernelResponse
3134
)
3235

3336

@@ -63,13 +66,21 @@ def sset(self, request: StaticSetRequest) -> SetResponse:
6366
...
6467

6568
@abc.abstractmethod
66-
def invoke(self, request: InvokeRequest) -> InvokeResponse:
69+
def invoke(self, request: InvokeRequest) -> Union[InvokeResponse, Callback]:
6770
...
6871

6972
@abc.abstractmethod
7073
def sinvoke(self, request: StaticInvokeRequest) -> InvokeResponse:
7174
...
7275

76+
@abc.abstractmethod
77+
def complete(self, request: CompleteRequest) -> Union[InvokeResponse, GetResponse]:
78+
...
79+
80+
@abc.abstractmethod
81+
def sync_complete(self, request: CompleteRequest, response_type: Type[KernelResponse]) -> Union[InvokeResponse, GetResponse]:
82+
...
83+
7384
@abc.abstractmethod
7485
def delete(self, request: DeleteRequest) -> DeleteResponse:
7586
...

packages/jsii-python-runtime/src/jsii/_kernel/providers/process.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@
5252
CompleteResponse,
5353
StatsRequest,
5454
StatsResponse,
55+
Callback,
56+
CompleteRequest,
57+
CompleteResponse,
5558
)
5659
from jsii.errors import JSIIError, JavaScriptError
5760

@@ -79,8 +82,17 @@ class _ErrorRespose:
7982
error: str
8083
stack: str
8184

85+
@attr.s(auto_attribs=True, frozen=True, slots=True)
86+
class _CallbackResponse:
87+
88+
callback: Callback
8289

83-
_ProcessResponse = Union[_OkayResponse, _ErrorRespose]
90+
@attr.s(auto_attribs=True, frozen=True, slots=True)
91+
class _CompleteRequest:
92+
93+
complete: CompleteRequest
94+
95+
_ProcessResponse = Union[_OkayResponse, _ErrorRespose, _CallbackResponse]
8496
# Workaround for mypy#5354
8597
_ProcessResponse_R: Type[Any]
8698
if not TYPE_CHECKING:
@@ -296,6 +308,8 @@ def send(
296308

297309
if isinstance(resp, _OkayResponse):
298310
return self._serializer.structure(resp.ok, response_type)
311+
elif isinstance(resp, _CallbackResponse):
312+
return resp.callback
299313
else:
300314
raise JSIIError(resp.error) from JavaScriptError(resp.stack)
301315

@@ -326,7 +340,7 @@ def sget(self, request: StaticGetRequest) -> GetResponse:
326340
def sset(self, request: StaticSetRequest) -> SetResponse:
327341
return self._process.send(request, SetResponse)
328342

329-
def invoke(self, request: InvokeRequest) -> InvokeResponse:
343+
def invoke(self, request: InvokeRequest) -> Union[InvokeResponse, Callback]:
330344
return self._process.send(request, InvokeResponse)
331345

332346
def sinvoke(self, request: StaticInvokeRequest) -> InvokeResponse:
@@ -347,6 +361,10 @@ def callbacks(self, request: CallbacksRequest) -> CallbacksResponse:
347361
def complete(self, request: CompleteRequest) -> CompleteResponse:
348362
return self._process.send(request, CompleteResponse)
349363

364+
def sync_complete(self, request: CompleteRequest, response_type: Type[KernelResponse]) -> Union[InvokeResponse, GetResponse]:
365+
resp = self._process.send(_CompleteRequest(complete=request), response_type)
366+
return resp
367+
350368
def stats(self, request: Optional[StatsRequest] = None) -> StatsResponse:
351369
if request is None:
352370
request = StatsRequest()

packages/jsii-python-runtime/src/jsii/_kernel/types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ class StatsResponse:
236236
GetResponse,
237237
InvokeResponse,
238238
StatsResponse,
239+
Callback,
239240
]
240241

241242

0 commit comments

Comments
 (0)