snowflake-cli-labs 2.6.1__py3-none-any.whl → 2.7.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.
Files changed (84) hide show
  1. snowflake/cli/__about__.py +1 -1
  2. snowflake/cli/api/cli_global_context.py +9 -0
  3. snowflake/cli/api/commands/decorators.py +9 -4
  4. snowflake/cli/api/commands/execution_metadata.py +40 -0
  5. snowflake/cli/api/commands/flags.py +45 -36
  6. snowflake/cli/api/commands/project_initialisation.py +5 -2
  7. snowflake/cli/api/commands/snow_typer.py +20 -9
  8. snowflake/cli/api/errno.py +27 -0
  9. snowflake/cli/api/feature_flags.py +1 -0
  10. snowflake/cli/api/identifiers.py +20 -3
  11. snowflake/cli/api/output/types.py +9 -0
  12. snowflake/cli/api/project/definition_manager.py +2 -2
  13. snowflake/cli/api/project/project_verification.py +23 -0
  14. snowflake/cli/api/project/schemas/entities/application_entity.py +50 -0
  15. snowflake/cli/api/project/schemas/entities/application_package_entity.py +63 -0
  16. snowflake/cli/api/project/schemas/entities/common.py +85 -0
  17. snowflake/cli/api/project/schemas/entities/entities.py +30 -0
  18. snowflake/cli/api/project/schemas/project_definition.py +114 -22
  19. snowflake/cli/api/project/schemas/streamlit/streamlit.py +5 -4
  20. snowflake/cli/api/project/schemas/template.py +77 -0
  21. snowflake/cli/{plugins/nativeapp/errno.py → api/rendering/__init__.py} +0 -2
  22. snowflake/cli/api/{utils/rendering.py → rendering/jinja.py} +3 -48
  23. snowflake/cli/api/rendering/project_definition_templates.py +39 -0
  24. snowflake/cli/api/rendering/project_templates.py +97 -0
  25. snowflake/cli/api/rendering/sql_templates.py +56 -0
  26. snowflake/cli/api/sql_execution.py +40 -1
  27. snowflake/cli/api/utils/definition_rendering.py +8 -5
  28. snowflake/cli/app/commands_registration/builtin_plugins.py +4 -0
  29. snowflake/cli/app/dev/docs/project_definition_docs_generator.py +2 -2
  30. snowflake/cli/app/loggers.py +3 -1
  31. snowflake/cli/app/printing.py +17 -7
  32. snowflake/cli/app/snow_connector.py +9 -1
  33. snowflake/cli/app/telemetry.py +41 -2
  34. snowflake/cli/plugins/connection/commands.py +4 -3
  35. snowflake/cli/plugins/connection/util.py +73 -18
  36. snowflake/cli/plugins/cortex/commands.py +2 -1
  37. snowflake/cli/plugins/git/commands.py +20 -4
  38. snowflake/cli/plugins/git/manager.py +44 -20
  39. snowflake/cli/plugins/init/__init__.py +13 -0
  40. snowflake/cli/plugins/init/commands.py +242 -0
  41. snowflake/cli/plugins/init/plugin_spec.py +30 -0
  42. snowflake/cli/plugins/nativeapp/codegen/artifact_processor.py +40 -0
  43. snowflake/cli/plugins/nativeapp/codegen/compiler.py +57 -27
  44. snowflake/cli/plugins/nativeapp/codegen/sandbox.py +99 -10
  45. snowflake/cli/plugins/nativeapp/codegen/setup/native_app_setup_processor.py +172 -0
  46. snowflake/cli/plugins/nativeapp/codegen/setup/setup_driver.py.source +56 -0
  47. snowflake/cli/plugins/nativeapp/codegen/snowpark/python_processor.py +21 -21
  48. snowflake/cli/plugins/nativeapp/commands.py +69 -6
  49. snowflake/cli/plugins/nativeapp/constants.py +0 -6
  50. snowflake/cli/plugins/nativeapp/exceptions.py +37 -12
  51. snowflake/cli/plugins/nativeapp/init.py +1 -1
  52. snowflake/cli/plugins/nativeapp/manager.py +114 -39
  53. snowflake/cli/plugins/nativeapp/project_model.py +8 -4
  54. snowflake/cli/plugins/nativeapp/run_processor.py +117 -102
  55. snowflake/cli/plugins/nativeapp/teardown_processor.py +7 -2
  56. snowflake/cli/plugins/nativeapp/v2_conversions/v2_to_v1_decorator.py +146 -0
  57. snowflake/cli/plugins/nativeapp/version/commands.py +19 -3
  58. snowflake/cli/plugins/nativeapp/version/version_processor.py +11 -3
  59. snowflake/cli/plugins/snowpark/commands.py +34 -26
  60. snowflake/cli/plugins/snowpark/common.py +88 -27
  61. snowflake/cli/plugins/snowpark/manager.py +16 -5
  62. snowflake/cli/plugins/snowpark/models.py +6 -0
  63. snowflake/cli/plugins/sql/commands.py +3 -5
  64. snowflake/cli/plugins/sql/manager.py +1 -1
  65. snowflake/cli/plugins/stage/commands.py +2 -2
  66. snowflake/cli/plugins/stage/diff.py +4 -2
  67. snowflake/cli/plugins/stage/manager.py +290 -86
  68. snowflake/cli/plugins/streamlit/commands.py +20 -6
  69. snowflake/cli/plugins/streamlit/manager.py +29 -27
  70. snowflake/cli/plugins/workspace/__init__.py +13 -0
  71. snowflake/cli/plugins/workspace/commands.py +35 -0
  72. snowflake/cli/plugins/workspace/plugin_spec.py +30 -0
  73. snowflake/cli/templates/default_snowpark/app/__init__.py +0 -13
  74. snowflake/cli/templates/default_snowpark/app/common.py +0 -15
  75. snowflake/cli/templates/default_snowpark/app/functions.py +0 -14
  76. snowflake/cli/templates/default_snowpark/app/procedures.py +0 -14
  77. snowflake/cli/templates/default_streamlit/common/hello.py +0 -15
  78. snowflake/cli/templates/default_streamlit/pages/my_page.py +0 -14
  79. snowflake/cli/templates/default_streamlit/streamlit_app.py +0 -14
  80. {snowflake_cli_labs-2.6.1.dist-info → snowflake_cli_labs-2.7.0rc1.dist-info}/METADATA +7 -6
  81. {snowflake_cli_labs-2.6.1.dist-info → snowflake_cli_labs-2.7.0rc1.dist-info}/RECORD +84 -64
  82. {snowflake_cli_labs-2.6.1.dist-info → snowflake_cli_labs-2.7.0rc1.dist-info}/WHEEL +0 -0
  83. {snowflake_cli_labs-2.6.1.dist-info → snowflake_cli_labs-2.7.0rc1.dist-info}/entry_points.txt +0 -0
  84. {snowflake_cli_labs-2.6.1.dist-info → snowflake_cli_labs-2.7.0rc1.dist-info}/licenses/LICENSE +0 -0
@@ -14,4 +14,4 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
- VERSION = "2.6.1"
17
+ VERSION = "2.7.0rc1"
@@ -47,6 +47,7 @@ class _ConnectionContext:
47
47
  self._temporary_connection: bool = False
48
48
  self._session_token: Optional[str] = None
49
49
  self._master_token: Optional[str] = None
50
+ self._token_file_path: Optional[Path] = None
50
51
 
51
52
  def __setattr__(self, key, value):
52
53
  """
@@ -182,6 +183,13 @@ class _ConnectionContext:
182
183
  def set_master_token(self, value: Optional[str]):
183
184
  self._master_token = value
184
185
 
186
+ @property
187
+ def token_file_path(self) -> Optional[Path]:
188
+ return self._token_file_path
189
+
190
+ def set_token_file_path(self, value: Optional[Path]):
191
+ self._token_file_path = value
192
+
185
193
  @property
186
194
  def connection(self) -> SnowflakeConnection:
187
195
  if not self._cached_connection:
@@ -201,6 +209,7 @@ class _ConnectionContext:
201
209
  "warehouse": self.warehouse,
202
210
  "session_token": self.session_token,
203
211
  "master_token": self.master_token,
212
+ "token_file_path": self.token_file_path,
204
213
  }
205
214
 
206
215
  def _build_connection(self):
@@ -39,6 +39,7 @@ from snowflake.cli.api.commands.flags import (
39
39
  SessionTokenOption,
40
40
  SilentOption,
41
41
  TemporaryConnectionOption,
42
+ TokenFilePathOption,
42
43
  UserOption,
43
44
  VerboseOption,
44
45
  WarehouseOption,
@@ -73,9 +74,7 @@ def global_options_with_connection(func: Callable):
73
74
  )
74
75
 
75
76
 
76
- def with_project_definition(
77
- project_name: Optional[str] = None, is_optional: bool = False
78
- ):
77
+ def with_project_definition(is_optional: bool = False):
79
78
  def _decorator(func: Callable):
80
79
 
81
80
  return _options_decorator_factory(
@@ -85,7 +84,7 @@ def with_project_definition(
85
84
  "project_definition",
86
85
  inspect.Parameter.KEYWORD_ONLY,
87
86
  annotation=Optional[str],
88
- default=project_definition_option(project_name, is_optional),
87
+ default=project_definition_option(is_optional),
89
88
  ),
90
89
  inspect.Parameter(
91
90
  "env_overrides",
@@ -252,6 +251,12 @@ GLOBAL_CONNECTION_OPTIONS = [
252
251
  annotation=Optional[str],
253
252
  default=MasterTokenOption,
254
253
  ),
254
+ inspect.Parameter(
255
+ "token_file_path",
256
+ inspect.Parameter.KEYWORD_ONLY,
257
+ annotation=Optional[str],
258
+ default=TokenFilePathOption,
259
+ ),
255
260
  inspect.Parameter(
256
261
  "database",
257
262
  inspect.Parameter.KEYWORD_ONLY,
@@ -0,0 +1,40 @@
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
+ import time
15
+ import uuid
16
+ from dataclasses import dataclass, field
17
+ from enum import Enum
18
+
19
+
20
+ class ExecutionStatus(Enum):
21
+ SUCCESS = "success"
22
+ FAILURE = "failure"
23
+
24
+
25
+ @dataclass
26
+ class ExecutionMetadata:
27
+ start_time: float = 0.0
28
+ end_time: float = 0.0
29
+ status: ExecutionStatus = ExecutionStatus.SUCCESS
30
+ execution_id: str = field(default_factory=lambda: uuid.uuid4().hex)
31
+
32
+ def __post_init__(self):
33
+ self.start_time = time.monotonic()
34
+
35
+ def complete(self, status: ExecutionStatus):
36
+ self.end_time = time.monotonic()
37
+ self.status = status
38
+
39
+ def get_duration(self):
40
+ return self.end_time - self.start_time
@@ -27,10 +27,10 @@ from click import ClickException
27
27
  from snowflake.cli.api.cli_global_context import cli_context_manager
28
28
  from snowflake.cli.api.commands.typer_pre_execute import register_pre_execute_command
29
29
  from snowflake.cli.api.console import cli_console
30
- from snowflake.cli.api.exceptions import MissingConfiguration, NoProjectDefinitionError
30
+ from snowflake.cli.api.exceptions import MissingConfiguration
31
31
  from snowflake.cli.api.output.formats import OutputFormat
32
32
  from snowflake.cli.api.project.definition_manager import DefinitionManager
33
- from snowflake.cli.api.utils.rendering import CONTEXT_KEY
33
+ from snowflake.cli.api.rendering.jinja import CONTEXT_KEY
34
34
 
35
35
  DEFAULT_CONTEXT_SETTINGS = {"help_option_names": ["--help", "-h"]}
36
36
 
@@ -140,6 +140,9 @@ class OverrideableOption:
140
140
  return generated_callback
141
141
 
142
142
 
143
+ from snowflake.cli.api.config import get_all_connections
144
+
145
+
143
146
  def _callback(provide_setter: Callable[[], Callable[[Any], Any]]):
144
147
  def callback(value):
145
148
  set_value = provide_setter()
@@ -160,6 +163,7 @@ ConnectionOption = typer.Option(
160
163
  ),
161
164
  show_default=False,
162
165
  rich_help_panel=_CONNECTION_SECTION,
166
+ autocompletion=lambda: list(get_all_connections()),
163
167
  )
164
168
 
165
169
  TemporaryConnectionOption = typer.Option(
@@ -272,6 +276,20 @@ MasterTokenOption = typer.Option(
272
276
  hidden=True,
273
277
  )
274
278
 
279
+ TokenFilePathOption = typer.Option(
280
+ None,
281
+ "--token-file-path",
282
+ help="Path to file with an OAuth token that should be used when connecting to Snowflake",
283
+ callback=_callback(
284
+ lambda: cli_context_manager.connection_context.set_token_file_path
285
+ ),
286
+ show_default=False,
287
+ rich_help_panel=_CONNECTION_SECTION,
288
+ exists=True,
289
+ file_okay=True,
290
+ dir_okay=False,
291
+ )
292
+
275
293
  DatabaseOption = typer.Option(
276
294
  None,
277
295
  "--database",
@@ -429,12 +447,24 @@ OnErrorOption = typer.Option(
429
447
  help="What to do when an error occurs. Defaults to break.",
430
448
  )
431
449
 
432
- VariablesOption = typer.Option(
433
- None,
434
- "--variable",
435
- "-D",
436
- help="Variables for the template. For example: `-D \"<key>=<value>\"`, string values must be in `''`.",
437
- show_default=False,
450
+ NoInteractiveOption = typer.Option(False, "--no-interactive", help="Disable prompting.")
451
+
452
+
453
+ def variables_option(description: str):
454
+ return typer.Option(
455
+ None,
456
+ "--variable",
457
+ "-D",
458
+ help=description,
459
+ show_default=False,
460
+ )
461
+
462
+
463
+ ExecuteVariablesOption = variables_option(
464
+ 'Variables for the execution context. For example: `-D "<key>=<value>"`. '
465
+ "For SQL files variables are use to expand the template and any unknown variable will cause an error. "
466
+ "For Python files variables are used to update os.environ dictionary. Provided keys are capitalized to adhere to best practices."
467
+ "In case of SQL files string values must be quoted in `''` (consider embedding quoting in the file).",
438
468
  )
439
469
 
440
470
 
@@ -500,7 +530,7 @@ def execution_identifier_argument(sf_object: str, example: str) -> typer.Argumen
500
530
  )
501
531
 
502
532
 
503
- def register_project_definition(project_name: Optional[str], is_optional: bool) -> None:
533
+ def register_project_definition(is_optional: bool) -> None:
504
534
  project_path = cli_context_manager.project_path_arg
505
535
  env_overrides_args = cli_context_manager.project_env_overrides_args
506
536
 
@@ -514,42 +544,21 @@ def register_project_definition(project_name: Optional[str], is_optional: bool)
514
544
  "Cannot find project definition (snowflake.yml). Please provide a path to the project or run this command in a valid project directory."
515
545
  )
516
546
 
517
- if project_name is not None and not getattr(project_definition, project_name, None):
518
- raise NoProjectDefinitionError(
519
- project_type=project_name, project_file=project_path
520
- )
521
-
522
547
  cli_context_manager.set_project_definition(project_definition)
523
548
  cli_context_manager.set_project_root(project_root)
524
549
  cli_context_manager.set_template_context(template_context)
525
550
 
526
551
 
527
- def _get_project_long_name(project_short_name: Optional[str]) -> str:
528
- if project_short_name is None:
529
- return "Snowflake"
530
-
531
- if project_short_name == "native_app":
532
- project_long_name = "Snowflake Native App"
533
- elif project_short_name == "streamlit":
534
- project_long_name = "Streamlit app"
535
- else:
536
- project_long_name = project_short_name.replace("_", " ").capitalize()
537
-
538
- return f"the {project_long_name}"
539
-
540
-
541
- def project_definition_option(project_name: Optional[str], is_optional: bool):
552
+ def project_definition_option(is_optional: bool):
542
553
  def project_definition_callback(project_path: str) -> None:
543
554
  cli_context_manager.set_project_path_arg(project_path)
544
- register_pre_execute_command(
545
- lambda: register_project_definition(project_name, is_optional)
546
- )
555
+ register_pre_execute_command(lambda: register_project_definition(is_optional))
547
556
 
548
557
  return typer.Option(
549
558
  None,
550
559
  "-p",
551
560
  "--project",
552
- help=f"Path where {_get_project_long_name(project_name)} project resides. "
561
+ help=f"Path where Snowflake project resides. "
553
562
  f"Defaults to current working directory.",
554
563
  callback=_callback(lambda: project_definition_callback),
555
564
  show_default=False,
@@ -617,11 +626,11 @@ class Variable:
617
626
 
618
627
  def parse_key_value_variables(variables: Optional[List[str]]) -> List[Variable]:
619
628
  """Util for parsing key=value input. Useful for commands accepting multiple input options."""
629
+ if not variables:
630
+ return []
620
631
  result: List[Variable] = []
621
-
622
- if variables is None:
632
+ if not variables:
623
633
  return result
624
-
625
634
  for p in variables:
626
635
  if "=" not in p:
627
636
  raise ClickException(f"Invalid variable: '{p}'")
@@ -35,7 +35,7 @@ def add_init_command(
35
35
  template: str,
36
36
  help_message: Optional[str] = None,
37
37
  ):
38
- @app.command()
38
+ @app.command(deprecated=True)
39
39
  def init(
40
40
  project_name: str = Argument(
41
41
  f"example_{project_type.lower()}",
@@ -47,6 +47,7 @@ def add_init_command(
47
47
  ),
48
48
  **options,
49
49
  ) -> CommandResult:
50
+
50
51
  _create_project_template(template, project_directory=project_name)
51
52
  return MessageResult(f"Initialized the new project in {project_name}/")
52
53
 
@@ -56,7 +57,9 @@ def add_init_command(
56
57
 
57
58
  init.__doc__ = (
58
59
  f"Initializes this directory with a sample set "
59
- f"of files for creating a {project_type_doc} project."
60
+ f"of files for creating a {project_type_doc} project. "
61
+ f"This command is deprecated and will be removed soon. "
62
+ f"Please use 'snow init' instead"
60
63
  )
61
64
 
62
65
  return init
@@ -24,6 +24,10 @@ from snowflake.cli.api.commands.decorators import (
24
24
  global_options,
25
25
  global_options_with_connection,
26
26
  )
27
+ from snowflake.cli.api.commands.execution_metadata import (
28
+ ExecutionMetadata,
29
+ ExecutionStatus,
30
+ )
27
31
  from snowflake.cli.api.commands.flags import DEFAULT_CONTEXT_SETTINGS
28
32
  from snowflake.cli.api.commands.typer_pre_execute import run_pre_execute_commands
29
33
  from snowflake.cli.api.exceptions import CommandReturnTypeError
@@ -91,15 +95,18 @@ class SnowTyper(typer.Typer):
91
95
  @wraps(command_callable)
92
96
  def command_callable_decorator(*args, **kw):
93
97
  """Wrapper around command callable. This is what happens at "runtime"."""
94
- self.pre_execute()
98
+ execution = ExecutionMetadata()
99
+ self.pre_execute(execution)
95
100
  try:
96
101
  result = command_callable(*args, **kw)
97
- return self.process_result(result)
102
+ self.process_result(result)
103
+ execution.complete(ExecutionStatus.SUCCESS)
98
104
  except Exception as err:
99
- self.exception_handler(err)
105
+ execution.complete(ExecutionStatus.FAILURE)
106
+ self.exception_handler(err, execution)
100
107
  raise
101
108
  finally:
102
- self.post_execute()
109
+ self.post_execute(execution)
103
110
 
104
111
  return super(SnowTyper, self).command(name=name, **kwargs)(
105
112
  command_callable_decorator
@@ -108,7 +115,7 @@ class SnowTyper(typer.Typer):
108
115
  return custom_command
109
116
 
110
117
  @staticmethod
111
- def pre_execute():
118
+ def pre_execute(execution: ExecutionMetadata):
112
119
  """
113
120
  Callback executed before running any command callable (after context execution).
114
121
  Pay attention to make this method safe to use if performed operations are not necessary
@@ -118,7 +125,7 @@ class SnowTyper(typer.Typer):
118
125
 
119
126
  log.debug("Executing command pre execution callback")
120
127
  run_pre_execute_commands()
121
- log_command_usage()
128
+ log_command_usage(execution)
122
129
 
123
130
  @staticmethod
124
131
  def process_result(result):
@@ -134,21 +141,25 @@ class SnowTyper(typer.Typer):
134
141
  print_result(result)
135
142
 
136
143
  @staticmethod
137
- def exception_handler(exception: Exception):
144
+ def exception_handler(exception: Exception, execution: ExecutionMetadata):
138
145
  """
139
146
  Callback executed on command execution error.
140
147
  """
148
+ from snowflake.cli.app.telemetry import log_command_execution_error
149
+
141
150
  log.debug("Executing command exception callback")
151
+ log_command_execution_error(exception, execution)
142
152
 
143
153
  @staticmethod
144
- def post_execute():
154
+ def post_execute(execution: ExecutionMetadata):
145
155
  """
146
156
  Callback executed after running any command callable. Pay attention to make this method safe to
147
157
  use if performed operations are not necessary for executing the command in proper way.
148
158
  """
149
- from snowflake.cli.app.telemetry import flush_telemetry
159
+ from snowflake.cli.app.telemetry import flush_telemetry, log_command_result
150
160
 
151
161
  log.debug("Executing command post execution callback")
162
+ log_command_result(execution)
152
163
  flush_telemetry()
153
164
 
154
165
 
@@ -0,0 +1,27 @@
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
+ # General errors
16
+ NO_WAREHOUSE_SELECTED_IN_SESSION = 606
17
+
18
+ DOES_NOT_EXIST_OR_NOT_AUTHORIZED = 2003
19
+ DOES_NOT_EXIST_OR_CANNOT_BE_PERFORMED = 2043
20
+
21
+ # Native Apps
22
+ CANNOT_UPGRADE_FROM_LOOSE_FILES_TO_VERSION = 93044
23
+ CANNOT_UPGRADE_FROM_VERSION_TO_LOOSE_FILES = 93045
24
+ ONLY_SUPPORTED_ON_DEV_MODE_APPLICATIONS = 93046
25
+ NOT_SUPPORTED_ON_DEV_MODE_APPLICATIONS = 93055
26
+ APPLICATION_NO_LONGER_AVAILABLE = 93079
27
+ APPLICATION_OWNS_EXTERNAL_OBJECTS = 93128
@@ -48,3 +48,4 @@ class FeatureFlag(FeatureFlagMixin):
48
48
  ENABLE_STREAMLIT_EMBEDDED_STAGE = BooleanFlag(
49
49
  "ENABLE_STREAMLIT_EMBEDDED_STAGE", False
50
50
  )
51
+ ENABLE_PROJECT_DEFINITION_V2 = BooleanFlag("ENABLE_PROJECT_DEFINITION_V2", False)
@@ -53,17 +53,27 @@ class FQN:
53
53
  return self._name
54
54
 
55
55
  @property
56
- def identifier(self) -> str:
56
+ def prefix(self) -> str:
57
57
  if self.database:
58
- return f"{self.database}.{self.schema if self.schema else 'PUBLIC'}.{self.name}"
58
+ return f"{self.database}.{self.schema if self.schema else 'PUBLIC'}"
59
59
  if self.schema:
60
- return f"{self.schema}.{self.name}"
60
+ return f"{self.schema}"
61
+ return ""
62
+
63
+ @property
64
+ def identifier(self) -> str:
65
+ if self.prefix:
66
+ return f"{self.prefix}.{self.name}"
61
67
  return self.name
62
68
 
63
69
  @property
64
70
  def url_identifier(self) -> str:
65
71
  return ".".join(identifier_for_url(part) for part in self.identifier.split("."))
66
72
 
73
+ @property
74
+ def sql_identifier(self) -> str:
75
+ return f"IDENTIFIER('{self.identifier}')"
76
+
67
77
  def __str__(self):
68
78
  return self.identifier
69
79
 
@@ -92,6 +102,13 @@ class FQN:
92
102
  unqualified_name = unqualified_name + signature
93
103
  return cls(name=unqualified_name, schema=schema, database=database)
94
104
 
105
+ @classmethod
106
+ def from_stage(cls, stage: str) -> "FQN":
107
+ name = stage
108
+ if stage.startswith("@"):
109
+ name = stage[1:]
110
+ return cls.from_string(name)
111
+
95
112
  @classmethod
96
113
  def from_identifier_model(cls, model: ObjectIdentifierBaseModel) -> "FQN":
97
114
  """Create an instance from object model."""
@@ -57,6 +57,15 @@ class MultipleResults(CommandResult):
57
57
  return (element for element in self._elements)
58
58
 
59
59
 
60
+ class StreamResult(CommandResult):
61
+ def __init__(self, generator: t.Generator[CommandResult, None, None]):
62
+ self._generator = generator
63
+
64
+ @property
65
+ def result(self):
66
+ return self._generator
67
+
68
+
60
69
  class QueryResult(CollectionResult):
61
70
  def __init__(self, cursor: SnowflakeCursor | DictCursor):
62
71
  self.column_names = [col.name for col in cursor.description]
@@ -20,7 +20,7 @@ from pathlib import Path
20
20
  from typing import List, Optional
21
21
 
22
22
  from snowflake.cli.api.project.definition import ProjectProperties, load_project
23
- from snowflake.cli.api.project.schemas.project_definition import ProjectDefinition
23
+ from snowflake.cli.api.project.schemas.project_definition import ProjectDefinitionV1
24
24
  from snowflake.cli.api.utils.types import Context
25
25
 
26
26
 
@@ -126,7 +126,7 @@ class DefinitionManager:
126
126
  return load_project(self._project_config_paths, self._context_overrides)
127
127
 
128
128
  @functools.cached_property
129
- def project_definition(self) -> ProjectDefinition:
129
+ def project_definition(self) -> ProjectDefinitionV1:
130
130
  return self._project_properties.project_definition
131
131
 
132
132
  @functools.cached_property
@@ -0,0 +1,23 @@
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 snowflake.cli.api.cli_global_context import cli_context
16
+ from snowflake.cli.api.exceptions import NoProjectDefinitionError
17
+
18
+
19
+ def assert_project_type(project_type: str):
20
+ if not getattr(cli_context.project_definition, project_type, None):
21
+ raise NoProjectDefinitionError(
22
+ project_type=project_type, project_file=cli_context.project_root
23
+ )
@@ -0,0 +1,50 @@
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 typing import Literal, Optional
18
+
19
+ from pydantic import AliasChoices, Field
20
+ from snowflake.cli.api.project.schemas.entities.application_package_entity import (
21
+ ApplicationPackageEntity,
22
+ )
23
+ from snowflake.cli.api.project.schemas.entities.common import (
24
+ EntityBase,
25
+ TargetField,
26
+ )
27
+ from snowflake.cli.api.project.schemas.updatable_model import (
28
+ UpdatableModel,
29
+ )
30
+
31
+
32
+ class ApplicationEntity(EntityBase):
33
+ type: Literal["application"] # noqa: A003
34
+ name: str = Field(
35
+ title="Name of the application created when this entity is deployed"
36
+ )
37
+ from_: ApplicationFromField = Field(
38
+ validation_alias=AliasChoices("from"),
39
+ title="An application package this entity should be created from",
40
+ )
41
+ debug: Optional[bool] = Field(
42
+ title="Whether to enable debug mode when using a named stage to create an application object",
43
+ default=None,
44
+ )
45
+
46
+
47
+ class ApplicationFromField(UpdatableModel):
48
+ target: TargetField[ApplicationPackageEntity] = Field(
49
+ title="Reference to an application package entity",
50
+ )
@@ -0,0 +1,63 @@
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
21
+ from snowflake.cli.api.project.schemas.entities.common import (
22
+ EntityBase,
23
+ )
24
+ from snowflake.cli.api.project.schemas.native_app.package import DistributionOptions
25
+ from snowflake.cli.api.project.schemas.native_app.path_mapping import PathMapping
26
+ from snowflake.cli.api.project.schemas.updatable_model import IdentifierField
27
+
28
+
29
+ class ApplicationPackageEntity(EntityBase):
30
+ type: Literal["application package"] # noqa: A003
31
+ name: str = Field(
32
+ title="Name of the application package created when this entity is deployed"
33
+ )
34
+ artifacts: List[Union[PathMapping, Path]] = Field(
35
+ title="List of paths or file source/destination pairs to add to the deploy root",
36
+ )
37
+ bundle_root: Optional[Path] = Field(
38
+ title="Folder at the root of your project where artifacts necessary to perform the bundle step are stored.",
39
+ default=Path("output/bundle/"),
40
+ )
41
+ deploy_root: Optional[Path] = Field(
42
+ title="Folder at the root of your project where the build step copies the artifacts",
43
+ default=Path("output/deploy/"),
44
+ )
45
+ generated_root: Optional[Path] = Field(
46
+ title="Subdirectory of the deploy root where files generated by the Snowflake CLI will be written.",
47
+ default=Path("__generated/"),
48
+ )
49
+ stage: Optional[str] = IdentifierField(
50
+ title="Identifier of the stage that stores the application artifacts.",
51
+ default="app_src.stage",
52
+ )
53
+ scratch_stage: Optional[str] = IdentifierField(
54
+ title="Identifier of the stage that stores temporary scratch data used by the Snowflake CLI.",
55
+ default="app_src.stage_snowflake_cli_scratch",
56
+ )
57
+ distribution: Optional[DistributionOptions] = Field(
58
+ title="Distribution of the application package created by the Snowflake CLI",
59
+ default="internal",
60
+ )
61
+ manifest: Path = Field(
62
+ title="Path to manifest.yml",
63
+ )