From 7d31e772ae53baf68cfab8734dd8a422c23a9aa4 Mon Sep 17 00:00:00 2001 From: Anton Tayanovskyy Date: Wed, 5 Oct 2022 15:19:29 -0400 Subject: [PATCH 1/5] Repro tf-bridge 611 --- pkg/codegen/testing/test/sdk_driver.go | 5 ++ .../regress-py-tfbridge-611/schema.json | 68 +++++++++++++++++++ pkg/codegen/utilities.go | 11 +++ 3 files changed, 84 insertions(+) create mode 100644 pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/schema.json diff --git a/pkg/codegen/testing/test/sdk_driver.go b/pkg/codegen/testing/test/sdk_driver.go index 8ad47e771164..0f1ca56f23ba 100644 --- a/pkg/codegen/testing/test/sdk_driver.go +++ b/pkg/codegen/testing/test/sdk_driver.go @@ -304,6 +304,11 @@ var PulumiPulumiSDKTests = []*SDKTest{ Description: "Generate a resource with secret properties", SkipCompileCheck: codegen.NewStringSet(dotnet), }, + { + Directory: "regress-py-tfbridge-611", + Description: "Regresses pulumi/pulumi-terraform-bridge#611", + Skip: allLanguages.Except("python/any").Union(codegen.NewStringSet("python/test", "python/py_compile")), + }, } var genSDKOnly bool diff --git a/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/schema.json b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/schema.json new file mode 100644 index 000000000000..cc70d823e1d4 --- /dev/null +++ b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/schema.json @@ -0,0 +1,68 @@ +{ + "name": "aws", + "meta": { + "moduleFormat": "(.*)(?:/[^/]*)" + }, + "language": { + "python": { + "compatibility": "tfbridge20", + "requires": { + "pulumi": ">=3.0.0,<4.0.0" + } + } + }, + "types": { + "aws:x/getPolicyDocumentStatement:getPolicyDocumentStatement": { + "properties": { + "actions": { + "type": "array", + "items": { + "type": "string" + }, + "language": { + "python": { + "mapCase": false + } + } + } + }, + "type": "object" + } + }, + "functions": { + "aws:x/iam/getPolicyDocument:getPolicyDocument": { + "inputs": { + "properties": { + "statements": { + "type": "array", + "items": { + "$ref": "#/types/aws:x/getPolicyDocumentStatement:getPolicyDocumentStatement" + } + } + }, + "type": "object" + }, + "outputs": { + "properties": { + "id": { + "type": "string" + }, + "json": { + "type": "string" + }, + "statements": { + "type": "array", + "items": { + "$ref": "#/types/aws:x/getPolicyDocumentStatement:getPolicyDocumentStatement" + } + } + }, + "type": "object", + "required": [ + "json", + "id" + ] + } + } + } +} diff --git a/pkg/codegen/utilities.go b/pkg/codegen/utilities.go index a372198824e0..ee4edf6c5060 100644 --- a/pkg/codegen/utilities.go +++ b/pkg/codegen/utilities.go @@ -86,6 +86,17 @@ func (ss StringSet) Subtract(other StringSet) StringSet { return result } +func (ss StringSet) Union(other StringSet) StringSet { + result := NewStringSet() + for v := range ss { + result.Add(v) + } + for v := range other { + result.Add(v) + } + return result +} + type Set map[interface{}]struct{} func (s Set) Add(v interface{}) { From 78f4eeebf952ac6b3943700dc7fc809479c564cc Mon Sep 17 00:00:00 2001 From: Anton Tayanovskyy Date: Wed, 5 Oct 2022 15:20:30 -0400 Subject: [PATCH 2/5] Accept codegen - demo invalid code generated --- .../python/codegen-manifest.json | 16 ++ .../python/pulumi_aws/README.md | 0 .../python/pulumi_aws/__init__.py | 31 +++ .../python/pulumi_aws/_utilities.py | 250 ++++++++++++++++++ .../python/pulumi_aws/provider.py | 71 +++++ .../python/pulumi_aws/pulumi-plugin.json | 4 + .../python/pulumi_aws/py.typed | 0 .../python/pulumi_aws/x/__init__.py | 16 ++ .../python/pulumi_aws/x/_inputs.py | 32 +++ .../python/pulumi_aws/x/iam/__init__.py | 8 + .../pulumi_aws/x/iam/get_policy_document.py | 82 ++++++ .../python/pulumi_aws/x/outputs.py | 28 ++ .../regress-py-tfbridge-611/python/setup.py | 59 +++++ 13 files changed, 597 insertions(+) create mode 100644 pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/codegen-manifest.json create mode 100644 pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/README.md create mode 100644 pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/__init__.py create mode 100644 pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/_utilities.py create mode 100644 pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/provider.py create mode 100644 pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/pulumi-plugin.json create mode 100644 pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/py.typed create mode 100644 pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/x/__init__.py create mode 100644 pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/x/_inputs.py create mode 100644 pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/x/iam/__init__.py create mode 100644 pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/x/iam/get_policy_document.py create mode 100644 pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/x/outputs.py create mode 100644 pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/setup.py diff --git a/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/codegen-manifest.json b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/codegen-manifest.json new file mode 100644 index 000000000000..6a4903477470 --- /dev/null +++ b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/codegen-manifest.json @@ -0,0 +1,16 @@ +{ + "emittedFiles": [ + "pulumi_aws/README.md", + "pulumi_aws/__init__.py", + "pulumi_aws/_utilities.py", + "pulumi_aws/provider.py", + "pulumi_aws/pulumi-plugin.json", + "pulumi_aws/py.typed", + "pulumi_aws/x/__init__.py", + "pulumi_aws/x/_inputs.py", + "pulumi_aws/x/iam/__init__.py", + "pulumi_aws/x/iam/get_policy_document.py", + "pulumi_aws/x/outputs.py", + "setup.py" + ] +} diff --git a/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/README.md b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/README.md new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/__init__.py b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/__init__.py new file mode 100644 index 000000000000..a3cc381f557d --- /dev/null +++ b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/__init__.py @@ -0,0 +1,31 @@ +# coding=utf-8 +# *** WARNING: this file was generated by test. *** +# *** Do not edit by hand unless you're certain you know what you are doing! *** + +from . import _utilities +import typing +# Export this package's modules as members: +from .provider import * + +# Make subpackages available: +if typing.TYPE_CHECKING: + import pulumi_aws.x as __x + x = __x +else: + x = _utilities.lazy_import('pulumi_aws.x') + +_utilities.register( + resource_modules=""" +[] +""", + resource_packages=""" +[ + { + "pkg": "aws", + "token": "pulumi:providers:aws", + "fqn": "pulumi_aws", + "class": "Provider" + } +] +""" +) diff --git a/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/_utilities.py b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/_utilities.py new file mode 100644 index 000000000000..be001eda26c9 --- /dev/null +++ b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/_utilities.py @@ -0,0 +1,250 @@ +# coding=utf-8 +# *** WARNING: this file was generated by test. *** +# *** Do not edit by hand unless you're certain you know what you are doing! *** + + +import importlib.util +import inspect +import json +import os +import pkg_resources +import sys +import typing + +import pulumi +import pulumi.runtime + +from semver import VersionInfo as SemverVersion +from parver import Version as PEP440Version + + +def get_env(*args): + for v in args: + value = os.getenv(v) + if value is not None: + return value + return None + + +def get_env_bool(*args): + str = get_env(*args) + if str is not None: + # NOTE: these values are taken from https://golang.org/src/strconv/atob.go?s=351:391#L1, which is what + # Terraform uses internally when parsing boolean values. + if str in ["1", "t", "T", "true", "TRUE", "True"]: + return True + if str in ["0", "f", "F", "false", "FALSE", "False"]: + return False + return None + + +def get_env_int(*args): + str = get_env(*args) + if str is not None: + try: + return int(str) + except: + return None + return None + + +def get_env_float(*args): + str = get_env(*args) + if str is not None: + try: + return float(str) + except: + return None + return None + + +def _get_semver_version(): + # __name__ is set to the fully-qualified name of the current module, In our case, it will be + # ._utilities. is the module we want to query the version for. + root_package, *rest = __name__.split('.') + + # pkg_resources uses setuptools to inspect the set of installed packages. We use it here to ask + # for the currently installed version of the root package (i.e. us) and get its version. + + # Unfortunately, PEP440 and semver differ slightly in incompatible ways. The Pulumi engine expects + # to receive a valid semver string when receiving requests from the language host, so it's our + # responsibility as the library to convert our own PEP440 version into a valid semver string. + + pep440_version_string = pkg_resources.require(root_package)[0].version + pep440_version = PEP440Version.parse(pep440_version_string) + (major, minor, patch) = pep440_version.release + prerelease = None + if pep440_version.pre_tag == 'a': + prerelease = f"alpha.{pep440_version.pre}" + elif pep440_version.pre_tag == 'b': + prerelease = f"beta.{pep440_version.pre}" + elif pep440_version.pre_tag == 'rc': + prerelease = f"rc.{pep440_version.pre}" + elif pep440_version.dev is not None: + prerelease = f"dev.{pep440_version.dev}" + + # The only significant difference between PEP440 and semver as it pertains to us is that PEP440 has explicit support + # for dev builds, while semver encodes them as "prerelease" versions. In order to bridge between the two, we convert + # our dev build version into a prerelease tag. This matches what all of our other packages do when constructing + # their own semver string. + return SemverVersion(major=major, minor=minor, patch=patch, prerelease=prerelease) + + +# Determine the version once and cache the value, which measurably improves program performance. +_version = _get_semver_version() +_version_str = str(_version) + + +def get_version(): + return _version_str + +def get_resource_opts_defaults() -> pulumi.ResourceOptions: + return pulumi.ResourceOptions( + version=get_version(), + plugin_download_url=get_plugin_download_url(), + ) + +def get_invoke_opts_defaults() -> pulumi.InvokeOptions: + return pulumi.InvokeOptions( + version=get_version(), + plugin_download_url=get_plugin_download_url(), + ) + +def get_resource_args_opts(resource_args_type, resource_options_type, *args, **kwargs): + """ + Return the resource args and options given the *args and **kwargs of a resource's + __init__ method. + """ + + resource_args, opts = None, None + + # If the first item is the resource args type, save it and remove it from the args list. + if args and isinstance(args[0], resource_args_type): + resource_args, args = args[0], args[1:] + + # Now look at the first item in the args list again. + # If the first item is the resource options class, save it. + if args and isinstance(args[0], resource_options_type): + opts = args[0] + + # If resource_args is None, see if "args" is in kwargs, and, if so, if it's typed as the + # the resource args type. + if resource_args is None: + a = kwargs.get("args") + if isinstance(a, resource_args_type): + resource_args = a + + # If opts is None, look it up in kwargs. + if opts is None: + opts = kwargs.get("opts") + + return resource_args, opts + + +# Temporary: just use pulumi._utils.lazy_import once everyone upgrades. +def lazy_import(fullname): + + import pulumi._utils as u + f = getattr(u, 'lazy_import', None) + if f is None: + f = _lazy_import_temp + + return f(fullname) + + +# Copied from pulumi._utils.lazy_import, see comments there. +def _lazy_import_temp(fullname): + m = sys.modules.get(fullname, None) + if m is not None: + return m + + spec = importlib.util.find_spec(fullname) + + m = sys.modules.get(fullname, None) + if m is not None: + return m + + loader = importlib.util.LazyLoader(spec.loader) + spec.loader = loader + module = importlib.util.module_from_spec(spec) + + m = sys.modules.get(fullname, None) + if m is not None: + return m + + sys.modules[fullname] = module + loader.exec_module(module) + return module + + +class Package(pulumi.runtime.ResourcePackage): + def __init__(self, pkg_info): + super().__init__() + self.pkg_info = pkg_info + + def version(self): + return _version + + def construct_provider(self, name: str, typ: str, urn: str) -> pulumi.ProviderResource: + if typ != self.pkg_info['token']: + raise Exception(f"unknown provider type {typ}") + Provider = getattr(lazy_import(self.pkg_info['fqn']), self.pkg_info['class']) + return Provider(name, pulumi.ResourceOptions(urn=urn)) + + +class Module(pulumi.runtime.ResourceModule): + def __init__(self, mod_info): + super().__init__() + self.mod_info = mod_info + + def version(self): + return _version + + def construct(self, name: str, typ: str, urn: str) -> pulumi.Resource: + class_name = self.mod_info['classes'].get(typ, None) + + if class_name is None: + raise Exception(f"unknown resource type {typ}") + + TheClass = getattr(lazy_import(self.mod_info['fqn']), class_name) + return TheClass(name, pulumi.ResourceOptions(urn=urn)) + + +def register(resource_modules, resource_packages): + resource_modules = json.loads(resource_modules) + resource_packages = json.loads(resource_packages) + + for pkg_info in resource_packages: + pulumi.runtime.register_resource_package(pkg_info['pkg'], Package(pkg_info)) + + for mod_info in resource_modules: + pulumi.runtime.register_resource_module( + mod_info['pkg'], + mod_info['mod'], + Module(mod_info)) + + +_F = typing.TypeVar('_F', bound=typing.Callable[..., typing.Any]) + + +def lift_output_func(func: typing.Any) -> typing.Callable[[_F], _F]: + """Decorator internally used on {fn}_output lifted function versions + to implement them automatically from the un-lifted function.""" + + func_sig = inspect.signature(func) + + def lifted_func(*args, opts=None, **kwargs): + bound_args = func_sig.bind(*args, **kwargs) + # Convert tuple to list, see pulumi/pulumi#8172 + args_list = list(bound_args.args) + return pulumi.Output.from_input({ + 'args': args_list, + 'kwargs': bound_args.kwargs + }).apply(lambda resolved_args: func(*resolved_args['args'], + opts=opts, + **resolved_args['kwargs'])) + + return (lambda _: lifted_func) + +def get_plugin_download_url(): + return None diff --git a/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/provider.py b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/provider.py new file mode 100644 index 000000000000..4d9b362baa1a --- /dev/null +++ b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/provider.py @@ -0,0 +1,71 @@ +# coding=utf-8 +# *** WARNING: this file was generated by test. *** +# *** Do not edit by hand unless you're certain you know what you are doing! *** + +import copy +import warnings +import pulumi +import pulumi.runtime +from typing import Any, Mapping, Optional, Sequence, Union, overload +from . import _utilities + +__all__ = ['ProviderArgs', 'Provider'] + +@pulumi.input_type +class ProviderArgs: + def __init__(__self__): + """ + The set of arguments for constructing a Provider resource. + """ + pass + + +class Provider(pulumi.ProviderResource): + @overload + def __init__(__self__, + resource_name: str, + opts: Optional[pulumi.ResourceOptions] = None, + __props__=None): + """ + Create a Aws resource with the given unique name, props, and options. + :param str resource_name: The name of the resource. + :param pulumi.ResourceOptions opts: Options for the resource. + """ + ... + @overload + def __init__(__self__, + resource_name: str, + args: Optional[ProviderArgs] = None, + opts: Optional[pulumi.ResourceOptions] = None): + """ + Create a Aws resource with the given unique name, props, and options. + :param str resource_name: The name of the resource. + :param ProviderArgs args: The arguments to use to populate this resource's properties. + :param pulumi.ResourceOptions opts: Options for the resource. + """ + ... + def __init__(__self__, resource_name: str, *args, **kwargs): + resource_args, opts = _utilities.get_resource_args_opts(ProviderArgs, pulumi.ResourceOptions, *args, **kwargs) + if resource_args is not None: + __self__._internal_init(resource_name, opts, **resource_args.__dict__) + else: + __self__._internal_init(resource_name, *args, **kwargs) + + def _internal_init(__self__, + resource_name: str, + opts: Optional[pulumi.ResourceOptions] = None, + __props__=None): + opts = pulumi.ResourceOptions.merge(_utilities.get_resource_opts_defaults(), opts) + if not isinstance(opts, pulumi.ResourceOptions): + raise TypeError('Expected resource options to be a ResourceOptions instance') + if opts.id is None: + if __props__ is not None: + raise TypeError('__props__ is only valid when passed in combination with a valid opts.id to get an existing resource') + __props__ = ProviderArgs.__new__(ProviderArgs) + + super(Provider, __self__).__init__( + 'aws', + resource_name, + __props__, + opts) + diff --git a/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/pulumi-plugin.json b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/pulumi-plugin.json new file mode 100644 index 000000000000..d54478ce8ff6 --- /dev/null +++ b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/pulumi-plugin.json @@ -0,0 +1,4 @@ +{ + "resource": true, + "name": "aws" +} diff --git a/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/py.typed b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/py.typed new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/x/__init__.py b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/x/__init__.py new file mode 100644 index 000000000000..68ade3125c72 --- /dev/null +++ b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/x/__init__.py @@ -0,0 +1,16 @@ +# coding=utf-8 +# *** WARNING: this file was generated by test. *** +# *** Do not edit by hand unless you're certain you know what you are doing! *** + +from .. import _utilities +import typing +from ._inputs import * +from . import outputs + +# Make subpackages available: +if typing.TYPE_CHECKING: + import pulumi_aws.x.iam as __iam + iam = __iam +else: + iam = _utilities.lazy_import('pulumi_aws.x.iam') + diff --git a/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/x/_inputs.py b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/x/_inputs.py new file mode 100644 index 000000000000..c1c13e711478 --- /dev/null +++ b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/x/_inputs.py @@ -0,0 +1,32 @@ +# coding=utf-8 +# *** WARNING: this file was generated by test. *** +# *** Do not edit by hand unless you're certain you know what you are doing! *** + +import copy +import warnings +import pulumi +import pulumi.runtime +from typing import Any, Mapping, Optional, Sequence, Union, overload +from .. import _utilities + +__all__ = [ + 'GetPolicyDocumentStatementArgs', +] + +@pulumi.input_type +class GetPolicyDocumentStatementArgs: + def __init__(__self__, *, + actions: Optional[Sequence[str]] = None): + if actions is not None: + pulumi.set(__self__, "actions", actions) + + @property + @pulumi.getter + def actions(self) -> Optional[Sequence[str]]: + return pulumi.get(self, "actions") + + @actions.setter + def actions(self, value: Optional[Sequence[str]]): + pulumi.set(self, "actions", value) + + diff --git a/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/x/iam/__init__.py b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/x/iam/__init__.py new file mode 100644 index 000000000000..bd2ed73b5ebd --- /dev/null +++ b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/x/iam/__init__.py @@ -0,0 +1,8 @@ +# coding=utf-8 +# *** WARNING: this file was generated by test. *** +# *** Do not edit by hand unless you're certain you know what you are doing! *** + +from ... import _utilities +import typing +# Export this package's modules as members: +from .get_policy_document import * diff --git a/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/x/iam/get_policy_document.py b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/x/iam/get_policy_document.py new file mode 100644 index 000000000000..48bd7fb200c8 --- /dev/null +++ b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/x/iam/get_policy_document.py @@ -0,0 +1,82 @@ +# coding=utf-8 +# *** WARNING: this file was generated by test. *** +# *** Do not edit by hand unless you're certain you know what you are doing! *** + +import copy +import warnings +import pulumi +import pulumi.runtime +from typing import Any, Mapping, Optional, Sequence, Union, overload +from ... import _utilities +from ... import x as _x + +__all__ = [ + 'GetPolicyDocumentResult', + 'AwaitableGetPolicyDocumentResult', + 'get_policy_document', + 'get_policy_document_output', +] + +@pulumi.output_type +class GetPolicyDocumentResult: + def __init__(__self__, id=None, json=None, statements=None): + if id and not isinstance(id, str): + raise TypeError("Expected argument 'id' to be a str") + pulumi.set(__self__, "id", id) + if json and not isinstance(json, str): + raise TypeError("Expected argument 'json' to be a str") + pulumi.set(__self__, "json", json) + if statements and not isinstance(statements, list): + raise TypeError("Expected argument 'statements' to be a list") + pulumi.set(__self__, "statements", statements) + + @property + @pulumi.getter + def id(self) -> str: + return pulumi.get(self, "id") + + @property + @pulumi.getter + def json(self) -> str: + return pulumi.get(self, "json") + + @property + @pulumi.getter + def statements(self) -> Optional[Sequence['_x.outputs.GetPolicyDocumentStatement']]: + return pulumi.get(self, "statements") + + +class AwaitableGetPolicyDocumentResult(GetPolicyDocumentResult): + # pylint: disable=using-constant-test + def __await__(self): + if False: + yield self + return GetPolicyDocumentResult( + id=self.id, + json=self.json, + statements=self.statements) + + +def get_policy_document(statements: Optional[Sequence[pulumi.InputType['_x.GetPolicyDocumentStatementArgs']]] = None, + opts: Optional[pulumi.InvokeOptions] = None) -> AwaitableGetPolicyDocumentResult: + """ + Use this data source to access information about an existing resource. + """ + __args__ = dict() + __args__['statements'] = statements + opts = pulumi.InvokeOptions.merge(_utilities.get_invoke_opts_defaults(), opts) + __ret__ = pulumi.runtime.invoke('aws:x/iam/getPolicyDocument:getPolicyDocument', __args__, opts=opts, typ=GetPolicyDocumentResult).value + + return AwaitableGetPolicyDocumentResult( + id=__ret__.id, + json=__ret__.json, + statements=__ret__.statements) + + +@_utilities.lift_output_func(get_policy_document) +def get_policy_document_output(statements: Optional[pulumi.Input[Optional[Sequence[pulumi.InputType['_x.GetPolicyDocumentStatementArgs']]]]] = None, + opts: Optional[pulumi.InvokeOptions] = None) -> pulumi.Output[GetPolicyDocumentResult]: + """ + Use this data source to access information about an existing resource. + """ + ... diff --git a/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/x/outputs.py b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/x/outputs.py new file mode 100644 index 000000000000..f44a78da363c --- /dev/null +++ b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/x/outputs.py @@ -0,0 +1,28 @@ +# coding=utf-8 +# *** WARNING: this file was generated by test. *** +# *** Do not edit by hand unless you're certain you know what you are doing! *** + +import copy +import warnings +import pulumi +import pulumi.runtime +from typing import Any, Mapping, Optional, Sequence, Union, overload +from .. import _utilities + +__all__ = [ + 'GetPolicyDocumentStatementResult', +] + +@pulumi.output_type +class GetPolicyDocumentStatementResult(dict): + def __init__(__self__, *, + actions: Optional[Sequence[str]] = None): + if actions is not None: + pulumi.set(__self__, "actions", actions) + + @property + @pulumi.getter + def actions(self) -> Optional[Sequence[str]]: + return pulumi.get(self, "actions") + + diff --git a/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/setup.py b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/setup.py new file mode 100644 index 000000000000..7dfccddadc17 --- /dev/null +++ b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/setup.py @@ -0,0 +1,59 @@ +# coding=utf-8 +# *** WARNING: this file was generated by test. *** +# *** Do not edit by hand unless you're certain you know what you are doing! *** + +import errno +from setuptools import setup, find_packages +from setuptools.command.install import install +from subprocess import check_call + + +VERSION = "0.0.0" +PLUGIN_VERSION = "0.0.0" + +class InstallPluginCommand(install): + def run(self): + install.run(self) + try: + check_call(['pulumi', 'plugin', 'install', 'resource', 'aws', PLUGIN_VERSION]) + except OSError as error: + if error.errno == errno.ENOENT: + print(f""" + There was an error installing the aws resource provider plugin. + It looks like `pulumi` is not installed on your system. + Please visit https://pulumi.com/ to install the Pulumi CLI. + You may try manually installing the plugin by running + `pulumi plugin install resource aws {PLUGIN_VERSION}` + """) + else: + raise + + +def readme(): + try: + with open('README.md', encoding='utf-8') as f: + return f.read() + except FileNotFoundError: + return "aws Pulumi Package - Development Version" + + +setup(name='pulumi_aws', + version=VERSION, + long_description=readme(), + long_description_content_type='text/markdown', + cmdclass={ + 'install': InstallPluginCommand, + }, + packages=find_packages(), + package_data={ + 'pulumi_aws': [ + 'py.typed', + 'pulumi-plugin.json', + ] + }, + install_requires=[ + 'parver>=0.2.1', + 'pulumi>=3.0.0,<4.0.0', + 'semver>=2.8.1' + ], + zip_safe=False) From e7c3f8a285f886eee092008d459da6584b8c0f1a Mon Sep 17 00:00:00 2001 From: Anton Tayanovskyy Date: Wed, 5 Oct 2022 15:22:06 -0400 Subject: [PATCH 3/5] Fix the issue --- pkg/codegen/python/gen.go | 41 +++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/pkg/codegen/python/gen.go b/pkg/codegen/python/gen.go index 0e489851c513..c82bf3d45a97 100644 --- a/pkg/codegen/python/gen.go +++ b/pkg/codegen/python/gen.go @@ -89,8 +89,16 @@ func title(s string) string { return string(append([]rune{unicode.ToUpper(runes[0])}, runes[1:]...)) } +type modLocator struct { + // Returns defining modlue for a given ObjectType. Returns nil + // for types that are not being generated in the current + // GeneratePacakge call. + objectTypeMod func(*schema.ObjectType) *modContext +} + type modContext struct { pkg *schema.Package + modLocator *modLocator mod string pyPkgName string types []*schema.ObjectType @@ -131,13 +139,21 @@ func (mod *modContext) addChild(child *modContext) { } func (mod *modContext) details(t *schema.ObjectType) *typeDetails { - details, ok := mod.typeDetails[t] + m := mod + + if mod.modLocator != nil { + if actualMod := mod.modLocator.objectTypeMod(t); actualMod != nil { + m = actualMod + } + } + + details, ok := m.typeDetails[t] if !ok { details = &typeDetails{} - if mod.typeDetails == nil { - mod.typeDetails = map[*schema.ObjectType]*typeDetails{} + if m.typeDetails == nil { + m.typeDetails = map[*schema.ObjectType]*typeDetails{} } - mod.typeDetails[t] = details + m.typeDetails[t] = details } return details } @@ -2616,6 +2632,7 @@ func generateModuleContextMap(tool string, pkg *schema.Package, info PackageInfo } // group resources, types, and functions into modules + // modules map will contain modContext entries for all modules in current package (pkg) modules := map[string]*modContext{} var getMod func(modName string, p *schema.Package) *modContext @@ -2766,6 +2783,22 @@ func generateModuleContextMap(tool string, pkg *schema.Package, info PackageInfo mod.extraSourceFiles = append(mod.extraSourceFiles, p) } + // Setup modLocator so that mod.typeDetails finds the right + // modContext for every ObjectType. + modLocator := &modLocator{ + objectTypeMod: func(t *schema.ObjectType) *modContext { + if t.Package != pkg { + return nil + } + + return getModFromToken(t.Token, t.Package) + }, + } + + for _, mod := range modules { + mod.modLocator = modLocator + } + return modules, nil } From 83d2a04b77561f38ec3905796eb506e9002972a5 Mon Sep 17 00:00:00 2001 From: Anton Tayanovskyy Date: Wed, 5 Oct 2022 15:22:32 -0400 Subject: [PATCH 4/5] Accept codegen demonstrating the fix --- .../python/pulumi_aws/x/iam/get_policy_document.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/x/iam/get_policy_document.py b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/x/iam/get_policy_document.py index 48bd7fb200c8..f00d96aa5251 100644 --- a/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/x/iam/get_policy_document.py +++ b/pkg/codegen/testing/test/testdata/regress-py-tfbridge-611/python/pulumi_aws/x/iam/get_policy_document.py @@ -42,7 +42,7 @@ def json(self) -> str: @property @pulumi.getter - def statements(self) -> Optional[Sequence['_x.outputs.GetPolicyDocumentStatement']]: + def statements(self) -> Optional[Sequence['_x.outputs.GetPolicyDocumentStatementResult']]: return pulumi.get(self, "statements") From 4952d6d7511d132e980493e5a8f030a82dc698c9 Mon Sep 17 00:00:00 2001 From: Anton Tayanovskyy Date: Wed, 5 Oct 2022 15:39:15 -0400 Subject: [PATCH 5/5] Add CHANGELOG --- ...n-python--py-sdkgen-fix-dangling-type-refs-tfbridge20.yaml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelog/pending/20221005--sdkgen-python--py-sdkgen-fix-dangling-type-refs-tfbridge20.yaml diff --git a/changelog/pending/20221005--sdkgen-python--py-sdkgen-fix-dangling-type-refs-tfbridge20.yaml b/changelog/pending/20221005--sdkgen-python--py-sdkgen-fix-dangling-type-refs-tfbridge20.yaml new file mode 100644 index 000000000000..ea6170b16228 --- /dev/null +++ b/changelog/pending/20221005--sdkgen-python--py-sdkgen-fix-dangling-type-refs-tfbridge20.yaml @@ -0,0 +1,4 @@ +changes: +- type: fix + scope: sdkgen/python + description: Fixes dangling type-refs generated under compatibility=tfbridge20 for schemas that refer to types aross modules.