snowflake-cli 3.0.2__py3-none-any.whl → 3.1.0__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 (57) hide show
  1. snowflake/cli/__about__.py +1 -1
  2. snowflake/cli/_app/cli_app.py +3 -0
  3. snowflake/cli/_app/dev/docs/templates/overview.rst.jinja2 +1 -1
  4. snowflake/cli/_app/dev/docs/templates/usage.rst.jinja2 +2 -2
  5. snowflake/cli/_app/telemetry.py +69 -4
  6. snowflake/cli/_plugins/connection/commands.py +40 -2
  7. snowflake/cli/_plugins/git/commands.py +6 -3
  8. snowflake/cli/_plugins/git/manager.py +5 -0
  9. snowflake/cli/_plugins/nativeapp/artifacts.py +13 -3
  10. snowflake/cli/_plugins/nativeapp/codegen/artifact_processor.py +1 -1
  11. snowflake/cli/_plugins/nativeapp/codegen/compiler.py +7 -0
  12. snowflake/cli/_plugins/nativeapp/codegen/sandbox.py +10 -10
  13. snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +2 -2
  14. snowflake/cli/_plugins/nativeapp/codegen/snowpark/extension_function_utils.py +1 -1
  15. snowflake/cli/_plugins/nativeapp/codegen/snowpark/python_processor.py +8 -8
  16. snowflake/cli/_plugins/nativeapp/commands.py +135 -186
  17. snowflake/cli/_plugins/nativeapp/entities/application.py +176 -24
  18. snowflake/cli/_plugins/nativeapp/entities/application_package.py +112 -136
  19. snowflake/cli/_plugins/nativeapp/exceptions.py +12 -0
  20. snowflake/cli/_plugins/nativeapp/manager.py +3 -26
  21. snowflake/cli/_plugins/nativeapp/v2_conversions/{v2_to_v1_decorator.py → compat.py} +131 -72
  22. snowflake/cli/_plugins/nativeapp/version/commands.py +30 -29
  23. snowflake/cli/_plugins/nativeapp/version/version_processor.py +1 -43
  24. snowflake/cli/_plugins/snowpark/common.py +60 -18
  25. snowflake/cli/_plugins/snowpark/package/anaconda_packages.py +2 -2
  26. snowflake/cli/_plugins/spcs/image_repository/commands.py +4 -37
  27. snowflake/cli/_plugins/spcs/image_repository/manager.py +4 -1
  28. snowflake/cli/_plugins/spcs/services/commands.py +36 -4
  29. snowflake/cli/_plugins/spcs/services/manager.py +36 -4
  30. snowflake/cli/_plugins/stage/commands.py +8 -3
  31. snowflake/cli/_plugins/stage/diff.py +16 -16
  32. snowflake/cli/_plugins/stage/manager.py +164 -73
  33. snowflake/cli/_plugins/stage/md5.py +1 -1
  34. snowflake/cli/_plugins/workspace/commands.py +21 -1
  35. snowflake/cli/_plugins/workspace/context.py +38 -0
  36. snowflake/cli/_plugins/workspace/manager.py +23 -13
  37. snowflake/cli/api/cli_global_context.py +3 -3
  38. snowflake/cli/api/commands/flags.py +23 -7
  39. snowflake/cli/api/config.py +7 -4
  40. snowflake/cli/api/connections.py +12 -1
  41. snowflake/cli/api/entities/common.py +4 -2
  42. snowflake/cli/api/entities/utils.py +17 -37
  43. snowflake/cli/api/exceptions.py +32 -0
  44. snowflake/cli/api/identifiers.py +8 -0
  45. snowflake/cli/api/project/definition_conversion.py +139 -40
  46. snowflake/cli/api/project/schemas/entities/common.py +11 -0
  47. snowflake/cli/api/project/schemas/project_definition.py +30 -25
  48. snowflake/cli/api/sql_execution.py +5 -7
  49. snowflake/cli/api/stage_path.py +241 -0
  50. snowflake/cli/api/utils/definition_rendering.py +3 -5
  51. {snowflake_cli-3.0.2.dist-info → snowflake_cli-3.1.0.dist-info}/METADATA +11 -11
  52. {snowflake_cli-3.0.2.dist-info → snowflake_cli-3.1.0.dist-info}/RECORD +55 -55
  53. snowflake/cli/_plugins/nativeapp/teardown_processor.py +0 -70
  54. snowflake/cli/_plugins/workspace/action_context.py +0 -18
  55. {snowflake_cli-3.0.2.dist-info → snowflake_cli-3.1.0.dist-info}/WHEEL +0 -0
  56. {snowflake_cli-3.0.2.dist-info → snowflake_cli-3.1.0.dist-info}/entry_points.txt +0 -0
  57. {snowflake_cli-3.0.2.dist-info → snowflake_cli-3.1.0.dist-info}/licenses/LICENSE +0 -0
@@ -14,4 +14,4 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
- VERSION = "3.0.2"
17
+ VERSION = "3.1.0"
@@ -15,6 +15,7 @@
15
15
  from __future__ import annotations
16
16
 
17
17
  import logging
18
+ import os
18
19
  import platform
19
20
  import sys
20
21
  from dataclasses import dataclass
@@ -141,6 +142,7 @@ def _info_callback(value: bool):
141
142
  {"key": "python_version", "value": sys.version},
142
143
  {"key": "system_info", "value": platform.platform()},
143
144
  {"key": "feature_flags", "value": get_feature_flags_section()},
145
+ {"key": "SNOWFLAKE_HOME", "value": os.getenv("SNOWFLAKE_HOME")},
144
146
  ],
145
147
  )
146
148
  print_result(result, output_format=OutputFormat.JSON)
@@ -155,6 +157,7 @@ def app_factory() -> SnowCliMainTyper:
155
157
  invoke_without_command=True,
156
158
  epilog=new_version_msg,
157
159
  result_callback=show_new_version_banner_callback(new_version_msg),
160
+ help=f"Snowflake CLI tool for developers [v{__about__.VERSION}]",
158
161
  )
159
162
  def default(
160
163
  ctx: typer.Context,
@@ -5,5 +5,5 @@ Global options
5
5
  ===============================================================================
6
6
  {% for param in options if not param.hidden %}
7
7
  :samp:`{% for p in param.opts %}{{ p }}{{ ", " if not loop.last }}{% endfor %}{% if not param.is_flag %} {{ '{' }}{{ param.name }}{{ '}' }}{% endif %}`
8
- {% if param.help %}{{ " " + param.help | replace("`", "``") }}{% if param.help[-1] != '.' %}.{% endif %}{% else %} TBD{% endif %}
8
+ {% if param.help %}{{ " " + param.help}}{% if param.help[-1] != '.' %}.{% endif %}{% else %} TBD{% endif %}
9
9
  {% endfor %}
@@ -26,7 +26,7 @@ Arguments
26
26
  {{ param.make_metavar().replace("[", "").replace("]", "").lower() }}
27
27
  {%- endif -%}
28
28
  {{ '}' }}`
29
- {% if param.help %}{{ " " + param.help | replace("`", "``") | replace("\n", " ") }}{% if param.help[-1] != '.' %}.{% endif %}{% if param.default %} Default: {{ param.default }}.{% endif %}{% else %} TBD{% endif %}
29
+ {% if param.help %}{{ " " + param.help | replace("\n", " ") }}{% if param.help[-1] != '.' %}.{% endif %}{% if param.default %} Default: {{ param.default }}.{% endif %}{% else %} TBD{% endif %}
30
30
  {% endfor %}
31
31
  {% else %}
32
32
 
@@ -48,7 +48,7 @@ Options
48
48
  {%- if param.type.name != "choice" %}{{ ' {' }}{% else %} {% endif %}{{ param.make_metavar() }}{% if param.type.name != "choice" %}{{ '}' }}
49
49
  {%- endif %}
50
50
  {%- endif %}`
51
- {% if param.help %}{{ " " + param.help | replace("`", "``") | replace("\n", " ") }}{% if param.help[-1] != '.' %}.{% endif %}{% if param.default is not none %} Default: {{ param.default }}.{% endif %}{% else %} TBD{% endif %}
51
+ {% if param.help %}{{ " " + param.help | replace("\n", " ") }}{% if param.help[-1] != '.' %}.{% endif %}{% if param.default is not none %} Default: {{ param.default }}.{% endif %}{% else %} TBD{% endif %}
52
52
  {% endfor -%}
53
53
  {% else %}
54
54
 
@@ -14,12 +14,14 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
+ import os
17
18
  import platform
18
19
  import sys
19
20
  from enum import Enum, unique
20
21
  from typing import Any, Dict, Union
21
22
 
22
23
  import click
24
+ import typer
23
25
  from snowflake.cli.__about__ import VERSION
24
26
  from snowflake.cli._app.constants import PARAM_APPLICATION_NAME
25
27
  from snowflake.cli.api.cli_global_context import (
@@ -30,6 +32,7 @@ from snowflake.cli.api.commands.execution_metadata import ExecutionMetadata
30
32
  from snowflake.cli.api.config import get_feature_flags_section
31
33
  from snowflake.cli.api.output.formats import OutputFormat
32
34
  from snowflake.cli.api.utils.error_handling import ignore_exceptions
35
+ from snowflake.connector import ProgrammingError
33
36
  from snowflake.connector.telemetry import (
34
37
  TelemetryData,
35
38
  TelemetryField,
@@ -59,6 +62,7 @@ class CLITelemetryField(Enum):
59
62
  COMMAND_RESULT_STATUS = "command_result_status"
60
63
  COMMAND_OUTPUT_TYPE = "command_output_type"
61
64
  COMMAND_EXECUTION_TIME = "command_execution_time"
65
+ COMMAND_CI_ENVIRONMENT = "command_ci_environment"
62
66
  # Configuration
63
67
  CONFIG_FEATURE_FLAGS = "config_feature_flags"
64
68
  # Metrics
@@ -67,6 +71,9 @@ class CLITelemetryField(Enum):
67
71
  EVENT = "event"
68
72
  ERROR_MSG = "error_msg"
69
73
  ERROR_TYPE = "error_type"
74
+ ERROR_CODE = "error_code"
75
+ ERROR_CAUSE = "error_cause"
76
+ SQL_STATE = "sql_state"
70
77
  IS_CLI_EXCEPTION = "is_cli_exception"
71
78
  # Project context
72
79
  PROJECT_DEFINITION_VERSION = "project_definition_version"
@@ -81,6 +88,43 @@ class TelemetryEvent(Enum):
81
88
  TelemetryDict = Dict[Union[CLITelemetryField, TelemetryField], Any]
82
89
 
83
90
 
91
+ def _is_cli_exception(exception: Exception) -> bool:
92
+ return isinstance(
93
+ exception,
94
+ (
95
+ click.ClickException,
96
+ typer.Exit,
97
+ typer.Abort,
98
+ BrokenPipeError,
99
+ KeyboardInterrupt,
100
+ ),
101
+ )
102
+
103
+
104
+ def _get_additional_exception_information(exception: Exception) -> TelemetryDict:
105
+ """
106
+ Attach the errno and sqlstate if the exception or the
107
+ cause of the exception is a ProgrammingError
108
+ """
109
+ additional_info = {}
110
+
111
+ if isinstance(exception, ProgrammingError):
112
+ additional_info[CLITelemetryField.ERROR_CODE] = exception.errno
113
+ additional_info[CLITelemetryField.SQL_STATE] = exception.sqlstate
114
+
115
+ if exception.__cause__:
116
+ cause = exception.__cause__
117
+ additional_info[CLITelemetryField.ERROR_CAUSE] = type(cause).__name__
118
+
119
+ if isinstance(cause, ProgrammingError):
120
+ if not additional_info.get(CLITelemetryField.ERROR_CODE):
121
+ additional_info[CLITelemetryField.ERROR_CODE] = cause.errno
122
+ if not additional_info.get(CLITelemetryField.SQL_STATE):
123
+ additional_info[CLITelemetryField.SQL_STATE] = cause.sqlstate
124
+
125
+ return additional_info
126
+
127
+
84
128
  def _get_command_metrics() -> TelemetryDict:
85
129
  cli_context = get_cli_context()
86
130
 
@@ -110,9 +154,14 @@ def _find_command_info() -> TelemetryDict:
110
154
 
111
155
 
112
156
  def _get_definition_version() -> str | None:
113
- cli_context = get_cli_context()
114
- if cli_context.project_definition:
115
- return cli_context.project_definition.definition_version
157
+ try:
158
+ cli_context = get_cli_context()
159
+ if cli_context.project_definition:
160
+ return cli_context.project_definition.definition_version
161
+ except Exception:
162
+ # Don't let an invalid project definition file break telemetry
163
+ # (especially for commands that don't normally load it)
164
+ pass
116
165
  return None
117
166
 
118
167
 
@@ -122,6 +171,20 @@ def _get_installation_source() -> CLIInstallationSource:
122
171
  return CLIInstallationSource.PYPI
123
172
 
124
173
 
174
+ def _get_ci_environment_type() -> str:
175
+ if "GITHUB_ACTIONS" in os.environ:
176
+ return "GITHUB_ACTIONS"
177
+ if "GITLAB_CI" in os.environ:
178
+ return "GITLAB_CI"
179
+ if "CIRCLECI" in os.environ:
180
+ return "CIRCLECI"
181
+ if "JENKINS_URL" in os.environ or "HUDSON_URL" in os.environ:
182
+ return "JENKINS"
183
+ if "TF_BUILD" in os.environ:
184
+ return "AZURE_DEVOPS"
185
+ return "UNKNOWN"
186
+
187
+
125
188
  def command_info() -> str:
126
189
  info = _find_command_info()
127
190
  command = ".".join(info[CLITelemetryField.COMMAND])
@@ -148,6 +211,7 @@ class CLITelemetryClient:
148
211
  CLITelemetryField.VERSION_CLI: VERSION,
149
212
  CLITelemetryField.VERSION_OS: platform.platform(),
150
213
  CLITelemetryField.VERSION_PYTHON: python_version(),
214
+ CLITelemetryField.COMMAND_CI_ENVIRONMENT: _get_ci_environment_type(),
151
215
  CLITelemetryField.CONFIG_FEATURE_FLAGS: {
152
216
  k: str(v) for k, v in get_feature_flags_section().items()
153
217
  },
@@ -202,7 +266,7 @@ def log_command_result(execution: ExecutionMetadata):
202
266
  @ignore_exceptions()
203
267
  def log_command_execution_error(exception: Exception, execution: ExecutionMetadata):
204
268
  exception_type: str = type(exception).__name__
205
- is_cli_exception: bool = issubclass(exception.__class__, click.ClickException)
269
+ is_cli_exception: bool = _is_cli_exception(exception)
206
270
  _telemetry.send(
207
271
  {
208
272
  TelemetryField.KEY_TYPE: TelemetryEvent.CMD_EXECUTION_ERROR.value,
@@ -210,6 +274,7 @@ def log_command_execution_error(exception: Exception, execution: ExecutionMetada
210
274
  CLITelemetryField.ERROR_TYPE: exception_type,
211
275
  CLITelemetryField.IS_CLI_EXCEPTION: is_cli_exception,
212
276
  CLITelemetryField.COMMAND_EXECUTION_TIME: execution.get_duration(),
277
+ **_get_additional_exception_information(exception),
213
278
  **_get_command_metrics(),
214
279
  }
215
280
  )
@@ -18,9 +18,10 @@ import logging
18
18
  import os.path
19
19
 
20
20
  import typer
21
- from click import ClickException, Context, Parameter # type: ignore
21
+ from click import ClickException, Context, Parameter, UsageError # type: ignore
22
22
  from click.core import ParameterSource # type: ignore
23
23
  from click.types import StringParamType
24
+ from snowflake import connector
24
25
  from snowflake.cli._plugins.connection.util import (
25
26
  strip_and_check_if_exists,
26
27
  strip_if_value_present,
@@ -342,7 +343,7 @@ def test(
342
343
  @app.command(requires_connection=False)
343
344
  def set_default(
344
345
  name: str = typer.Argument(
345
- help="Name of the connection, as defined in your `config.toml`",
346
+ help="Name of the connection, as defined in your `config.toml` file",
346
347
  show_default=False,
347
348
  ),
348
349
  **options,
@@ -351,3 +352,40 @@ def set_default(
351
352
  get_connection_dict(connection_name=name)
352
353
  set_config_value(section=None, key="default_connection_name", value=name)
353
354
  return MessageResult(f"Default connection set to: {name}")
355
+
356
+
357
+ @app.command(requires_connection=True)
358
+ def generate_jwt(
359
+ **options,
360
+ ) -> CommandResult:
361
+ """Generate and display a JWT token."""
362
+ connection_details = get_cli_context().connection_context.update_from_config()
363
+
364
+ msq_template = (
365
+ "{} is not set in the connection context, but required for JWT generation."
366
+ )
367
+ if not connection_details.user:
368
+ raise UsageError(msq_template.format("User"))
369
+ if not connection_details.account:
370
+ raise UsageError(msq_template.format("Account"))
371
+ if not connection_details.private_key_file:
372
+ raise UsageError(msq_template.format("Private key file"))
373
+ passphrase = os.getenv("PRIVATE_KEY_PASSPHRASE", None)
374
+ if not passphrase:
375
+ passphrase = typer.prompt(
376
+ "Enter private key file password (Press enter if none)",
377
+ hide_input=True,
378
+ type=str,
379
+ default="",
380
+ )
381
+
382
+ try:
383
+ token = connector.auth.get_token_from_private_key(
384
+ user=connection_details.user,
385
+ account=connection_details.account,
386
+ privatekey_path=connection_details.private_key_file,
387
+ key_password=passphrase,
388
+ )
389
+ return MessageResult(token)
390
+ except ValueError as err:
391
+ raise ClickException(str(err))
@@ -333,12 +333,15 @@ def execute(
333
333
  **options,
334
334
  ):
335
335
  """
336
- Execute immediate all files from the repository path. Files can be filtered with glob like pattern,
336
+ Execute immediate all files from the repository path. Files can be filtered with a glob-like pattern,
337
337
  e.g. `@my_repo/branches/main/*.sql`, `@my_repo/branches/main/dev/*`. Only files with `.sql`
338
- extension will be executed.
338
+ or `.py` extension will be executed.
339
339
  """
340
340
  results = GitManager().execute(
341
- stage_path=repository_path, on_error=on_error, variables=variables
341
+ stage_path_str=repository_path,
342
+ on_error=on_error,
343
+ variables=variables,
344
+ requires_temporary_stage=True,
342
345
  )
343
346
  return CollectionResult(results)
344
347
 
@@ -26,6 +26,7 @@ from snowflake.cli._plugins.stage.manager import (
26
26
  UserStagePathParts,
27
27
  )
28
28
  from snowflake.cli.api.identifiers import FQN
29
+ from snowflake.cli.api.stage_path import StagePath
29
30
  from snowflake.connector.cursor import SnowflakeCursor
30
31
 
31
32
  # Replace magic numbers with constants
@@ -78,6 +79,10 @@ class GitStagePathParts(StagePathParts):
78
79
 
79
80
 
80
81
  class GitManager(StageManager):
82
+ @staticmethod
83
+ def build_path(stage_path: str) -> StagePathParts:
84
+ return StagePath.from_git_str(stage_path)
85
+
81
86
  def show_branches(self, repo_name: str, like: str) -> SnowflakeCursor:
82
87
  return self._execute_query(f"show git branches like '{like}' in {repo_name}")
83
88
 
@@ -225,9 +225,7 @@ class _ArtifactPathMap:
225
225
 
226
226
  current_is_dir = self._dest_is_dir.get(dest, None)
227
227
  if current_is_dir is not None and current_is_dir != is_dir:
228
- raise ArtifactError(
229
- "Conflicting type for destination path: {canonical_dest}"
230
- )
228
+ raise ArtifactError(f"Conflicting type for destination path: {dest}")
231
229
 
232
230
  parent = dest.parent
233
231
  if parent != dest:
@@ -240,9 +238,21 @@ class BundleMap:
240
238
  """
241
239
  Computes the mapping between project directory artifacts (aka source artifacts) to their deploy root location
242
240
  (aka destination artifact). This information is primarily used when bundling a native applications project.
241
+
242
+ :param project_root: The root directory of the project and base for all relative paths. Must be an absolute path.
243
+ :param deploy_root: The directory where artifacts should be copied to. Must be an absolute path.
243
244
  """
244
245
 
245
246
  def __init__(self, *, project_root: Path, deploy_root: Path):
247
+ # If a relative path ends up here, it's a bug in the app and can lead to other
248
+ # subtle bugs as paths would be resolved relative to the current working directory.
249
+ assert (
250
+ project_root.is_absolute()
251
+ ), f"Project root {project_root} must be an absolute path."
252
+ assert (
253
+ deploy_root.is_absolute()
254
+ ), f"Deploy root {deploy_root} must be an absolute path."
255
+
246
256
  self._project_root: Path = resolve_without_follow(project_root)
247
257
  self._deploy_root: Path = resolve_without_follow(deploy_root)
248
258
  self._artifact_map = _ArtifactPathMap(project_root=self._project_root)
@@ -36,7 +36,7 @@ class UnsupportedArtifactProcessorError(ClickException):
36
36
 
37
37
 
38
38
  def is_python_file_artifact(src: Path, _: Path):
39
- """Determines whether the provided source path is an existing python file."""
39
+ """Determines whether the provided source path is an existing Python file."""
40
40
  return src.is_file() and src.suffix == ".py"
41
41
 
42
42
 
@@ -65,10 +65,17 @@ class NativeAppCompiler:
65
65
  self,
66
66
  bundle_ctx: BundleContext,
67
67
  ):
68
+ self._assert_absolute_paths(bundle_ctx)
68
69
  self._bundle_ctx = bundle_ctx
69
70
  # dictionary of all processors created and shared between different artifact objects.
70
71
  self.cached_processors: Dict[str, ArtifactProcessor] = {}
71
72
 
73
+ @staticmethod
74
+ def _assert_absolute_paths(bundle_ctx: BundleContext):
75
+ for name in ["Project", "Deploy", "Bundle", "Generated"]:
76
+ path = getattr(bundle_ctx, f"{name.lower()}_root")
77
+ assert path.is_absolute(), f"{name} root {path} must be an absolute path."
78
+
72
79
  def compile_artifacts(self):
73
80
  """
74
81
  Go through every artifact object in the project definition of a native app, and execute processors in order of specification for each of the artifact object.
@@ -29,10 +29,10 @@ EnvVars = Mapping[str, str] # Only support str -> str for cross-platform compat
29
29
 
30
30
 
31
31
  class SandboxExecutionError(ClickException):
32
- """An error occurred while executing a python script."""
32
+ """An error occurred while executing a Python script."""
33
33
 
34
34
  def __init__(self, error: str):
35
- super().__init__(f"Failed to execute python script. {error}")
35
+ super().__init__(f"Failed to execute Python script. {error}")
36
36
 
37
37
 
38
38
  def _get_active_venv_dir() -> Optional[str]:
@@ -63,7 +63,7 @@ def _execute_python_interpreter(
63
63
  env_vars: Optional[EnvVars],
64
64
  ) -> subprocess.CompletedProcess:
65
65
  if not python_executable:
66
- raise SandboxExecutionError("No python executable found")
66
+ raise SandboxExecutionError("No Python executable found")
67
67
 
68
68
  if isinstance(python_executable, str) or isinstance(python_executable, Path):
69
69
  args = [python_executable]
@@ -106,7 +106,7 @@ def _execute_in_venv(
106
106
  f"venv path must be an existing directory: {resolved_venv_path}"
107
107
  )
108
108
 
109
- # find the python interpreter for this environment. There is no need to activate environment prior to invoking the
109
+ # find the Python interpreter for this environment. There is no need to activate environment prior to invoking the
110
110
  # interpreter, as venv maintains the invariant that invoking any of the scripts will set up the virtual environment
111
111
  # correctly. activation scripts are only used for convenience in interactive shells.
112
112
  if _is_ms_windows():
@@ -116,7 +116,7 @@ def _execute_in_venv(
116
116
 
117
117
  if not python_executable.is_file():
118
118
  raise SandboxExecutionError(
119
- f"No venv python executable found: {resolved_venv_path}"
119
+ f"No venv Python executable found: {resolved_venv_path}"
120
120
  )
121
121
 
122
122
  return _execute_python_interpreter(
@@ -189,14 +189,14 @@ def execute_script_in_sandbox(
189
189
  **kwargs,
190
190
  ) -> subprocess.CompletedProcess:
191
191
  """
192
- Executes a python script in a sandboxed environment, and returns its output. The script is executed in a different
192
+ Executes a Python script in a sandboxed environment, and returns its output. The script is executed in a different
193
193
  process. The execution environment is determined by the `env_type` argument. By default, the logic will attempt
194
194
  to auto-detect the correct environment by looking for an active venv or conda environment. If none can be found, it
195
- will use the system's default python executable, as determined by the user's path. As a last resort, the current
196
- python execution environment will be used (still in a subprocess).
195
+ will use the system's default Python executable, as determined by the user's path. As a last resort, the current
196
+ Python execution environment will be used (still in a subprocess).
197
197
 
198
198
  Parameters:
199
- script_source (str): The python script to be executed, as a string.
199
+ script_source (str): The Python script to be executed, as a string.
200
200
  env_type: The type of execution environment to use (default: ExecutionEnvironmentType.AUTO_DETECT).
201
201
  cwd (Optional[Union[str, Path]]): An optional path to use as the current directory when executing the script.
202
202
  timeout (Optional[int]): An optional timeout in seconds when executing the script. Defaults to no timeout.
@@ -248,7 +248,7 @@ def execute_script_in_sandbox(
248
248
  class SandboxEnvBuilder(EnvBuilder):
249
249
  """
250
250
  A virtual environment builder that can be used to build an environment suitable for
251
- executing user-provided python scripts in an isolated sandbox.
251
+ executing user-provided Python scripts in an isolated sandbox.
252
252
  """
253
253
 
254
254
  def __init__(self, path: Path, **kwargs) -> None:
@@ -167,14 +167,14 @@ class NativeAppSetupProcessor(ArtifactProcessor):
167
167
  )
168
168
  except Exception as e:
169
169
  raise ClickException(
170
- f"Exception while executing python setup script logic: {e}"
170
+ f"Exception while executing Python setup script logic: {e}"
171
171
  )
172
172
 
173
173
  if result.returncode == 0:
174
174
  return json.loads(result.stdout)
175
175
  else:
176
176
  raise ClickException(
177
- f"Failed to execute python setup script logic: {result.stderr}"
177
+ f"Failed to execute Python setup script logic: {result.stderr}"
178
178
  )
179
179
 
180
180
  def _edit_setup_sql(self, modifications: List[dict]) -> None:
@@ -194,7 +194,7 @@ def deannotate_module_source(
194
194
 
195
195
  module_lines = module_source.splitlines()
196
196
  for definition in definitions:
197
- # Comment out all decorators. As per the python grammar, decorators must be terminated by a
197
+ # Comment out all decorators. As per the Python grammar, decorators must be terminated by a
198
198
  # new line, so the line ranges can't overlap.
199
199
  for decorator in definition.decorator_list:
200
200
  decorator_id = _get_decorator_id(decorator)
@@ -132,12 +132,12 @@ def _execute_in_sandbox(
132
132
  )
133
133
  except SandboxExecutionError as sdbx_err:
134
134
  cc.warning(
135
- f"Could not fetch Snowpark objects from {py_file} due to {sdbx_err}, continuing execution for the rest of the python files."
135
+ f"Could not fetch Snowpark objects from {py_file} due to {sdbx_err}, continuing execution for the rest of the Python files."
136
136
  )
137
137
  return None
138
138
  except Exception as err:
139
139
  cc.warning(
140
- f"Could not fetch Snowpark objects from {py_file} due to {err}, continuing execution for the rest of the python files."
140
+ f"Could not fetch Snowpark objects from {py_file} due to {err}, continuing execution for the rest of the Python files."
141
141
  )
142
142
  return None
143
143
 
@@ -145,22 +145,22 @@ def _execute_in_sandbox(
145
145
  cc.warning(
146
146
  f"Could not fetch Snowpark objects from {py_file} due to the following error:\n {completed_process.stderr}"
147
147
  )
148
- cc.warning("Continuing execution for the rest of the python files.")
148
+ cc.warning("Continuing execution for the rest of the Python files.")
149
149
  return None
150
150
 
151
151
  try:
152
152
  return json.loads(completed_process.stdout)
153
153
  except Exception as exc:
154
154
  cc.warning(
155
- f"Could not load JSON into python due to the following exception: {exc}"
155
+ f"Could not load JSON into Python due to the following exception: {exc}"
156
156
  )
157
- cc.warning(f"Continuing execution for the rest of the python files.")
157
+ cc.warning(f"Continuing execution for the rest of the Python files.")
158
158
  return None
159
159
 
160
160
 
161
161
  class SnowparkAnnotationProcessor(ArtifactProcessor):
162
162
  """
163
- Built-in Processor to discover Snowpark-annotated objects in a given set of python files,
163
+ Built-in Processor to discover Snowpark-annotated objects in a given set of Python files,
164
164
  and generate SQL code for creation of extension functions based on those discovered objects.
165
165
  """
166
166
 
@@ -174,7 +174,7 @@ class SnowparkAnnotationProcessor(ArtifactProcessor):
174
174
  **kwargs,
175
175
  ) -> None:
176
176
  """
177
- Collects code annotations from Snowpark python files containing extension functions and augments the existing
177
+ Collects code annotations from Snowpark Python files containing extension functions and augments the existing
178
178
  setup script with generated SQL that registers these functions.
179
179
  """
180
180
 
@@ -360,7 +360,7 @@ class SnowparkAnnotationProcessor(ArtifactProcessor):
360
360
 
361
361
  def generate_new_sql_file_name(self, py_file: Path) -> Path:
362
362
  """
363
- Generates a SQL filename for the generated root from the python file, and creates its parent directories.
363
+ Generates a SQL filename for the generated root from the Python file, and creates its parent directories.
364
364
  """
365
365
  relative_py_file = py_file.relative_to(self._bundle_ctx.deploy_root)
366
366
  sql_file = Path(