Skip to content

Commit

Permalink
feat(python): Add support for synchronous callbacks (#407)
Browse files Browse the repository at this point in the history
  • Loading branch information
garnaat authored Mar 28, 2019
1 parent 0f7559c commit 4cb91b3
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 57 deletions.
11 changes: 5 additions & 6 deletions packages/jsii-python-runtime/bin/generate-calc
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,10 @@ subprocess.run(
"install",
"--force-reinstall",
"--upgrade",
"--find-links",
os.path.abspath("."),
"--find-links",
os.path.abspath(".env/jsii-calc/python"),
"jsii-calc",
],
]
+
[x for x in os.listdir(".") if x.endswith(".whl")]
+
[os.path.join('.env/jsii-calc/python', x) for x in os.listdir(".env/jsii-calc/python") if x.endswith(".whl")],
check=True,
)
102 changes: 88 additions & 14 deletions packages/jsii-python-runtime/src/jsii/_kernel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,25 @@
import inspect
import itertools

from typing import Any, List, Optional, Type
from typing import Any, List, Optional, Type, Union

import functools

import attr

from jsii.errors import JSIIError
from jsii import _reference_map
from jsii._utils import Singleton
from jsii._kernel.providers import BaseProvider, ProcessProvider
from jsii._kernel.types import JSClass, Referenceable
from jsii._kernel.types import Callback
from jsii._kernel.types import (
EnumRef,
LoadRequest,
BeginRequest,
CallbacksRequest,
CreateRequest,
CreateResponse,
CompleteRequest,
DeleteRequest,
EndRequest,
Expand All @@ -30,6 +33,13 @@
StatsRequest,
ObjRef,
Override,
CompleteRequest,
CompleteResponse,
GetResponse,
SetResponse,
InvokeResponse,
KernelResponse,
BeginResponse
)


Expand All @@ -40,12 +50,6 @@ class Object:
__jsii_type__ = "Object"


def _handle_callback(kernel, callback):
obj = _reference_map.resolve_id(callback.invoke.objref.ref)
method = getattr(obj, callback.cookie)
return method(*callback.invoke.args)


def _get_overides(klass: JSClass, obj: Any) -> List[Override]:
overrides = []

Expand Down Expand Up @@ -120,6 +124,39 @@ def _make_reference_for_native(kernel, d):
return d


def _handle_callback(kernel, callback):
# need to handle get, set requests here as well as invoke requests
if callback.invoke:
obj = _reference_map.resolve_id(callback.invoke.objref.ref)
method = getattr(obj, callback.cookie)
return method(*callback.invoke.args)
elif callback.get:
obj = _reference_map.resolve_id(callback.get.objref.ref)
return getattr(obj, callback.cookie)
elif callback.set:
obj = _reference_map.resolve_id(callback.set.objref.ref)
return setattr(obj, callback.cookie, callback.set.value)
else:
raise JSIIError("Callback does not contain invoke|get|set")


def _callback_till_result(kernel, response: Callback, response_type: Type[KernelResponse]) -> Any:
while isinstance(response, Callback):
try:
result = _handle_callback(kernel, response)
except Exception as exc:
response = kernel.sync_complete(response.cbid, str(exc), None, response_type)
else:
response = kernel.sync_complete(response.cbid, None, result, response_type)

if isinstance(response, InvokeResponse):
return response.result
elif isinstance(response, GetResponse):
return response.value
else:
return response


@attr.s(auto_attribs=True, frozen=True, slots=True)
class Statistics:

Expand Down Expand Up @@ -155,33 +192,42 @@ def create(

overrides = _get_overides(klass, obj)

obj.__jsii_ref__ = self.provider.create(
response = self.provider.create(
CreateRequest(
fqn=klass.__jsii_type__,
args=_make_reference_for_native(self, args),
overrides=overrides,
)
)

if isinstance(response, Callback):
obj.__jsii_ref__ = _callback_till_result(self, response, CreateResponse)
else:
obj.__jsii_ref__ = response
return obj.__jsii_ref__

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

@_dereferenced
def get(self, obj: Referenceable, property: str) -> Any:
return self.provider.get(
response = self.provider.get(
GetRequest(objref=obj.__jsii_ref__, property=property)
).value
)
if isinstance(response, Callback):
return _callback_till_result(self, response, GetResponse)
else:
return response.value

def set(self, obj: Referenceable, property: str, value: Any) -> None:
self.provider.set(
response = self.provider.set(
SetRequest(
objref=obj.__jsii_ref__,
property=property,
value=_make_reference_for_native(self, value),
)
)
if isinstance(response, Callback):
_callback_till_result(self, response, SetResponse)

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

return self.provider.invoke(
response = self.provider.invoke(
InvokeRequest(
objref=obj.__jsii_ref__,
method=method,
args=_make_reference_for_native(self, args),
)
).result
)
if isinstance(response, Callback):
return _callback_till_result(self, response, InvokeResponse)
else:
return response.result

@_dereferenced
def sinvoke(
Expand All @@ -229,6 +279,28 @@ def sinvoke(
).result

@_dereferenced
def complete(
self, cbid: str, err: Optional[str], result: Any
) -> Any:
return self.provider.complete(
CompleteRequest(
cbid=cbid,
err=err,
result=result
)
)

def sync_complete(
self, cbid: str, err: Optional[str], result: Any, response_type: Type[KernelResponse]
) -> Any:
return self.provider.sync_complete(
CompleteRequest(
cbid=cbid,
err=err,
result=result),
response_type=response_type
)

def ainvoke(
self, obj: Referenceable, method: str, args: Optional[List[Any]] = None
) -> Any:
Expand All @@ -242,6 +314,8 @@ def ainvoke(
args=_make_reference_for_native(self, args),
)
)
if isinstance(promise, Callback):
promise = _callback_till_result(self, promise, BeginResponse)

callbacks = self.provider.callbacks(CallbacksRequest()).callbacks
while callbacks:
Expand Down
15 changes: 13 additions & 2 deletions packages/jsii-python-runtime/src/jsii/_kernel/providers/base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import abc

from typing import Optional
from typing import Optional, Union, Type

from jsii._kernel.types import (
LoadRequest,
Expand Down Expand Up @@ -28,6 +28,9 @@
CompleteResponse,
StatsRequest,
StatsResponse,
Callback,
CompleteRequest,
KernelResponse
)


Expand Down Expand Up @@ -63,13 +66,21 @@ def sset(self, request: StaticSetRequest) -> SetResponse:
...

@abc.abstractmethod
def invoke(self, request: InvokeRequest) -> InvokeResponse:
def invoke(self, request: InvokeRequest) -> Union[InvokeResponse, Callback]:
...

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

@abc.abstractmethod
def complete(self, request: CompleteRequest) -> Union[InvokeResponse, GetResponse]:
...

@abc.abstractmethod
def sync_complete(self, request: CompleteRequest, response_type: Type[KernelResponse]) -> Union[InvokeResponse, GetResponse]:
...

@abc.abstractmethod
def delete(self, request: DeleteRequest) -> DeleteResponse:
...
Expand Down
22 changes: 20 additions & 2 deletions packages/jsii-python-runtime/src/jsii/_kernel/providers/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@
CompleteResponse,
StatsRequest,
StatsResponse,
Callback,
CompleteRequest,
CompleteResponse,
)
from jsii.errors import JSIIError, JavaScriptError

Expand Down Expand Up @@ -79,8 +82,17 @@ class _ErrorRespose:
error: str
stack: str

@attr.s(auto_attribs=True, frozen=True, slots=True)
class _CallbackResponse:

callback: Callback

_ProcessResponse = Union[_OkayResponse, _ErrorRespose]
@attr.s(auto_attribs=True, frozen=True, slots=True)
class _CompleteRequest:

complete: CompleteRequest

_ProcessResponse = Union[_OkayResponse, _ErrorRespose, _CallbackResponse]
# Workaround for mypy#5354
_ProcessResponse_R: Type[Any]
if not TYPE_CHECKING:
Expand Down Expand Up @@ -296,6 +308,8 @@ def send(

if isinstance(resp, _OkayResponse):
return self._serializer.structure(resp.ok, response_type)
elif isinstance(resp, _CallbackResponse):
return resp.callback
else:
raise JSIIError(resp.error) from JavaScriptError(resp.stack)

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

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

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

def sync_complete(self, request: CompleteRequest, response_type: Type[KernelResponse]) -> Union[InvokeResponse, GetResponse]:
resp = self._process.send(_CompleteRequest(complete=request), response_type)
return resp

def stats(self, request: Optional[StatsRequest] = None) -> StatsResponse:
if request is None:
request = StatsRequest()
Expand Down
1 change: 1 addition & 0 deletions packages/jsii-python-runtime/src/jsii/_kernel/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ class StatsResponse:
GetResponse,
InvokeResponse,
StatsResponse,
Callback,
]


Expand Down
Loading

0 comments on commit 4cb91b3

Please sign in to comment.