snowflake-cli-labs 2.8.0rc0__py3-none-any.whl → 3.0.0rc0__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 +6 -6
- snowflake/cli/{app → _app}/telemetry.py +4 -5
- snowflake/cli/{plugins → _plugins}/connection/commands.py +22 -5
- 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 +6 -5
- 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 +16 -18
- snowflake/cli/{plugins → _plugins}/nativeapp/codegen/setup/native_app_setup_processor.py +24 -28
- snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/extension_function_utils.py +4 -4
- snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/python_processor.py +20 -24
- snowflake/cli/{plugins → _plugins}/nativeapp/commands.py +171 -42
- snowflake/cli/{plugins → _plugins}/nativeapp/common_flags.py +1 -1
- snowflake/cli/{plugins → _plugins}/nativeapp/init.py +1 -1
- snowflake/cli/_plugins/nativeapp/manager.py +601 -0
- snowflake/cli/{plugins/connection → _plugins/nativeapp}/plugin_spec.py +1 -1
- snowflake/cli/{plugins → _plugins}/nativeapp/project_model.py +34 -11
- snowflake/cli/{plugins → _plugins}/nativeapp/run_processor.py +25 -23
- snowflake/cli/{plugins → _plugins}/nativeapp/teardown_processor.py +8 -8
- 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 +510 -0
- snowflake/cli/_plugins/snowpark/common.py +252 -0
- snowflake/cli/{plugins → _plugins}/snowpark/models.py +0 -7
- snowflake/cli/{plugins → _plugins}/snowpark/package/anaconda_packages.py +1 -1
- 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 +19 -15
- 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 +8 -6
- snowflake/cli/{plugins → _plugins}/stage/plugin_spec.py +1 -1
- snowflake/cli/_plugins/stage/utils.py +54 -0
- snowflake/cli/_plugins/streamlit/commands.py +242 -0
- snowflake/cli/{plugins → _plugins}/streamlit/manager.py +47 -70
- snowflake/cli/_plugins/streamlit/plugin_spec.py +30 -0
- snowflake/cli/_plugins/workspace/action_context.py +11 -0
- snowflake/cli/_plugins/workspace/commands.py +113 -0
- snowflake/cli/_plugins/workspace/manager.py +57 -0
- snowflake/cli/{plugins → _plugins}/workspace/plugin_spec.py +1 -1
- snowflake/cli/api/cli_global_context.py +34 -7
- snowflake/cli/api/commands/common.py +25 -0
- snowflake/cli/api/commands/decorators.py +4 -3
- snowflake/cli/api/commands/experimental_behaviour.py +2 -3
- snowflake/cli/api/commands/flags.py +73 -174
- snowflake/cli/api/commands/overrideable_parameter.py +143 -0
- snowflake/cli/api/commands/snow_typer.py +5 -4
- snowflake/cli/api/commands/typer_pre_execute.py +3 -3
- snowflake/cli/api/commands/utils.py +18 -0
- snowflake/cli/api/config.py +1 -1
- snowflake/cli/api/console/abc.py +5 -2
- snowflake/cli/api/entities/application_entity.py +12 -0
- snowflake/cli/api/entities/application_package_entity.py +260 -0
- snowflake/cli/api/entities/common.py +47 -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 +321 -0
- snowflake/cli/api/exceptions.py +19 -3
- snowflake/cli/api/feature_flags.py +2 -1
- snowflake/cli/api/identifiers.py +41 -9
- snowflake/cli/api/project/definition.py +13 -5
- snowflake/cli/api/project/definition_manager.py +12 -1
- snowflake/cli/api/project/errors.py +16 -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} +26 -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 +97 -23
- snowflake/cli/api/project/schemas/updatable_model.py +11 -3
- snowflake/cli/api/project/util.py +23 -6
- snowflake/cli/api/rendering/jinja.py +28 -8
- snowflake/cli/api/rendering/sql_templates.py +41 -12
- snowflake/cli/api/secure_path.py +3 -0
- snowflake/cli/api/sql_execution.py +35 -19
- snowflake/cli/api/utils/definition_rendering.py +14 -2
- {snowflake_cli_labs-2.8.0rc0.dist-info → snowflake_cli_labs-3.0.0rc0.dist-info}/METADATA +12 -12
- snowflake_cli_labs-3.0.0rc0.dist-info/RECORD +234 -0
- snowflake_cli_labs-3.0.0rc0.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/manager.py +0 -819
- 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/commands.py +0 -186
- 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.0rc0.dist-info/RECORD +0 -240
- snowflake_cli_labs-2.8.0rc0.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/setup/setup_driver.py.source +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/exceptions.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.0rc0.dist-info → snowflake_cli_labs-3.0.0rc0.dist-info}/WHEEL +0 -0
- {snowflake_cli_labs-2.8.0rc0.dist-info → snowflake_cli_labs-3.0.0rc0.dist-info}/licenses/LICENSE +0 -0
|
@@ -14,40 +14,38 @@
|
|
|
14
14
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
|
-
from pathlib import Path
|
|
18
17
|
from typing import List, Literal, Optional, Union
|
|
19
18
|
|
|
20
|
-
from pydantic import Field
|
|
19
|
+
from pydantic import Field, field_validator
|
|
21
20
|
from snowflake.cli.api.project.schemas.entities.common import (
|
|
22
|
-
|
|
21
|
+
EntityModelBase,
|
|
23
22
|
)
|
|
23
|
+
from snowflake.cli.api.project.schemas.identifier_model import Identifier
|
|
24
24
|
from snowflake.cli.api.project.schemas.native_app.package import DistributionOptions
|
|
25
25
|
from snowflake.cli.api.project.schemas.native_app.path_mapping import PathMapping
|
|
26
26
|
from snowflake.cli.api.project.schemas.updatable_model import (
|
|
27
27
|
DiscriminatorField,
|
|
28
28
|
IdentifierField,
|
|
29
29
|
)
|
|
30
|
+
from snowflake.cli.api.project.util import append_test_resource_suffix
|
|
30
31
|
|
|
31
32
|
|
|
32
|
-
class
|
|
33
|
+
class ApplicationPackageEntityModel(EntityModelBase):
|
|
33
34
|
type: Literal["application package"] = DiscriminatorField() # noqa: A003
|
|
34
|
-
|
|
35
|
-
title="Name of the application package created when this entity is deployed"
|
|
36
|
-
)
|
|
37
|
-
artifacts: List[Union[PathMapping, Path]] = Field(
|
|
35
|
+
artifacts: List[Union[PathMapping, str]] = Field(
|
|
38
36
|
title="List of paths or file source/destination pairs to add to the deploy root",
|
|
39
37
|
)
|
|
40
|
-
bundle_root: Optional[
|
|
38
|
+
bundle_root: Optional[str] = Field(
|
|
41
39
|
title="Folder at the root of your project where artifacts necessary to perform the bundle step are stored.",
|
|
42
|
-
default=
|
|
40
|
+
default="output/bundle/",
|
|
43
41
|
)
|
|
44
|
-
deploy_root: Optional[
|
|
42
|
+
deploy_root: Optional[str] = Field(
|
|
45
43
|
title="Folder at the root of your project where the build step copies the artifacts",
|
|
46
|
-
default=
|
|
44
|
+
default="output/deploy/",
|
|
47
45
|
)
|
|
48
|
-
generated_root: Optional[
|
|
46
|
+
generated_root: Optional[str] = Field(
|
|
49
47
|
title="Subdirectory of the deploy root where files generated by the Snowflake CLI will be written.",
|
|
50
|
-
default=
|
|
48
|
+
default="__generated/",
|
|
51
49
|
)
|
|
52
50
|
stage: Optional[str] = IdentifierField(
|
|
53
51
|
title="Identifier of the stage that stores the application artifacts.",
|
|
@@ -61,6 +59,19 @@ class ApplicationPackageEntity(EntityBase):
|
|
|
61
59
|
title="Distribution of the application package created by the Snowflake CLI",
|
|
62
60
|
default="internal",
|
|
63
61
|
)
|
|
64
|
-
manifest:
|
|
62
|
+
manifest: str = Field(
|
|
65
63
|
title="Path to manifest.yml",
|
|
66
64
|
)
|
|
65
|
+
|
|
66
|
+
@field_validator("identifier")
|
|
67
|
+
@classmethod
|
|
68
|
+
def append_test_resource_suffix_to_identifier(
|
|
69
|
+
cls, input_value: Identifier | str
|
|
70
|
+
) -> Identifier | str:
|
|
71
|
+
identifier = (
|
|
72
|
+
input_value.name if isinstance(input_value, Identifier) else input_value
|
|
73
|
+
)
|
|
74
|
+
with_suffix = append_test_resource_suffix(identifier)
|
|
75
|
+
if isinstance(input_value, Identifier):
|
|
76
|
+
return input_value.model_copy(update=dict(name=with_suffix))
|
|
77
|
+
return with_suffix
|
|
@@ -15,18 +15,25 @@
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
17
|
from abc import ABC
|
|
18
|
-
from typing import Generic, List, Optional, TypeVar
|
|
18
|
+
from typing import Generic, List, Optional, TypeVar, Union
|
|
19
19
|
|
|
20
|
-
from pydantic import Field
|
|
21
|
-
from snowflake.cli.api.
|
|
22
|
-
|
|
23
|
-
)
|
|
20
|
+
from pydantic import Field, PrivateAttr, field_validator
|
|
21
|
+
from snowflake.cli.api.identifiers import FQN
|
|
22
|
+
from snowflake.cli.api.project.schemas.identifier_model import Identifier
|
|
24
23
|
from snowflake.cli.api.project.schemas.updatable_model import (
|
|
25
24
|
IdentifierField,
|
|
26
25
|
UpdatableModel,
|
|
27
26
|
)
|
|
28
27
|
|
|
29
28
|
|
|
29
|
+
class SqlScriptHookType(UpdatableModel):
|
|
30
|
+
sql_script: str = Field(title="SQL file path relative to the project root")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# Currently sql_script is the only supported hook type. Change to a Union once other hook types are added
|
|
34
|
+
PostDeployHook = SqlScriptHookType
|
|
35
|
+
|
|
36
|
+
|
|
30
37
|
class MetaField(UpdatableModel):
|
|
31
38
|
warehouse: Optional[str] = IdentifierField(
|
|
32
39
|
title="Warehouse used to run the scripts", default=None
|
|
@@ -39,6 +46,19 @@ class MetaField(UpdatableModel):
|
|
|
39
46
|
title="Actions that will be executed after the application object is created/upgraded",
|
|
40
47
|
default=None,
|
|
41
48
|
)
|
|
49
|
+
use_mixins: Optional[List[str]] = Field(
|
|
50
|
+
title="Name of the mixin used to fill the entity fields",
|
|
51
|
+
default=None,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
@field_validator("use_mixins", mode="before")
|
|
55
|
+
@classmethod
|
|
56
|
+
def ensure_use_mixins_is_a_list(
|
|
57
|
+
cls, mixins: Optional[str | List[str]]
|
|
58
|
+
) -> Optional[List[str]]:
|
|
59
|
+
if isinstance(mixins, str):
|
|
60
|
+
return [mixins]
|
|
61
|
+
return mixins
|
|
42
62
|
|
|
43
63
|
|
|
44
64
|
class DefaultsField(UpdatableModel):
|
|
@@ -53,12 +73,38 @@ class DefaultsField(UpdatableModel):
|
|
|
53
73
|
)
|
|
54
74
|
|
|
55
75
|
|
|
56
|
-
class
|
|
76
|
+
class EntityModelBase(ABC, UpdatableModel):
|
|
57
77
|
@classmethod
|
|
58
78
|
def get_type(cls) -> str:
|
|
59
79
|
return cls.model_fields["type"].annotation.__args__[0]
|
|
60
80
|
|
|
61
81
|
meta: Optional[MetaField] = Field(title="Meta fields", default=None)
|
|
82
|
+
identifier: Optional[Union[Identifier | str]] = Field(
|
|
83
|
+
title="Entity identifier", default=None
|
|
84
|
+
)
|
|
85
|
+
# Set by parent model in post validation. To reference it use `entity_id`.
|
|
86
|
+
_entity_id: str = PrivateAttr(default=None)
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def entity_id(self):
|
|
90
|
+
return self._entity_id
|
|
91
|
+
|
|
92
|
+
def set_entity_id(self, value: str):
|
|
93
|
+
self._entity_id = value
|
|
94
|
+
|
|
95
|
+
def validate_identifier(self):
|
|
96
|
+
"""Helper that's used by ProjectDefinition validator."""
|
|
97
|
+
if not self._entity_id and not self.identifier:
|
|
98
|
+
raise ValueError("Missing entity identifier")
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def fqn(self) -> FQN:
|
|
102
|
+
if isinstance(self.identifier, str):
|
|
103
|
+
return FQN.from_string(self.identifier)
|
|
104
|
+
if isinstance(self.identifier, Identifier):
|
|
105
|
+
return FQN.from_identifier_model_v2(self.identifier)
|
|
106
|
+
if self.entity_id:
|
|
107
|
+
return FQN.from_string(self.entity_id)
|
|
62
108
|
|
|
63
109
|
|
|
64
110
|
TargetType = TypeVar("TargetType")
|
|
@@ -76,3 +122,31 @@ class TargetField(UpdatableModel, Generic[TargetType]):
|
|
|
76
122
|
them in __pydantic_generic_metadata__
|
|
77
123
|
"""
|
|
78
124
|
return self.__pydantic_generic_metadata__["args"][0]
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
from typing import Dict, List, Optional
|
|
128
|
+
|
|
129
|
+
from pydantic import Field
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class ExternalAccessBaseModel:
|
|
133
|
+
external_access_integrations: Optional[List[str]] = Field(
|
|
134
|
+
title="Names of external access integrations needed for this entity to access external networks",
|
|
135
|
+
default=[],
|
|
136
|
+
)
|
|
137
|
+
secrets: Optional[Dict[str, str]] = Field(
|
|
138
|
+
title="Assigns the names of secrets to variables so that you can use the variables to reference the secrets",
|
|
139
|
+
default={},
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
def get_external_access_integrations_sql(self) -> str | None:
|
|
143
|
+
if not self.external_access_integrations:
|
|
144
|
+
return None
|
|
145
|
+
external_access_integration_name = ", ".join(self.external_access_integrations)
|
|
146
|
+
return f"external_access_integrations=({external_access_integration_name})"
|
|
147
|
+
|
|
148
|
+
def get_secrets_sql(self) -> str | None:
|
|
149
|
+
if not self.secrets:
|
|
150
|
+
return None
|
|
151
|
+
secrets = ", ".join(f"'{key}'={value}" for key, value in self.secrets.items())
|
|
152
|
+
return f"secrets=({secrets})"
|
|
@@ -14,17 +14,47 @@
|
|
|
14
14
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
|
-
from typing import Union, get_args
|
|
17
|
+
from typing import Dict, List, Union, get_args
|
|
18
18
|
|
|
19
|
-
from snowflake.cli.api.
|
|
20
|
-
|
|
21
|
-
)
|
|
22
|
-
from snowflake.cli.api.project.schemas.entities.application_package_entity import (
|
|
19
|
+
from snowflake.cli.api.entities.application_entity import ApplicationEntity
|
|
20
|
+
from snowflake.cli.api.entities.application_package_entity import (
|
|
23
21
|
ApplicationPackageEntity,
|
|
24
22
|
)
|
|
23
|
+
from snowflake.cli.api.entities.snowpark_entity import FunctionEntity, ProcedureEntity
|
|
24
|
+
from snowflake.cli.api.entities.streamlit_entity import StreamlitEntity
|
|
25
|
+
from snowflake.cli.api.project.schemas.entities.application_entity_model import (
|
|
26
|
+
ApplicationEntityModel,
|
|
27
|
+
)
|
|
28
|
+
from snowflake.cli.api.project.schemas.entities.application_package_entity_model import (
|
|
29
|
+
ApplicationPackageEntityModel,
|
|
30
|
+
)
|
|
31
|
+
from snowflake.cli.api.project.schemas.entities.snowpark_entity import (
|
|
32
|
+
FunctionEntityModel,
|
|
33
|
+
ProcedureEntityModel,
|
|
34
|
+
)
|
|
35
|
+
from snowflake.cli.api.project.schemas.entities.streamlit_entity_model import (
|
|
36
|
+
StreamlitEntityModel,
|
|
37
|
+
)
|
|
25
38
|
|
|
26
|
-
Entity = Union[
|
|
39
|
+
Entity = Union[
|
|
40
|
+
ApplicationEntity,
|
|
41
|
+
ApplicationPackageEntity,
|
|
42
|
+
StreamlitEntity,
|
|
43
|
+
ProcedureEntity,
|
|
44
|
+
FunctionEntity,
|
|
45
|
+
]
|
|
46
|
+
EntityModel = Union[
|
|
47
|
+
ApplicationEntityModel,
|
|
48
|
+
ApplicationPackageEntityModel,
|
|
49
|
+
StreamlitEntityModel,
|
|
50
|
+
FunctionEntityModel,
|
|
51
|
+
ProcedureEntityModel,
|
|
52
|
+
]
|
|
27
53
|
|
|
28
|
-
ALL_ENTITIES = [*get_args(Entity)]
|
|
54
|
+
ALL_ENTITIES: List[Entity] = [*get_args(Entity)]
|
|
55
|
+
ALL_ENTITY_MODELS: List[EntityModel] = [*get_args(EntityModel)]
|
|
29
56
|
|
|
30
|
-
|
|
57
|
+
v2_entity_model_types_map = {e.get_type(): e for e in ALL_ENTITY_MODELS}
|
|
58
|
+
v2_entity_model_to_entity_map: Dict[EntityModel, Entity] = {
|
|
59
|
+
e.get_entity_model_type(): e for e in ALL_ENTITIES
|
|
60
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
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 pathlib import Path
|
|
18
|
+
from typing import List, Literal, Optional, Union
|
|
19
|
+
|
|
20
|
+
from pydantic import Field, field_validator
|
|
21
|
+
from snowflake.cli.api.identifiers import FQN
|
|
22
|
+
from snowflake.cli.api.project.schemas.entities.common import (
|
|
23
|
+
EntityModelBase,
|
|
24
|
+
ExternalAccessBaseModel,
|
|
25
|
+
)
|
|
26
|
+
from snowflake.cli.api.project.schemas.snowpark.argument import Argument
|
|
27
|
+
from snowflake.cli.api.project.schemas.updatable_model import (
|
|
28
|
+
DiscriminatorField,
|
|
29
|
+
UpdatableModel,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class PathMapping(UpdatableModel):
|
|
34
|
+
class Config:
|
|
35
|
+
frozen = True
|
|
36
|
+
|
|
37
|
+
src: Path = Field(title="Source path (relative to project root)", default=None)
|
|
38
|
+
|
|
39
|
+
dest: Optional[str] = Field(
|
|
40
|
+
title="Destination path on stage",
|
|
41
|
+
description="Paths are relative to stage root; paths ending with a slash indicate that the destination is a directory which source files should be copied into.",
|
|
42
|
+
default=None,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class SnowparkEntityModel(EntityModelBase, ExternalAccessBaseModel):
|
|
47
|
+
handler: str = Field(
|
|
48
|
+
title="Function’s or procedure’s implementation of the object inside source module",
|
|
49
|
+
examples=["functions.hello_function"],
|
|
50
|
+
)
|
|
51
|
+
returns: str = Field(
|
|
52
|
+
title="Type of the result"
|
|
53
|
+
) # TODO: again, consider Literal/Enum
|
|
54
|
+
signature: Union[str, List[Argument]] = Field(
|
|
55
|
+
title="The signature parameter describes consecutive arguments passed to the object"
|
|
56
|
+
)
|
|
57
|
+
runtime: Optional[Union[str, float]] = Field(
|
|
58
|
+
title="Python version to use when executing ", default=None
|
|
59
|
+
)
|
|
60
|
+
imports: Optional[List[str]] = Field(
|
|
61
|
+
title="Stage and path to previously uploaded files you want to import",
|
|
62
|
+
default=[],
|
|
63
|
+
)
|
|
64
|
+
stage: str = Field(title="Stage in which artifacts will be stored")
|
|
65
|
+
artifacts: List[Union[PathMapping, str]] = Field(title="List of required sources")
|
|
66
|
+
|
|
67
|
+
@field_validator("artifacts")
|
|
68
|
+
@classmethod
|
|
69
|
+
def _convert_artifacts(cls, artifacts: Union[dict, str]):
|
|
70
|
+
_artifacts = []
|
|
71
|
+
for artefact in artifacts:
|
|
72
|
+
if isinstance(artefact, PathMapping):
|
|
73
|
+
_artifacts.append(artefact)
|
|
74
|
+
else:
|
|
75
|
+
_artifacts.append(PathMapping(src=artefact))
|
|
76
|
+
return _artifacts
|
|
77
|
+
|
|
78
|
+
@field_validator("runtime")
|
|
79
|
+
@classmethod
|
|
80
|
+
def convert_runtime(cls, runtime_input: Union[str, float]) -> str:
|
|
81
|
+
if isinstance(runtime_input, float):
|
|
82
|
+
return str(runtime_input)
|
|
83
|
+
return runtime_input
|
|
84
|
+
|
|
85
|
+
@field_validator("artifacts")
|
|
86
|
+
@classmethod
|
|
87
|
+
def validate_artifacts(cls, artifacts: List[Path]) -> List[Path]:
|
|
88
|
+
for artefact in artifacts:
|
|
89
|
+
if "*" in str(artefact):
|
|
90
|
+
raise ValueError("Glob patterns not supported for Snowpark artifacts.")
|
|
91
|
+
return artifacts
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def udf_sproc_identifier(self) -> UdfSprocIdentifier:
|
|
95
|
+
return UdfSprocIdentifier.from_definition(self)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class ProcedureEntityModel(SnowparkEntityModel):
|
|
99
|
+
type: Literal["procedure"] = DiscriminatorField() # noqa: A003
|
|
100
|
+
execute_as_caller: Optional[bool] = Field(
|
|
101
|
+
title="Determine whether the procedure is executed with the privileges of "
|
|
102
|
+
"the owner (you) or with the privileges of the caller",
|
|
103
|
+
default=False,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class FunctionEntityModel(SnowparkEntityModel):
|
|
108
|
+
type: Literal["function"] = DiscriminatorField() # noqa: A003
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class UdfSprocIdentifier:
|
|
112
|
+
def __init__(self, identifier: FQN, arg_names, arg_types, arg_defaults):
|
|
113
|
+
self._identifier = identifier
|
|
114
|
+
self._arg_names = arg_names
|
|
115
|
+
self._arg_types = arg_types
|
|
116
|
+
self._arg_defaults = arg_defaults
|
|
117
|
+
|
|
118
|
+
def _identifier_from_signature(self, sig: List[str], for_sql: bool = False):
|
|
119
|
+
signature = self._comma_join(sig)
|
|
120
|
+
id_ = self._identifier.sql_identifier if for_sql else self._identifier
|
|
121
|
+
return f"{id_}({signature})"
|
|
122
|
+
|
|
123
|
+
@staticmethod
|
|
124
|
+
def _comma_join(*args):
|
|
125
|
+
return ", ".join(*args)
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def identifier_with_arg_names(self):
|
|
129
|
+
return self._identifier_from_signature(self._arg_names)
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def identifier_with_arg_types(self):
|
|
133
|
+
return self._identifier_from_signature(self._arg_types)
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def identifier_with_arg_names_types(self):
|
|
137
|
+
sig = [f"{n} {t}" for n, t in zip(self._arg_names, self._arg_types)]
|
|
138
|
+
return self._identifier_from_signature(sig)
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
def identifier_with_arg_names_types_defaults(self):
|
|
142
|
+
return self._identifier_from_signature(self._full_signature())
|
|
143
|
+
|
|
144
|
+
def _is_signature_type_a_string(self, sig_type: str) -> bool:
|
|
145
|
+
return sig_type.lower() in ["string", "varchar"]
|
|
146
|
+
|
|
147
|
+
def _full_signature(self):
|
|
148
|
+
sig = []
|
|
149
|
+
for name, _type, _default in zip(
|
|
150
|
+
self._arg_names, self._arg_types, self._arg_defaults
|
|
151
|
+
):
|
|
152
|
+
s = f"{name} {_type}"
|
|
153
|
+
if _default:
|
|
154
|
+
if self._is_signature_type_a_string(_type):
|
|
155
|
+
_default = f"'{_default}'"
|
|
156
|
+
s += f" default {_default}"
|
|
157
|
+
sig.append(s)
|
|
158
|
+
return sig
|
|
159
|
+
|
|
160
|
+
@property
|
|
161
|
+
def identifier_for_sql(self):
|
|
162
|
+
return self._identifier_from_signature(self._full_signature(), for_sql=True)
|
|
163
|
+
|
|
164
|
+
@classmethod
|
|
165
|
+
def from_definition(cls, udf_sproc: SnowparkEntityModel):
|
|
166
|
+
names = []
|
|
167
|
+
types = []
|
|
168
|
+
defaults = []
|
|
169
|
+
if udf_sproc.signature and udf_sproc.signature != "null":
|
|
170
|
+
for arg in udf_sproc.signature:
|
|
171
|
+
names.append(arg.name) # type:ignore
|
|
172
|
+
types.append(arg.arg_type) # type:ignore
|
|
173
|
+
defaults.append(arg.default) # type:ignore
|
|
174
|
+
|
|
175
|
+
identifier = udf_sproc.fqn.using_context()
|
|
176
|
+
return cls(identifier, names, types, defaults)
|
|
@@ -0,0 +1,73 @@
|
|
|
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
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import List, Literal, Optional
|
|
18
|
+
|
|
19
|
+
from pydantic import Field, model_validator
|
|
20
|
+
from snowflake.cli.api.project.schemas.entities.common import (
|
|
21
|
+
EntityModelBase,
|
|
22
|
+
ExternalAccessBaseModel,
|
|
23
|
+
)
|
|
24
|
+
from snowflake.cli.api.project.schemas.updatable_model import (
|
|
25
|
+
DiscriminatorField,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class StreamlitEntityModel(EntityModelBase, ExternalAccessBaseModel):
|
|
30
|
+
type: Literal["streamlit"] = DiscriminatorField() # noqa: A003
|
|
31
|
+
title: Optional[str] = Field(
|
|
32
|
+
title="Human-readable title for the Streamlit dashboard", default=None
|
|
33
|
+
)
|
|
34
|
+
query_warehouse: str = Field(
|
|
35
|
+
title="Snowflake warehouse to host the app", default=None
|
|
36
|
+
)
|
|
37
|
+
main_file: Optional[str] = Field(
|
|
38
|
+
title="Entrypoint file of the Streamlit app", default="streamlit_app.py"
|
|
39
|
+
)
|
|
40
|
+
pages_dir: Optional[str] = Field(title="Streamlit pages", default=None)
|
|
41
|
+
stage: Optional[str] = Field(
|
|
42
|
+
title="Stage in which the app’s artifacts will be stored", default="streamlit"
|
|
43
|
+
)
|
|
44
|
+
# Possibly can be PathMapping
|
|
45
|
+
artifacts: Optional[List[Path]] = Field(
|
|
46
|
+
title="List of files which should be deployed. Each file needs to exist locally. "
|
|
47
|
+
"Main file needs to be included in the artifacts.",
|
|
48
|
+
default=None,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
@model_validator(mode="after")
|
|
52
|
+
def main_file_must_be_in_artifacts(self):
|
|
53
|
+
if not self.artifacts:
|
|
54
|
+
return self
|
|
55
|
+
|
|
56
|
+
if Path(self.main_file) not in self.artifacts:
|
|
57
|
+
raise ValueError(
|
|
58
|
+
f"Specified main file {self.main_file} is not included in artifacts."
|
|
59
|
+
)
|
|
60
|
+
return self
|
|
61
|
+
|
|
62
|
+
@model_validator(mode="after")
|
|
63
|
+
def artifacts_must_exists(self):
|
|
64
|
+
if not self.artifacts:
|
|
65
|
+
return self
|
|
66
|
+
|
|
67
|
+
for artifact in self.artifacts:
|
|
68
|
+
if not artifact.exists():
|
|
69
|
+
raise ValueError(
|
|
70
|
+
f"Specified artifact {artifact} does not exist locally."
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
return self
|
|
@@ -17,7 +17,16 @@ from __future__ import annotations
|
|
|
17
17
|
from typing import Optional, cast
|
|
18
18
|
|
|
19
19
|
from pydantic import Field
|
|
20
|
-
from snowflake.cli.api.project.schemas.updatable_model import
|
|
20
|
+
from snowflake.cli.api.project.schemas.updatable_model import (
|
|
21
|
+
IdentifierField,
|
|
22
|
+
UpdatableModel,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Identifier(UpdatableModel):
|
|
27
|
+
name: str = Field(title="Entity name")
|
|
28
|
+
schema_: str = Field(title="Entity schema", alias="schema", default=None)
|
|
29
|
+
database: str = Field(title="Entity database", default=None)
|
|
21
30
|
|
|
22
31
|
|
|
23
32
|
class ObjectIdentifierBaseModel:
|
|
@@ -16,19 +16,13 @@ from __future__ import annotations
|
|
|
16
16
|
|
|
17
17
|
from typing import List, Optional
|
|
18
18
|
|
|
19
|
-
from pydantic import Field
|
|
19
|
+
from pydantic import Field, field_validator
|
|
20
|
+
from snowflake.cli.api.project.schemas.entities.common import PostDeployHook
|
|
20
21
|
from snowflake.cli.api.project.schemas.updatable_model import (
|
|
21
22
|
IdentifierField,
|
|
22
23
|
UpdatableModel,
|
|
23
24
|
)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class SqlScriptHookType(UpdatableModel):
|
|
27
|
-
sql_script: str = Field(title="SQL file path relative to the project root")
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
# Currently sql_script is the only supported hook type. Change to a Union once other hook types are added
|
|
31
|
-
PostDeployHook = SqlScriptHookType
|
|
25
|
+
from snowflake.cli.api.project.util import append_test_resource_suffix
|
|
32
26
|
|
|
33
27
|
|
|
34
28
|
class Application(UpdatableModel):
|
|
@@ -53,6 +47,11 @@ class Application(UpdatableModel):
|
|
|
53
47
|
default=None,
|
|
54
48
|
)
|
|
55
49
|
|
|
50
|
+
@field_validator("name")
|
|
51
|
+
@classmethod
|
|
52
|
+
def append_test_resource_suffix_to_name(cls, input_value: str) -> str:
|
|
53
|
+
return append_test_resource_suffix(input_value)
|
|
54
|
+
|
|
56
55
|
|
|
57
56
|
class ApplicationV11(Application):
|
|
58
57
|
# Templated defaults only supported in v1.1+
|
|
@@ -17,11 +17,12 @@ from __future__ import annotations
|
|
|
17
17
|
from typing import List, Literal, Optional
|
|
18
18
|
|
|
19
19
|
from pydantic import Field, field_validator, model_validator
|
|
20
|
-
from snowflake.cli.api.project.schemas.
|
|
20
|
+
from snowflake.cli.api.project.schemas.entities.common import PostDeployHook
|
|
21
21
|
from snowflake.cli.api.project.schemas.updatable_model import (
|
|
22
22
|
IdentifierField,
|
|
23
23
|
UpdatableModel,
|
|
24
24
|
)
|
|
25
|
+
from snowflake.cli.api.project.util import append_test_resource_suffix
|
|
25
26
|
|
|
26
27
|
DistributionOptions = Literal["internal", "external", "INTERNAL", "EXTERNAL"]
|
|
27
28
|
|
|
@@ -50,6 +51,11 @@ class Package(UpdatableModel):
|
|
|
50
51
|
default=None,
|
|
51
52
|
)
|
|
52
53
|
|
|
54
|
+
@field_validator("name")
|
|
55
|
+
@classmethod
|
|
56
|
+
def append_test_resource_suffix_to_name(cls, input_value: str) -> str:
|
|
57
|
+
return append_test_resource_suffix(input_value)
|
|
58
|
+
|
|
53
59
|
@field_validator("scripts")
|
|
54
60
|
@classmethod
|
|
55
61
|
def validate_scripts(cls, input_list):
|