Skip to content

decorative_secrets.defaults

ApplyConditionalDefaultsOptions dataclass

This class contains options governing the behavior of the apply_conditional_defaults decorator.

Attributes:

  • filter_parameter_defaults (tuple[typing.Any, ...] | dict[str, tuple[typing.Any, ...]]) –

    This may either be:

    1. A tuple of values which should be filtered out of keyword arguments when both the passed keyword argument value and the parameter default value in the function signature are one of these values.
    2. A dictionary mapping parameter names to tuples of values applying the filtering logic described above on a per-parameter basis.

    Defaults to ()—indicating no filtering should occur.

Examples:

from decorative_secrets.defaults import (
    apply_conditional_defaults,
    ApplyConditionalDefaultsOptions,
)
@apply_conditional_defaults(
    lambda environment: environment == "prod",
    ApplyConditionalDefaultsOptions(filter_parameter_defaults=(None,)),
    source_directory="/in/prod",
    target_directory="/out/prod",
)
@apply_conditional_defaults(
    lambda environment: environment == "dev",
    ApplyConditionalDefaultsOptions(filter_parameter_defaults=(None,)),
    source_directory="/in/dev",
    target_directory="/out/dev",
)
@apply_conditional_defaults(
    lambda environment: environment == "stage",
    ApplyConditionalDefaultsOptions(filter_parameter_defaults=(None,)),
    source_directory="/in/stage",
    target_directory="/out/stage",
)
def get_environment_source_target(
    environment: str = "dev",
    source_directory: str | None = None,
    target_directory: str | None = None,
) -> tuple[str, str | None, str | None]:
    return (environment, source_directory, target_directory)

get_environment_source_target("stage", None, None)
# ('stage', '/in/stage', '/out/stage')

get_environment_source_target(environment="prod", None, None)
# ('prod', '/in/prod', '/out/prod')

get_environment_source_target(None, None, None)
# ('dev', '/in/dev', '/out/dev')
Source code in src/decorative_secrets/defaults.py
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
@dataclass(frozen=True)
class ApplyConditionalDefaultsOptions:
    """
    This class contains options governing the behavior of the
    [apply_conditional_defaults
    ](./#decorative_secrets.defaults.apply_conditional_defaults) decorator.

    Attributes:
        filter_parameter_defaults: This may either be:

            1.  A tuple of values which should be filtered out of keyword
                arguments when both the passed keyword argument value *and*
                the parameter default value in the function signature
                are one of these values.
            2.  A dictionary mapping parameter names to tuples of values
                applying the filtering logic described above on a per-parameter
                basis.

            Defaults to `()`—indicating no filtering should occur.

    Examples:
        ```python
        from decorative_secrets.defaults import (
            apply_conditional_defaults,
            ApplyConditionalDefaultsOptions,
        )
        @apply_conditional_defaults(
            lambda environment: environment == "prod",
            ApplyConditionalDefaultsOptions(filter_parameter_defaults=(None,)),
            source_directory="/in/prod",
            target_directory="/out/prod",
        )
        @apply_conditional_defaults(
            lambda environment: environment == "dev",
            ApplyConditionalDefaultsOptions(filter_parameter_defaults=(None,)),
            source_directory="/in/dev",
            target_directory="/out/dev",
        )
        @apply_conditional_defaults(
            lambda environment: environment == "stage",
            ApplyConditionalDefaultsOptions(filter_parameter_defaults=(None,)),
            source_directory="/in/stage",
            target_directory="/out/stage",
        )
        def get_environment_source_target(
            environment: str = "dev",
            source_directory: str | None = None,
            target_directory: str | None = None,
        ) -> tuple[str, str | None, str | None]:
            return (environment, source_directory, target_directory)

        get_environment_source_target("stage", None, None)
        # ('stage', '/in/stage', '/out/stage')

        get_environment_source_target(environment="prod", None, None)
        # ('prod', '/in/prod', '/out/prod')

        get_environment_source_target(None, None, None)
        # ('dev', '/in/dev', '/out/dev')
        ```
    """

    filter_parameter_defaults: (
        tuple[Any, ...] | dict[str, tuple[Any, ...]]
    ) = ()

apply_conditional_defaults

apply_conditional_defaults(
    condition: collections.abc.Callable[..., bool],
    *default_args: typing.Any,
    **default_kwargs: typing.Any
) -> collections.abc.Callable[
    ..., collections.abc.Callable[..., typing.Any]
]

This function decorates another function in order to apply a set of default keyword or positional/keyword argument values dependent on the outcome of passing an applicable subset of those arguments to the condition function.

Parameters:

  • condition (collections.abc.Callable[..., bool]) –

    A function which accepts a subset of the decorated function's arguments and returns a boolean value indicating whether or not to apply the default argument values.

  • *default_args (typing.Any, default: () ) –

    A set of positional argument values to apply as defaults if the condition is met. Note: If any of these arguments are an instance of ApplyConditionalDefaultsOptions , the options will be used to determine decorator behavior, and will be redacted from the arguments passed to the decorated function.

  • **default_kwargs (typing.Any, default: {} ) –

    A set of keyword argument values to apply as defaults if the condition is met.

Examples:

from decorative_secrets.defaults import apply_conditional_defaults


@apply_conditional_defaults(
    lambda environment: environment == "prod",
    source_directory="/in/prod",
    target_directory="/out/prod",
)
@apply_conditional_defaults(
    lambda environment: environment == "dev",
    source_directory="/in/dev",
    target_directory="/out/dev",
)
@apply_conditional_defaults(
    lambda environment: environment == "stage",
    source_directory="/in/stage",
    target_directory="/out/stage",
)
def get_environment_source_target(
    environment: str = "dev",
    source_directory: str = "/dev/null",
    target_directory: str = "/dev/null",
) -> tuple[str, str, str]:
    return (environment, source_directory, target_directory)


get_environment_source_target("stage")
# ('stage', '/in/stage', '/out/stage')

get_environment_source_target(environment="prod")
# ('prod', '/in/prod', '/out/prod')

get_environment_source_target()
# ('dev', '/in/dev', '/out/dev')
Source code in src/decorative_secrets/defaults.py
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def apply_conditional_defaults(
    condition: Callable[..., bool], *default_args: Any, **default_kwargs: Any
) -> Callable[..., Callable[..., Any]]:
    """
    This function decorates another function in order to apply a set of
    *default* keyword or positional/keyword argument values dependent on the
    outcome of passing an applicable subset of those arguments to the
    `condition` function.

    Parameters:
        condition: A function which accepts a subset of the decorated
            function's arguments and returns a boolean value indicating
            whether or not to apply the default argument values.
        *default_args: A set of positional argument values to apply as
            defaults if the condition is met. Note: If any of these arguments
            are an instance of [ApplyConditionalDefaultsOptions
            ](./#decorative_secrets.defaults.ApplyConditionalDefaultsOptions),
            the options will be used to determine decorator behavior, and will
            be redacted from the arguments passed to the decorated function.
        **default_kwargs: A set of keyword argument values to apply as
            defaults if the condition is met.

    Examples:
        ```python
        from decorative_secrets.defaults import apply_conditional_defaults


        @apply_conditional_defaults(
            lambda environment: environment == "prod",
            source_directory="/in/prod",
            target_directory="/out/prod",
        )
        @apply_conditional_defaults(
            lambda environment: environment == "dev",
            source_directory="/in/dev",
            target_directory="/out/dev",
        )
        @apply_conditional_defaults(
            lambda environment: environment == "stage",
            source_directory="/in/stage",
            target_directory="/out/stage",
        )
        def get_environment_source_target(
            environment: str = "dev",
            source_directory: str = "/dev/null",
            target_directory: str = "/dev/null",
        ) -> tuple[str, str, str]:
            return (environment, source_directory, target_directory)


        get_environment_source_target("stage")
        # ('stage', '/in/stage', '/out/stage')

        get_environment_source_target(environment="prod")
        # ('prod', '/in/prod', '/out/prod')

        get_environment_source_target()
        # ('dev', '/in/dev', '/out/dev')
        ```
    """
    options: ApplyConditionalDefaultsOptions
    default_args, options = _get_args_options(*default_args)

    def decorating_function(
        function: Callable[..., Any],
    ) -> Callable[..., Any]:
        original_function: Callable[..., Any] = unwrap_function(function)
        function_signature: Signature = signature(original_function)
        condition_signature: Signature = signature(condition)

        def get_args_kwargs(
            *args: Any, **kwargs: Any
        ) -> tuple[tuple[Any, ...], dict[str, Any]]:
            # First we consolidate the keyword arguments with any arguments
            # which are passed to parameters which can be either positional
            # *or* keyword arguments, and were passed as positional arguments
            args = merge_function_signature_args_kwargs(
                function_signature, args, kwargs
            )
            # Get the arguments and keyword arguments applicable to the
            # condition function
            condition_args: tuple[Any, ...]
            condition_kwargs: dict[str, Any]
            kwargs_or_defaults: dict[str, Any] = kwargs.copy()
            # Use function signature defaults for any missing keyword
            # parameters
            key: str
            value: Any
            for key, value in get_signature_parameter_names_defaults(
                function_signature
            ).items():
                if (
                    options.filter_parameter_defaults
                    and (value in options.filter_parameter_defaults)
                    and (key in kwargs)
                    and (kwargs[key] == value)
                ):
                    # If the keyword argument value is one of the filtered
                    # default values, and equals the parameter default—drop it
                    # from the `kwargs` dictionary to allow the argument
                    # to be superseded by any applicable conditional defaults.
                    del kwargs[key]
                kwargs_or_defaults.setdefault(key, value)
            condition_args, condition_kwargs = (
                get_function_signature_applicable_args_kwargs(
                    condition_signature, args, kwargs_or_defaults
                )
            )
            if condition(*condition_args, **condition_kwargs):
                len_args: int = len(args)
                if len(default_args) > len_args:
                    # Extend args if there are more default args than args
                    args = (*args, *default_args[len_args:])
                kwargs.update(default_kwargs)
            return args, kwargs

        if iscoroutinefunction(function):

            @wraps(function)
            async def wrapper(*args: Any, **kwargs: Any) -> Any:
                """
                This function wraps the original and applies conditional
                defaults
                """
                args, kwargs = get_args_kwargs(*args, **kwargs)
                # Execute the wrapped function
                return await function(*args, **kwargs)

        else:

            @wraps(function)
            def wrapper(*args: Any, **kwargs: Any) -> Any:
                """
                This function wraps the original and applies conditional
                defaults
                """
                args, kwargs = get_args_kwargs(*args, **kwargs)
                # Execute the wrapped function
                return function(*args, **kwargs)

        return wrapper

    return decorating_function