Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
39 changes: 30 additions & 9 deletions packages/jsii-pacmak/lib/targets/python.ts
Original file line number Diff line number Diff line change
Expand Up @@ -857,6 +857,11 @@ class Method extends BaseMethod {
protected readonly jsiiMethod: string = "invoke";
}

class AsyncMethod extends BaseMethod {
protected readonly implicitParameter: string = "self";
protected readonly jsiiMethod: string = "ainvoke";
}

class StaticProperty extends BaseProperty {
protected readonly decorator: string = "classproperty";
protected readonly implicitParameter: string = "cls";
Expand Down Expand Up @@ -1473,15 +1478,27 @@ class PythonGenerator extends Generator {
protected onMethod(cls: spec.ClassType, method: spec.Method) {
const { parameters = [] } = method;

this.getPythonType(cls.fqn).addMember(
new Method(
toPythonMethodName(method.name!, method.protected),
method.name,
parameters,
method.returns,
{ abstract: method.abstract, liftedProp: this.getliftedProp(method) },
)
);
if (this.isAsyncMethod(method)) {
this.getPythonType(cls.fqn).addMember(
new AsyncMethod(
toPythonMethodName(method.name!, method.protected),
method.name,
parameters,
method.returns,
{ abstract: method.abstract, liftedProp: this.getliftedProp(method) },
)
);
} else {
this.getPythonType(cls.fqn).addMember(
new Method(
toPythonMethodName(method.name!, method.protected),
method.name,
parameters,
method.returns,
{ abstract: method.abstract, liftedProp: this.getliftedProp(method) },
)
);
}
}

protected onProperty(cls: spec.ClassType, prop: spec.Property) {
Expand Down Expand Up @@ -1651,4 +1668,8 @@ class PythonGenerator extends Generator {

return abstractBases;
}

private isAsyncMethod(method: spec.Method): boolean {
return method.returns !== undefined && method.returns.promise !== undefined && method.returns.promise;
}
}
2 changes: 2 additions & 0 deletions packages/jsii-python-runtime/src/jsii/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
sget = kernel.sget
sset = kernel.sset
invoke = kernel.invoke
ainvoke = kernel.ainvoke
sinvoke = kernel.sinvoke
stats = kernel.stats

Expand All @@ -56,6 +57,7 @@
"sget",
"sset",
"invoke",
"ainvoke",
"sinvoke",
"stats",
]
46 changes: 46 additions & 0 deletions packages/jsii-python-runtime/src/jsii/_kernel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@
from jsii._kernel.types import (
EnumRef,
LoadRequest,
BeginRequest,
CallbacksRequest,
CreateRequest,
CompleteRequest,
DeleteRequest,
EndRequest,
GetRequest,
InvokeRequest,
SetRequest,
Expand All @@ -36,6 +40,12 @@ class Object:
__jsii_type__ = "Object"


def _handle_callback(kernel, callback):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note, this doesn't actually handle everything, this is one of the things that we need to coordinate with the sync callbacks with over who wants to "own" doing the complete implementation of this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you just want to merge this version and then I can do a PR with the updated version?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That works for me.

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 @@ -218,6 +228,42 @@ def sinvoke(
)
).result

@_dereferenced
def ainvoke(
self, obj: Referenceable, method: str, args: Optional[List[Any]] = None
) -> Any:
if args is None:
args = []

promise = self.provider.begin(
BeginRequest(
objref=obj.__jsii_ref__,
method=method,
args=_make_reference_for_native(self, args),
)
)

callbacks = self.provider.callbacks(CallbacksRequest()).callbacks
while callbacks:
for callback in callbacks:
try:
result = _handle_callback(self, callback)
except Exception as exc:
# TODO: Maybe we want to print the whole traceback here?
complete = self.provider.complete(
CompleteRequest(cbid=callback.cbid, err=str(exc))
)
else:
complete = self.provider.complete(
CompleteRequest(cbid=callback.cbid, result=result)
)

assert complete.cbid == callback.cbid

callbacks = self.provider.callbacks(CallbacksRequest()).callbacks

return self.provider.end(EndRequest(promiseid=promise.promiseid)).result

def stats(self):
resp = self.provider.stats(StatsRequest())

Expand Down
24 changes: 24 additions & 0 deletions packages/jsii-python-runtime/src/jsii/_kernel/providers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@
StaticGetRequest,
StaticInvokeRequest,
StaticSetRequest,
BeginRequest,
BeginResponse,
EndRequest,
EndResponse,
CallbacksRequest,
CallbacksResponse,
CompleteRequest,
CompleteResponse,
StatsRequest,
StatsResponse,
)
Expand Down Expand Up @@ -66,6 +74,22 @@ def sinvoke(self, request: StaticInvokeRequest) -> InvokeResponse:
def delete(self, request: DeleteRequest) -> DeleteResponse:
...

@abc.abstractmethod
def begin(self, request: BeginRequest) -> BeginResponse:
...

@abc.abstractmethod
def end(self, request: EndRequest) -> EndResponse:
...

@abc.abstractmethod
def callbacks(self, request: CallbacksRequest) -> CallbacksResponse:
...

@abc.abstractmethod
def complete(self, request: CompleteRequest) -> CompleteResponse:
...

@abc.abstractmethod
def stats(self, request: Optional[StatsRequest] = None) -> StatsResponse:
...
35 changes: 35 additions & 0 deletions packages/jsii-python-runtime/src/jsii/_kernel/providers/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@
StaticGetRequest,
StaticInvokeRequest,
StaticSetRequest,
BeginRequest,
BeginResponse,
EndRequest,
EndResponse,
CallbacksRequest,
CallbacksResponse,
CompleteRequest,
CompleteResponse,
StatsRequest,
StatsResponse,
)
Expand Down Expand Up @@ -183,6 +191,21 @@ def __init__(self):
StaticInvokeRequest,
_with_api_key("sinvoke", self._serializer.unstructure_attrs_asdict),
)
self._serializer.register_unstructure_hook(
BeginRequest,
_with_api_key("begin", self._serializer.unstructure_attrs_asdict),
)
self._serializer.register_unstructure_hook(
EndRequest, _with_api_key("end", self._serializer.unstructure_attrs_asdict)
)
self._serializer.register_unstructure_hook(
CallbacksRequest,
_with_api_key("callbacks", self._serializer.unstructure_attrs_asdict),
)
self._serializer.register_unstructure_hook(
CompleteRequest,
_with_api_key("complete", self._serializer.unstructure_attrs_asdict),
)
self._serializer.register_unstructure_hook(
StatsRequest,
_with_api_key("stats", self._serializer.unstructure_attrs_asdict),
Expand Down Expand Up @@ -337,6 +360,18 @@ def sinvoke(self, request: StaticInvokeRequest) -> InvokeResponse:
def delete(self, request: DeleteRequest) -> DeleteResponse:
return self._process.send(request, DeleteResponse)

def begin(self, request: BeginRequest) -> BeginResponse:
return self._process.send(request, BeginResponse)

def end(self, request: EndRequest) -> EndResponse:
return self._process.send(request, EndResponse)

def callbacks(self, request: CallbacksRequest) -> CallbacksResponse:
return self._process.send(request, CallbacksResponse)

def complete(self, request: CompleteRequest) -> CompleteResponse:
return self._process.send(request, CompleteResponse)

def stats(self, request: Optional[StatsRequest] = None) -> StatsResponse:
if request is None:
request = StatsRequest()
Expand Down
8 changes: 4 additions & 4 deletions packages/jsii-python-runtime/src/jsii/_kernel/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,10 @@ class EndResponse:
class Callback:

cbid: str
cookie: Optional[str]
invoke: Optional[InvokeRequest]
get: Optional[GetRequest]
set: Optional[SetRequest]
cookie: Optional[str] = None
invoke: Optional[InvokeRequest] = None
get: Optional[GetRequest] = None
set: Optional[SetRequest] = None


@attr.s(auto_attribs=True, frozen=True, slots=True)
Expand Down
4 changes: 4 additions & 0 deletions packages/jsii-python-runtime/src/jsii/_reference_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,13 @@ def resolve(self, kernel, ref):

return inst

def resolve_id(self, id):
return self._refs[id]


_refs = _ReferenceMap(_types)


register_reference = _refs.register
resolve_reference = _refs.resolve
resolve_id = _refs.resolve_id
14 changes: 3 additions & 11 deletions packages/jsii-python-runtime/tests/test_compliance.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@
# Tests as closely as possible to make keeping them in sync easier.

# These map distinct reasons for failures, so we an easily find them.
xfail_async = pytest.mark.xfail(reason="Implement async methods", strict=True)
xfail_callbacks = pytest.mark.xfail(reason="Implement callback support", strict=True)
xfail_callbacks = pytest.mark.skip(reason="Implement callback support")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to switch this to a skip, because running these tests and failing on them left the process in a bad state.



class DerivedFromAllTypes(AllTypes):
Expand Down Expand Up @@ -464,39 +463,33 @@ def test_creationOfNativeObjectsFromJavaScriptObjects():
assert unmarshalled_native_obj.__class__ == MulTen


@xfail_async
def test_asyncOverrides_callAsyncMethod():
obj = AsyncVirtualMethods()
assert obj.call_me() == 128
assert obj.override_me(44) == 528


@xfail_async
def test_asyncOverrides_overrideAsyncMethod():
obj = OverrideAsyncMethods()
obj.call_me() == 4452


@xfail_async
def test_asyncOverrides_overrideAsyncMethodByParentClass():
obj = OverrideAsyncMethodsByBaseClass()
obj.call_me() == 4452


@xfail_async
def test_asyncOverrides_overrideCallsSuper():
obj = OverrideCallsSuper()
assert obj.override_me(12) == 1441
assert obj.call_me() == 1209


@xfail_async
def test_asyncOverrides_twoOverrides():
obj = TwoOverrides()
assert obj.call_me() == 684


@xfail_async
def test_asyncOverrides_overrideThrows():
class ThrowingAsyncVirtualMethods(AsyncVirtualMethods):
def override_me(self, mult):
Expand Down Expand Up @@ -783,15 +776,14 @@ def test_reservedKeywordsAreSlugifiedInMethodNames():
obj.return_()


@xfail_async
def test_nodeStandardLibrary():
obj = NodeStandardLibrary()

assert obj.fs_read_file() == "Hello, resource!"
assert obj.fs_read_file_sync() == "Hello, resource! SYNC!"
assert len(obj.get_os_platform()) > 0
assert len(obj.os_platform) > 0
assert (
obj.crypto_sha_256()
obj.crypto_sha256()
== "6a2da20943931e9834fc12cfe5bb47bbd9ae43489a30726962b576f4e3993e50"
)

Expand Down