These changes make the `pulumi.runtime.invoke` function invokable in a synchronous manner. Because this function still needs to perform asynchronous work under the covers--namely awaiting a provider URN and ID if a provider instance is present in the `InvokeOptions`--this requires some creativity. This creativity comes in the form of a helper function, `_sync_await`, that performs a logical yield from the currently running event, manually runs the event loop until the given future completes, performs a logical resume back to the currently executing event, and returns the result of the future. The code in `_sync_await` is a bit scary, as it relies upon knowledge of (and functions in) the internals of the `asyncio` package. The necessary work performed in this function was derived from the implementations of `task_step` (which pointed out the need to call `_{enter,leave}_task`) and `BaseEventLoop.run_forever` (which illustrated how the event loop is pumped). In addition to potential breaking changes to these internals, the code may not work if a user has provided an alternative implementation for `EventLoop`. That said, the code is a close enough copy of `BaseEventLoop.run_forever` that it should be a reasonable solution.
60 lines
2.3 KiB
Python
60 lines
2.3 KiB
Python
# Copyright 2016-2018, Pulumi Corporation.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
from pulumi import ProviderResource, ComponentResource, CustomResource, Output, InvokeOptions, ResourceOptions, log
|
|
from pulumi.runtime import invoke
|
|
|
|
def assert_eq(l, r):
|
|
assert l == r
|
|
|
|
|
|
class MyResource(CustomResource):
|
|
value: Output[int]
|
|
|
|
def __init__(self, name, value, opts=None):
|
|
CustomResource.__init__(self, "test:index:MyResource", name, props={
|
|
"value": value,
|
|
}, opts=opts)
|
|
|
|
class MyProvider(ProviderResource):
|
|
def __init__(self, name, opts=None):
|
|
ProviderResource.__init__(self, "test", name, {}, opts)
|
|
|
|
|
|
class MyComponent(ComponentResource):
|
|
def __init__(self, name, opts=None):
|
|
ComponentResource.__init__(self, "test:index:MyComponent", name, {}, opts)
|
|
|
|
|
|
# Explicitly use a provider for an Invoke.
|
|
prov = MyProvider("testprov")
|
|
def do_provider_invoke():
|
|
value = invoke("test:index:MyFunction", props={"value": 9000}, opts=InvokeOptions(provider=prov)).value
|
|
return value["value"]
|
|
|
|
res = MyResource("resourceA", do_provider_invoke())
|
|
res.value.apply(lambda v: assert_eq(v, 9001))
|
|
# The Invoke RPC call should contain a reference to prov.
|
|
|
|
|
|
# Implicitly use a provider for an Invoke by passing a parent to InvokeOptions. The parent's provider is used when
|
|
# performing the invoke.
|
|
componentRes = MyComponent("resourceB", opts=ResourceOptions(providers={"test": prov}))
|
|
|
|
def do_provider_invoke_with_parent(parent):
|
|
value = invoke("test:index:MyFunctionWithParent", props={"value": 41}, opts=InvokeOptions(parent=parent)).value
|
|
return value["value"]
|
|
|
|
res2 = MyResource("resourceC", do_provider_invoke_with_parent(componentRes))
|
|
res2.value.apply(lambda v: assert_eq(v, 42))
|
|
# The Invoke RPC call should again contain a reference to prov.
|