pulumi/sdk/python/lib/test/langhost/first_class_provider_invoke/__main__.py
Pat Gavlin 63eb7abb59
Make pulumi.runtime.invoke synchronous. (#3019)
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.
2019-08-02 14:19:56 -07:00

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.