https://pulumi.com logo
#python
Title
# python
g

great-sunset-355

07/29/2021, 7:28 AM
What's the best way to get a proper type hinting for Outputs? Is there any cast method or anything available? acm certificate has the output of type
CertificateDomainValidationOptionArgs
https://www.pulumi.com/docs/reference/pkg/aws/acm/certificate/#outputs I'd like to be able to use it as
cert_validation_option.domain_name
, etc...
e

enough-garden-22763

07/29/2021, 2:28 PM
I’m not sure I follow this. CertificateDomainValidationOptionArgs is not CertificateDomainValidationOption. Where are you trying to use the domain_name>
E.g.
Copy code
cert: acm.Certificate
  cert.domain_validation_options: Output[Sequence[CertificateDomainValidationOption]]
  cert.domain_validation_options.apply(lambda x: x[0]): Output[CertificateDomainValidationOption]

  domain_name = cert.domain_validation_options.apply(lambda x: x[0]).apply(lambda x: x.domain_name)
  domain_name: Output[str]
IF you have some place that accepts an
Input[str]
you can now pass
domain_name: Output[str]
into it as Input arguments accept Output.
You cannot unpack Output[str] to str.
g

great-sunset-355

07/29/2021, 3:01 PM
I think this is the problem
lambda x: x[0]).apply(lambda x: x.domain_name)
that my IDEs are not able to suggest the attributes of
x
which is ultimately what I'm trying to achieve. It's the lost information that bothers me
e

enough-garden-22763

07/29/2021, 3:05 PM
Oh! Thanks for clarifying that!
I think I’ve been filing this before: https://github.com/pulumi/pulumi-aws/issues/1516
Let me move this to pulumi/pulumi and escalate it, I’d very much like to get this fixed.
g

great-sunset-355

07/29/2021, 3:06 PM
Yeah there are a couple of them possibly caused by my discussions. I'm trying to find suitable workarounds in the meantime
e

enough-garden-22763

07/29/2021, 3:08 PM
It’s going to be tough to find a workaround to for IDE completion and type checking if properties are marked as typed Any. You can make the programs work but without the IDE/typechecker support.
There: https://github.com/pulumi/pulumi/issues/7679 lobby to squeeze this in soon.
g

great-sunset-355

07/29/2021, 3:09 PM
Since I laid my hands CDK API, I tend to compare it with other tools because I think they nailed it. Looks to me like AWSX is going in the same direction but I wasn't able to test it yet since it's not available for python afaik
I love how pydantic simplified instantiating dictionaries into objects together with some data validation. So I'm looking for a way how to get this experience back from
pulumi.Output
because they can also return a dict but it is not a dict but
Output[dict]
e

enough-garden-22763

07/29/2021, 3:15 PM
I’ll need to ask around about AWSX, not sure there.
g

great-sunset-355

07/29/2021, 3:15 PM
One possible workaround I found was using
Output.future()
and
await
inside an async function then scheduling the function with
loop.create_task
However due to my limited experience with python async I'm not able to tell if there are any potential side effects. It seems to be a similar case to
Output.apply()
- where documentation recommends to avoid creating resources inside
apply()
Copy code
import pulumi
from pydantic import BaseModel

class MyZone(BaseModel):
    zone_id: str
    force_destroy: bool





async def fun():
    ref_zone = pulumi.StackReference(stack_name)
    print('lol')
    result = await ref_zone.outputs.future()
    print(result)
    my_zone = MyZone.parse_obj(result['my_zone'])
    print(my_zone)
    ec2.SecurityGroup(
        f"mhh{my_zone.zone_id}"
    )

loop = asyncio.get_running_loop()
loop.create_task(fun())
e

enough-garden-22763

07/29/2021, 3:18 PM
Hm. Reducing Output to future and awaiting in future will definitely break some features Pulumi implements in the Output layer, like dependency tracking and unknown/secret propagation.
You can however get an
Output[MyZone]
in the code above I think?
Copy code
ref_zone.outputs.apply(lambda result: MyZone.parse_obj(result['my_zone'])): Output[MyZone]
You will not be able to coax Output[MyZone] into using it in the
name
parameter of SecurityGroup though, that’s true. I’ll need to ask what is the recommended practice around this.
From pure Python view, your workaround can be made to work, consider asyncio.run:
Copy code
async def main():
    print('Hello ...')
    await asyncio.sleep(1)
    print('... World!')

# Python 3.7+
asyncio.run(main())
With create_task the program does not wait (though Pulumi might be making it to wait in the current version). With asyncio.run you’re explicitly asking to run an async function and wait till it’s done.
Losing dependency tracking: so since your workaround is unpacking Output to future, Pulumi engine is no longer aware that SecurityGroup resource depends on the StackReference resource, but since the program is explicitly blocking and waiting, that may be okay as it will allocate in the right order anyway.
Consider though if it’s worth it. I’m digging up the docs on SecurityGroup, so it seems there’s two names in play:
Copy code
# ec2
 class SecurityGroup(pulumi.CustomResource):

    @overload
    def __init__(__self__,
                 resource_name: str,
                 ...
                 name: Optional[pulumi.Input[str]] = None,
                 ...
                 __props__=None):
        """
        # BUILT BY genResourceInitDocstring()
        :param str resource_name: The name of the resource.  ### this is the name as Pulumi sees it
        ...
        :param pulumi.Input[str] name: Name of the security group. If omitted, this provider will assign a random, unique name.  ### this is actual name in AWS
        """
        ...
g

great-sunset-355

07/29/2021, 3:36 PM
It's not specific to the SecurityGroup I picked it as an example resource that is quick to deploy. Also I think using
asyncio.run
gave me error about multiple loops but will have to revisit the test later on
🤔 1
thank you for now!
e

enough-garden-22763

07/29/2021, 3:37 PM
So you could do this:
Copy code
my_zone: Output[MyZone] = ref_zone.outputs.apply(lambda result: MyZone.parse_obj(result['my_zone']))
sg = SecurityGroup(
  "mhh", # pulumi name does not depend on my_zone
  resource_name=my_zone.apply(lambda z: f"mhh{z.zone_id}")  # but AWS name does
)
TLDR - if passing Output to Input is more idiomatic Pulumi than unpacking Output in async layer. Interesting, I see. Thanks for that, I believe we have something in the works for removing the multiple event loops issues. I’ll check that with your program to repro. Thanks!
g

great-sunset-355

07/29/2021, 4:38 PM
Agree with about that! still my ultimate goal is to get IDE to tell me that
z.
has the attribute of
z.zone_id
I'll try the similar example in C# to see if the IDE experience is somewhat different there then I'll think if there is something like that of python typhinting
because if you do
List[str]
you get hinted methods from that
list_items[0].join()
if we could get the same experience for Output in python that would be amazing