nominal 1.96.0__tar.gz → 1.97.0__tar.gz

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 (107) hide show
  1. {nominal-1.96.0 → nominal-1.97.0}/CHANGELOG.md +12 -0
  2. {nominal-1.96.0 → nominal-1.97.0}/PKG-INFO +1 -1
  3. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/_utils/multipart.py +43 -19
  4. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/client.py +52 -0
  5. {nominal-1.96.0 → nominal-1.97.0}/pyproject.toml +1 -1
  6. {nominal-1.96.0 → nominal-1.97.0}/.gitignore +0 -0
  7. {nominal-1.96.0 → nominal-1.97.0}/LICENSE +0 -0
  8. {nominal-1.96.0 → nominal-1.97.0}/README.md +0 -0
  9. {nominal-1.96.0 → nominal-1.97.0}/nominal/__init__.py +0 -0
  10. {nominal-1.96.0 → nominal-1.97.0}/nominal/__main__.py +0 -0
  11. {nominal-1.96.0 → nominal-1.97.0}/nominal/_utils/README.md +0 -0
  12. {nominal-1.96.0 → nominal-1.97.0}/nominal/_utils/__init__.py +0 -0
  13. {nominal-1.96.0 → nominal-1.97.0}/nominal/_utils/dataclass_tools.py +0 -0
  14. {nominal-1.96.0 → nominal-1.97.0}/nominal/_utils/deprecation_tools.py +0 -0
  15. {nominal-1.96.0 → nominal-1.97.0}/nominal/_utils/iterator_tools.py +0 -0
  16. {nominal-1.96.0 → nominal-1.97.0}/nominal/_utils/streaming_tools.py +0 -0
  17. {nominal-1.96.0 → nominal-1.97.0}/nominal/_utils/timing_tools.py +0 -0
  18. {nominal-1.96.0 → nominal-1.97.0}/nominal/cli/__init__.py +0 -0
  19. {nominal-1.96.0 → nominal-1.97.0}/nominal/cli/__main__.py +0 -0
  20. {nominal-1.96.0 → nominal-1.97.0}/nominal/cli/attachment.py +0 -0
  21. {nominal-1.96.0 → nominal-1.97.0}/nominal/cli/auth.py +0 -0
  22. {nominal-1.96.0 → nominal-1.97.0}/nominal/cli/config.py +0 -0
  23. {nominal-1.96.0 → nominal-1.97.0}/nominal/cli/dataset.py +0 -0
  24. {nominal-1.96.0 → nominal-1.97.0}/nominal/cli/download.py +0 -0
  25. {nominal-1.96.0 → nominal-1.97.0}/nominal/cli/mis.py +0 -0
  26. {nominal-1.96.0 → nominal-1.97.0}/nominal/cli/run.py +0 -0
  27. {nominal-1.96.0 → nominal-1.97.0}/nominal/cli/util/__init__.py +0 -0
  28. {nominal-1.96.0 → nominal-1.97.0}/nominal/cli/util/click_log_handler.py +0 -0
  29. {nominal-1.96.0 → nominal-1.97.0}/nominal/cli/util/global_decorators.py +0 -0
  30. {nominal-1.96.0 → nominal-1.97.0}/nominal/cli/util/verify_connection.py +0 -0
  31. {nominal-1.96.0 → nominal-1.97.0}/nominal/config/__init__.py +0 -0
  32. {nominal-1.96.0 → nominal-1.97.0}/nominal/config/_config.py +0 -0
  33. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/__init__.py +0 -0
  34. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/_clientsbunch.py +0 -0
  35. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/_constants.py +0 -0
  36. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/_stream/__init__.py +0 -0
  37. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/_stream/batch_processor.py +0 -0
  38. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/_stream/batch_processor_proto.py +0 -0
  39. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/_stream/write_stream.py +0 -0
  40. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/_stream/write_stream_base.py +0 -0
  41. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/_utils/README.md +0 -0
  42. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/_utils/__init__.py +0 -0
  43. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/_utils/api_tools.py +0 -0
  44. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/_utils/multipart_downloader.py +0 -0
  45. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/_utils/networking.py +0 -0
  46. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/_utils/pagination_tools.py +0 -0
  47. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/_utils/query_tools.py +0 -0
  48. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/_utils/queueing.py +0 -0
  49. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/asset.py +0 -0
  50. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/attachment.py +0 -0
  51. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/bounds.py +0 -0
  52. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/channel.py +0 -0
  53. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/checklist.py +0 -0
  54. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/connection.py +0 -0
  55. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/containerized_extractors.py +0 -0
  56. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/data_review.py +0 -0
  57. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/dataset.py +0 -0
  58. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/dataset_file.py +0 -0
  59. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/datasource.py +0 -0
  60. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/event.py +0 -0
  61. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/exceptions.py +0 -0
  62. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/filetype.py +0 -0
  63. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/log.py +0 -0
  64. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/run.py +0 -0
  65. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/secret.py +0 -0
  66. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/unit.py +0 -0
  67. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/user.py +0 -0
  68. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/video.py +0 -0
  69. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/video_file.py +0 -0
  70. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/workbook.py +0 -0
  71. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/workbook_template.py +0 -0
  72. {nominal-1.96.0 → nominal-1.97.0}/nominal/core/workspace.py +0 -0
  73. {nominal-1.96.0 → nominal-1.97.0}/nominal/exceptions/__init__.py +0 -0
  74. {nominal-1.96.0 → nominal-1.97.0}/nominal/experimental/__init__.py +0 -0
  75. {nominal-1.96.0 → nominal-1.97.0}/nominal/experimental/compute/README.md +0 -0
  76. {nominal-1.96.0 → nominal-1.97.0}/nominal/experimental/compute/__init__.py +0 -0
  77. {nominal-1.96.0 → nominal-1.97.0}/nominal/experimental/compute/_buckets.py +0 -0
  78. {nominal-1.96.0 → nominal-1.97.0}/nominal/experimental/compute/dsl/__init__.py +0 -0
  79. {nominal-1.96.0 → nominal-1.97.0}/nominal/experimental/compute/dsl/_enum_expr_impls.py +0 -0
  80. {nominal-1.96.0 → nominal-1.97.0}/nominal/experimental/compute/dsl/_numeric_expr_impls.py +0 -0
  81. {nominal-1.96.0 → nominal-1.97.0}/nominal/experimental/compute/dsl/_range_expr_impls.py +0 -0
  82. {nominal-1.96.0 → nominal-1.97.0}/nominal/experimental/compute/dsl/exprs.py +0 -0
  83. {nominal-1.96.0 → nominal-1.97.0}/nominal/experimental/compute/dsl/params.py +0 -0
  84. {nominal-1.96.0 → nominal-1.97.0}/nominal/experimental/logging/__init__.py +0 -0
  85. {nominal-1.96.0 → nominal-1.97.0}/nominal/experimental/logging/click_log_handler.py +0 -0
  86. {nominal-1.96.0 → nominal-1.97.0}/nominal/experimental/logging/nominal_log_handler.py +0 -0
  87. {nominal-1.96.0 → nominal-1.97.0}/nominal/experimental/logging/rich_log_handler.py +0 -0
  88. {nominal-1.96.0 → nominal-1.97.0}/nominal/experimental/rust_streaming/__init__.py +0 -0
  89. {nominal-1.96.0 → nominal-1.97.0}/nominal/experimental/rust_streaming/rust_write_stream.py +0 -0
  90. {nominal-1.96.0 → nominal-1.97.0}/nominal/experimental/stream_v2/__init__.py +0 -0
  91. {nominal-1.96.0 → nominal-1.97.0}/nominal/experimental/stream_v2/_serializer.py +0 -0
  92. {nominal-1.96.0 → nominal-1.97.0}/nominal/experimental/stream_v2/_write_stream.py +0 -0
  93. {nominal-1.96.0 → nominal-1.97.0}/nominal/experimental/video_processing/__init__.py +0 -0
  94. {nominal-1.96.0 → nominal-1.97.0}/nominal/experimental/video_processing/resolution.py +0 -0
  95. {nominal-1.96.0 → nominal-1.97.0}/nominal/experimental/video_processing/video_conversion.py +0 -0
  96. {nominal-1.96.0 → nominal-1.97.0}/nominal/nominal.py +0 -0
  97. {nominal-1.96.0 → nominal-1.97.0}/nominal/py.typed +0 -0
  98. {nominal-1.96.0 → nominal-1.97.0}/nominal/thirdparty/__init__.py +0 -0
  99. {nominal-1.96.0 → nominal-1.97.0}/nominal/thirdparty/matlab/__init__.py +0 -0
  100. {nominal-1.96.0 → nominal-1.97.0}/nominal/thirdparty/matlab/_matlab.py +0 -0
  101. {nominal-1.96.0 → nominal-1.97.0}/nominal/thirdparty/pandas/__init__.py +0 -0
  102. {nominal-1.96.0 → nominal-1.97.0}/nominal/thirdparty/pandas/_pandas.py +0 -0
  103. {nominal-1.96.0 → nominal-1.97.0}/nominal/thirdparty/polars/__init__.py +0 -0
  104. {nominal-1.96.0 → nominal-1.97.0}/nominal/thirdparty/polars/polars_export_handler.py +0 -0
  105. {nominal-1.96.0 → nominal-1.97.0}/nominal/thirdparty/tdms/__init__.py +0 -0
  106. {nominal-1.96.0 → nominal-1.97.0}/nominal/thirdparty/tdms/_tdms.py +0 -0
  107. {nominal-1.96.0 → nominal-1.97.0}/nominal/ts/__init__.py +0 -0
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.97.0](https://github.com/nominal-io/nominal-client/compare/v1.96.0...v1.97.0) (2025-12-04)
4
+
5
+
6
+ ### Features
7
+
8
+ * add create_workbook_template ([#535](https://github.com/nominal-io/nominal-client/issues/535)) ([9c98975](https://github.com/nominal-io/nominal-client/commit/9c989753828c39e417bafed3db2981444132aeac))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * add per-part retries during multipart uploads ([#537](https://github.com/nominal-io/nominal-client/issues/537)) ([ffdbc4a](https://github.com/nominal-io/nominal-client/commit/ffdbc4ac6f82562fb9eff3fed4b254b784aa89bc))
14
+
3
15
  ## [1.96.0](https://github.com/nominal-io/nominal-client/compare/v1.95.0...v1.96.0) (2025-12-03)
4
16
 
5
17
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nominal
3
- Version: 1.96.0
3
+ Version: 1.97.0
4
4
  Summary: Automate Nominal workflows in Python
5
5
  Project-URL: Homepage, https://nominal.io
6
6
  Project-URL: Documentation, https://docs.nominal.io
@@ -27,29 +27,53 @@ def _sign_and_upload_part_job(
27
27
  upload_id: str,
28
28
  q: Queue[bytes],
29
29
  part: int,
30
+ num_retries: int = 3,
30
31
  ) -> requests.Response:
31
32
  data = q.get()
33
+
32
34
  try:
33
- response = upload_client.sign_part(auth_header, key, part, upload_id)
34
- logger.debug(
35
- "successfully signed multipart upload part",
36
- extra={"key": key, "part": part, "upload_id": upload_id, "response.url": response.url},
37
- )
38
- put_response = requests.put(
39
- response.url,
40
- data=data,
41
- headers=response.headers,
42
- verify=upload_client._verify,
43
- )
44
- logger.debug(
45
- "put multipart upload part",
46
- extra={"url": response.url, "size": len(data), "status_code": put_response.status_code},
35
+ last_ex: Exception | None = None
36
+ for attempt in range(num_retries):
37
+ try:
38
+ log_extras = {"key": key, "part": part, "upload_id": upload_id, "attempt": attempt + 1}
39
+
40
+ logger.debug("Signing part %d for upload", part, extra=log_extras)
41
+ sign_response = upload_client.sign_part(auth_header, key, part, upload_id)
42
+ logger.debug(
43
+ "Successfully signed part %d for upload",
44
+ part,
45
+ extra={"response.url": sign_response.url, **log_extras},
46
+ )
47
+
48
+ logger.debug("Pushing part %d for multipart upload", extra=log_extras)
49
+ put_response = requests.put(
50
+ sign_response.url,
51
+ data=data,
52
+ headers=sign_response.headers,
53
+ verify=upload_client._verify,
54
+ )
55
+ logger.debug(
56
+ "Finished pushing part %d for multipart upload with status %d",
57
+ part,
58
+ put_response.status_code,
59
+ extra={"response.url": put_response.url, **log_extras},
60
+ )
61
+ put_response.raise_for_status()
62
+ return put_response
63
+ except Exception as ex:
64
+ logger.warning(
65
+ "Failed to upload part %d: %s",
66
+ part,
67
+ ex,
68
+ extra=log_extras,
69
+ )
70
+ last_ex = ex
71
+
72
+ raise (
73
+ last_ex
74
+ if last_ex
75
+ else RuntimeError(f"Unknown error uploading part {part} for upload_id={upload_id} and key={key}")
47
76
  )
48
- put_response.raise_for_status()
49
- return put_response
50
- except Exception as e:
51
- logger.exception("error uploading part", exc_info=e, extra={"key": key, "upload_id": upload_id, "part": part})
52
- raise e
53
77
  finally:
54
78
  q.task_done()
55
79
 
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import enum
4
4
  import logging
5
+ import uuid
5
6
  from dataclasses import dataclass, field
6
7
  from datetime import datetime, timedelta
7
8
  from io import TextIOBase
@@ -21,10 +22,12 @@ from nominal_api import (
21
22
  scout_catalog,
22
23
  scout_checks_api,
23
24
  scout_datasource_connection_api,
25
+ scout_layout_api,
24
26
  scout_notebook_api,
25
27
  scout_run_api,
26
28
  scout_template_api,
27
29
  scout_video_api,
30
+ scout_workbookcommon_api,
28
31
  secrets_api,
29
32
  storage_datasource_api,
30
33
  )
@@ -1478,3 +1481,52 @@ class NominalClient:
1478
1481
  "nominal.core.NominalClient.create_workbook_from_template",
1479
1482
  "use 'nominal.core.WorkbookTemplate.create_workbook' instead",
1480
1483
  )
1484
+
1485
+ def create_workbook_template(
1486
+ self,
1487
+ title: str,
1488
+ *,
1489
+ description: str | None = None,
1490
+ labels: list[str] | None = None,
1491
+ properties: dict[str, str] | None = None,
1492
+ commit_message: str | None = None,
1493
+ workspace: Workspace | str | None = None,
1494
+ ) -> WorkbookTemplate:
1495
+ """Create an empty workbook template.
1496
+
1497
+ Args:
1498
+ title: Title of the workbook template
1499
+ description: Description of the workbook template
1500
+ labels: Labels to attach to the workbook template
1501
+ properties: Properties to attach to the workbook template
1502
+ commit_message: An optional message to include with the creation of the template
1503
+ workspace: Workspace to create the workbook template in.
1504
+
1505
+ Returns:
1506
+ The created WorkbookTemplate
1507
+ """
1508
+ request = scout_template_api.CreateTemplateRequest(
1509
+ title=title,
1510
+ description=description if description is not None else "",
1511
+ labels=labels if labels is not None else [],
1512
+ properties=properties if properties is not None else {},
1513
+ is_published=False,
1514
+ layout=scout_layout_api.WorkbookLayout(
1515
+ v1=scout_layout_api.WorkbookLayoutV1(
1516
+ root_panel=scout_layout_api.Panel(
1517
+ tabbed=scout_layout_api.TabbedPanel(
1518
+ v1=scout_layout_api.TabbedPanelV1(
1519
+ id=str(uuid.uuid4()),
1520
+ tabs=[],
1521
+ )
1522
+ )
1523
+ )
1524
+ )
1525
+ ),
1526
+ content=scout_workbookcommon_api.WorkbookContent(channel_variables={}, charts={}),
1527
+ message=commit_message if commit_message is not None else "Initial blank workbook template",
1528
+ workspace=self._workspace_rid_for_search(workspace or WorkspaceSearchType.ALL),
1529
+ )
1530
+
1531
+ template = self._clients.template.create(self._clients.auth_header, request)
1532
+ return WorkbookTemplate._from_conjure(self._clients, template)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nominal"
3
- version = "1.96.0"
3
+ version = "1.97.0"
4
4
  description = "Automate Nominal workflows in Python"
5
5
  authors = [
6
6
  { name = "Alexander Reynolds", email = "alex.reynolds@nominal.io" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes