peak-sdk 1.6.0__py3-none-any.whl → 1.8.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. peak/_metadata.py +78 -58
  2. peak/_version.py +1 -1
  3. peak/callbacks.py +22 -2
  4. peak/cli/args.py +19 -0
  5. peak/cli/helpers.py +12 -8
  6. peak/cli/press/apps/deployments.py +72 -18
  7. peak/cli/press/apps/specs.py +29 -11
  8. peak/cli/press/blocks/deployments.py +71 -18
  9. peak/cli/press/blocks/specs.py +95 -35
  10. peak/cli/press/deployments.py +40 -1
  11. peak/cli/press/specs.py +2 -2
  12. peak/cli/resources/alerts/emails.py +4 -5
  13. peak/cli/resources/artifacts.py +9 -9
  14. peak/cli/resources/images.py +29 -18
  15. peak/cli/resources/services.py +6 -7
  16. peak/cli/resources/tenants.py +4 -2
  17. peak/cli/resources/users.py +3 -3
  18. peak/cli/resources/webapps.py +6 -6
  19. peak/cli/resources/workflows.py +24 -25
  20. peak/compression.py +28 -13
  21. peak/exceptions.py +15 -1
  22. peak/handler.py +5 -1
  23. peak/helpers.py +38 -0
  24. peak/output.py +13 -6
  25. peak/press/apps.py +43 -3
  26. peak/press/blocks.py +450 -138
  27. peak/press/deployments.py +25 -0
  28. peak/resources/images.py +309 -86
  29. peak/sample_yaml/press/apps/specs/create_app_spec.yaml +2 -0
  30. peak/sample_yaml/press/apps/specs/create_app_spec_release.yaml +2 -0
  31. peak/sample_yaml/press/blocks/specs/service/api/create_block_spec.yaml +102 -0
  32. peak/sample_yaml/press/blocks/specs/service/api/create_block_spec_release.yaml +88 -0
  33. peak/sample_yaml/press/blocks/specs/service/webapp/create_block_spec.yaml +103 -0
  34. peak/sample_yaml/press/blocks/specs/service/webapp/create_block_spec_release.yaml +89 -0
  35. peak/sample_yaml/press/blocks/specs/{create_block_spec.yaml → workflow/create_block_spec.yaml} +20 -1
  36. peak/sample_yaml/press/blocks/specs/{create_block_spec_release.yaml → workflow/create_block_spec_release.yaml} +20 -1
  37. peak/sample_yaml/resources/images/dockerfile/create_image.yaml +3 -0
  38. peak/sample_yaml/resources/images/dockerfile/create_image_version.yaml +3 -0
  39. peak/sample_yaml/resources/images/dockerfile/update_version.yaml +3 -0
  40. peak/sample_yaml/resources/images/github/create_image.yaml +3 -0
  41. peak/sample_yaml/resources/images/github/create_image_version.yaml +3 -0
  42. peak/sample_yaml/resources/images/github/update_version.yaml +3 -0
  43. peak/sample_yaml/resources/images/upload/create_image.yaml +3 -0
  44. peak/sample_yaml/resources/images/upload/create_image_version.yaml +3 -0
  45. peak/sample_yaml/resources/images/upload/create_or_update_image.yaml +3 -0
  46. peak/sample_yaml/resources/images/upload/update_version.yaml +3 -0
  47. peak/sample_yaml/resources/workflows/create_or_update_workflow.yaml +9 -1
  48. peak/sample_yaml/resources/workflows/create_workflow.yaml +9 -1
  49. peak/sample_yaml/resources/workflows/patch_workflow.yaml +9 -1
  50. peak/sample_yaml/resources/workflows/update_workflow.yaml +9 -1
  51. peak/session.py +1 -1
  52. peak/template.py +21 -2
  53. {peak_sdk-1.6.0.dist-info → peak_sdk-1.8.0.dist-info}/METADATA +18 -18
  54. {peak_sdk-1.6.0.dist-info → peak_sdk-1.8.0.dist-info}/RECORD +57 -53
  55. {peak_sdk-1.6.0.dist-info → peak_sdk-1.8.0.dist-info}/LICENSE +0 -0
  56. {peak_sdk-1.6.0.dist-info → peak_sdk-1.8.0.dist-info}/WHEEL +0 -0
  57. {peak_sdk-1.6.0.dist-info → peak_sdk-1.8.0.dist-info}/entry_points.txt +0 -0
@@ -107,7 +107,7 @@ MAPPING = {
107
107
  }
108
108
 
109
109
 
110
- @app.command("list", short_help="List services.", options_metavar="list_services")
110
+ @app.command("list", short_help="List services.")
111
111
  def list_services(
112
112
  ctx: typer.Context,
113
113
  page_size: Optional[int] = args.PAGE_SIZE,
@@ -155,7 +155,7 @@ def list_services(
155
155
  writer.write(response)
156
156
 
157
157
 
158
- @app.command(short_help="Create a new service.", options_metavar="create_service")
158
+ @app.command(short_help="Create a new service.")
159
159
  def create(
160
160
  ctx: typer.Context,
161
161
  file: Annotated[Optional[str], typer.Argument(..., help=FILE_HELP_STRING)] = None,
@@ -267,7 +267,7 @@ def create(
267
267
  writer.write(response)
268
268
 
269
269
 
270
- @app.command(short_help="Update an existing service.", options_metavar="update_service")
270
+ @app.command(short_help="Update an existing service.")
271
271
  def update(
272
272
  ctx: typer.Context,
273
273
  service_id: str = _SERVICE_ID,
@@ -383,7 +383,6 @@ def update(
383
383
 
384
384
  @app.command(
385
385
  short_help="Create a new service or Update an existing service.",
386
- options_metavar="create_or_update_service",
387
386
  )
388
387
  def create_or_update(
389
388
  ctx: typer.Context,
@@ -502,7 +501,7 @@ def create_or_update(
502
501
  writer.write(response)
503
502
 
504
503
 
505
- @app.command(short_help="Delete an existing service.", options_metavar="delete_service")
504
+ @app.command(short_help="Delete an existing service.")
506
505
  def delete(
507
506
  ctx: typer.Context,
508
507
  service_id: str = _SERVICE_ID,
@@ -536,7 +535,7 @@ def delete(
536
535
  writer.write(response)
537
536
 
538
537
 
539
- @app.command(short_help="Describe details of a service.", options_metavar="describe_service")
538
+ @app.command(short_help="Describe details of a service.")
540
539
  def describe(
541
540
  ctx: typer.Context,
542
541
  service_id: str = _SERVICE_ID,
@@ -588,7 +587,7 @@ def describe(
588
587
  writer.write(response)
589
588
 
590
589
 
591
- @app.command(short_help="Test API type service", options_metavar="test_service")
590
+ @app.command(short_help="Test API type service")
592
591
  def test(
593
592
  ctx: typer.Context,
594
593
  service_name: str = _SERVICE_NAME,
@@ -32,13 +32,15 @@ app = typer.Typer(
32
32
  short_help="Create and manage Tenant Settings.",
33
33
  )
34
34
 
35
- _ENTITY_TYPE = typer.Option(..., help="Entity type to be used in this operation (e.g. - feed, workflow).")
35
+ _ENTITY_TYPE = typer.Option(
36
+ ...,
37
+ help="Entity type to be used in this operation (e.g. - `workflow`, `webapp`, `api-deployment`).",
38
+ )
36
39
 
37
40
 
38
41
  @app.command(
39
42
  "list-instance-options",
40
43
  short_help="List tenant instance options.",
41
- options_metavar="list_tenant_instance_options",
42
44
  )
43
45
  def list_instance_options(
44
46
  ctx: typer.Context,
@@ -24,7 +24,7 @@ from typing import Optional
24
24
  import typer
25
25
  from peak import Session
26
26
  from peak.cli.args import OUTPUT_TYPES, PAGING
27
- from peak.constants import OutputTypesNoTable
27
+ from peak.constants import OutputTypes, OutputTypesNoTable
28
28
  from peak.output import Writer
29
29
  from peak.resources import users
30
30
 
@@ -41,7 +41,7 @@ _AUTH_TOKEN = typer.Option(
41
41
  )
42
42
 
43
43
 
44
- @app.command(short_help="Check user permission for a specific feature.", options_metavar="check_permission")
44
+ @app.command(short_help="Check user permission for a specific feature.")
45
45
  def check_permission(
46
46
  ctx: typer.Context,
47
47
  feature: str = _FEATURE,
@@ -68,4 +68,4 @@ def check_permission(
68
68
 
69
69
  with writer.pager():
70
70
  response = user_client.check_permissions({feature: action})
71
- writer.write(response.get(feature, False))
71
+ writer.write(response.get(feature, False), output_type=OutputTypes.json)
@@ -84,7 +84,7 @@ MAPPING = {"imageId": "imageDetails", "versionId": "imageDetails", "instanceType
84
84
  DEPRECATION_MESSAGE = "The Web App commands are deprecated and will eventually be removed. We recommend using the Service feature which offers a more comprehensive and flexible solution for managing web applications and API deployments.\n"
85
85
 
86
86
 
87
- @app.command("list", short_help="List webapps.", options_metavar="list_webapps")
87
+ @app.command("list", short_help="List webapps.")
88
88
  def list_webapps(
89
89
  ctx: typer.Context,
90
90
  page_size: Optional[int] = args.PAGE_SIZE,
@@ -138,7 +138,7 @@ def list_webapps(
138
138
  writer.write(response, deprecation_message=DEPRECATION_MESSAGE)
139
139
 
140
140
 
141
- @app.command(short_help="Create a new webapp.", options_metavar="create_webapp")
141
+ @app.command(short_help="Create a new webapp.")
142
142
  def create(
143
143
  ctx: typer.Context,
144
144
  file: Annotated[Optional[str], typer.Argument(..., help=FILE_HELP_STRING)] = None,
@@ -229,7 +229,7 @@ def create(
229
229
  writer.write(response, deprecation_message=DEPRECATION_MESSAGE)
230
230
 
231
231
 
232
- @app.command(short_help="Update an existing webapp.", options_metavar="update_webapp")
232
+ @app.command(short_help="Update an existing webapp.")
233
233
  def update(
234
234
  ctx: typer.Context,
235
235
  webapp_id: str = _WEBAPP_ID,
@@ -322,7 +322,7 @@ def update(
322
322
  writer.write(response, deprecation_message=DEPRECATION_MESSAGE)
323
323
 
324
324
 
325
- @app.command(short_help="Create a new webapp or Update an existing webapp.", options_metavar="create_or_update_webapp")
325
+ @app.command(short_help="Create a new webapp or Update an existing webapp.")
326
326
  def create_or_update(
327
327
  ctx: typer.Context,
328
328
  file: Annotated[Optional[str], typer.Argument(..., help=FILE_HELP_STRING)] = None,
@@ -415,7 +415,7 @@ def create_or_update(
415
415
  writer.write(response, deprecation_message=DEPRECATION_MESSAGE)
416
416
 
417
417
 
418
- @app.command(short_help="Delete an existing webapp.", options_metavar="delete_webapp")
418
+ @app.command(short_help="Delete an existing webapp.")
419
419
  def delete(
420
420
  ctx: typer.Context,
421
421
  webapp_id: str = _WEBAPP_ID,
@@ -457,7 +457,7 @@ def delete(
457
457
  writer.write(response, deprecation_message=DEPRECATION_MESSAGE)
458
458
 
459
459
 
460
- @app.command(short_help="Describe details of a webapp.", options_metavar="describe_webapp")
460
+ @app.command(short_help="Describe details of a webapp.")
461
461
  def describe(
462
462
  ctx: typer.Context,
463
463
  webapp_id: str = _WEBAPP_ID,
@@ -93,7 +93,7 @@ _COUNT = typer.Option(
93
93
  )
94
94
 
95
95
 
96
- @app.command(short_help="Create a new workflow.", options_metavar="create_workflow")
96
+ @app.command(short_help="Create a new workflow.")
97
97
  def create(
98
98
  ctx: typer.Context,
99
99
  file: str = args.TEMPLATE_PATH,
@@ -119,12 +119,12 @@ def create(
119
119
  watchers (list(map) | required: false):
120
120
  - events (map):
121
121
  success (boolean | required: false): Whether to call event on success.
122
- fail (boolean | required: false): Whether to call event on success.
122
+ fail (boolean | required: false): Whether to call event on failure.
123
123
  runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
124
124
  user (string): User to be notified.
125
125
  - events (map):
126
126
  success (boolean | required: false): Whether to call event on success.
127
- fail (boolean | required: false): Whether to call event on success.
127
+ fail (boolean | required: false): Whether to call event on failure.
128
128
  runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
129
129
  webhook (map):
130
130
  name (string): Name of the webhook.
@@ -132,7 +132,7 @@ def create(
132
132
  payload (string): Webhook payload.
133
133
  - events (map):
134
134
  success (boolean | required: false): Whether to call event on success.
135
- fail (boolean | required: false): Whether to call event on success.
135
+ fail (boolean | required: false): Whether to call event on failure.
136
136
  runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
137
137
  email (map):
138
138
  name (string): Name of the email watcher.
@@ -216,7 +216,7 @@ def create(
216
216
  writer.write(response)
217
217
 
218
218
 
219
- @app.command(short_help="Update an existing workflow.", options_metavar="update_workflow")
219
+ @app.command(short_help="Update an existing workflow.")
220
220
  def update(
221
221
  ctx: typer.Context,
222
222
  workflow_id: int = _WORKFLOW_ID,
@@ -243,12 +243,12 @@ def update(
243
243
  watchers (list(map) | required: false):
244
244
  - events (map):
245
245
  success (boolean | required: false): Whether to call event on success.
246
- fail (boolean | required: false): Whether to call event on success.
246
+ fail (boolean | required: false): Whether to call event on failure.
247
247
  runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
248
248
  user (string): User to be notified.
249
249
  - events (map):
250
250
  success (boolean | required: false): Whether to call event on success.
251
- fail (boolean | required: false): Whether to call event on success.
251
+ fail (boolean | required: false): Whether to call event on failure.
252
252
  runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
253
253
  webhook (map):
254
254
  name (string): Name of the webhook.
@@ -256,7 +256,7 @@ def update(
256
256
  payload (string): Webhook payload.
257
257
  - events (map):
258
258
  success (boolean | required: false): Whether to call event on success.
259
- fail (boolean | required: false): Whether to call event on success.
259
+ fail (boolean | required: false): Whether to call event on failure.
260
260
  runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
261
261
  email (map):
262
262
  name (string): Name of the email watcher.
@@ -342,7 +342,6 @@ def update(
342
342
 
343
343
  @app.command(
344
344
  short_help="Create a new workflow or Update an existing workflow.",
345
- options_metavar="create_or_update_workflow",
346
345
  )
347
346
  def create_or_update(
348
347
  ctx: typer.Context,
@@ -367,12 +366,12 @@ def create_or_update(
367
366
  watchers (list(map) | required: false):
368
367
  - events (map):
369
368
  success (boolean | required: false): Whether to call event on success.
370
- fail (boolean | required: false): Whether to call event on success.
369
+ fail (boolean | required: false): Whether to call event on failure.
371
370
  runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
372
371
  user (string): User to be notified.
373
372
  - events (map):
374
373
  success (boolean | required: false): Whether to call event on success.
375
- fail (boolean | required: false): Whether to call event on success.
374
+ fail (boolean | required: false): Whether to call event on failure.
376
375
  runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
377
376
  webhook (map):
378
377
  name (string): Name of the webhook.
@@ -380,7 +379,7 @@ def create_or_update(
380
379
  payload (string): Webhook payload.
381
380
  - events (map):
382
381
  success (boolean | required: false): Whether to call event on success.
383
- fail (boolean | required: false): Whether to call event on success.
382
+ fail (boolean | required: false): Whether to call event on failure.
384
383
  runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
385
384
  email (map):
386
385
  name (string): Name of the email watcher.
@@ -464,7 +463,7 @@ def create_or_update(
464
463
  writer.write(response)
465
464
 
466
465
 
467
- @app.command(short_help="Update required fields of an existing workflow.", options_metavar="patch_workflow")
466
+ @app.command(short_help="Update required fields of an existing workflow.")
468
467
  def patch(
469
468
  ctx: typer.Context,
470
469
  workflow_id: int = _WORKFLOW_ID,
@@ -521,12 +520,12 @@ def patch(
521
520
  watchers (list(map) | required: false):
522
521
  - events (map):
523
522
  success (boolean | required: false): Whether to call event on success.
524
- fail (boolean | required: false): Whether to call event on success.
523
+ fail (boolean | required: false): Whether to call event on failure.
525
524
  runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
526
525
  user (string): User to be notified.
527
526
  - events (map):
528
527
  success (boolean | required: false): Whether to call event on success.
529
- fail (boolean | required: false): Whether to call event on success.
528
+ fail (boolean | required: false): Whether to call event on failure.
530
529
  runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
531
530
  webhook (map):
532
531
  name (string): Name of the webhook.
@@ -534,7 +533,7 @@ def patch(
534
533
  payload (string): Webhook payload.
535
534
  - events (map):
536
535
  success (boolean | required: false): Whether to call event on success.
537
- fail (boolean | required: false): Whether to call event on success.
536
+ fail (boolean | required: false): Whether to call event on failure.
538
537
  runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
539
538
  email (map):
540
539
  name (string): Name of the email watcher.
@@ -640,7 +639,7 @@ def patch(
640
639
  writer.write(response)
641
640
 
642
641
 
643
- @app.command("list", short_help="List workflows.", options_metavar="list_workflows")
642
+ @app.command("list", short_help="List workflows.")
644
643
  def list_workflows(
645
644
  ctx: typer.Context,
646
645
  page_size: Optional[int] = args.PAGE_SIZE,
@@ -690,7 +689,7 @@ def list_workflows(
690
689
  writer.write(response)
691
690
 
692
691
 
693
- @app.command(short_help="Describe details of a workflow.", options_metavar="describe_workflow")
692
+ @app.command(short_help="Describe details of a workflow.")
694
693
  def describe(
695
694
  ctx: typer.Context,
696
695
  workflow_id: int = _WORKFLOW_ID,
@@ -734,7 +733,7 @@ def describe(
734
733
  writer.write(response)
735
734
 
736
735
 
737
- @app.command(short_help="Delete a workflow.", options_metavar="delete_workflow")
736
+ @app.command(short_help="Delete a workflow.")
738
737
  def delete(
739
738
  ctx: typer.Context,
740
739
  workflow_id: int = _WORKFLOW_ID,
@@ -766,7 +765,7 @@ def delete(
766
765
  writer.write(response)
767
766
 
768
767
 
769
- @app.command(short_help="Start a workflow run.", options_metavar="execute_workflow")
768
+ @app.command(short_help="Start a workflow run.")
770
769
  def execute(
771
770
  ctx: typer.Context,
772
771
  workflow_id: int = _WORKFLOW_ID,
@@ -828,7 +827,7 @@ def execute(
828
827
  writer.write(response)
829
828
 
830
829
 
831
- @app.command(short_help="List all available resources.", options_metavar="list_workflow_resources")
830
+ @app.command(short_help="List all available resources.")
832
831
  def list_resources(
833
832
  ctx: typer.Context,
834
833
  paging: Optional[bool] = PAGING, # noqa: ARG001
@@ -866,7 +865,7 @@ def list_resources(
866
865
  writer.write(response)
867
866
 
868
867
 
869
- @app.command(short_help="List default resources.", options_metavar="list_workflow_default_resources")
868
+ @app.command(short_help="List default resources.")
870
869
  def list_default_resources(
871
870
  ctx: typer.Context,
872
871
  paging: Optional[bool] = PAGING, # noqa: ARG001
@@ -899,7 +898,7 @@ def list_default_resources(
899
898
  writer.write(response)
900
899
 
901
900
 
902
- @app.command(short_help="List executions for the given workflow.", options_metavar="list_workflow_executions")
901
+ @app.command(short_help="List executions for the given workflow.")
903
902
  def list_executions(
904
903
  ctx: typer.Context,
905
904
  workflow_id: int = _WORKFLOW_ID,
@@ -951,7 +950,7 @@ def list_executions(
951
950
  writer.write(response)
952
951
 
953
952
 
954
- @app.command(short_help="Get workflow execution logs.", options_metavar="get_execution_logs")
953
+ @app.command(short_help="Get workflow execution logs.")
955
954
  def get_execution_logs(
956
955
  ctx: typer.Context,
957
956
  workflow_id: int = _WORKFLOW_ID_OPTION,
@@ -1039,7 +1038,7 @@ def get_execution_logs(
1039
1038
  next_token = response.get("nextToken", None)
1040
1039
 
1041
1040
 
1042
- @app.command(short_help="Get workflow execution details.", options_metavar="get_execution_details")
1041
+ @app.command(short_help="Get workflow execution details.")
1043
1042
  def get_execution_details(
1044
1043
  ctx: typer.Context,
1045
1044
  workflow_id: int = _WORKFLOW_ID_OPTION,
peak/compression.py CHANGED
@@ -19,6 +19,7 @@
19
19
  # # along with this program. If not, see <https://apache.org/licenses/LICENSE-2.0>
20
20
  #
21
21
  """Compression module to create zip file to be used as artifact."""
22
+
22
23
  from __future__ import annotations
23
24
 
24
25
  import contextlib
@@ -26,7 +27,7 @@ import os
26
27
  import tempfile
27
28
  import zipfile
28
29
  from pathlib import Path
29
- from typing import Iterable, Iterator, Optional, Set
30
+ from typing import Any, Dict, Iterable, Iterator, Optional, Set
30
31
 
31
32
  from pathspec import PathSpec
32
33
 
@@ -163,7 +164,7 @@ def _load_ignore_patterns(path_obj: Path, ignore_files: Optional[list[str]]) ->
163
164
 
164
165
 
165
166
  def print_file_tree(files: Iterable[str]) -> None:
166
- """Prints list of files in tree format.
167
+ """Prints list of files in tree format with specific limits per level.
167
168
 
168
169
  Args:
169
170
  files (list[str]): List of file paths
@@ -171,33 +172,47 @@ def print_file_tree(files: Iterable[str]) -> None:
171
172
  writer = output.Writer(ignore_debug_mode=True)
172
173
  files_dict = _build_files_dict(files)
173
174
 
174
- def _print_tree(files_dict: dict[str, dict], indent: str) -> None: # type: ignore[type-arg]
175
+ limits = {1: 100, 2: 100, 3: 50}
176
+ default_limit = 25
177
+
178
+ def _print_tree(files_dict: Dict[str, Any], indent: str, level: int) -> None:
179
+ limit = limits.get(level, default_limit)
180
+ count = 0
181
+
182
+ for key, value in files_dict.items():
183
+ if isinstance(value, dict):
184
+ writer.write(f"{indent}{key}/")
185
+ new_indent = indent + ("| " if indent else "├── ")
186
+ _print_tree(value, new_indent, level + 1)
187
+
175
188
  for key, value in files_dict.items():
176
- writer.write(f"{indent}{key}")
177
- if value:
178
- new_indent = "| " if indent else "├── "
179
- _print_tree(value, new_indent + indent)
189
+ if not isinstance(value, dict):
190
+ if count >= limit:
191
+ writer.write(f"{indent}...")
192
+ break
193
+ writer.write(f"{indent}{key}")
194
+ count += 1
180
195
 
181
- _print_tree(files_dict, "")
196
+ _print_tree(files_dict, "", 1)
182
197
 
183
198
 
184
- def _build_files_dict(files: Iterable[str]) -> dict[str, dict]: # type: ignore[type-arg]
199
+ def _build_files_dict(files: Iterable[str]) -> Dict[str, Any]:
185
200
  """Builds a nested dictionary from list of files.
186
201
 
187
202
  Args:
188
203
  files (list[str]): List of file paths to process.
189
204
 
190
205
  Returns:
191
- dict[str, dict]: Nested dict file tree structure.
206
+ dict[str, Any]: Nested dict file tree structure.
192
207
  """
193
- files_dict: dict[str, dict] = {} # type: ignore[type-arg]
208
+ files_dict: Dict[str, Any] = {}
194
209
  for f in files:
195
210
  components = Path(os.path.normpath(f)).parts
196
211
  current_dir = files_dict
197
212
  for directory in components[:-1]:
198
- if directory not in current_dir:
213
+ if directory not in current_dir or current_dir[directory] is None:
199
214
  current_dir[directory] = {}
200
215
  current_dir = current_dir[directory]
201
216
  if components[-1] not in current_dir:
202
- current_dir[components[-1]] = {}
217
+ current_dir[components[-1]] = None
203
218
  return files_dict
peak/exceptions.py CHANGED
@@ -19,6 +19,7 @@
19
19
  # # along with this program. If not, see <https://apache.org/licenses/LICENSE-2.0>
20
20
  #
21
21
  """Exceptions for the Peak API."""
22
+
22
23
  from __future__ import annotations
23
24
 
24
25
  from collections import defaultdict
@@ -103,6 +104,17 @@ class PayloadTooLargeException(BaseHttpException):
103
104
 
104
105
  STATUS_CODE = 413
105
106
 
107
+ def __init__(self, message: str = "") -> None:
108
+ """Throw exception with custom message.
109
+
110
+ Args:
111
+ message (str): Additional message to add to exception.
112
+ """
113
+ error_message: str = (
114
+ "Please use '--dry-run' with your command to preview the file tree and optimize the file contents."
115
+ )
116
+ super().__init__(f"{message}. {error_message}")
117
+
106
118
 
107
119
  class UnprocessableEntityException(BaseHttpException):
108
120
  """The server understands the request, but it was unable to process the contained instructions."""
@@ -154,7 +166,9 @@ class FileLimitExceededException(PeakBaseException):
154
166
  message (str): Additional message to add to exception.
155
167
  units (str): Units of the maximum size.
156
168
  """
157
- error_message: str = f"Compressed directory size is over {max_size}{units}."
169
+ error_message: str = (
170
+ f"Compressed directory size is over {max_size}{units}. Please use '--dry-run' with your command to preview the file tree and optimize the file contents."
171
+ )
158
172
  super().__init__(f"{error_message} {message}")
159
173
 
160
174
 
peak/handler.py CHANGED
@@ -19,6 +19,7 @@
19
19
  # # along with this program. If not, see <https://apache.org/licenses/LICENSE-2.0>
20
20
  #
21
21
  """Handler for sending requests to the API."""
22
+
22
23
  from __future__ import annotations
23
24
 
24
25
  import contextlib
@@ -35,7 +36,7 @@ from urllib3.util import Retry
35
36
  import peak.config
36
37
  from peak.compression import compress, get_files_to_include, print_file_tree
37
38
  from peak.constants import ContentType, HttpMethods
38
- from peak.exceptions import BaseHttpException
39
+ from peak.exceptions import BaseHttpException, PayloadTooLargeException
39
40
  from peak.output import Writer
40
41
  from peak.telemetry import telemetry
41
42
  from peak.validators import check_file_size
@@ -200,6 +201,9 @@ class HandlerUtils(AuthRetrySession):
200
201
  if 200 <= response.status_code < 300: # noqa: PLR2004
201
202
  return response
202
203
 
204
+ if response.status_code == 413: # noqa: PLR2004
205
+ raise PayloadTooLargeException(response.json().get("detail", ""))
206
+
203
207
  raise BaseHttpException.REGISTRY[response.status_code](response.json())
204
208
 
205
209
 
peak/helpers.py CHANGED
@@ -20,10 +20,12 @@
20
20
  #
21
21
 
22
22
  """Collection of basic helper functions."""
23
+
23
24
  from __future__ import annotations
24
25
 
25
26
  import inspect
26
27
  import json
28
+ import re
27
29
  from datetime import datetime, timezone
28
30
  from types import FrameType
29
31
  from typing import Any, Dict, List, Optional
@@ -205,6 +207,42 @@ def remove_none_values(data: Any) -> Dict[str, Any]:
205
207
  return data # type: ignore[no-any-return]
206
208
 
207
209
 
210
+ def convert_to_snake_case(s: str) -> str:
211
+ """Converts a given string from camelCase or TitleCase to snake case.
212
+
213
+ Args:
214
+ s (str): The string to be converted.
215
+
216
+ Returns:
217
+ str: The converted string in snake case.
218
+ """
219
+ return re.sub(r"(?<!^)(?=[A-Z])", "_", s).lower()
220
+
221
+
222
+ def convert_keys_to_snake_case(data: Dict[str, Any], *, convert_nested: bool = False) -> Dict[str, Any]:
223
+ """Converts keys of a dictionary to snake case.
224
+
225
+ Args:
226
+ data (Dict[str, Any]): Dictionary to be converted.
227
+ convert_nested (bool): Whether to convert nested keys as well. Default is False.
228
+
229
+ Returns:
230
+ Dict[str, Any]: Dictionary with keys converted to snake case.
231
+ """
232
+
233
+ def convert_dict(d: Dict[str, Any]) -> Dict[str, Any]:
234
+ new_dict = {}
235
+ for k, v in d.items():
236
+ new_key = convert_to_snake_case(k)
237
+ if convert_nested and isinstance(v, dict):
238
+ new_dict[new_key] = convert_dict(v)
239
+ else:
240
+ new_dict[new_key] = v
241
+ return new_dict
242
+
243
+ return convert_dict(data)
244
+
245
+
208
246
  def format_date(timestamp: str, time_format: str = "%Y/%m/%d %H:%M:%S") -> str:
209
247
  """Format a timestamp to a given format.
210
248
 
peak/output.py CHANGED
@@ -56,6 +56,7 @@ class Writer:
56
56
  self,
57
57
  data: Any,
58
58
  deprecation_message: str | None = None,
59
+ output_type: OutputTypes | None = None,
59
60
  ) -> None:
60
61
  """Write logs to the terminal.
61
62
 
@@ -63,27 +64,33 @@ class Writer:
63
64
  data (Any): Data to be printed on the terminal.
64
65
  This handles dry-run, debug mode and exit code for the CLI.
65
66
  deprecation_message (str, optional): Deprecation message to be printed on the terminal.
67
+ output_type (OutputTypes, optional): Override for the output type set in the config.
66
68
  """
67
- output_type = config.OUTPUT_TYPE
69
+ output_type_parsed = output_type or config.OUTPUT_TYPE
68
70
  table_params = config.TABLE_PARAMS
69
71
 
70
72
  if not config.DEBUG_MODE or self.ignore_debug_mode:
71
73
  if deprecation_message:
72
74
  self._print_deprecation_warning(deprecation_message)
73
- if output_type == OutputTypes.yaml.value:
75
+ if output_type_parsed == OutputTypes.yaml.value:
74
76
  self.__yaml(data)
75
- elif output_type == OutputTypes.table.value:
77
+ elif output_type_parsed == OutputTypes.table.value:
76
78
  self.__table(data, table_params)
77
79
  else:
78
- self.__json(data)
80
+ self.__json(data, output_type)
79
81
 
80
- def __json(self, data: Any) -> None:
82
+ def __json(self, data: Any, output_type: OutputTypes | None = None) -> None:
81
83
  """Write logs to the terminal in JSON format.
82
84
 
83
85
  Args:
84
86
  data (Any): Data to be printed on the terminal.
87
+ output_type (OutputTypes): If passed, JSON parser would be used to print output
88
+ even if the data is not a dictionary.
85
89
  """
86
- console.print(data)
90
+ if isinstance(data, dict) or output_type == OutputTypes.json:
91
+ console.print_json(data=data)
92
+ else:
93
+ console.print(data)
87
94
 
88
95
  def __table(self, data: dict[Any, Any], params: dict[str, Any]) -> None:
89
96
  """Write logs to the terminal in a tabular format.