nominal 1.95.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.
- {nominal-1.95.0 → nominal-1.97.0}/CHANGELOG.md +21 -0
- {nominal-1.95.0 → nominal-1.97.0}/PKG-INFO +2 -2
- {nominal-1.95.0 → nominal-1.97.0}/nominal/cli/__init__.py +1 -2
- {nominal-1.95.0 → nominal-1.97.0}/nominal/cli/auth.py +2 -13
- {nominal-1.95.0 → nominal-1.97.0}/nominal/cli/dataset.py +2 -2
- {nominal-1.95.0 → nominal-1.97.0}/nominal/cli/util/click_log_handler.py +6 -3
- {nominal-1.95.0 → nominal-1.97.0}/nominal/cli/util/global_decorators.py +6 -48
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/_utils/multipart.py +43 -19
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/client.py +54 -21
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/data_review.py +5 -2
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/dataset.py +27 -38
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/event.py +1 -6
- nominal-1.97.0/nominal/core/exceptions.py +78 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/run.py +148 -148
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/workbook.py +5 -8
- {nominal-1.95.0 → nominal-1.97.0}/nominal/nominal.py +4 -3
- {nominal-1.95.0 → nominal-1.97.0}/pyproject.toml +2 -2
- nominal-1.95.0/nominal/core/exceptions.py +0 -39
- {nominal-1.95.0 → nominal-1.97.0}/.gitignore +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/LICENSE +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/README.md +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/__init__.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/__main__.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/_utils/README.md +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/_utils/__init__.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/_utils/dataclass_tools.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/_utils/deprecation_tools.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/_utils/iterator_tools.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/_utils/streaming_tools.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/_utils/timing_tools.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/cli/__main__.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/cli/attachment.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/cli/config.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/cli/download.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/cli/mis.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/cli/run.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/cli/util/__init__.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/cli/util/verify_connection.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/config/__init__.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/config/_config.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/__init__.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/_clientsbunch.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/_constants.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/_stream/__init__.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/_stream/batch_processor.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/_stream/batch_processor_proto.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/_stream/write_stream.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/_stream/write_stream_base.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/_utils/README.md +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/_utils/__init__.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/_utils/api_tools.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/_utils/multipart_downloader.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/_utils/networking.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/_utils/pagination_tools.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/_utils/query_tools.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/_utils/queueing.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/asset.py +176 -176
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/attachment.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/bounds.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/channel.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/checklist.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/connection.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/containerized_extractors.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/dataset_file.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/datasource.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/filetype.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/log.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/secret.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/unit.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/user.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/video.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/video_file.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/workbook_template.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/core/workspace.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/exceptions/__init__.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/experimental/__init__.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/experimental/compute/README.md +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/experimental/compute/__init__.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/experimental/compute/_buckets.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/experimental/compute/dsl/__init__.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/experimental/compute/dsl/_enum_expr_impls.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/experimental/compute/dsl/_numeric_expr_impls.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/experimental/compute/dsl/_range_expr_impls.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/experimental/compute/dsl/exprs.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/experimental/compute/dsl/params.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/experimental/logging/__init__.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/experimental/logging/click_log_handler.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/experimental/logging/nominal_log_handler.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/experimental/logging/rich_log_handler.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/experimental/rust_streaming/__init__.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/experimental/rust_streaming/rust_write_stream.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/experimental/stream_v2/__init__.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/experimental/stream_v2/_serializer.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/experimental/stream_v2/_write_stream.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/experimental/video_processing/__init__.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/experimental/video_processing/resolution.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/experimental/video_processing/video_conversion.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/py.typed +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/thirdparty/__init__.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/thirdparty/matlab/__init__.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/thirdparty/matlab/_matlab.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/thirdparty/pandas/__init__.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/thirdparty/pandas/_pandas.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/thirdparty/polars/__init__.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/thirdparty/polars/polars_export_handler.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/thirdparty/tdms/__init__.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/thirdparty/tdms/_tdms.py +0 -0
- {nominal-1.95.0 → nominal-1.97.0}/nominal/ts/__init__.py +0 -0
|
@@ -1,5 +1,26 @@
|
|
|
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
|
+
|
|
15
|
+
## [1.96.0](https://github.com/nominal-io/nominal-client/compare/v1.95.0...v1.96.0) (2025-12-03)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* add tags to dataflash apis ([#534](https://github.com/nominal-io/nominal-client/issues/534)) ([00cd79f](https://github.com/nominal-io/nominal-client/commit/00cd79fdb84e299a4013b69ce9639cfc75644b32))
|
|
21
|
+
* remove deprecated fields, methods, files ([#525](https://github.com/nominal-io/nominal-client/issues/525)) ([e11175d](https://github.com/nominal-io/nominal-client/commit/e11175de0e303d97d91efa7eaf6ee6010d6b08ef))
|
|
22
|
+
* reorder methods in run and asset class ([#530](https://github.com/nominal-io/nominal-client/issues/530)) ([5813157](https://github.com/nominal-io/nominal-client/commit/58131574bd85a82d36f7e76f18279c8c7cfaf1a8))
|
|
23
|
+
|
|
3
24
|
## [1.95.0](https://github.com/nominal-io/nominal-client/compare/v1.94.0...v1.95.0) (2025-11-19)
|
|
4
25
|
|
|
5
26
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nominal
|
|
3
|
-
Version: 1.
|
|
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
|
|
@@ -20,7 +20,7 @@ Requires-Dist: cachetools>=6.1.0
|
|
|
20
20
|
Requires-Dist: click<9,>=8
|
|
21
21
|
Requires-Dist: conjure-python-client<4,>=3.1.0
|
|
22
22
|
Requires-Dist: ffmpeg-python>=0.2.0
|
|
23
|
-
Requires-Dist: nominal-api==0.
|
|
23
|
+
Requires-Dist: nominal-api==0.1019.0
|
|
24
24
|
Requires-Dist: nominal-streaming==0.5.8
|
|
25
25
|
Requires-Dist: openpyxl>=0.0.0
|
|
26
26
|
Requires-Dist: pandas>=0.0.0
|
|
@@ -2,7 +2,7 @@ import importlib.metadata
|
|
|
2
2
|
|
|
3
3
|
import click
|
|
4
4
|
|
|
5
|
-
from nominal.cli import attachment,
|
|
5
|
+
from nominal.cli import attachment, config, dataset, download, mis, run
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
@click.group(context_settings={"show_default": True, "help_option_names": ("-h", "--help")})
|
|
@@ -12,7 +12,6 @@ def nom() -> None:
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
nom.add_command(attachment.attachment_cmd)
|
|
15
|
-
nom.add_command(auth.auth_cmd)
|
|
16
15
|
nom.add_command(config.config_cmd)
|
|
17
16
|
nom.add_command(dataset.dataset_cmd)
|
|
18
17
|
nom.add_command(download.download_cmd)
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import warnings
|
|
4
|
-
|
|
5
3
|
import click
|
|
6
4
|
|
|
7
5
|
from nominal.cli.util.global_decorators import global_options
|
|
8
|
-
from nominal.
|
|
9
|
-
from nominal.config import _config as _deprecated_config
|
|
6
|
+
from nominal.core.exceptions import NominalMethodRemovedError
|
|
10
7
|
|
|
11
8
|
|
|
12
9
|
@click.group(name="auth")
|
|
@@ -25,12 +22,4 @@ def set_token(token: str, base_url: str) -> None:
|
|
|
25
22
|
|
|
26
23
|
Update the token for a given URL in the (deprecated) Nominal config file
|
|
27
24
|
"""
|
|
28
|
-
|
|
29
|
-
"`nom auth set-token` is deprecated, use `nom config profile add` instead",
|
|
30
|
-
UserWarning,
|
|
31
|
-
stacklevel=2,
|
|
32
|
-
)
|
|
33
|
-
path = _deprecated_config._DEFAULT_NOMINAL_CONFIG_PATH
|
|
34
|
-
validate_token_url(token, base_url, None)
|
|
35
|
-
_deprecated_config.set_token(base_url, token)
|
|
36
|
-
click.secho(f"Successfully set token for '{base_url}' in {path}", fg="green")
|
|
25
|
+
raise NominalMethodRemovedError("nominal auth set-token", "use 'nominal config profile add' instead")
|
|
@@ -70,7 +70,7 @@ def upload_csv(
|
|
|
70
70
|
description=description,
|
|
71
71
|
prefix_tree_delimiter=channel_name_delimiter,
|
|
72
72
|
)
|
|
73
|
-
dataset.add_tabular_data(
|
|
73
|
+
dataset_file = dataset.add_tabular_data(
|
|
74
74
|
file,
|
|
75
75
|
timestamp_column=timestamp_column,
|
|
76
76
|
timestamp_type=timestamp_type,
|
|
@@ -78,7 +78,7 @@ def upload_csv(
|
|
|
78
78
|
|
|
79
79
|
# block until ingestion completed, if requested
|
|
80
80
|
if wait:
|
|
81
|
-
|
|
81
|
+
dataset_file.poll_until_ingestion_completed()
|
|
82
82
|
|
|
83
83
|
click.echo(dataset)
|
|
84
84
|
|
|
@@ -4,7 +4,7 @@ import logging
|
|
|
4
4
|
|
|
5
5
|
from typing_extensions import deprecated
|
|
6
6
|
|
|
7
|
-
from nominal.
|
|
7
|
+
from nominal.core.exceptions import NominalMethodRemovedError
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
@deprecated(
|
|
@@ -20,7 +20,10 @@ def install_log_handler(level: int = logging.WARNING, no_color: bool = False) ->
|
|
|
20
20
|
no_color: If true, prevents log messages from being stylized by severity level
|
|
21
21
|
|
|
22
22
|
"""
|
|
23
|
-
|
|
23
|
+
raise NominalMethodRemovedError(
|
|
24
|
+
"nominal.cli.util.install_log_handler",
|
|
25
|
+
"use nominal.experimental.logging.install_click_log_handler instead",
|
|
26
|
+
)
|
|
24
27
|
|
|
25
28
|
|
|
26
|
-
__all__ = ["
|
|
29
|
+
__all__ = ["install_log_handler"]
|
|
@@ -8,7 +8,6 @@ import typing
|
|
|
8
8
|
|
|
9
9
|
import click
|
|
10
10
|
|
|
11
|
-
from nominal.config._config import _DEFAULT_NOMINAL_CONFIG_PATH, get_token
|
|
12
11
|
from nominal.core.client import NominalClient
|
|
13
12
|
from nominal.experimental.logging import install_click_log_handler
|
|
14
13
|
|
|
@@ -105,14 +104,14 @@ def client_options(func: typing.Callable[Param, T]) -> typing.Callable[..., T]:
|
|
|
105
104
|
"""Decorator to add click options to a click command for dynamically creating and injecting an instance of the
|
|
106
105
|
NominalClient into commands based on user-provided flags to configure its creation.
|
|
107
106
|
|
|
108
|
-
This will add
|
|
109
|
-
configurations before spawning a NominalClient.
|
|
107
|
+
This will add an option --profile which perform the aforementioned configurations before spawning a NominalClient.
|
|
110
108
|
|
|
111
109
|
NOTE: any click command utilizing this decorator MUST accept a key-value argument pair named client of type
|
|
112
110
|
NominalClient.
|
|
113
111
|
"""
|
|
114
112
|
profile_option = click.option(
|
|
115
113
|
"--profile",
|
|
114
|
+
required=True,
|
|
116
115
|
help=(
|
|
117
116
|
"If provided, use the given named config profile for instantiating a Nominal Client. "
|
|
118
117
|
"This is the preferred mechanism for instantiating a client today-- see `nom config profile add` "
|
|
@@ -120,26 +119,6 @@ def client_options(func: typing.Callable[Param, T]) -> typing.Callable[..., T]:
|
|
|
120
119
|
"--base-url."
|
|
121
120
|
),
|
|
122
121
|
)
|
|
123
|
-
url_option = click.option(
|
|
124
|
-
"--base-url",
|
|
125
|
-
default="https://api.gov.nominal.io/api",
|
|
126
|
-
show_default=True,
|
|
127
|
-
help="Base URL of the Nominal API to hit. Useful for hitting other clusters, e.g., staging for internal users.",
|
|
128
|
-
)
|
|
129
|
-
token_path_option = click.option(
|
|
130
|
-
"--token-path",
|
|
131
|
-
default=_DEFAULT_NOMINAL_CONFIG_PATH,
|
|
132
|
-
type=click.Path(dir_okay=False, resolve_path=True, path_type=pathlib.Path),
|
|
133
|
-
show_default=True,
|
|
134
|
-
help="Path to the yaml file containing the Nominal access token for authenticating with the API",
|
|
135
|
-
)
|
|
136
|
-
token_option = click.option(
|
|
137
|
-
"--token",
|
|
138
|
-
help=(
|
|
139
|
-
"API Access token to use when creating the nominal client. "
|
|
140
|
-
"If provided, takes precedence over --token-path and --base-url"
|
|
141
|
-
),
|
|
142
|
-
)
|
|
143
122
|
trust_store_option = click.option(
|
|
144
123
|
"--trust-store-path",
|
|
145
124
|
type=click.Path(dir_okay=False, exists=True, resolve_path=True, path_type=pathlib.Path),
|
|
@@ -154,35 +133,14 @@ def client_options(func: typing.Callable[Param, T]) -> typing.Callable[..., T]:
|
|
|
154
133
|
*args: Param.args,
|
|
155
134
|
**kwargs: Param.kwargs,
|
|
156
135
|
) -> T:
|
|
157
|
-
profile: str
|
|
158
|
-
base_url: str = kwargs.pop("base_url", "") # type: ignore[assignment]
|
|
159
|
-
token: str | None = kwargs.pop("token") # type: ignore[assignment]
|
|
160
|
-
token_path: pathlib.Path = kwargs.pop("token_path") # type: ignore[assignment]
|
|
136
|
+
profile: str = kwargs.pop("profile") # type: ignore[assignment]
|
|
161
137
|
trust_store_path: pathlib.Path | None = kwargs.pop("trust_store_path") # type: ignore[assignment]
|
|
162
138
|
|
|
163
139
|
trust_store_str = str(trust_store_path) if trust_store_path else None
|
|
164
140
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
client = NominalClient.from_profile(profile, trust_store_path=trust_store_str)
|
|
168
|
-
kwargs["client"] = client
|
|
169
|
-
return func(*args, **kwargs)
|
|
170
|
-
|
|
171
|
-
logger.warning(
|
|
172
|
-
"Creating a Nominal Client without using '--profile' is deprecated! "
|
|
173
|
-
"See 'https://docs.nominal.io/core/sdk/python-client/authentication' for details..."
|
|
174
|
-
)
|
|
175
|
-
|
|
176
|
-
if token is None:
|
|
177
|
-
if token_path.exists():
|
|
178
|
-
token = get_token(base_url, token_path)
|
|
179
|
-
else:
|
|
180
|
-
raise ValueError(
|
|
181
|
-
f"Cannot instantiate client: no token provided and token path {token_path} does not exist."
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
client = NominalClient.create(base_url, token=token, trust_store_path=trust_store_str)
|
|
141
|
+
logger.info("Instantiating client from profile '%s'", profile)
|
|
142
|
+
client = NominalClient.from_profile(profile, trust_store_path=trust_store_str)
|
|
185
143
|
kwargs["client"] = client
|
|
186
144
|
return func(*args, **kwargs)
|
|
187
145
|
|
|
188
|
-
return profile_option(
|
|
146
|
+
return profile_option(trust_store_option(wrapped_function))
|
|
@@ -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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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,17 +22,18 @@ 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
|
)
|
|
31
34
|
from typing_extensions import Self, deprecated
|
|
32
35
|
|
|
33
36
|
from nominal import ts
|
|
34
|
-
from nominal._utils.deprecation_tools import warn_on_deprecated_argument
|
|
35
37
|
from nominal.config import NominalConfig, _config
|
|
36
38
|
from nominal.core._clientsbunch import ClientsBunch
|
|
37
39
|
from nominal.core._constants import DEFAULT_API_BASE_URL
|
|
@@ -94,7 +96,7 @@ from nominal.core.dataset import (
|
|
|
94
96
|
)
|
|
95
97
|
from nominal.core.datasource import DataSource
|
|
96
98
|
from nominal.core.event import Event, EventType
|
|
97
|
-
from nominal.core.exceptions import NominalConfigError, NominalError, NominalIngestError
|
|
99
|
+
from nominal.core.exceptions import NominalConfigError, NominalError, NominalIngestError, NominalMethodRemovedError
|
|
98
100
|
from nominal.core.filetype import FileType, FileTypes
|
|
99
101
|
from nominal.core.run import Run
|
|
100
102
|
from nominal.core.secret import Secret
|
|
@@ -338,10 +340,6 @@ class NominalClient:
|
|
|
338
340
|
for raw_dataset in search_datasets_paginated(self._clients.catalog, self._clients.auth_header, query):
|
|
339
341
|
yield Dataset._from_conjure(self._clients, raw_dataset)
|
|
340
342
|
|
|
341
|
-
@warn_on_deprecated_argument(
|
|
342
|
-
"workspace_rid",
|
|
343
|
-
"`workspace_rid` has been deprecated and will be removed in a future version. Use `workspace` instead.",
|
|
344
|
-
)
|
|
345
343
|
def search_datasets(
|
|
346
344
|
self,
|
|
347
345
|
*,
|
|
@@ -351,7 +349,6 @@ class NominalClient:
|
|
|
351
349
|
properties: Mapping[str, str] | None = None,
|
|
352
350
|
before: str | datetime | IntegralNanosecondsUTC | None = None,
|
|
353
351
|
after: str | datetime | IntegralNanosecondsUTC | None = None,
|
|
354
|
-
workspace_rid: Workspace | str | None = None,
|
|
355
352
|
workspace: WorkspaceSearchT = WorkspaceSearchType.ALL,
|
|
356
353
|
archived: bool | None = None,
|
|
357
354
|
) -> Sequence[Dataset]:
|
|
@@ -365,7 +362,6 @@ class NominalClient:
|
|
|
365
362
|
properties: A mapping of key-value pairs that must ALL be present on a secret to be included.
|
|
366
363
|
before: Searches for datasets created before some time (inclusive).
|
|
367
364
|
after: Searches for datasets created before after time (inclusive).
|
|
368
|
-
workspace_rid: deprecated. use `workspace` instead.
|
|
369
365
|
workspace: Filters search to given workspace.
|
|
370
366
|
archived: Filters results to either archived or unarchived datasets.
|
|
371
367
|
|
|
@@ -377,12 +373,6 @@ class NominalClient:
|
|
|
377
373
|
Returns:
|
|
378
374
|
All datasets which match all of the provided conditions
|
|
379
375
|
"""
|
|
380
|
-
if workspace is not None and workspace_rid is not None:
|
|
381
|
-
raise ValueError("Both `workspace` and `workspace_rid` provided-- must use one or the other.")
|
|
382
|
-
|
|
383
|
-
if workspace_rid is not None:
|
|
384
|
-
workspace = workspace_rid
|
|
385
|
-
|
|
386
376
|
query = create_search_datasets_query(
|
|
387
377
|
exact_match=exact_match,
|
|
388
378
|
search_text=search_text,
|
|
@@ -1477,7 +1467,7 @@ class NominalClient:
|
|
|
1477
1467
|
|
|
1478
1468
|
@deprecated(
|
|
1479
1469
|
"Calling `NominalClient.create_workbook_from_template` is deprecated and will be removed "
|
|
1480
|
-
"in a future release. Use `
|
|
1470
|
+
"in a future release. Use `WorkbookTemplate.create_workbook` instead"
|
|
1481
1471
|
)
|
|
1482
1472
|
def create_workbook_from_template(
|
|
1483
1473
|
self,
|
|
@@ -1487,13 +1477,56 @@ class NominalClient:
|
|
|
1487
1477
|
description: str | None = None,
|
|
1488
1478
|
is_draft: bool = False,
|
|
1489
1479
|
) -> Workbook:
|
|
1490
|
-
|
|
1480
|
+
raise NominalMethodRemovedError(
|
|
1481
|
+
"nominal.core.NominalClient.create_workbook_from_template",
|
|
1482
|
+
"use 'nominal.core.WorkbookTemplate.create_workbook' instead",
|
|
1483
|
+
)
|
|
1491
1484
|
|
|
1492
|
-
|
|
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
|
|
1493
1507
|
"""
|
|
1494
|
-
|
|
1495
|
-
return template.create_workbook(
|
|
1508
|
+
request = scout_template_api.CreateTemplateRequest(
|
|
1496
1509
|
title=title,
|
|
1497
|
-
description=description,
|
|
1498
|
-
|
|
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),
|
|
1499
1529
|
)
|
|
1530
|
+
|
|
1531
|
+
template = self._clients.template.create(self._clients.auth_header, request)
|
|
1532
|
+
return WorkbookTemplate._from_conjure(self._clients, template)
|
|
@@ -21,6 +21,7 @@ from typing_extensions import Self, deprecated
|
|
|
21
21
|
from nominal.core import checklist, event
|
|
22
22
|
from nominal.core._clientsbunch import HasScoutParams
|
|
23
23
|
from nominal.core._utils.api_tools import HasRid
|
|
24
|
+
from nominal.core.exceptions import NominalMethodRemovedError
|
|
24
25
|
from nominal.ts import IntegralNanosecondsUTC, _SecondsNanos
|
|
25
26
|
|
|
26
27
|
|
|
@@ -75,8 +76,10 @@ class DataReview(HasRid):
|
|
|
75
76
|
)
|
|
76
77
|
def get_violations(self) -> Sequence[CheckViolation]:
|
|
77
78
|
"""Retrieves the list of check violations for the data review."""
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
raise NominalMethodRemovedError(
|
|
80
|
+
"nominal.core.DataReview.get_violations",
|
|
81
|
+
"use 'nominal.core.DataReview.get_events()' instead",
|
|
82
|
+
)
|
|
80
83
|
|
|
81
84
|
def get_events(self) -> Sequence[event.Event]:
|
|
82
85
|
"""Retrieves the list of events for the data review."""
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
import time
|
|
5
4
|
from dataclasses import dataclass
|
|
6
5
|
from datetime import timedelta
|
|
7
6
|
from io import TextIOBase
|
|
@@ -20,7 +19,7 @@ from nominal.core.bounds import Bounds
|
|
|
20
19
|
from nominal.core.containerized_extractors import ContainerizedExtractor
|
|
21
20
|
from nominal.core.dataset_file import DatasetFile
|
|
22
21
|
from nominal.core.datasource import DataSource
|
|
23
|
-
from nominal.core.exceptions import NominalIngestError,
|
|
22
|
+
from nominal.core.exceptions import NominalIngestError, NominalIngestMultiError, NominalMethodRemovedError
|
|
24
23
|
from nominal.core.filetype import FileType, FileTypes
|
|
25
24
|
from nominal.core.log import LogPoint, _write_logs
|
|
26
25
|
from nominal.ts import (
|
|
@@ -55,38 +54,12 @@ class Dataset(DataSource, RefreshableMixin[scout_catalog.EnrichedDataset]):
|
|
|
55
54
|
"obtained when ingesting files or by calling `dataset.list_files()`."
|
|
56
55
|
)
|
|
57
56
|
def poll_until_ingestion_completed(self, interval: timedelta = timedelta(seconds=1)) -> Self:
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
NominalIngestError: if the ingest status is not known
|
|
65
|
-
|
|
66
|
-
"""
|
|
67
|
-
while True:
|
|
68
|
-
progress = self._clients.catalog.get_ingest_progress_v2(self._clients.auth_header, self.rid)
|
|
69
|
-
if progress.ingest_status.type == "success":
|
|
70
|
-
break
|
|
71
|
-
elif progress.ingest_status.type == "inProgress": # "type" strings are camelCase
|
|
72
|
-
pass
|
|
73
|
-
elif progress.ingest_status.type == "error":
|
|
74
|
-
error = progress.ingest_status.error
|
|
75
|
-
if error is not None:
|
|
76
|
-
raise NominalIngestFailed(
|
|
77
|
-
f"ingest failed for dataset {self.rid!r}: {error.message} ({error.error_type})"
|
|
78
|
-
)
|
|
79
|
-
raise NominalIngestError(
|
|
80
|
-
f"ingest status type marked as 'error' but with no instance for dataset {self.rid!r}"
|
|
81
|
-
)
|
|
82
|
-
else:
|
|
83
|
-
raise NominalIngestError(
|
|
84
|
-
f"unhandled ingest status {progress.ingest_status.type!r} for dataset {self.rid!r}"
|
|
85
|
-
)
|
|
86
|
-
time.sleep(interval.total_seconds())
|
|
87
|
-
|
|
88
|
-
# Update metadata now that data has successfully ingested
|
|
89
|
-
return self.refresh()
|
|
57
|
+
raise NominalMethodRemovedError(
|
|
58
|
+
"nominal.core.Dataset.poll_until_ingestion_completed",
|
|
59
|
+
"poll for ingestion completion on individual 'nominal.core.DatasetFile's, "
|
|
60
|
+
"which are obtained when ingesting files or by calling "
|
|
61
|
+
"'nominal.core.Dataset.list_files()' etc.",
|
|
62
|
+
)
|
|
90
63
|
|
|
91
64
|
def update(
|
|
92
65
|
self,
|
|
@@ -339,8 +312,14 @@ class Dataset(DataSource, RefreshableMixin[scout_catalog.EnrichedDataset]):
|
|
|
339
312
|
def add_ardupilot_dataflash(
|
|
340
313
|
self,
|
|
341
314
|
path: Path | str,
|
|
315
|
+
tags: Mapping[str, str] | None = None,
|
|
342
316
|
) -> DatasetFile:
|
|
343
|
-
"""Add a Dataflash file to an existing dataset.
|
|
317
|
+
"""Add a Dataflash file to an existing dataset.
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
path: Path to the Dataflash file to add to this dataset.
|
|
321
|
+
tags: key-value pairs to apply as tags to all data uniformly in the file.
|
|
322
|
+
"""
|
|
344
323
|
dataflash_path = Path(path)
|
|
345
324
|
s3_path = upload_multipart_file(
|
|
346
325
|
self._clients.auth_header,
|
|
@@ -352,7 +331,7 @@ class Dataset(DataSource, RefreshableMixin[scout_catalog.EnrichedDataset]):
|
|
|
352
331
|
target = ingest_api.DatasetIngestTarget(
|
|
353
332
|
existing=ingest_api.ExistingDatasetIngestDestination(dataset_rid=self.rid)
|
|
354
333
|
)
|
|
355
|
-
request = _create_dataflash_ingest_request(s3_path, target)
|
|
334
|
+
request = _create_dataflash_ingest_request(s3_path, target, tags)
|
|
356
335
|
resp = self._clients.ingest.ingest(self._clients.auth_header, request)
|
|
357
336
|
return self._handle_ingest_response(resp)
|
|
358
337
|
|
|
@@ -548,6 +527,10 @@ class Dataset(DataSource, RefreshableMixin[scout_catalog.EnrichedDataset]):
|
|
|
548
527
|
)
|
|
549
528
|
|
|
550
529
|
|
|
530
|
+
@deprecated(
|
|
531
|
+
"poll_until_ingestion_completed() is deprecated and will be removed in a future release. "
|
|
532
|
+
"Instead, call poll_until_ingestion_completed() on individual DatasetFiles."
|
|
533
|
+
)
|
|
551
534
|
def poll_until_ingestion_completed(datasets: Iterable[Dataset], interval: timedelta = timedelta(seconds=1)) -> None:
|
|
552
535
|
"""Block until all dataset ingestions have completed (succeeded or failed).
|
|
553
536
|
|
|
@@ -562,7 +545,8 @@ def poll_until_ingestion_completed(datasets: Iterable[Dataset], interval: timede
|
|
|
562
545
|
errors = {}
|
|
563
546
|
for dataset in datasets:
|
|
564
547
|
try:
|
|
565
|
-
dataset.
|
|
548
|
+
for dataset_file in dataset.list_files():
|
|
549
|
+
dataset_file.poll_until_ingestion_completed(interval=interval)
|
|
566
550
|
except NominalIngestError as e:
|
|
567
551
|
errors[dataset.rid] = e
|
|
568
552
|
if errors:
|
|
@@ -611,12 +595,17 @@ def _create_dataset(
|
|
|
611
595
|
return client.create_dataset(auth_header, request)
|
|
612
596
|
|
|
613
597
|
|
|
614
|
-
def _create_dataflash_ingest_request(
|
|
598
|
+
def _create_dataflash_ingest_request(
|
|
599
|
+
s3_path: str,
|
|
600
|
+
target: ingest_api.DatasetIngestTarget,
|
|
601
|
+
tags: Mapping[str, str] | None = None,
|
|
602
|
+
) -> ingest_api.IngestRequest:
|
|
615
603
|
return ingest_api.IngestRequest(
|
|
616
604
|
ingest_api.IngestOptions(
|
|
617
605
|
dataflash=ingest_api.DataflashOpts(
|
|
618
606
|
source=ingest_api.IngestSource(s3=ingest_api.S3IngestSource(path=s3_path)),
|
|
619
607
|
target=target,
|
|
608
|
+
additional_file_tags={**tags} if tags else None,
|
|
620
609
|
)
|
|
621
610
|
),
|
|
622
611
|
)
|
|
@@ -7,7 +7,7 @@ from enum import Enum
|
|
|
7
7
|
from typing import Iterable, Mapping, Protocol, Sequence
|
|
8
8
|
|
|
9
9
|
from nominal_api import event
|
|
10
|
-
from typing_extensions import Self
|
|
10
|
+
from typing_extensions import Self
|
|
11
11
|
|
|
12
12
|
from nominal.core._clientsbunch import HasScoutParams
|
|
13
13
|
from nominal.core._utils.api_tools import HasRid, RefreshableMixin, rid_from_instance_or_string
|
|
@@ -45,11 +45,6 @@ class Event(HasRid, RefreshableMixin[event.Event]):
|
|
|
45
45
|
|
|
46
46
|
return resp[0]
|
|
47
47
|
|
|
48
|
-
@property
|
|
49
|
-
@deprecated("The uuid field of an event is deprecated and will be removed in a future release")
|
|
50
|
-
def uuid(self) -> str:
|
|
51
|
-
return self._uuid
|
|
52
|
-
|
|
53
48
|
def update(
|
|
54
49
|
self,
|
|
55
50
|
*,
|