Source code for sceptre.resolvers.placeholders

from contextlib import contextmanager
from enum import Enum
from threading import Lock
from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
    from sceptre import resolvers

# This is a toggle used for globally enabling placeholder values out of resolvers when they error
# while resolving. This is important when performing actions on stacks like validation or generation
# when their dependencies have not been deployed yet and those dependencies are expressed in stack
# resolvers that are used in those actions, especially sceptre_user_data.
_RESOLVE_PLACEHOLDER_ON_ERROR = False


[docs]class PlaceholderType(Enum): explicit = 1 # Looks like "{ !MyClass(argument) }" alphanum = 2 # Looks like MyClassargument none = 3 # Resolves to None
_placeholder_lock = Lock()
[docs]@contextmanager def use_resolver_placeholders_on_error(): """A context manager that toggles on placeholders for resolvers that error out. This should NOT be used while creating/launching stacks, but it is often required when validating or diffing stacks whose dependencies haven't yet been deployed and that reference those dependencies with resolvers, especially in the sceptre_user_data. """ global _RESOLVE_PLACEHOLDER_ON_ERROR try: with _placeholder_lock: _RESOLVE_PLACEHOLDER_ON_ERROR = True yield finally: with _placeholder_lock: _RESOLVE_PLACEHOLDER_ON_ERROR = False
[docs]def are_placeholders_enabled() -> bool: """Indicates whether placeholders have been globally enabled or not.""" with _placeholder_lock: return _RESOLVE_PLACEHOLDER_ON_ERROR
[docs]def create_placeholder_value( resolver: "resolvers.Resolver", placeholder_type: PlaceholderType ) -> Any: placeholder_func = _placeholders[placeholder_type] return placeholder_func(resolver)
def _create_explicit_resolver_placeholder(resolver: "resolvers.Resolver") -> str: """Creates a placeholder value to be substituted for the resolved value when placeholders are allowed and the value cannot be resolved. The placeholder will look like one of: * { !ClassName } -> used when there is no argument * { !ClassName(argument) } -> used when there is a string argument * { !ClassName({'key': 'value'}) } -> used when there is a dict argument :param resolver: The resolver to create a placeholder for :return: The placeholder value """ return f"{{ {resolver} }}" def _create_alphanumeric_placeholder(resolver: "resolvers.Resolver") -> str: """Creates a placeholder value that is only composed of alphanumeric characters. This is more useful when performing operations that send a template to CloudFormation, which will have stricter requirements for values in templates. Values from this function will not be as readable, but they are more likely to be valid when passed to a template. The placeholder will look like one of: * ClassName -> used when there is no argument * ClassNameargument -> used when there is a string argument * ClassNamekeyvalue -> used when there is a dict argument :param resolver: The resolver to create a placeholder for :return: The placeholder value """ explicit_placeholder = _create_explicit_resolver_placeholder(resolver) alphanum_placeholder = "".join(c for c in explicit_placeholder if c.isalnum()) return alphanum_placeholder _placeholders = { PlaceholderType.explicit: _create_explicit_resolver_placeholder, PlaceholderType.alphanum: _create_alphanumeric_placeholder, PlaceholderType.none: lambda resolver: None, }