Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(python): Add support for synchronous callbacks #407

Merged
merged 17 commits into from
Mar 28, 2019
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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