snowflake-cli 3.0.1__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 (61) 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/commands.py +0 -2
  25. snowflake/cli/_plugins/snowpark/common.py +60 -18
  26. snowflake/cli/_plugins/snowpark/package/anaconda_packages.py +2 -2
  27. snowflake/cli/_plugins/snowpark/package/commands.py +0 -2
  28. snowflake/cli/_plugins/snowpark/package_utils.py +27 -38
  29. snowflake/cli/_plugins/snowpark/snowpark_entity_model.py +5 -2
  30. snowflake/cli/_plugins/spcs/image_repository/commands.py +4 -37
  31. snowflake/cli/_plugins/spcs/image_repository/manager.py +4 -1
  32. snowflake/cli/_plugins/spcs/services/commands.py +36 -4
  33. snowflake/cli/_plugins/spcs/services/manager.py +36 -4
  34. snowflake/cli/_plugins/stage/commands.py +8 -3
  35. snowflake/cli/_plugins/stage/diff.py +16 -16
  36. snowflake/cli/_plugins/stage/manager.py +164 -73
  37. snowflake/cli/_plugins/stage/md5.py +1 -1
  38. snowflake/cli/_plugins/workspace/commands.py +21 -1
  39. snowflake/cli/_plugins/workspace/context.py +38 -0
  40. snowflake/cli/_plugins/workspace/manager.py +23 -13
  41. snowflake/cli/api/cli_global_context.py +3 -3
  42. snowflake/cli/api/commands/flags.py +23 -7
  43. snowflake/cli/api/config.py +7 -4
  44. snowflake/cli/api/connections.py +12 -1
  45. snowflake/cli/api/entities/common.py +4 -2
  46. snowflake/cli/api/entities/utils.py +17 -37
  47. snowflake/cli/api/exceptions.py +32 -0
  48. snowflake/cli/api/identifiers.py +8 -0
  49. snowflake/cli/api/project/definition_conversion.py +139 -40
  50. snowflake/cli/api/project/schemas/entities/common.py +11 -0
  51. snowflake/cli/api/project/schemas/project_definition.py +30 -25
  52. snowflake/cli/api/sql_execution.py +5 -7
  53. snowflake/cli/api/stage_path.py +241 -0
  54. snowflake/cli/api/utils/definition_rendering.py +3 -5
  55. {snowflake_cli-3.0.1.dist-info → snowflake_cli-3.1.0.dist-info}/METADATA +11 -11
  56. {snowflake_cli-3.0.1.dist-info → snowflake_cli-3.1.0.dist-info}/RECORD +59 -59
  57. snowflake/cli/_plugins/nativeapp/teardown_processor.py +0 -70
  58. snowflake/cli/_plugins/workspace/action_context.py +0 -18
  59. {snowflake_cli-3.0.1.dist-info → snowflake_cli-3.1.0.dist-info}/WHEEL +0 -0
  60. {snowflake_cli-3.0.1.dist-info → snowflake_cli-3.1.0.dist-info}/entry_points.txt +0 -0
  61. {snowflake_cli-3.0.1.dist-info → snowflake_cli-3.1.0.dist-info}/licenses/LICENSE +0 -0
@@ -21,7 +21,6 @@ import os
21
21
  import re
22
22
  import subprocess
23
23
  from pathlib import Path
24
- from textwrap import dedent
25
24
  from typing import Dict, List, Optional
26
25
 
27
26
  from click import ClickException
@@ -97,6 +96,7 @@ def get_package_name_from_pip_wheel(package: str, index_url: str | None = None)
97
96
  download_dir=tmp_dir.path,
98
97
  index_url=index_url,
99
98
  dependencies=False,
99
+ raise_on_error=False,
100
100
  )
101
101
  file_list = [
102
102
  f.path.name for f in tmp_dir.iterdir() if f.path.name.endswith(".whl")
@@ -117,7 +117,6 @@ def _write_requirements_file(file_path: SecurePath, requirements: List[Requireme
117
117
 
118
118
  @dataclasses.dataclass
119
119
  class DownloadUnavailablePackagesResult:
120
- succeeded: bool
121
120
  error_message: str | None = None
122
121
  anaconda_packages: List[Requirement] = dataclasses.field(default_factory=list)
123
122
  downloaded_packages_details: List[RequirementWithFiles] = dataclasses.field(
@@ -151,8 +150,7 @@ def download_unavailable_packages(
151
150
  if not requirements:
152
151
  # all packages are available in Snowflake
153
152
  return DownloadUnavailablePackagesResult(
154
- succeeded=True,
155
- anaconda_packages=packages_in_snowflake,
153
+ anaconda_packages=packages_in_snowflake
156
154
  )
157
155
 
158
156
  # download all packages with their dependencies
@@ -160,19 +158,14 @@ def download_unavailable_packages(
160
158
  # This is a Windows workaround where use TemporaryDirectory instead of NamedTemporaryFile
161
159
  requirements_file = downloads_dir / "requirements.txt"
162
160
  _write_requirements_file(requirements_file, requirements) # type: ignore
163
- pip_wheel_result = pip_wheel(
161
+ pip_wheel(
164
162
  package_name=None,
165
163
  requirements_file=requirements_file.path, # type: ignore
166
164
  download_dir=downloads_dir.path,
167
165
  index_url=pip_index_url,
168
166
  dependencies=True,
167
+ raise_on_error=True,
169
168
  )
170
- if pip_wheel_result != 0:
171
- log.info(_pip_failed_log_msg(pip_wheel_result))
172
- return DownloadUnavailablePackagesResult(
173
- succeeded=False,
174
- error_message=_pip_failed_log_msg(pip_wheel_result),
175
- )
176
169
 
177
170
  # scan all downloaded packages and filter out ones available on Anaconda
178
171
  dependencies = split_downloaded_dependencies(
@@ -196,7 +189,6 @@ def download_unavailable_packages(
196
189
  for package in dependencies.unavailable_dependencies_wheels:
197
190
  package.extract_files(target_dir.path)
198
191
  return DownloadUnavailablePackagesResult(
199
- succeeded=True,
200
192
  anaconda_packages=packages_in_snowflake,
201
193
  downloaded_packages_details=[
202
194
  RequirementWithFiles(requirement=dep.requirement, files=dep.namelist())
@@ -211,7 +203,8 @@ def pip_wheel(
211
203
  download_dir: Path,
212
204
  index_url: Optional[str],
213
205
  dependencies: bool = True,
214
- ):
206
+ raise_on_error: bool = True,
207
+ ) -> int:
215
208
  command = ["-m", "pip", "wheel", "-w", str(download_dir)]
216
209
  if package_name:
217
210
  command.append(package_name)
@@ -222,23 +215,30 @@ def pip_wheel(
222
215
  if not dependencies:
223
216
  command.append("--no-deps")
224
217
 
225
- try:
218
+ log.info(
219
+ "Running pip wheel with command: %s",
220
+ " ".join([str(com) for com in command]),
221
+ )
222
+ result = subprocess.run(
223
+ ["python", *command],
224
+ capture_output=True,
225
+ text=True,
226
+ encoding=locale.getpreferredencoding(),
227
+ )
228
+ if result.returncode != 0:
226
229
  log.info(
227
- "Running pip wheel with command: %s",
228
- " ".join([str(com) for com in command]),
229
- )
230
- process = subprocess.run(
231
- ["python", *command],
232
- capture_output=True,
233
- text=True,
234
- encoding=locale.getpreferredencoding(),
230
+ "pip wheel finished with error code %d. Details: %s",
231
+ result.returncode,
232
+ result.stdout + result.stderr,
235
233
  )
236
- except subprocess.CalledProcessError as e:
237
- log.error("Encountered error %s", e.stderr)
238
- raise ClickException(f"Encountered error while running pip wheel.")
234
+ if raise_on_error:
235
+ raise ClickException(
236
+ f"pip wheel finished with error code {result.returncode}. Please re-run with --verbose or --debug for more details."
237
+ )
238
+ else:
239
+ log.info("pip wheel command executed successfully")
239
240
 
240
- log.info("Pip wheel command executed successfully")
241
- return process.returncode
241
+ return result.returncode
242
242
 
243
243
 
244
244
  @dataclasses.dataclass
@@ -341,14 +341,3 @@ def _log_dependencies_found_in_conda(available_dependencies: List[Requirement])
341
341
  )
342
342
  else:
343
343
  log.info("None of the package dependencies were found on Anaconda")
344
-
345
-
346
- def _pip_failed_log_msg(return_code: int) -> str:
347
- return dedent(
348
- f"""
349
- pip failed with return code {return_code}. Most likely reasons:
350
- * incorrect package name or version
351
- * package isn't compatible with host architecture (most probably due to .so libraries)
352
- * pip is not installed correctly
353
- """
354
- )
@@ -147,8 +147,11 @@ class UdfSprocIdentifier:
147
147
  self._arg_names, self._arg_types, self._arg_defaults
148
148
  ):
149
149
  s = f"{name} {_type}"
150
- if _default:
151
- if self._is_signature_type_a_string(_type):
150
+ if _default is not None:
151
+ if (
152
+ self._is_signature_type_a_string(_type)
153
+ and _default.lower() != "null"
154
+ ):
152
155
  _default = f"'{_default}'"
153
156
  s += f" default {_default}"
154
157
  sig.append(s)
@@ -39,6 +39,7 @@ from snowflake.cli.api.identifiers import FQN
39
39
  from snowflake.cli.api.output.types import (
40
40
  CollectionResult,
41
41
  MessageResult,
42
+ QueryResult,
42
43
  SingleQueryResult,
43
44
  )
44
45
  from snowflake.cli.api.project.util import is_valid_object_name
@@ -99,44 +100,10 @@ def list_images(
99
100
  **options,
100
101
  ) -> CollectionResult:
101
102
  """Lists images in the given repository."""
102
- repository_manager = ImageRepositoryManager()
103
- database = repository_manager.get_database()
104
- schema = repository_manager.get_schema()
105
- url = repository_manager.get_repository_url(name.identifier)
106
- api_url = repository_manager.get_repository_api_url(url)
107
- bearer_login = RegistryManager().login_to_registry(api_url)
108
- repos = []
109
- query: Optional[str] = f"{api_url}/_catalog?n=10"
110
-
111
- while query:
112
- # Make paginated catalog requests
113
- response = requests.get(
114
- query, headers={"Authorization": f"Bearer {bearer_login}"}
115
- )
116
-
117
- if response.status_code != 200:
118
- raise ClickException(f"Call to the registry failed {response.text}")
119
-
120
- data = json.loads(response.text)
121
- if "repositories" in data:
122
- repos.extend(data["repositories"])
123
-
124
- if "Link" in response.headers:
125
- # There are more results
126
- query = f"{api_url}/_catalog?n=10&last={repos[-1]}"
127
- else:
128
- query = None
129
-
130
- images = []
131
- for repo in repos:
132
- prefix = f"/{database}/{schema}/{name}/"
133
- repo = repo.replace("baserepo/", prefix)
134
- images.append({"image": repo})
135
-
136
- return CollectionResult(images)
103
+ return QueryResult(ImageRepositoryManager().list_images(name.identifier))
137
104
 
138
105
 
139
- @app.command("list-tags", requires_connection=True)
106
+ @app.command("list-tags", requires_connection=True, deprecated=True)
140
107
  def list_tags(
141
108
  name: FQN = REPO_NAME_ARGUMENT,
142
109
  image_name: str = typer.Option(
@@ -149,7 +116,7 @@ def list_tags(
149
116
  ),
150
117
  **options,
151
118
  ) -> CollectionResult:
152
- """Lists tags for the given image in a repository."""
119
+ """Lists tags for the given image in a repository. This command is deprecated and will be removed in a future release. Use `list-images` instead."""
153
120
 
154
121
  repository_manager = ImageRepositoryManager()
155
122
  url = repository_manager.get_repository_url(name.identifier)
@@ -18,6 +18,7 @@ from snowflake.cli._plugins.spcs.common import handle_object_already_exists
18
18
  from snowflake.cli.api.constants import ObjectType
19
19
  from snowflake.cli.api.identifiers import FQN
20
20
  from snowflake.cli.api.sql_execution import SqlExecutionMixin
21
+ from snowflake.connector.cursor import SnowflakeCursor
21
22
  from snowflake.connector.errors import ProgrammingError
22
23
 
23
24
 
@@ -32,7 +33,6 @@ class ImageRepositoryManager(SqlExecutionMixin):
32
33
  return self._conn.role
33
34
 
34
35
  def get_repository_url(self, repo_name: str, with_scheme: bool = True):
35
-
36
36
  repo_row = self.show_specific_object(
37
37
  "image repositories", repo_name, check_schema=True
38
38
  )
@@ -82,3 +82,6 @@ class ImageRepositoryManager(SqlExecutionMixin):
82
82
  handle_object_already_exists(
83
83
  e, ObjectType.IMAGE_REPOSITORY, name, replace_available=True
84
84
  )
85
+
86
+ def list_images(self, repo_name: str) -> SnowflakeCursor:
87
+ return self._execute_query(f"show images in image repository {repo_name}")
@@ -131,7 +131,7 @@ def create(
131
131
  external_access_integrations: Optional[List[str]] = typer.Option(
132
132
  None,
133
133
  "--eai-name",
134
- help="Identifies External Access Integrations(EAI) that the service can access. This option may be specified multiple times for multiple EAIs.",
134
+ help="Identifies external access integrations (EAI) that the service can access. This option may be specified multiple times for multiple EAIs.",
135
135
  ),
136
136
  query_warehouse: Optional[str] = QueryWarehouseOption(),
137
137
  tags: Optional[List[Tag]] = TagOption(help="Tag for the service."),
@@ -174,7 +174,7 @@ def execute_job(
174
174
  external_access_integrations: Optional[List[str]] = typer.Option(
175
175
  None,
176
176
  "--eai-name",
177
- help="Identifies External Access Integrations(EAI) that the job service can access. This option may be specified multiple times for multiple EAIs.",
177
+ help="Identifies external access integrations (EAI) that the job service can access. This option may be specified multiple times for multiple EAIs.",
178
178
  ),
179
179
  query_warehouse: Optional[str] = QueryWarehouseOption(),
180
180
  comment: Optional[str] = CommentOption(help=_COMMENT_HELP),
@@ -194,10 +194,10 @@ def execute_job(
194
194
  return SingleQueryResult(cursor)
195
195
 
196
196
 
197
- @app.command(requires_connection=True)
197
+ @app.command(requires_connection=True, deprecated=True)
198
198
  def status(name: FQN = ServiceNameArgument, **options) -> CommandResult:
199
199
  """
200
- Retrieves the status of a service.
200
+ Retrieves the status of a service. This command is deprecated and will be removed in a future release. Use `describe` instead to get service status and use `list-instances` and `list-containers` to get more detailed information about service instances and containers.
201
201
  """
202
202
  cursor = ServiceManager().status(service_name=name.identifier)
203
203
  return QueryJsonValueResult(cursor)
@@ -259,6 +259,32 @@ def list_endpoints(name: FQN = ServiceNameArgument, **options):
259
259
  return QueryResult(ServiceManager().list_endpoints(service_name=name.identifier))
260
260
 
261
261
 
262
+ @app.command("list-instances", requires_connection=True)
263
+ def list_service_instances(name: FQN = ServiceNameArgument, **options) -> CommandResult:
264
+ """
265
+ Lists all service instances in a service.
266
+ """
267
+ return QueryResult(ServiceManager().list_instances(service_name=name.identifier))
268
+
269
+
270
+ @app.command("list-containers", requires_connection=True)
271
+ def list_service_containers(
272
+ name: FQN = ServiceNameArgument, **options
273
+ ) -> CommandResult:
274
+ """
275
+ Lists all service containers in a service.
276
+ """
277
+ return QueryResult(ServiceManager().list_containers(service_name=name.identifier))
278
+
279
+
280
+ @app.command("list-roles", requires_connection=True)
281
+ def list_service_roles(name: FQN = ServiceNameArgument, **options) -> CommandResult:
282
+ """
283
+ Lists all service roles in a service.
284
+ """
285
+ return QueryResult(ServiceManager().list_roles(service_name=name.identifier))
286
+
287
+
262
288
  @app.command(requires_connection=True)
263
289
  def suspend(name: FQN = ServiceNameArgument, **options) -> CommandResult:
264
290
  """
@@ -282,6 +308,11 @@ def set_property(
282
308
  max_instances: Optional[int] = MaxInstancesOption(show_default=False),
283
309
  query_warehouse: Optional[str] = QueryWarehouseOption(show_default=False),
284
310
  auto_resume: Optional[bool] = AutoResumeOption(default=None, show_default=False),
311
+ external_access_integrations: Optional[List[str]] = typer.Option(
312
+ None,
313
+ "--eai-name",
314
+ help="Identifies external access integrations (EAI) that the service can access. This option may be specified multiple times for multiple EAIs.",
315
+ ),
285
316
  comment: Optional[str] = CommentOption(help=_COMMENT_HELP, show_default=False),
286
317
  **options,
287
318
  ):
@@ -294,6 +325,7 @@ def set_property(
294
325
  max_instances=max_instances,
295
326
  query_warehouse=query_warehouse,
296
327
  auto_resume=auto_resume,
328
+ external_access_integrations=external_access_integrations,
297
329
  comment=comment,
298
330
  )
299
331
  return SingleQueryResult(cursor)
@@ -147,6 +147,15 @@ class ServiceManager(SqlExecutionMixin):
147
147
  def list_endpoints(self, service_name: str) -> SnowflakeCursor:
148
148
  return self._execute_query(f"show endpoints in service {service_name}")
149
149
 
150
+ def list_instances(self, service_name: str) -> SnowflakeCursor:
151
+ return self._execute_query(f"show service instances in service {service_name}")
152
+
153
+ def list_containers(self, service_name: str) -> SnowflakeCursor:
154
+ return self._execute_query(f"show service containers in service {service_name}")
155
+
156
+ def list_roles(self, service_name: str) -> SnowflakeCursor:
157
+ return self._execute_query(f"show roles in service {service_name}")
158
+
150
159
  def suspend(self, service_name: str):
151
160
  return self._execute_query(f"alter service {service_name} suspend")
152
161
 
@@ -160,6 +169,7 @@ class ServiceManager(SqlExecutionMixin):
160
169
  max_instances: Optional[int],
161
170
  query_warehouse: Optional[str],
162
171
  auto_resume: Optional[bool],
172
+ external_access_integrations: Optional[List[str]],
163
173
  comment: Optional[str],
164
174
  ):
165
175
  property_pairs = [
@@ -167,6 +177,7 @@ class ServiceManager(SqlExecutionMixin):
167
177
  ("max_instances", max_instances),
168
178
  ("query_warehouse", query_warehouse),
169
179
  ("auto_resume", auto_resume),
180
+ ("external_access_integrations", external_access_integrations),
170
181
  ("comment", comment),
171
182
  ]
172
183
 
@@ -175,10 +186,31 @@ class ServiceManager(SqlExecutionMixin):
175
186
  raise NoPropertiesProvidedError(
176
187
  f"No properties specified for service '{service_name}'. Please provide at least one property to set."
177
188
  )
178
- query: List[str] = [f"alter service {service_name} set"]
179
- for property_name, value in property_pairs:
180
- if value is not None:
181
- query.append(f"{property_name} = {value}")
189
+ query: List[str] = [f"alter service {service_name} set "]
190
+
191
+ if min_instances is not None:
192
+ query.append(f" min_instances = {min_instances}")
193
+
194
+ if max_instances is not None:
195
+ query.append(f" max_instances = {max_instances}")
196
+
197
+ if query_warehouse is not None:
198
+ query.append(f" query_warehouse = {query_warehouse}")
199
+
200
+ if auto_resume is not None:
201
+ query.append(f" auto_resume = {auto_resume}")
202
+
203
+ if external_access_integrations is not None:
204
+ external_access_integration_list = ",".join(
205
+ f"{e}" for e in external_access_integrations
206
+ )
207
+ query.append(
208
+ f"external_access_integrations = ({external_access_integration_list})"
209
+ )
210
+
211
+ if comment is not None:
212
+ query.append(f" comment = {comment}")
213
+
182
214
  return self._execute_query(strip_empty_lines(query))
183
215
 
184
216
  def unset_property(
@@ -38,6 +38,7 @@ from snowflake.cli.api.commands.flags import (
38
38
  OnErrorOption,
39
39
  PatternOption,
40
40
  identifier_stage_argument,
41
+ identifier_stage_path_argument,
41
42
  like_option,
42
43
  )
43
44
  from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
@@ -60,6 +61,10 @@ app = SnowTyperFactory(
60
61
  )
61
62
 
62
63
  StageNameArgument = identifier_stage_argument(sf_object="stage", example="@my_stage")
64
+ StagePathArgument = identifier_stage_path_argument(
65
+ sf_object="stage", example="@my_stage/path"
66
+ )
67
+
63
68
 
64
69
  add_object_command_aliases(
65
70
  app=app,
@@ -74,7 +79,7 @@ add_object_command_aliases(
74
79
 
75
80
  @app.command("list-files", requires_connection=True)
76
81
  def stage_list_files(
77
- stage_name: str = StageNameArgument, pattern=PatternOption, **options
82
+ stage_name: str = StagePathArgument, pattern=PatternOption, **options
78
83
  ) -> CommandResult:
79
84
  """
80
85
  Lists the stage contents.
@@ -208,11 +213,11 @@ def execute(
208
213
  **options,
209
214
  ):
210
215
  """
211
- Execute immediate all files from the stage path. Files can be filtered with glob like pattern,
216
+ Execute immediate all files from the stage path. Files can be filtered with a glob-like pattern,
212
217
  e.g. `@stage/*.sql`, `@stage/dev/*`. Only files with `.sql` extension will be executed.
213
218
  """
214
219
  results = StageManager().execute(
215
- stage_path=stage_path, on_error=on_error, variables=variables
220
+ stage_path_str=stage_path, on_error=on_error, variables=variables
216
221
  )
217
222
  return CollectionResult(results)
218
223
 
@@ -30,7 +30,7 @@ from .md5 import UnknownMD5FormatError, file_matches_md5sum
30
30
 
31
31
  log = logging.getLogger(__name__)
32
32
 
33
- StagePath = PurePosixPath # alias PurePosixPath as StagePath for clarity
33
+ StagePathType = PurePosixPath # alias PurePosixPath as StagePath for clarity
34
34
 
35
35
 
36
36
  @dataclass
@@ -39,16 +39,16 @@ class DiffResult:
39
39
  Each collection is a list of stage paths ('/'-separated, regardless of the platform), relative to the stage root.
40
40
  """
41
41
 
42
- identical: List[StagePath] = field(default_factory=list)
42
+ identical: List[StagePathType] = field(default_factory=list)
43
43
  "Files with matching md5sums"
44
44
 
45
- different: List[StagePath] = field(default_factory=list)
45
+ different: List[StagePathType] = field(default_factory=list)
46
46
  "Files that may be different between the stage and the local directory"
47
47
 
48
- only_local: List[StagePath] = field(default_factory=list)
48
+ only_local: List[StagePathType] = field(default_factory=list)
49
49
  "Files that only exist in the local directory"
50
50
 
51
- only_on_stage: List[StagePath] = field(default_factory=list)
51
+ only_on_stage: List[StagePathType] = field(default_factory=list)
52
52
  "Files that only exist on the stage"
53
53
 
54
54
  def has_changes(self) -> bool:
@@ -83,12 +83,12 @@ def enumerate_files(path: Path) -> List[Path]:
83
83
  return paths
84
84
 
85
85
 
86
- def strip_stage_name(path: str) -> StagePath:
86
+ def strip_stage_name(path: str) -> StagePathType:
87
87
  """Returns the given stage path without the stage name as the first part."""
88
- return StagePath(*path.split("/")[1:])
88
+ return StagePathType(*path.split("/")[1:])
89
89
 
90
90
 
91
- def build_md5_map(list_stage_cursor: DictCursor) -> Dict[StagePath, Optional[str]]:
91
+ def build_md5_map(list_stage_cursor: DictCursor) -> Dict[StagePathType, Optional[str]]:
92
92
  """
93
93
  Returns a mapping of relative stage paths to their md5sums.
94
94
  """
@@ -99,7 +99,7 @@ def build_md5_map(list_stage_cursor: DictCursor) -> Dict[StagePath, Optional[str
99
99
 
100
100
 
101
101
  def preserve_from_diff(
102
- diff: DiffResult, stage_paths_to_sync: Collection[StagePath]
102
+ diff: DiffResult, stage_paths_to_sync: Collection[StagePathType]
103
103
  ) -> DiffResult:
104
104
  """
105
105
  Returns a filtered version of the provided diff, keeping only the provided stage paths.
@@ -163,7 +163,7 @@ def compute_stage_diff(
163
163
  return result
164
164
 
165
165
 
166
- def get_stage_subpath(stage_path: StagePath) -> str:
166
+ def get_stage_subpath(stage_path: StagePathType) -> str:
167
167
  """
168
168
  Returns the parent portion of a stage path, as a string, for inclusion in the fully qualified stage path. Note that
169
169
  '.' treated specially here, and so the return value of this call is not a `StagePath` instance.
@@ -172,21 +172,21 @@ def get_stage_subpath(stage_path: StagePath) -> str:
172
172
  return "" if parent == "." else parent
173
173
 
174
174
 
175
- def to_stage_path(filename: Path) -> StagePath:
175
+ def to_stage_path(filename: Path) -> StagePathType:
176
176
  """
177
177
  Returns the stage file name, with the path separator suitably transformed if needed.
178
178
  """
179
- return StagePath(*filename.parts)
179
+ return StagePathType(*filename.parts)
180
180
 
181
181
 
182
- def to_local_path(stage_path: StagePath) -> Path:
182
+ def to_local_path(stage_path: StagePathType) -> Path:
183
183
  return Path(*stage_path.parts)
184
184
 
185
185
 
186
186
  def delete_only_on_stage_files(
187
187
  stage_manager: StageManager,
188
188
  stage_fqn: str,
189
- only_on_stage: List[StagePath],
189
+ only_on_stage: List[StagePathType],
190
190
  role: Optional[str] = None,
191
191
  ):
192
192
  """
@@ -200,7 +200,7 @@ def put_files_on_stage(
200
200
  stage_manager: StageManager,
201
201
  stage_fqn: str,
202
202
  deploy_root_path: Path,
203
- stage_paths: List[StagePath],
203
+ stage_paths: List[StagePathType],
204
204
  role: Optional[str] = None,
205
205
  overwrite: bool = False,
206
206
  ):
@@ -254,7 +254,7 @@ def sync_local_diff_with_stage(
254
254
 
255
255
 
256
256
  def _to_src_dest_pair(
257
- stage_path: StagePath, bundle_map: Optional[BundleMap]
257
+ stage_path: StagePathType, bundle_map: Optional[BundleMap]
258
258
  ) -> Tuple[Optional[str], str]:
259
259
  if not bundle_map:
260
260
  return None, str(stage_path)