snowflake-cli-labs 2.8.0rc1__py3-none-any.whl → 3.0.0rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- snowflake/cli/__about__.py +1 -1
- snowflake/cli/{app → _app}/__main__.py +1 -1
- snowflake/cli/{app → _app}/cli_app.py +12 -12
- snowflake/cli/{app → _app}/commands_registration/builtin_plugins.py +13 -19
- snowflake/cli/{app → _app}/commands_registration/command_plugins_loader.py +9 -9
- snowflake/cli/{app → _app}/commands_registration/commands_registration_with_callbacks.py +4 -4
- snowflake/cli/{app → _app}/commands_registration/exception_logging.py +2 -2
- snowflake/cli/{app → _app}/commands_registration/typer_registration.py +2 -2
- snowflake/cli/{app → _app}/dev/docs/commands_docs_generator.py +30 -12
- snowflake/cli/{app → _app}/dev/docs/generator.py +3 -3
- snowflake/cli/{app → _app}/dev/docs/project_definition_docs_generator.py +4 -4
- snowflake/cli/{app → _app}/dev/docs/templates/usage.rst.jinja2 +14 -4
- snowflake/cli/{app → _app}/main_typer.py +2 -2
- snowflake/cli/{app → _app}/printing.py +2 -2
- snowflake/cli/{app → _app}/snow_connector.py +24 -17
- snowflake/cli/{app → _app}/telemetry.py +4 -5
- snowflake/cli/{plugins → _plugins}/connection/commands.py +25 -7
- snowflake/cli/_plugins/connection/plugin_spec.py +30 -0
- snowflake/cli/{plugins → _plugins}/connection/util.py +16 -0
- snowflake/cli/{plugins → _plugins}/cortex/commands.py +54 -49
- snowflake/cli/{plugins → _plugins}/cortex/constants.py +1 -1
- snowflake/cli/{plugins → _plugins}/cortex/manager.py +5 -5
- snowflake/cli/{plugins → _plugins}/cortex/plugin_spec.py +1 -1
- snowflake/cli/{plugins → _plugins}/git/commands.py +32 -20
- snowflake/cli/{plugins → _plugins}/git/manager.py +20 -11
- snowflake/cli/{plugins → _plugins}/git/plugin_spec.py +1 -1
- snowflake/cli/{plugins → _plugins}/init/commands.py +10 -6
- snowflake/cli/{plugins → _plugins}/init/plugin_spec.py +1 -1
- snowflake/cli/{plugins → _plugins}/nativeapp/artifacts.py +14 -0
- snowflake/cli/_plugins/nativeapp/bundle_context.py +31 -0
- snowflake/cli/{plugins → _plugins}/nativeapp/codegen/artifact_processor.py +3 -3
- snowflake/cli/{plugins → _plugins}/nativeapp/codegen/compiler.py +32 -18
- snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +249 -0
- snowflake/cli/{plugins → _plugins}/nativeapp/codegen/setup/setup_driver.py.source +5 -2
- snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/extension_function_utils.py +4 -4
- snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/python_processor.py +23 -29
- snowflake/cli/_plugins/nativeapp/codegen/templates/templates_processor.py +93 -0
- snowflake/cli/{plugins → _plugins}/nativeapp/commands.py +171 -42
- snowflake/cli/{plugins → _plugins}/nativeapp/common_flags.py +1 -1
- snowflake/cli/{plugins → _plugins}/nativeapp/exceptions.py +3 -3
- snowflake/cli/{plugins → _plugins}/nativeapp/init.py +1 -1
- snowflake/cli/_plugins/nativeapp/manager.py +572 -0
- snowflake/cli/{plugins/connection → _plugins/nativeapp}/plugin_spec.py +1 -1
- snowflake/cli/{plugins → _plugins}/nativeapp/project_model.py +35 -19
- snowflake/cli/{plugins → _plugins}/nativeapp/run_processor.py +25 -23
- snowflake/cli/{plugins → _plugins}/nativeapp/teardown_processor.py +24 -110
- snowflake/cli/{plugins → _plugins}/nativeapp/v2_conversions/v2_to_v1_decorator.py +47 -28
- snowflake/cli/{plugins → _plugins}/nativeapp/version/commands.py +15 -12
- snowflake/cli/{plugins → _plugins}/nativeapp/version/version_processor.py +22 -20
- snowflake/cli/{plugins → _plugins}/notebook/commands.py +8 -6
- snowflake/cli/{plugins → _plugins}/notebook/manager.py +14 -14
- snowflake/cli/{plugins → _plugins}/notebook/plugin_spec.py +1 -1
- snowflake/cli/{plugins → _plugins}/notebook/types.py +0 -1
- snowflake/cli/{plugins → _plugins}/object/command_aliases.py +6 -5
- snowflake/cli/{plugins → _plugins}/object/commands.py +16 -10
- snowflake/cli/{plugins → _plugins}/object/manager.py +7 -6
- snowflake/cli/{plugins → _plugins}/object/plugin_spec.py +1 -1
- snowflake/cli/_plugins/snowpark/commands.py +450 -0
- snowflake/cli/_plugins/snowpark/common.py +268 -0
- snowflake/cli/{plugins → _plugins}/snowpark/models.py +0 -7
- snowflake/cli/{plugins → _plugins}/snowpark/package/anaconda_packages.py +2 -36
- snowflake/cli/{plugins → _plugins}/snowpark/package/commands.py +13 -74
- snowflake/cli/{plugins → _plugins}/snowpark/package/manager.py +4 -3
- snowflake/cli/{plugins → _plugins}/snowpark/package_utils.py +5 -5
- snowflake/cli/{plugins/nativeapp → _plugins/snowpark}/plugin_spec.py +1 -1
- snowflake/cli/_plugins/snowpark/snowpark_project_paths.py +109 -0
- snowflake/cli/{plugins → _plugins}/snowpark/snowpark_shared.py +0 -36
- snowflake/cli/{plugins → _plugins}/snowpark/zipper.py +16 -8
- snowflake/cli/{plugins → _plugins}/spcs/__init__.py +5 -7
- snowflake/cli/{plugins → _plugins}/spcs/compute_pool/commands.py +29 -28
- snowflake/cli/{plugins → _plugins}/spcs/compute_pool/manager.py +3 -3
- snowflake/cli/{plugins → _plugins}/spcs/image_registry/commands.py +3 -3
- snowflake/cli/{plugins → _plugins}/spcs/image_repository/commands.py +25 -19
- snowflake/cli/{plugins → _plugins}/spcs/image_repository/manager.py +1 -1
- snowflake/cli/{plugins → _plugins}/spcs/plugin_spec.py +1 -1
- snowflake/cli/{plugins → _plugins}/spcs/services/commands.py +66 -32
- snowflake/cli/{plugins → _plugins}/spcs/services/manager.py +43 -5
- snowflake/cli/{plugins → _plugins}/sql/commands.py +20 -17
- snowflake/cli/{plugins → _plugins}/sql/manager.py +1 -1
- snowflake/cli/{plugins → _plugins}/sql/plugin_spec.py +1 -1
- snowflake/cli/{plugins → _plugins}/stage/commands.py +20 -17
- snowflake/cli/{plugins → _plugins}/stage/diff.py +1 -47
- snowflake/cli/{plugins → _plugins}/stage/manager.py +54 -21
- snowflake/cli/{plugins → _plugins}/stage/plugin_spec.py +1 -1
- snowflake/cli/_plugins/stage/utils.py +54 -0
- snowflake/cli/{plugins → _plugins}/streamlit/commands.py +59 -62
- snowflake/cli/{plugins → _plugins}/streamlit/manager.py +51 -70
- snowflake/cli/_plugins/streamlit/plugin_spec.py +30 -0
- snowflake/cli/_plugins/workspace/action_context.py +17 -0
- snowflake/cli/_plugins/workspace/commands.py +194 -0
- snowflake/cli/_plugins/workspace/manager.py +73 -0
- snowflake/cli/{plugins → _plugins}/workspace/plugin_spec.py +1 -1
- snowflake/cli/api/cli_global_context.py +40 -13
- snowflake/cli/api/commands/common.py +25 -0
- snowflake/cli/api/commands/decorators.py +5 -4
- snowflake/cli/api/commands/experimental_behaviour.py +2 -3
- snowflake/cli/api/commands/flags.py +97 -179
- snowflake/cli/api/commands/overrideable_parameter.py +143 -0
- snowflake/cli/api/commands/snow_typer.py +14 -6
- snowflake/cli/api/commands/typer_pre_execute.py +3 -3
- snowflake/cli/api/commands/utils.py +18 -0
- snowflake/cli/api/config.py +18 -5
- snowflake/cli/api/console/abc.py +5 -2
- snowflake/cli/api/constants.py +11 -0
- snowflake/cli/api/entities/application_entity.py +12 -0
- snowflake/cli/api/entities/application_package_entity.py +553 -0
- snowflake/cli/api/entities/common.py +51 -0
- snowflake/cli/api/entities/snowpark_entity.py +29 -0
- snowflake/cli/api/entities/streamlit_entity.py +12 -0
- snowflake/cli/api/entities/utils.py +357 -0
- snowflake/cli/api/exceptions.py +31 -5
- snowflake/cli/api/feature_flags.py +0 -1
- snowflake/cli/api/identifiers.py +41 -9
- snowflake/cli/api/project/definition.py +37 -6
- snowflake/cli/api/project/definition_conversion.py +194 -0
- snowflake/cli/api/project/definition_manager.py +12 -1
- snowflake/cli/api/project/project_verification.py +3 -3
- snowflake/cli/api/project/schemas/entities/{application_entity.py → application_entity_model.py} +21 -9
- snowflake/cli/api/project/schemas/entities/{application_package_entity.py → application_package_entity_model.py} +43 -15
- snowflake/cli/api/project/schemas/entities/common.py +80 -6
- snowflake/cli/api/project/schemas/entities/entities.py +38 -8
- snowflake/cli/api/project/schemas/entities/snowpark_entity.py +176 -0
- snowflake/cli/api/project/schemas/entities/streamlit_entity_model.py +73 -0
- snowflake/cli/api/project/schemas/identifier_model.py +10 -1
- snowflake/cli/api/project/schemas/native_app/application.py +8 -9
- snowflake/cli/api/project/schemas/native_app/package.py +7 -1
- snowflake/cli/api/project/schemas/project_definition.py +98 -27
- snowflake/cli/api/project/schemas/updatable_model.py +11 -3
- snowflake/cli/api/project/util.py +23 -6
- snowflake/cli/api/rendering/jinja.py +14 -8
- snowflake/cli/api/rendering/project_definition_templates.py +1 -1
- snowflake/cli/api/rendering/sql_templates.py +43 -11
- snowflake/cli/api/secure_path.py +16 -18
- snowflake/cli/api/secure_utils.py +90 -1
- snowflake/cli/api/sql_execution.py +48 -19
- snowflake/cli/api/utils/definition_rendering.py +18 -8
- {snowflake_cli_labs-2.8.0rc1.dist-info → snowflake_cli_labs-3.0.0rc1.dist-info}/METADATA +13 -13
- snowflake_cli_labs-3.0.0rc1.dist-info/RECORD +236 -0
- snowflake_cli_labs-3.0.0rc1.dist-info/entry_points.txt +2 -0
- snowflake/cli/api/commands/project_initialisation.py +0 -65
- snowflake/cli/app/build_and_push.sh +0 -8
- snowflake/cli/plugins/nativeapp/codegen/setup/native_app_setup_processor.py +0 -172
- snowflake/cli/plugins/nativeapp/manager.py +0 -823
- snowflake/cli/plugins/object_stage_deprecated/__init__.py +0 -15
- snowflake/cli/plugins/object_stage_deprecated/commands.py +0 -122
- snowflake/cli/plugins/object_stage_deprecated/plugin_spec.py +0 -32
- snowflake/cli/plugins/snowpark/commands.py +0 -548
- snowflake/cli/plugins/snowpark/common.py +0 -307
- snowflake/cli/plugins/snowpark/manager.py +0 -109
- snowflake/cli/plugins/snowpark/plugin_spec.py +0 -30
- snowflake/cli/plugins/snowpark/snowpark_package_paths.py +0 -65
- snowflake/cli/plugins/spcs/jobs/commands.py +0 -78
- snowflake/cli/plugins/spcs/jobs/manager.py +0 -53
- snowflake/cli/plugins/streamlit/plugin_spec.py +0 -30
- snowflake/cli/plugins/workspace/commands.py +0 -35
- snowflake/cli/templates/default_snowpark/.gitignore +0 -4
- snowflake/cli/templates/default_snowpark/app/__init__.py +0 -0
- snowflake/cli/templates/default_snowpark/app/common.py +0 -2
- snowflake/cli/templates/default_snowpark/app/functions.py +0 -15
- snowflake/cli/templates/default_snowpark/app/procedures.py +0 -22
- snowflake/cli/templates/default_snowpark/requirements.txt +0 -1
- snowflake/cli/templates/default_snowpark/snowflake.yml +0 -23
- snowflake/cli/templates/default_streamlit/.gitignore +0 -4
- snowflake/cli/templates/default_streamlit/common/hello.py +0 -2
- snowflake/cli/templates/default_streamlit/environment.yml +0 -6
- snowflake/cli/templates/default_streamlit/pages/my_page.py +0 -3
- snowflake/cli/templates/default_streamlit/snowflake.yml +0 -10
- snowflake/cli/templates/default_streamlit/streamlit_app.py +0 -4
- snowflake_cli_labs-2.8.0rc1.dist-info/RECORD +0 -240
- snowflake_cli_labs-2.8.0rc1.dist-info/entry_points.txt +0 -2
- /snowflake/cli/{app → _app}/__init__.py +0 -0
- /snowflake/cli/{app → _app}/api_impl/__init__.py +0 -0
- /snowflake/cli/{app → _app}/api_impl/plugin/__init__.py +0 -0
- /snowflake/cli/{app → _app}/api_impl/plugin/plugin_config_provider_impl.py +0 -0
- /snowflake/cli/{app → _app}/commands_registration/__init__.py +0 -0
- /snowflake/cli/{app → _app}/commands_registration/threadsafe.py +0 -0
- /snowflake/cli/{app → _app}/constants.py +0 -0
- /snowflake/cli/{app → _app}/dev/__init__.py +0 -0
- /snowflake/cli/{app → _app}/dev/commands_structure.py +0 -0
- /snowflake/cli/{app → _app}/dev/docs/__init__.py +0 -0
- /snowflake/cli/{app → _app}/dev/docs/project_definition_generate_json_schema.py +0 -0
- /snowflake/cli/{app → _app}/dev/docs/template_utils.py +0 -0
- /snowflake/cli/{app → _app}/dev/docs/templates/definition_description.rst.jinja2 +0 -0
- /snowflake/cli/{app → _app}/dev/docs/templates/overview.rst.jinja2 +0 -0
- /snowflake/cli/{app → _app}/dev/pycharm_remote_debug.py +0 -0
- /snowflake/cli/{app → _app}/loggers.py +0 -0
- /snowflake/cli/{plugins → _plugins}/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/connection/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/cortex/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/cortex/types.py +0 -0
- /snowflake/cli/{plugins → _plugins}/git/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/init/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/sandbox.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/callback_source.py.jinja +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/models.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/constants.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/feature_flags.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/policy.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/utils.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/version/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/notebook/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/notebook/exceptions.py +0 -0
- /snowflake/cli/{plugins → _plugins}/object/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/object/common.py +0 -0
- /snowflake/cli/{plugins → _plugins}/snowpark/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/snowpark/package/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/snowpark/package/utils.py +0 -0
- /snowflake/cli/{plugins → _plugins}/spcs/common.py +0 -0
- /snowflake/cli/{plugins → _plugins}/spcs/compute_pool/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/spcs/image_registry/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/spcs/image_registry/manager.py +0 -0
- /snowflake/cli/{plugins → _plugins}/spcs/image_repository/__init__.py +0 -0
- /snowflake/cli/{plugins/spcs/jobs → _plugins/spcs/services}/__init__.py +0 -0
- /snowflake/cli/{plugins/spcs/services → _plugins/sql}/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/sql/snowsql_templating.py +0 -0
- /snowflake/cli/{plugins/sql → _plugins/stage}/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/stage/md5.py +0 -0
- /snowflake/cli/{plugins/stage → _plugins/streamlit}/__init__.py +0 -0
- /snowflake/cli/{plugins/streamlit → _plugins/workspace}/__init__.py +0 -0
- /snowflake/cli/{plugins/workspace → api/project/schemas/entities}/__init__.py +0 -0
- {snowflake_cli_labs-2.8.0rc1.dist-info → snowflake_cli_labs-3.0.0rc1.dist-info}/WHEEL +0 -0
- {snowflake_cli_labs-2.8.0rc1.dist-info → snowflake_cli_labs-3.0.0rc1.dist-info}/licenses/LICENSE +0 -0
|
@@ -15,19 +15,21 @@
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
17
|
import tempfile
|
|
18
|
-
from dataclasses import dataclass
|
|
19
|
-
from enum import Enum
|
|
20
|
-
from inspect import signature
|
|
21
18
|
from pathlib import Path
|
|
22
|
-
from typing import Any, Callable,
|
|
19
|
+
from typing import Any, Callable, Optional
|
|
23
20
|
|
|
24
21
|
import click
|
|
25
22
|
import typer
|
|
26
23
|
from click import ClickException
|
|
27
|
-
from snowflake.cli.api.cli_global_context import
|
|
24
|
+
from snowflake.cli.api.cli_global_context import get_cli_context_manager
|
|
25
|
+
from snowflake.cli.api.commands.common import OnErrorType
|
|
26
|
+
from snowflake.cli.api.commands.overrideable_parameter import OverrideableOption
|
|
28
27
|
from snowflake.cli.api.commands.typer_pre_execute import register_pre_execute_command
|
|
28
|
+
from snowflake.cli.api.commands.utils import parse_key_value_variables
|
|
29
|
+
from snowflake.cli.api.config import get_all_connections
|
|
29
30
|
from snowflake.cli.api.console import cli_console
|
|
30
31
|
from snowflake.cli.api.exceptions import MissingConfiguration
|
|
32
|
+
from snowflake.cli.api.identifiers import FQN
|
|
31
33
|
from snowflake.cli.api.output.formats import OutputFormat
|
|
32
34
|
from snowflake.cli.api.project.definition_manager import DefinitionManager
|
|
33
35
|
from snowflake.cli.api.rendering.jinja import CONTEXT_KEY
|
|
@@ -38,111 +40,6 @@ _CONNECTION_SECTION = "Connection configuration"
|
|
|
38
40
|
_CLI_BEHAVIOUR = "Global configuration"
|
|
39
41
|
|
|
40
42
|
|
|
41
|
-
class OnErrorType(Enum):
|
|
42
|
-
BREAK = "break"
|
|
43
|
-
CONTINUE = "continue"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
class OverrideableOption:
|
|
47
|
-
"""
|
|
48
|
-
Class that allows you to generate instances of typer.models.OptionInfo with some default properties while allowing
|
|
49
|
-
specific values to be overridden.
|
|
50
|
-
|
|
51
|
-
Custom parameters:
|
|
52
|
-
- mutually_exclusive (Tuple[str]|List[str]): A list of parameter names that this Option is not compatible with. If this Option has
|
|
53
|
-
a truthy value and any of the other parameters in the mutually_exclusive list has a truthy value, a
|
|
54
|
-
ClickException will be thrown. Note that mutually_exclusive can contain an option's own name but does not require
|
|
55
|
-
it.
|
|
56
|
-
"""
|
|
57
|
-
|
|
58
|
-
def __init__(
|
|
59
|
-
self,
|
|
60
|
-
default: Any,
|
|
61
|
-
*param_decls: str,
|
|
62
|
-
mutually_exclusive: Optional[List[str] | Tuple[str]] = None,
|
|
63
|
-
**kwargs,
|
|
64
|
-
):
|
|
65
|
-
self.default = default
|
|
66
|
-
self.param_decls = param_decls
|
|
67
|
-
self.mutually_exclusive = mutually_exclusive
|
|
68
|
-
self.kwargs = kwargs
|
|
69
|
-
|
|
70
|
-
def __call__(self, **kwargs) -> typer.models.OptionInfo:
|
|
71
|
-
"""
|
|
72
|
-
Returns a typer.models.OptionInfo instance initialized with the specified default values along with any overrides
|
|
73
|
-
from kwargs. Note that if you are overriding param_decls, you must pass an iterable of strings, you cannot use
|
|
74
|
-
positional arguments like you can with typer.Option. Does not modify the original instance.
|
|
75
|
-
"""
|
|
76
|
-
default = kwargs.get("default", self.default)
|
|
77
|
-
param_decls = kwargs.get("param_decls", self.param_decls)
|
|
78
|
-
mutually_exclusive = kwargs.get("mutually_exclusive", self.mutually_exclusive)
|
|
79
|
-
if not isinstance(param_decls, list) and not isinstance(param_decls, tuple):
|
|
80
|
-
raise TypeError("param_decls must be a list or tuple")
|
|
81
|
-
passed_kwargs = self.kwargs.copy()
|
|
82
|
-
passed_kwargs.update(kwargs)
|
|
83
|
-
if passed_kwargs.get("callback", None) or mutually_exclusive:
|
|
84
|
-
passed_kwargs["callback"] = self._callback_factory(
|
|
85
|
-
passed_kwargs.get("callback", None), mutually_exclusive
|
|
86
|
-
)
|
|
87
|
-
for non_kwarg in ["default", "param_decls", "mutually_exclusive"]:
|
|
88
|
-
passed_kwargs.pop(non_kwarg, None)
|
|
89
|
-
return typer.Option(default, *param_decls, **passed_kwargs)
|
|
90
|
-
|
|
91
|
-
class InvalidCallbackSignature(ClickException):
|
|
92
|
-
def __init__(self, callback):
|
|
93
|
-
super().__init__(
|
|
94
|
-
f"Signature {signature(callback)} is not valid for an OverrideableOption callback function. Must have at most one parameter with each of the following types: (typer.Context, typer.CallbackParam, Any Other Type)"
|
|
95
|
-
)
|
|
96
|
-
|
|
97
|
-
def _callback_factory(
|
|
98
|
-
self, callback, mutually_exclusive: Optional[List[str] | Tuple[str]]
|
|
99
|
-
):
|
|
100
|
-
callback = callback if callback else lambda x: x
|
|
101
|
-
|
|
102
|
-
# inspect existing_callback to make sure signature is valid
|
|
103
|
-
existing_params = signature(callback).parameters
|
|
104
|
-
# at most one parameter with each type in [typer.Context, typer.CallbackParam, any other type]
|
|
105
|
-
limits = [
|
|
106
|
-
lambda x: x == typer.Context,
|
|
107
|
-
lambda x: x == typer.CallbackParam,
|
|
108
|
-
lambda x: x != typer.Context and x != typer.CallbackParam,
|
|
109
|
-
]
|
|
110
|
-
for limit in limits:
|
|
111
|
-
if len([v for v in existing_params.values() if limit(v.annotation)]) > 1:
|
|
112
|
-
raise self.InvalidCallbackSignature(callback)
|
|
113
|
-
|
|
114
|
-
def generated_callback(ctx: typer.Context, param: typer.CallbackParam, value):
|
|
115
|
-
if mutually_exclusive:
|
|
116
|
-
for name in mutually_exclusive:
|
|
117
|
-
if value and ctx.params.get(
|
|
118
|
-
name, False
|
|
119
|
-
): # if the current parameter is set to True and a previous parameter is also Truthy
|
|
120
|
-
curr_opt = param.opts[0]
|
|
121
|
-
other_opt = [x for x in ctx.command.params if x.name == name][
|
|
122
|
-
0
|
|
123
|
-
].opts[0]
|
|
124
|
-
raise click.ClickException(
|
|
125
|
-
f"Options '{curr_opt}' and '{other_opt}' are incompatible."
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
# pass args to existing callback based on its signature (this is how Typer infers callback args)
|
|
129
|
-
passed_params = {}
|
|
130
|
-
for existing_param in existing_params:
|
|
131
|
-
annotation = existing_params[existing_param].annotation
|
|
132
|
-
if annotation == typer.Context:
|
|
133
|
-
passed_params[existing_param] = ctx
|
|
134
|
-
elif annotation == typer.CallbackParam:
|
|
135
|
-
passed_params[existing_param] = param
|
|
136
|
-
else:
|
|
137
|
-
passed_params[existing_param] = value
|
|
138
|
-
return callback(**passed_params)
|
|
139
|
-
|
|
140
|
-
return generated_callback
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
from snowflake.cli.api.config import get_all_connections
|
|
144
|
-
|
|
145
|
-
|
|
146
43
|
def _callback(provide_setter: Callable[[], Callable[[Any], Any]]):
|
|
147
44
|
def callback(value):
|
|
148
45
|
set_value = provide_setter()
|
|
@@ -159,11 +56,11 @@ ConnectionOption = typer.Option(
|
|
|
159
56
|
"--environment",
|
|
160
57
|
help=f"Name of the connection, as defined in your `config.toml`. Default: `default`.",
|
|
161
58
|
callback=_callback(
|
|
162
|
-
lambda:
|
|
59
|
+
lambda: get_cli_context_manager().connection_context.set_connection_name
|
|
163
60
|
),
|
|
164
61
|
show_default=False,
|
|
165
62
|
rich_help_panel=_CONNECTION_SECTION,
|
|
166
|
-
|
|
63
|
+
shell_complete=lambda _, __, ___: list(get_all_connections()),
|
|
167
64
|
)
|
|
168
65
|
|
|
169
66
|
TemporaryConnectionOption = typer.Option(
|
|
@@ -172,7 +69,7 @@ TemporaryConnectionOption = typer.Option(
|
|
|
172
69
|
"-x",
|
|
173
70
|
help="Uses connection defined with command line parameters, instead of one defined in config",
|
|
174
71
|
callback=_callback(
|
|
175
|
-
lambda:
|
|
72
|
+
lambda: get_cli_context_manager().connection_context.set_temporary_connection
|
|
176
73
|
),
|
|
177
74
|
is_flag=True,
|
|
178
75
|
rich_help_panel=_CONNECTION_SECTION,
|
|
@@ -183,7 +80,9 @@ AccountOption = typer.Option(
|
|
|
183
80
|
"--account",
|
|
184
81
|
"--accountname",
|
|
185
82
|
help="Name assigned to your Snowflake account. Overrides the value specified for the connection.",
|
|
186
|
-
callback=_callback(
|
|
83
|
+
callback=_callback(
|
|
84
|
+
lambda: get_cli_context_manager().connection_context.set_account
|
|
85
|
+
),
|
|
187
86
|
show_default=False,
|
|
188
87
|
rich_help_panel=_CONNECTION_SECTION,
|
|
189
88
|
)
|
|
@@ -193,7 +92,7 @@ UserOption = typer.Option(
|
|
|
193
92
|
"--user",
|
|
194
93
|
"--username",
|
|
195
94
|
help="Username to connect to Snowflake. Overrides the value specified for the connection.",
|
|
196
|
-
callback=_callback(lambda:
|
|
95
|
+
callback=_callback(lambda: get_cli_context_manager().connection_context.set_user),
|
|
197
96
|
show_default=False,
|
|
198
97
|
rich_help_panel=_CONNECTION_SECTION,
|
|
199
98
|
)
|
|
@@ -206,7 +105,9 @@ def _password_callback(value: str):
|
|
|
206
105
|
if value:
|
|
207
106
|
cli_console.message(PLAIN_PASSWORD_MSG)
|
|
208
107
|
|
|
209
|
-
return _callback(lambda:
|
|
108
|
+
return _callback(lambda: get_cli_context_manager().connection_context.set_password)(
|
|
109
|
+
value
|
|
110
|
+
)
|
|
210
111
|
|
|
211
112
|
|
|
212
113
|
PasswordOption = typer.Option(
|
|
@@ -225,7 +126,7 @@ AuthenticatorOption = typer.Option(
|
|
|
225
126
|
help="Snowflake authenticator. Overrides the value specified for the connection.",
|
|
226
127
|
hide_input=True,
|
|
227
128
|
callback=_callback(
|
|
228
|
-
lambda:
|
|
129
|
+
lambda: get_cli_context_manager().connection_context.set_authenticator
|
|
229
130
|
),
|
|
230
131
|
show_default=False,
|
|
231
132
|
rich_help_panel=_CONNECTION_SECTION,
|
|
@@ -233,11 +134,12 @@ AuthenticatorOption = typer.Option(
|
|
|
233
134
|
|
|
234
135
|
PrivateKeyPathOption = typer.Option(
|
|
235
136
|
None,
|
|
137
|
+
"--private-key-file",
|
|
236
138
|
"--private-key-path",
|
|
237
|
-
help="Snowflake private key path. Overrides the value specified for the connection.",
|
|
139
|
+
help="Snowflake private key file path. Overrides the value specified for the connection.",
|
|
238
140
|
hide_input=True,
|
|
239
141
|
callback=_callback(
|
|
240
|
-
lambda:
|
|
142
|
+
lambda: get_cli_context_manager().connection_context.set_private_key_file
|
|
241
143
|
),
|
|
242
144
|
show_default=False,
|
|
243
145
|
rich_help_panel=_CONNECTION_SECTION,
|
|
@@ -252,7 +154,7 @@ SessionTokenOption = typer.Option(
|
|
|
252
154
|
help="Snowflake session token. Can be used only in conjunction with --master-token. Overrides the value specified for the connection.",
|
|
253
155
|
hide_input=True,
|
|
254
156
|
callback=_callback(
|
|
255
|
-
lambda:
|
|
157
|
+
lambda: get_cli_context_manager().connection_context.set_session_token
|
|
256
158
|
),
|
|
257
159
|
show_default=False,
|
|
258
160
|
rich_help_panel=_CONNECTION_SECTION,
|
|
@@ -267,7 +169,9 @@ MasterTokenOption = typer.Option(
|
|
|
267
169
|
"--master-token",
|
|
268
170
|
help="Snowflake master token. Can be used only in conjunction with --session-token. Overrides the value specified for the connection.",
|
|
269
171
|
hide_input=True,
|
|
270
|
-
callback=_callback(
|
|
172
|
+
callback=_callback(
|
|
173
|
+
lambda: get_cli_context_manager().connection_context.set_master_token
|
|
174
|
+
),
|
|
271
175
|
show_default=False,
|
|
272
176
|
rich_help_panel=_CONNECTION_SECTION,
|
|
273
177
|
exists=True,
|
|
@@ -281,7 +185,7 @@ TokenFilePathOption = typer.Option(
|
|
|
281
185
|
"--token-file-path",
|
|
282
186
|
help="Path to file with an OAuth token that should be used when connecting to Snowflake",
|
|
283
187
|
callback=_callback(
|
|
284
|
-
lambda:
|
|
188
|
+
lambda: get_cli_context_manager().connection_context.set_token_file_path
|
|
285
189
|
),
|
|
286
190
|
show_default=False,
|
|
287
191
|
rich_help_panel=_CONNECTION_SECTION,
|
|
@@ -295,7 +199,9 @@ DatabaseOption = typer.Option(
|
|
|
295
199
|
"--database",
|
|
296
200
|
"--dbname",
|
|
297
201
|
help="Database to use. Overrides the value specified for the connection.",
|
|
298
|
-
callback=_callback(
|
|
202
|
+
callback=_callback(
|
|
203
|
+
lambda: get_cli_context_manager().connection_context.set_database
|
|
204
|
+
),
|
|
299
205
|
show_default=False,
|
|
300
206
|
rich_help_panel=_CONNECTION_SECTION,
|
|
301
207
|
)
|
|
@@ -305,7 +211,7 @@ SchemaOption = typer.Option(
|
|
|
305
211
|
"--schema",
|
|
306
212
|
"--schemaname",
|
|
307
213
|
help="Database schema to use. Overrides the value specified for the connection.",
|
|
308
|
-
callback=_callback(lambda:
|
|
214
|
+
callback=_callback(lambda: get_cli_context_manager().connection_context.set_schema),
|
|
309
215
|
show_default=False,
|
|
310
216
|
rich_help_panel=_CONNECTION_SECTION,
|
|
311
217
|
)
|
|
@@ -315,7 +221,7 @@ RoleOption = typer.Option(
|
|
|
315
221
|
"--role",
|
|
316
222
|
"--rolename",
|
|
317
223
|
help="Role to use. Overrides the value specified for the connection.",
|
|
318
|
-
callback=_callback(lambda:
|
|
224
|
+
callback=_callback(lambda: get_cli_context_manager().connection_context.set_role),
|
|
319
225
|
show_default=False,
|
|
320
226
|
rich_help_panel=_CONNECTION_SECTION,
|
|
321
227
|
)
|
|
@@ -324,7 +230,9 @@ WarehouseOption = typer.Option(
|
|
|
324
230
|
None,
|
|
325
231
|
"--warehouse",
|
|
326
232
|
help="Warehouse to use. Overrides the value specified for the connection.",
|
|
327
|
-
callback=_callback(
|
|
233
|
+
callback=_callback(
|
|
234
|
+
lambda: get_cli_context_manager().connection_context.set_warehouse
|
|
235
|
+
),
|
|
328
236
|
show_default=False,
|
|
329
237
|
rich_help_panel=_CONNECTION_SECTION,
|
|
330
238
|
)
|
|
@@ -333,7 +241,9 @@ MfaPasscodeOption = typer.Option(
|
|
|
333
241
|
None,
|
|
334
242
|
"--mfa-passcode",
|
|
335
243
|
help="Token to use for multi-factor authentication (MFA)",
|
|
336
|
-
callback=_callback(
|
|
244
|
+
callback=_callback(
|
|
245
|
+
lambda: get_cli_context_manager().connection_context.set_mfa_passcode
|
|
246
|
+
),
|
|
337
247
|
prompt="MFA passcode",
|
|
338
248
|
prompt_required=False,
|
|
339
249
|
show_default=False,
|
|
@@ -344,18 +254,31 @@ EnableDiagOption = typer.Option(
|
|
|
344
254
|
False,
|
|
345
255
|
"--enable-diag",
|
|
346
256
|
help="Run python connector diagnostic test",
|
|
347
|
-
callback=_callback(
|
|
257
|
+
callback=_callback(
|
|
258
|
+
lambda: get_cli_context_manager().connection_context.set_enable_diag
|
|
259
|
+
),
|
|
348
260
|
show_default=False,
|
|
349
261
|
is_flag=True,
|
|
350
262
|
rich_help_panel=_CONNECTION_SECTION,
|
|
351
263
|
)
|
|
352
264
|
|
|
265
|
+
# Set default via callback to avoid including tempdir path in generated docs (snow --docs).
|
|
266
|
+
# Use constant instead of None, as None is removed from telemetry data.
|
|
267
|
+
_DIAG_LOG_DEFAULT_VALUE = "<temporary_directory>"
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def _diag_log_path_callback(path: str):
|
|
271
|
+
if path != _DIAG_LOG_DEFAULT_VALUE:
|
|
272
|
+
return path
|
|
273
|
+
return tempfile.gettempdir()
|
|
274
|
+
|
|
275
|
+
|
|
353
276
|
DiagLogPathOption: Path = typer.Option(
|
|
354
|
-
|
|
277
|
+
_DIAG_LOG_DEFAULT_VALUE,
|
|
355
278
|
"--diag-log-path",
|
|
356
279
|
help="Diagnostic report path",
|
|
357
280
|
callback=_callback(
|
|
358
|
-
lambda:
|
|
281
|
+
lambda: get_cli_context_manager().connection_context.set_diag_log_path
|
|
359
282
|
),
|
|
360
283
|
show_default=False,
|
|
361
284
|
rich_help_panel=_CONNECTION_SECTION,
|
|
@@ -368,7 +291,7 @@ DiagAllowlistPathOption: Path = typer.Option(
|
|
|
368
291
|
"--diag-allowlist-path",
|
|
369
292
|
help="Diagnostic report path to optional allowlist",
|
|
370
293
|
callback=_callback(
|
|
371
|
-
lambda:
|
|
294
|
+
lambda: get_cli_context_manager().connection_context.set_diag_allowlist_path
|
|
372
295
|
),
|
|
373
296
|
show_default=False,
|
|
374
297
|
rich_help_panel=_CONNECTION_SECTION,
|
|
@@ -381,7 +304,7 @@ OutputFormatOption = typer.Option(
|
|
|
381
304
|
"--format",
|
|
382
305
|
help="Specifies the output format.",
|
|
383
306
|
case_sensitive=False,
|
|
384
|
-
callback=_callback(lambda:
|
|
307
|
+
callback=_callback(lambda: get_cli_context_manager().set_output_format),
|
|
385
308
|
rich_help_panel=_CLI_BEHAVIOUR,
|
|
386
309
|
)
|
|
387
310
|
|
|
@@ -389,7 +312,7 @@ SilentOption = typer.Option(
|
|
|
389
312
|
False,
|
|
390
313
|
"--silent",
|
|
391
314
|
help="Turns off intermediate output to console.",
|
|
392
|
-
callback=_callback(lambda:
|
|
315
|
+
callback=_callback(lambda: get_cli_context_manager().set_silent),
|
|
393
316
|
is_flag=True,
|
|
394
317
|
rich_help_panel=_CLI_BEHAVIOUR,
|
|
395
318
|
is_eager=True,
|
|
@@ -400,7 +323,7 @@ VerboseOption = typer.Option(
|
|
|
400
323
|
"--verbose",
|
|
401
324
|
"-v",
|
|
402
325
|
help="Displays log entries for log levels `info` and higher.",
|
|
403
|
-
callback=_callback(lambda:
|
|
326
|
+
callback=_callback(lambda: get_cli_context_manager().set_verbose),
|
|
404
327
|
is_flag=True,
|
|
405
328
|
rich_help_panel=_CLI_BEHAVIOUR,
|
|
406
329
|
)
|
|
@@ -409,7 +332,7 @@ DebugOption = typer.Option(
|
|
|
409
332
|
False,
|
|
410
333
|
"--debug",
|
|
411
334
|
help="Displays log entries for log levels `debug` and higher; debug logs contains additional information.",
|
|
412
|
-
callback=_callback(lambda:
|
|
335
|
+
callback=_callback(lambda: get_cli_context_manager().set_enable_tracebacks),
|
|
413
336
|
is_flag=True,
|
|
414
337
|
rich_help_panel=_CLI_BEHAVIOUR,
|
|
415
338
|
)
|
|
@@ -450,6 +373,10 @@ OnErrorOption = typer.Option(
|
|
|
450
373
|
NoInteractiveOption = typer.Option(False, "--no-interactive", help="Disable prompting.")
|
|
451
374
|
|
|
452
375
|
|
|
376
|
+
def entity_argument(entity_type: str) -> typer.Argument:
|
|
377
|
+
return typer.Argument(None, help=f"ID of {entity_type} entity.")
|
|
378
|
+
|
|
379
|
+
|
|
453
380
|
def variables_option(description: str):
|
|
454
381
|
return typer.Option(
|
|
455
382
|
None,
|
|
@@ -508,17 +435,46 @@ def experimental_option(
|
|
|
508
435
|
"--experimental",
|
|
509
436
|
help=help_text,
|
|
510
437
|
hidden=True,
|
|
511
|
-
callback=_callback(lambda:
|
|
438
|
+
callback=_callback(lambda: get_cli_context_manager().set_experimental),
|
|
512
439
|
is_flag=True,
|
|
513
440
|
rich_help_panel=_CLI_BEHAVIOUR,
|
|
514
441
|
)
|
|
515
442
|
|
|
516
443
|
|
|
517
|
-
|
|
444
|
+
class IdentifierType(click.ParamType):
|
|
445
|
+
name = "TEXT"
|
|
446
|
+
|
|
447
|
+
def convert(self, value, param, ctx):
|
|
448
|
+
return FQN.from_string(value)
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
class IdentifierStageType(click.ParamType):
|
|
452
|
+
name = "TEXT"
|
|
453
|
+
|
|
454
|
+
def convert(self, value, param, ctx):
|
|
455
|
+
return FQN.from_stage(value)
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def identifier_argument(
|
|
459
|
+
sf_object: str,
|
|
460
|
+
example: str,
|
|
461
|
+
click_type: click.ParamType = IdentifierType(),
|
|
462
|
+
callback: Callable | None = None,
|
|
463
|
+
) -> typer.Argument:
|
|
518
464
|
return typer.Argument(
|
|
519
465
|
...,
|
|
520
466
|
help=f"Identifier of the {sf_object}. For example: {example}",
|
|
521
467
|
show_default=False,
|
|
468
|
+
click_type=click_type,
|
|
469
|
+
callback=callback,
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
def identifier_stage_argument(
|
|
474
|
+
sf_object: str, example: str, callback: Callable | None = None
|
|
475
|
+
) -> typer.Argument:
|
|
476
|
+
return identifier_argument(
|
|
477
|
+
sf_object, example, click_type=IdentifierStageType(), callback=callback
|
|
522
478
|
)
|
|
523
479
|
|
|
524
480
|
|
|
@@ -531,6 +487,7 @@ def execution_identifier_argument(sf_object: str, example: str) -> typer.Argumen
|
|
|
531
487
|
|
|
532
488
|
|
|
533
489
|
def register_project_definition(is_optional: bool) -> None:
|
|
490
|
+
cli_context_manager = get_cli_context_manager()
|
|
534
491
|
project_path = cli_context_manager.project_path_arg
|
|
535
492
|
env_overrides_args = cli_context_manager.project_env_overrides_args
|
|
536
493
|
|
|
@@ -551,7 +508,7 @@ def register_project_definition(is_optional: bool) -> None:
|
|
|
551
508
|
|
|
552
509
|
def project_definition_option(is_optional: bool):
|
|
553
510
|
def project_definition_callback(project_path: str) -> None:
|
|
554
|
-
|
|
511
|
+
get_cli_context_manager().set_project_path_arg(project_path)
|
|
555
512
|
register_pre_execute_command(lambda: register_project_definition(is_optional))
|
|
556
513
|
|
|
557
514
|
return typer.Option(
|
|
@@ -570,30 +527,17 @@ def project_env_overrides_option():
|
|
|
570
527
|
env_overrides_args_map = {
|
|
571
528
|
v.key: v.value for v in parse_key_value_variables(env_overrides_args_list)
|
|
572
529
|
}
|
|
573
|
-
|
|
530
|
+
get_cli_context_manager().set_project_env_overrides_args(env_overrides_args_map)
|
|
574
531
|
|
|
575
532
|
return typer.Option(
|
|
576
533
|
[],
|
|
577
534
|
"--env",
|
|
578
|
-
help="String in format of key=value. Overrides variables from env section used for
|
|
535
|
+
help="String in format of key=value. Overrides variables from env section used for templates.",
|
|
579
536
|
callback=_callback(lambda: project_env_overrides_callback),
|
|
580
537
|
show_default=False,
|
|
581
538
|
)
|
|
582
539
|
|
|
583
540
|
|
|
584
|
-
def readable_file_option(param_name: str, help_str: str) -> typer.Option:
|
|
585
|
-
return typer.Option(
|
|
586
|
-
None,
|
|
587
|
-
param_name,
|
|
588
|
-
exists=True,
|
|
589
|
-
file_okay=True,
|
|
590
|
-
dir_okay=False,
|
|
591
|
-
readable=True,
|
|
592
|
-
help=help_str,
|
|
593
|
-
show_default=False,
|
|
594
|
-
)
|
|
595
|
-
|
|
596
|
-
|
|
597
541
|
def deprecated_flag_callback(msg: str):
|
|
598
542
|
def _warning_callback(ctx: click.Context, param: click.Parameter, value: Any):
|
|
599
543
|
if ctx.get_parameter_source(param.name) != click.core.ParameterSource.DEFAULT: # type: ignore[attr-defined]
|
|
@@ -612,29 +556,3 @@ def deprecated_flag_callback_enum(msg: str):
|
|
|
612
556
|
return value.value
|
|
613
557
|
|
|
614
558
|
return _warning_callback
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
@dataclass
|
|
618
|
-
class Variable:
|
|
619
|
-
key: str
|
|
620
|
-
value: str
|
|
621
|
-
|
|
622
|
-
def __init__(self, key: str, value: str):
|
|
623
|
-
self.key = key
|
|
624
|
-
self.value = value
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
def parse_key_value_variables(variables: Optional[List[str]]) -> List[Variable]:
|
|
628
|
-
"""Util for parsing key=value input. Useful for commands accepting multiple input options."""
|
|
629
|
-
if not variables:
|
|
630
|
-
return []
|
|
631
|
-
result: List[Variable] = []
|
|
632
|
-
if not variables:
|
|
633
|
-
return result
|
|
634
|
-
for p in variables:
|
|
635
|
-
if "=" not in p:
|
|
636
|
-
raise ClickException(f"Invalid variable: '{p}'")
|
|
637
|
-
|
|
638
|
-
key, value = p.split("=", 1)
|
|
639
|
-
result.append(Variable(key.strip(), value.strip()))
|
|
640
|
-
return result
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Copyright (c) 2024 Snowflake Inc.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from abc import ABC, abstractmethod
|
|
18
|
+
from inspect import signature
|
|
19
|
+
from typing import Any, List, Optional, Tuple
|
|
20
|
+
|
|
21
|
+
import typer
|
|
22
|
+
from click import ClickException
|
|
23
|
+
from snowflake.cli.api.exceptions import IncompatibleParametersError
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class _OverrideableParameter(ABC):
|
|
27
|
+
"""
|
|
28
|
+
Class that allows you to generate instances of typer.models.OptionInfo with some default properties while allowing
|
|
29
|
+
specific values to be overridden.
|
|
30
|
+
|
|
31
|
+
Custom parameters:
|
|
32
|
+
- mutually_exclusive (Tuple[str]|List[str]): A list of parameter names that this Option is not compatible with. If this Option has
|
|
33
|
+
a truthy value and any of the other parameters in the mutually_exclusive list has a truthy value, a
|
|
34
|
+
ClickException will be thrown. Note that mutually_exclusive can contain an option's own name but does not require
|
|
35
|
+
it.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
default: Any = ...,
|
|
41
|
+
*param_decls: str,
|
|
42
|
+
mutually_exclusive: Optional[List[str] | Tuple[str]] = None,
|
|
43
|
+
**kwargs,
|
|
44
|
+
):
|
|
45
|
+
self.default = default
|
|
46
|
+
self.param_decls = param_decls
|
|
47
|
+
self.mutually_exclusive = mutually_exclusive
|
|
48
|
+
self.kwargs = kwargs
|
|
49
|
+
|
|
50
|
+
def __call__(self, **kwargs) -> typer.models.ParameterInfo:
|
|
51
|
+
"""
|
|
52
|
+
Returns a typer.models.OptionInfo instance initialized with the specified default values along with any overrides
|
|
53
|
+
from kwargs. Note that if you are overriding param_decls, you must pass an iterable of strings, you cannot use
|
|
54
|
+
positional arguments like you can with typer.Option. Does not modify the original instance.
|
|
55
|
+
"""
|
|
56
|
+
default = kwargs.get("default", self.default)
|
|
57
|
+
param_decls = kwargs.get("param_decls", self.param_decls)
|
|
58
|
+
mutually_exclusive = kwargs.get("mutually_exclusive", self.mutually_exclusive)
|
|
59
|
+
if not isinstance(param_decls, list) and not isinstance(param_decls, tuple):
|
|
60
|
+
raise TypeError("param_decls must be a list or tuple")
|
|
61
|
+
passed_kwargs = self.kwargs.copy()
|
|
62
|
+
passed_kwargs.update(kwargs)
|
|
63
|
+
if passed_kwargs.get("callback", None) or mutually_exclusive:
|
|
64
|
+
passed_kwargs["callback"] = self._callback_factory(
|
|
65
|
+
passed_kwargs.get("callback", None), mutually_exclusive
|
|
66
|
+
)
|
|
67
|
+
for non_kwarg in ["default", "param_decls", "mutually_exclusive"]:
|
|
68
|
+
passed_kwargs.pop(non_kwarg, None)
|
|
69
|
+
|
|
70
|
+
return self.get_parameter(default, *param_decls, **passed_kwargs)
|
|
71
|
+
|
|
72
|
+
@abstractmethod
|
|
73
|
+
def get_parameter(
|
|
74
|
+
self, default: Any = None, *param_decls: str, **kwargs
|
|
75
|
+
) -> typer.models.ParameterInfo:
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
class InvalidCallbackSignature(ClickException):
|
|
79
|
+
def __init__(self, callback):
|
|
80
|
+
super().__init__(
|
|
81
|
+
f"Signature {signature(callback)} is not valid for an OverrideableOption callback function. Must have "
|
|
82
|
+
f"at most one parameter with each of the following types: (typer.Context, typer.CallbackParam, "
|
|
83
|
+
f"Any Other Type)"
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
def _callback_factory(
|
|
87
|
+
self, callback, mutually_exclusive: Optional[List[str] | Tuple[str]]
|
|
88
|
+
):
|
|
89
|
+
callback = callback if callback else lambda x: x
|
|
90
|
+
|
|
91
|
+
# inspect existing_callback to make sure signature is valid
|
|
92
|
+
existing_params = signature(callback).parameters
|
|
93
|
+
# at most one parameter with each type in [typer.Context, typer.CallbackParam, any other type]
|
|
94
|
+
limits = [
|
|
95
|
+
lambda x: x == typer.Context,
|
|
96
|
+
lambda x: x == typer.CallbackParam,
|
|
97
|
+
lambda x: x != typer.Context and x != typer.CallbackParam,
|
|
98
|
+
]
|
|
99
|
+
for limit in limits:
|
|
100
|
+
if len([v for v in existing_params.values() if limit(v.annotation)]) > 1:
|
|
101
|
+
raise self.InvalidCallbackSignature(callback)
|
|
102
|
+
|
|
103
|
+
def generated_callback(ctx: typer.Context, param: typer.CallbackParam, value):
|
|
104
|
+
if mutually_exclusive:
|
|
105
|
+
for name in mutually_exclusive:
|
|
106
|
+
if value and ctx.params.get(
|
|
107
|
+
name, False
|
|
108
|
+
): # if the current parameter is set to True and a previous parameter is also Truthy
|
|
109
|
+
curr_opt = param.opts[0]
|
|
110
|
+
other_opt = [x for x in ctx.command.params if x.name == name][
|
|
111
|
+
0
|
|
112
|
+
].opts[0]
|
|
113
|
+
raise IncompatibleParametersError([curr_opt, other_opt])
|
|
114
|
+
|
|
115
|
+
# pass args to existing callback based on its signature (this is how Typer infers callback args)
|
|
116
|
+
passed_params = {}
|
|
117
|
+
for existing_param in existing_params:
|
|
118
|
+
annotation = existing_params[existing_param].annotation
|
|
119
|
+
if annotation == typer.Context:
|
|
120
|
+
passed_params[existing_param] = ctx
|
|
121
|
+
elif annotation == typer.CallbackParam:
|
|
122
|
+
passed_params[existing_param] = param
|
|
123
|
+
else:
|
|
124
|
+
passed_params[existing_param] = value
|
|
125
|
+
return callback(**passed_params)
|
|
126
|
+
|
|
127
|
+
return generated_callback
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class OverrideableArgument(_OverrideableParameter):
|
|
131
|
+
def get_parameter(
|
|
132
|
+
self, default: Any = ..., *param_decls: str, **kwargs
|
|
133
|
+
) -> typer.models.ArgumentInfo:
|
|
134
|
+
return typer.Argument(default, *param_decls, **kwargs)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
# OverrideableOption doesn't work with flags with type List[Any] and default None, because typer executes the callback
|
|
138
|
+
# function which converts the default value iterating over it, but None is not iterable.
|
|
139
|
+
class OverrideableOption(_OverrideableParameter):
|
|
140
|
+
def get_parameter(
|
|
141
|
+
self, default: Any = ..., *param_decls: str, **kwargs
|
|
142
|
+
) -> typer.models.OptionInfo:
|
|
143
|
+
return typer.Option(default, *param_decls, **kwargs)
|