nominal 1.102.0__py3-none-any.whl → 1.103.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.
- CHANGELOG.md +8 -0
- nominal/core/_types.py +6 -0
- nominal/core/attachment.py +3 -1
- nominal/core/client.py +3 -2
- nominal/core/connection.py +3 -3
- nominal/core/dataset.py +17 -16
- nominal/core/dataset_file.py +5 -2
- nominal/core/datasource.py +4 -4
- nominal/core/filetype.py +7 -5
- nominal/core/video.py +3 -2
- nominal/experimental/migration/migration_utils.py +86 -6
- nominal/experimental/rust_streaming/rust_write_stream.py +3 -2
- nominal/experimental/video_processing/video_conversion.py +5 -2
- nominal/nominal.py +9 -8
- nominal/thirdparty/tdms/_tdms.py +4 -3
- {nominal-1.102.0.dist-info → nominal-1.103.0.dist-info}/METADATA +1 -1
- {nominal-1.102.0.dist-info → nominal-1.103.0.dist-info}/RECORD +20 -19
- {nominal-1.102.0.dist-info → nominal-1.103.0.dist-info}/WHEEL +0 -0
- {nominal-1.102.0.dist-info → nominal-1.103.0.dist-info}/entry_points.txt +0 -0
- {nominal-1.102.0.dist-info → nominal-1.103.0.dist-info}/licenses/LICENSE +0 -0
CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.103.0](https://github.com/nominal-io/nominal-client/compare/v1.102.0...v1.103.0) (2026-01-06)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add clone/copy run ([#563](https://github.com/nominal-io/nominal-client/issues/563)) ([276c511](https://github.com/nominal-io/nominal-client/commit/276c511d10c73b9f80ea2efed32d4047fe33795b))
|
|
9
|
+
* universally support paths and strings, and add PathLike alias ([#566](https://github.com/nominal-io/nominal-client/issues/566)) ([860dd3d](https://github.com/nominal-io/nominal-client/commit/860dd3d67456d269356b66b5434b904647e5a2ff))
|
|
10
|
+
|
|
3
11
|
## [1.102.0](https://github.com/nominal-io/nominal-client/compare/v1.101.0...v1.102.0) (2026-01-05)
|
|
4
12
|
|
|
5
13
|
|
nominal/core/_types.py
ADDED
nominal/core/attachment.py
CHANGED
|
@@ -10,6 +10,7 @@ from nominal_api import attachments_api
|
|
|
10
10
|
from typing_extensions import Self
|
|
11
11
|
|
|
12
12
|
from nominal.core._clientsbunch import HasScoutParams
|
|
13
|
+
from nominal.core._types import PathLike
|
|
13
14
|
from nominal.core._utils.api_tools import HasRid, RefreshableMixin
|
|
14
15
|
from nominal.ts import IntegralNanosecondsUTC, _SecondsNanos
|
|
15
16
|
|
|
@@ -69,11 +70,12 @@ class Attachment(HasRid, RefreshableMixin[attachments_api.Attachment]):
|
|
|
69
70
|
# this acts like a file-like object in binary-mode.
|
|
70
71
|
return cast(BinaryIO, response)
|
|
71
72
|
|
|
72
|
-
def write(self, path:
|
|
73
|
+
def write(self, path: PathLike, mkdir: bool = True) -> None:
|
|
73
74
|
"""Write an attachment to the filesystem.
|
|
74
75
|
|
|
75
76
|
`path` should be the path you want to save to, i.e. a file, not a directory.
|
|
76
77
|
"""
|
|
78
|
+
path = Path(path)
|
|
77
79
|
if mkdir:
|
|
78
80
|
path.parent.mkdir(exist_ok=True, parents=True)
|
|
79
81
|
with open(path, "wb") as wf:
|
nominal/core/client.py
CHANGED
|
@@ -37,6 +37,7 @@ from nominal.config import NominalConfig, _config
|
|
|
37
37
|
from nominal.core._clientsbunch import ClientsBunch
|
|
38
38
|
from nominal.core._constants import DEFAULT_API_BASE_URL
|
|
39
39
|
from nominal.core._event_types import EventType
|
|
40
|
+
from nominal.core._types import PathLike
|
|
40
41
|
from nominal.core._utils.api_tools import (
|
|
41
42
|
Link,
|
|
42
43
|
LinkDict,
|
|
@@ -853,7 +854,7 @@ class NominalClient:
|
|
|
853
854
|
|
|
854
855
|
def create_attachment(
|
|
855
856
|
self,
|
|
856
|
-
attachment_file:
|
|
857
|
+
attachment_file: PathLike,
|
|
857
858
|
*,
|
|
858
859
|
description: str | None = None,
|
|
859
860
|
properties: Mapping[str, str] | None = None,
|
|
@@ -965,7 +966,7 @@ class NominalClient:
|
|
|
965
966
|
)
|
|
966
967
|
def create_video_from_mcap(
|
|
967
968
|
self,
|
|
968
|
-
path:
|
|
969
|
+
path: PathLike,
|
|
969
970
|
topic: str,
|
|
970
971
|
name: str | None = None,
|
|
971
972
|
description: str | None = None,
|
nominal/core/connection.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import pathlib
|
|
4
3
|
from dataclasses import dataclass
|
|
5
4
|
from datetime import timedelta
|
|
6
5
|
from typing import Literal, Sequence, overload
|
|
@@ -8,6 +7,7 @@ from typing import Literal, Sequence, overload
|
|
|
8
7
|
from nominal_api import scout_datasource_connection_api
|
|
9
8
|
|
|
10
9
|
from nominal.core._stream.write_stream import DataStream
|
|
10
|
+
from nominal.core._types import PathLike
|
|
11
11
|
from nominal.core.datasource import DataSource, _get_write_stream
|
|
12
12
|
|
|
13
13
|
|
|
@@ -71,7 +71,7 @@ class StreamingConnection(Connection):
|
|
|
71
71
|
batch_size: int = 50_000,
|
|
72
72
|
max_wait: timedelta = timedelta(seconds=1),
|
|
73
73
|
data_format: Literal["rust_experimental"] | None = None,
|
|
74
|
-
file_fallback:
|
|
74
|
+
file_fallback: PathLike | None = None,
|
|
75
75
|
log_level: str | None = None,
|
|
76
76
|
num_workers: int | None = None,
|
|
77
77
|
) -> DataStream: ...
|
|
@@ -80,7 +80,7 @@ class StreamingConnection(Connection):
|
|
|
80
80
|
batch_size: int = 50_000,
|
|
81
81
|
max_wait: timedelta = timedelta(seconds=1),
|
|
82
82
|
data_format: Literal["json", "protobuf", "experimental", "rust_experimental"] | None = None,
|
|
83
|
-
file_fallback:
|
|
83
|
+
file_fallback: PathLike | None = None,
|
|
84
84
|
log_level: str | None = None,
|
|
85
85
|
num_workers: int | None = None,
|
|
86
86
|
) -> DataStream:
|
nominal/core/dataset.py
CHANGED
|
@@ -14,6 +14,7 @@ from typing_extensions import Self, deprecated
|
|
|
14
14
|
|
|
15
15
|
from nominal.core._stream.batch_processor import process_log_batch
|
|
16
16
|
from nominal.core._stream.write_stream import LogStream, WriteStream
|
|
17
|
+
from nominal.core._types import PathLike
|
|
17
18
|
from nominal.core._utils.api_tools import RefreshableMixin
|
|
18
19
|
from nominal.core._utils.multipart import path_upload_name, upload_multipart_file, upload_multipart_io
|
|
19
20
|
from nominal.core.bounds import Bounds
|
|
@@ -112,7 +113,7 @@ class Dataset(DataSource, RefreshableMixin[scout_catalog.EnrichedDataset]):
|
|
|
112
113
|
|
|
113
114
|
def add_tabular_data(
|
|
114
115
|
self,
|
|
115
|
-
path:
|
|
116
|
+
path: PathLike,
|
|
116
117
|
timestamp_column: str,
|
|
117
118
|
timestamp_type: _AnyTimestampType,
|
|
118
119
|
tag_columns: Mapping[str, str] | None = None,
|
|
@@ -206,7 +207,7 @@ class Dataset(DataSource, RefreshableMixin[scout_catalog.EnrichedDataset]):
|
|
|
206
207
|
|
|
207
208
|
def add_avro_stream(
|
|
208
209
|
self,
|
|
209
|
-
path:
|
|
210
|
+
path: PathLike,
|
|
210
211
|
) -> DatasetFile:
|
|
211
212
|
"""Upload an avro stream file with a specific schema, described below.
|
|
212
213
|
|
|
@@ -278,7 +279,7 @@ class Dataset(DataSource, RefreshableMixin[scout_catalog.EnrichedDataset]):
|
|
|
278
279
|
|
|
279
280
|
def add_journal_json(
|
|
280
281
|
self,
|
|
281
|
-
path:
|
|
282
|
+
path: PathLike,
|
|
282
283
|
) -> DatasetFile:
|
|
283
284
|
"""Add a journald jsonl file to an existing dataset."""
|
|
284
285
|
log_path = Path(path)
|
|
@@ -310,7 +311,7 @@ class Dataset(DataSource, RefreshableMixin[scout_catalog.EnrichedDataset]):
|
|
|
310
311
|
|
|
311
312
|
def add_mcap(
|
|
312
313
|
self,
|
|
313
|
-
path:
|
|
314
|
+
path: PathLike,
|
|
314
315
|
include_topics: Iterable[str] | None = None,
|
|
315
316
|
exclude_topics: Iterable[str] | None = None,
|
|
316
317
|
) -> DatasetFile:
|
|
@@ -384,7 +385,7 @@ class Dataset(DataSource, RefreshableMixin[scout_catalog.EnrichedDataset]):
|
|
|
384
385
|
|
|
385
386
|
def add_ardupilot_dataflash(
|
|
386
387
|
self,
|
|
387
|
-
path:
|
|
388
|
+
path: PathLike,
|
|
388
389
|
tags: Mapping[str, str] | None = None,
|
|
389
390
|
) -> DatasetFile:
|
|
390
391
|
"""Add a Dataflash file to an existing dataset.
|
|
@@ -415,7 +416,7 @@ class Dataset(DataSource, RefreshableMixin[scout_catalog.EnrichedDataset]):
|
|
|
415
416
|
def add_containerized(
|
|
416
417
|
self,
|
|
417
418
|
extractor: str | ContainerizedExtractor,
|
|
418
|
-
sources: Mapping[str,
|
|
419
|
+
sources: Mapping[str, PathLike],
|
|
419
420
|
tag: str | None = None,
|
|
420
421
|
*,
|
|
421
422
|
arguments: Mapping[str, str] | None = None,
|
|
@@ -425,7 +426,7 @@ class Dataset(DataSource, RefreshableMixin[scout_catalog.EnrichedDataset]):
|
|
|
425
426
|
def add_containerized(
|
|
426
427
|
self,
|
|
427
428
|
extractor: str | ContainerizedExtractor,
|
|
428
|
-
sources: Mapping[str,
|
|
429
|
+
sources: Mapping[str, PathLike],
|
|
429
430
|
tag: str | None = None,
|
|
430
431
|
*,
|
|
431
432
|
arguments: Mapping[str, str] | None = None,
|
|
@@ -436,7 +437,7 @@ class Dataset(DataSource, RefreshableMixin[scout_catalog.EnrichedDataset]):
|
|
|
436
437
|
def add_containerized(
|
|
437
438
|
self,
|
|
438
439
|
extractor: str | ContainerizedExtractor,
|
|
439
|
-
sources: Mapping[str,
|
|
440
|
+
sources: Mapping[str, PathLike],
|
|
440
441
|
tag: str | None = None,
|
|
441
442
|
*,
|
|
442
443
|
arguments: Mapping[str, str] | None = None,
|
|
@@ -708,7 +709,7 @@ class _DatasetWrapper(abc.ABC):
|
|
|
708
709
|
def add_tabular_data(
|
|
709
710
|
self,
|
|
710
711
|
data_scope_name: str,
|
|
711
|
-
path:
|
|
712
|
+
path: PathLike,
|
|
712
713
|
*,
|
|
713
714
|
timestamp_column: str,
|
|
714
715
|
timestamp_type: _AnyTimestampType,
|
|
@@ -735,7 +736,7 @@ class _DatasetWrapper(abc.ABC):
|
|
|
735
736
|
def add_avro_stream(
|
|
736
737
|
self,
|
|
737
738
|
data_scope_name: str,
|
|
738
|
-
path:
|
|
739
|
+
path: PathLike,
|
|
739
740
|
) -> DatasetFile:
|
|
740
741
|
"""Upload an avro stream file to the dataset selected by `data_scope_name`.
|
|
741
742
|
|
|
@@ -761,7 +762,7 @@ class _DatasetWrapper(abc.ABC):
|
|
|
761
762
|
def add_journal_json(
|
|
762
763
|
self,
|
|
763
764
|
data_scope_name: str,
|
|
764
|
-
path:
|
|
765
|
+
path: PathLike,
|
|
765
766
|
) -> DatasetFile:
|
|
766
767
|
"""Add a journald json file to the dataset selected by `data_scope_name`.
|
|
767
768
|
|
|
@@ -787,7 +788,7 @@ class _DatasetWrapper(abc.ABC):
|
|
|
787
788
|
def add_mcap(
|
|
788
789
|
self,
|
|
789
790
|
data_scope_name: str,
|
|
790
|
-
path:
|
|
791
|
+
path: PathLike,
|
|
791
792
|
*,
|
|
792
793
|
include_topics: Iterable[str] | None = None,
|
|
793
794
|
exclude_topics: Iterable[str] | None = None,
|
|
@@ -815,7 +816,7 @@ class _DatasetWrapper(abc.ABC):
|
|
|
815
816
|
def add_ardupilot_dataflash(
|
|
816
817
|
self,
|
|
817
818
|
data_scope_name: str,
|
|
818
|
-
path:
|
|
819
|
+
path: PathLike,
|
|
819
820
|
tags: Mapping[str, str] | None = None,
|
|
820
821
|
) -> DatasetFile:
|
|
821
822
|
"""Add a Dataflash file to the dataset selected by `data_scope_name`.
|
|
@@ -835,7 +836,7 @@ class _DatasetWrapper(abc.ABC):
|
|
|
835
836
|
self,
|
|
836
837
|
data_scope_name: str,
|
|
837
838
|
extractor: str | ContainerizedExtractor,
|
|
838
|
-
sources: Mapping[str,
|
|
839
|
+
sources: Mapping[str, PathLike],
|
|
839
840
|
*,
|
|
840
841
|
tag: str | None = None,
|
|
841
842
|
tags: Mapping[str, str] | None = None,
|
|
@@ -845,7 +846,7 @@ class _DatasetWrapper(abc.ABC):
|
|
|
845
846
|
self,
|
|
846
847
|
data_scope_name: str,
|
|
847
848
|
extractor: str | ContainerizedExtractor,
|
|
848
|
-
sources: Mapping[str,
|
|
849
|
+
sources: Mapping[str, PathLike],
|
|
849
850
|
*,
|
|
850
851
|
tag: str | None = None,
|
|
851
852
|
tags: Mapping[str, str] | None = None,
|
|
@@ -856,7 +857,7 @@ class _DatasetWrapper(abc.ABC):
|
|
|
856
857
|
self,
|
|
857
858
|
data_scope_name: str,
|
|
858
859
|
extractor: str | ContainerizedExtractor,
|
|
859
|
-
sources: Mapping[str,
|
|
860
|
+
sources: Mapping[str, PathLike],
|
|
860
861
|
*,
|
|
861
862
|
tag: str | None = None,
|
|
862
863
|
tags: Mapping[str, str] | None = None,
|
nominal/core/dataset_file.py
CHANGED
|
@@ -13,6 +13,7 @@ from nominal_api import api, ingest_api, scout_catalog
|
|
|
13
13
|
from typing_extensions import Self
|
|
14
14
|
|
|
15
15
|
from nominal.core._clientsbunch import HasScoutParams
|
|
16
|
+
from nominal.core._types import PathLike
|
|
16
17
|
from nominal.core._utils.api_tools import RefreshableMixin
|
|
17
18
|
from nominal.core._utils.multipart import DEFAULT_CHUNK_SIZE
|
|
18
19
|
from nominal.core._utils.multipart_downloader import (
|
|
@@ -128,7 +129,7 @@ class DatasetFile(RefreshableMixin[scout_catalog.DatasetFile]):
|
|
|
128
129
|
|
|
129
130
|
def download(
|
|
130
131
|
self,
|
|
131
|
-
output_directory:
|
|
132
|
+
output_directory: PathLike,
|
|
132
133
|
*,
|
|
133
134
|
part_size: int = DEFAULT_CHUNK_SIZE,
|
|
134
135
|
num_retries: int = 3,
|
|
@@ -148,6 +149,7 @@ class DatasetFile(RefreshableMixin[scout_catalog.DatasetFile]):
|
|
|
148
149
|
FileExistsError: File already exists at destination
|
|
149
150
|
RuntimeError: Error downloading file
|
|
150
151
|
"""
|
|
152
|
+
output_directory = pathlib.Path(output_directory)
|
|
151
153
|
if output_directory.exists() and not output_directory.is_dir():
|
|
152
154
|
raise NotADirectoryError(f"Output directory is not a directory: {output_directory}")
|
|
153
155
|
|
|
@@ -160,7 +162,7 @@ class DatasetFile(RefreshableMixin[scout_catalog.DatasetFile]):
|
|
|
160
162
|
|
|
161
163
|
def download_original_files(
|
|
162
164
|
self,
|
|
163
|
-
output_directory:
|
|
165
|
+
output_directory: PathLike,
|
|
164
166
|
*,
|
|
165
167
|
part_size: int = DEFAULT_CHUNK_SIZE,
|
|
166
168
|
num_retries: int = 3,
|
|
@@ -184,6 +186,7 @@ class DatasetFile(RefreshableMixin[scout_catalog.DatasetFile]):
|
|
|
184
186
|
NOTE: any file that fails to download will result in an error log and will not be returned
|
|
185
187
|
as an output path
|
|
186
188
|
"""
|
|
189
|
+
output_directory = pathlib.Path(output_directory)
|
|
187
190
|
if output_directory.exists() and not output_directory.is_dir():
|
|
188
191
|
raise NotADirectoryError(f"Output directory is not a directory: {output_directory}")
|
|
189
192
|
|
nominal/core/datasource.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
import pathlib
|
|
5
4
|
from dataclasses import dataclass, field
|
|
6
5
|
from datetime import timedelta
|
|
7
6
|
from typing import Iterable, Literal, Mapping, Protocol, Sequence, overload
|
|
@@ -27,6 +26,7 @@ from nominal._utils import batched
|
|
|
27
26
|
from nominal.core._clientsbunch import HasScoutParams, ProtoWriteService
|
|
28
27
|
from nominal.core._stream.batch_processor import process_batch_legacy
|
|
29
28
|
from nominal.core._stream.write_stream import DataStream, WriteStream
|
|
29
|
+
from nominal.core._types import PathLike
|
|
30
30
|
from nominal.core._utils.api_tools import HasRid
|
|
31
31
|
from nominal.core.channel import Channel, ChannelDataType
|
|
32
32
|
from nominal.core.unit import UnitMapping, _build_unit_update, _error_on_invalid_units
|
|
@@ -125,7 +125,7 @@ class DataSource(HasRid):
|
|
|
125
125
|
batch_size: int = 50_000,
|
|
126
126
|
max_wait: timedelta = timedelta(seconds=1),
|
|
127
127
|
data_format: Literal["rust_experimental"] | None = None,
|
|
128
|
-
file_fallback:
|
|
128
|
+
file_fallback: PathLike | None = None,
|
|
129
129
|
log_level: str | None = None,
|
|
130
130
|
num_workers: int | None = None,
|
|
131
131
|
) -> DataStream: ...
|
|
@@ -134,7 +134,7 @@ class DataSource(HasRid):
|
|
|
134
134
|
batch_size: int = 50_000,
|
|
135
135
|
max_wait: timedelta = timedelta(seconds=1),
|
|
136
136
|
data_format: Literal["json", "protobuf", "experimental", "rust_experimental"] | None = None,
|
|
137
|
-
file_fallback:
|
|
137
|
+
file_fallback: PathLike | None = None,
|
|
138
138
|
log_level: str | None = None,
|
|
139
139
|
num_workers: int | None = None,
|
|
140
140
|
) -> DataStream:
|
|
@@ -327,7 +327,7 @@ def _get_write_stream(
|
|
|
327
327
|
batch_size: int,
|
|
328
328
|
max_wait: timedelta,
|
|
329
329
|
data_format: Literal["json", "protobuf", "experimental", "rust_experimental"] | None,
|
|
330
|
-
file_fallback:
|
|
330
|
+
file_fallback: PathLike | None,
|
|
331
331
|
log_level: str | None,
|
|
332
332
|
num_workers: int | None,
|
|
333
333
|
write_rid: str,
|
nominal/core/filetype.py
CHANGED
|
@@ -5,6 +5,8 @@ import mimetypes
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from typing import NamedTuple
|
|
7
7
|
|
|
8
|
+
from nominal.core._types import PathLike
|
|
9
|
+
|
|
8
10
|
logger = logging.getLogger(__name__)
|
|
9
11
|
|
|
10
12
|
|
|
@@ -13,7 +15,7 @@ class FileType(NamedTuple):
|
|
|
13
15
|
mimetype: str
|
|
14
16
|
|
|
15
17
|
@classmethod
|
|
16
|
-
def from_path(cls, path:
|
|
18
|
+
def from_path(cls, path: PathLike, default_mimetype: str = "application/octect-stream") -> FileType:
|
|
17
19
|
path = Path(path)
|
|
18
20
|
|
|
19
21
|
# Note: not using path.suffix because this fails for files with multiple suffixes,
|
|
@@ -70,7 +72,7 @@ class FileType(NamedTuple):
|
|
|
70
72
|
return self in FileTypes._VIDEO_TYPES
|
|
71
73
|
|
|
72
74
|
@classmethod
|
|
73
|
-
def from_path_dataset(cls, path:
|
|
75
|
+
def from_path_dataset(cls, path: PathLike) -> FileType:
|
|
74
76
|
file_type = cls.from_path(path)
|
|
75
77
|
if not file_type.is_parquet_file() and not file_type.is_csv():
|
|
76
78
|
allowed_extensions = (*FileTypes._PARQUET_FILE_TYPES, *FileTypes._CSV_TYPES)
|
|
@@ -79,7 +81,7 @@ class FileType(NamedTuple):
|
|
|
79
81
|
return file_type
|
|
80
82
|
|
|
81
83
|
@classmethod
|
|
82
|
-
def from_tabular(cls, path:
|
|
84
|
+
def from_tabular(cls, path: PathLike) -> FileType:
|
|
83
85
|
file_type = cls.from_path(path)
|
|
84
86
|
if not file_type.is_csv() and not file_type.is_parquet():
|
|
85
87
|
allowed_extensions = (
|
|
@@ -92,7 +94,7 @@ class FileType(NamedTuple):
|
|
|
92
94
|
return file_type
|
|
93
95
|
|
|
94
96
|
@classmethod
|
|
95
|
-
def from_path_journal_json(cls, path:
|
|
97
|
+
def from_path_journal_json(cls, path: PathLike) -> FileType:
|
|
96
98
|
file_type = cls.from_path(path)
|
|
97
99
|
if not file_type.is_journal():
|
|
98
100
|
raise ValueError(
|
|
@@ -102,7 +104,7 @@ class FileType(NamedTuple):
|
|
|
102
104
|
return file_type
|
|
103
105
|
|
|
104
106
|
@classmethod
|
|
105
|
-
def from_video(cls, path:
|
|
107
|
+
def from_video(cls, path: PathLike) -> FileType:
|
|
106
108
|
file_type = cls.from_path(path)
|
|
107
109
|
if not file_type.is_video():
|
|
108
110
|
raise ValueError(f"video path '{path}' must end in one of {[f.extension for f in FileTypes._VIDEO_TYPES]}")
|
nominal/core/video.py
CHANGED
|
@@ -14,6 +14,7 @@ from nominal_api import api, ingest_api, scout_video, scout_video_api, upload_ap
|
|
|
14
14
|
from typing_extensions import Self
|
|
15
15
|
|
|
16
16
|
from nominal.core._clientsbunch import HasScoutParams
|
|
17
|
+
from nominal.core._types import PathLike
|
|
17
18
|
from nominal.core._utils.api_tools import HasRid, RefreshableMixin
|
|
18
19
|
from nominal.core._utils.multipart import path_upload_name, upload_multipart_io
|
|
19
20
|
from nominal.core.exceptions import NominalIngestError, NominalIngestFailed
|
|
@@ -118,7 +119,7 @@ class Video(HasRid, RefreshableMixin[scout_video_api.Video]):
|
|
|
118
119
|
|
|
119
120
|
def add_file(
|
|
120
121
|
self,
|
|
121
|
-
path:
|
|
122
|
+
path: PathLike,
|
|
122
123
|
start: datetime | IntegralNanosecondsUTC | None = None,
|
|
123
124
|
frame_timestamps: Sequence[IntegralNanosecondsUTC] | None = None,
|
|
124
125
|
description: str | None = None,
|
|
@@ -212,7 +213,7 @@ class Video(HasRid, RefreshableMixin[scout_video_api.Video]):
|
|
|
212
213
|
|
|
213
214
|
def add_mcap(
|
|
214
215
|
self,
|
|
215
|
-
path:
|
|
216
|
+
path: PathLike,
|
|
216
217
|
topic: str,
|
|
217
218
|
description: str | None = None,
|
|
218
219
|
) -> VideoFile:
|
|
@@ -23,6 +23,9 @@ from nominal.core import (
|
|
|
23
23
|
WorkbookTemplate,
|
|
24
24
|
)
|
|
25
25
|
from nominal.core._event_types import EventType, SearchEventOriginType
|
|
26
|
+
from nominal.core._utils.api_tools import Link, LinkDict
|
|
27
|
+
from nominal.core.attachment import Attachment
|
|
28
|
+
from nominal.core.run import Run
|
|
26
29
|
from nominal.ts import (
|
|
27
30
|
IntegralNanosecondsDuration,
|
|
28
31
|
IntegralNanosecondsUTC,
|
|
@@ -523,6 +526,75 @@ def copy_event_from(
|
|
|
523
526
|
return new_event
|
|
524
527
|
|
|
525
528
|
|
|
529
|
+
def clone_run(source_run: Run, destination_client: NominalClient) -> Run:
|
|
530
|
+
"""Clones a run, maintaining all properties, linked assets, and attachments.
|
|
531
|
+
|
|
532
|
+
Args:
|
|
533
|
+
source_run: The run to copy from.
|
|
534
|
+
destination_client: The destination client.
|
|
535
|
+
|
|
536
|
+
Returns:
|
|
537
|
+
The cloned run.
|
|
538
|
+
"""
|
|
539
|
+
return copy_run_from(source_run=source_run, destination_client=destination_client)
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
def copy_run_from(
|
|
543
|
+
source_run: Run,
|
|
544
|
+
destination_client: NominalClient,
|
|
545
|
+
*,
|
|
546
|
+
new_name: str | None = None,
|
|
547
|
+
new_start: datetime | IntegralNanosecondsUTC | None = None,
|
|
548
|
+
new_end: datetime | IntegralNanosecondsUTC | None = None,
|
|
549
|
+
new_description: str | None = None,
|
|
550
|
+
new_properties: Mapping[str, str] | None = None,
|
|
551
|
+
new_labels: Sequence[str] = (),
|
|
552
|
+
new_links: Sequence[str | Link | LinkDict] = (),
|
|
553
|
+
new_attachments: Iterable[Attachment] | Iterable[str] = (),
|
|
554
|
+
new_assets: Sequence[Asset | str] = (),
|
|
555
|
+
) -> Run:
|
|
556
|
+
"""Copy a run from the source to the destination client.
|
|
557
|
+
|
|
558
|
+
Args:
|
|
559
|
+
source_run: The source Run to copy.
|
|
560
|
+
destination_client: The NominalClient to create the copied run in.
|
|
561
|
+
new_name: Optionally override the name of the copied run. Defaults to original name.
|
|
562
|
+
new_start: Optionally override the start time of the copied run. Defaults to original start time.
|
|
563
|
+
new_end: Optionally override the end time of the copied run. Defaults to original end time.
|
|
564
|
+
new_description: Optionally override the description of the copied run. Defaults to original description.
|
|
565
|
+
new_properties: Optionally override the properties of the copied run. Defaults to original properties.
|
|
566
|
+
new_labels: Optionally override the labels of the copied run. Defaults to original labels.
|
|
567
|
+
new_links: Optionally override the links of the copied run. Defaults to original links.
|
|
568
|
+
new_attachments: Optionally override the attachments of the copied run. Defaults to original attachments.
|
|
569
|
+
new_assets: Optionally override the linked assets of the copied run. Defaults to original linked assets.
|
|
570
|
+
|
|
571
|
+
Returns:
|
|
572
|
+
The newly created Run in the destination client.
|
|
573
|
+
"""
|
|
574
|
+
log_extras = {
|
|
575
|
+
"destination_client_workspace": destination_client.get_workspace(destination_client._clients.workspace_rid).rid
|
|
576
|
+
}
|
|
577
|
+
logger.debug(
|
|
578
|
+
"Copying run %s (rid: %s)",
|
|
579
|
+
source_run.name,
|
|
580
|
+
source_run.rid,
|
|
581
|
+
extra=log_extras,
|
|
582
|
+
)
|
|
583
|
+
new_run = destination_client.create_run(
|
|
584
|
+
name=new_name or source_run.name,
|
|
585
|
+
start=new_start or source_run.start,
|
|
586
|
+
end=new_end or source_run.end,
|
|
587
|
+
description=new_description or source_run.description,
|
|
588
|
+
properties=new_properties or source_run.properties,
|
|
589
|
+
labels=new_labels or source_run.labels,
|
|
590
|
+
assets=new_assets or source_run.assets,
|
|
591
|
+
links=new_links or source_run.links,
|
|
592
|
+
attachments=new_attachments or source_run.list_attachments(),
|
|
593
|
+
)
|
|
594
|
+
logger.debug("New run created: %s (rid: %s)", new_run.name, new_run.rid, extra=log_extras)
|
|
595
|
+
return new_run
|
|
596
|
+
|
|
597
|
+
|
|
526
598
|
def clone_asset(
|
|
527
599
|
source_asset: Asset,
|
|
528
600
|
destination_client: NominalClient,
|
|
@@ -537,7 +609,11 @@ def clone_asset(
|
|
|
537
609
|
The newly created Asset in the target client.
|
|
538
610
|
"""
|
|
539
611
|
return copy_asset_from(
|
|
540
|
-
source_asset=source_asset,
|
|
612
|
+
source_asset=source_asset,
|
|
613
|
+
destination_client=destination_client,
|
|
614
|
+
include_data=True,
|
|
615
|
+
include_events=True,
|
|
616
|
+
include_runs=True,
|
|
541
617
|
)
|
|
542
618
|
|
|
543
619
|
|
|
@@ -551,6 +627,7 @@ def copy_asset_from(
|
|
|
551
627
|
new_asset_labels: Sequence[str] | None = None,
|
|
552
628
|
include_data: bool = False,
|
|
553
629
|
include_events: bool = False,
|
|
630
|
+
include_runs: bool = False,
|
|
554
631
|
) -> Asset:
|
|
555
632
|
"""Copy an asset from the source to the destination client.
|
|
556
633
|
|
|
@@ -563,6 +640,7 @@ def copy_asset_from(
|
|
|
563
640
|
new_asset_labels: Optional new labels for the copied asset. If not provided, the original labels are used.
|
|
564
641
|
include_data: Whether to include data in the copied asset.
|
|
565
642
|
include_events: Whether to include events in the copied dataset.
|
|
643
|
+
include_runs: Whether to include runs in the copied asset.
|
|
566
644
|
|
|
567
645
|
Returns:
|
|
568
646
|
The new asset created.
|
|
@@ -579,21 +657,23 @@ def copy_asset_from(
|
|
|
579
657
|
)
|
|
580
658
|
if include_data:
|
|
581
659
|
source_datasets = source_asset.list_datasets()
|
|
582
|
-
new_datasets = []
|
|
583
660
|
for data_scope, source_dataset in source_datasets:
|
|
584
661
|
new_dataset = clone_dataset(
|
|
585
662
|
source_dataset=source_dataset,
|
|
586
663
|
destination_client=destination_client,
|
|
587
664
|
)
|
|
588
|
-
new_datasets.append(new_dataset)
|
|
589
665
|
new_asset.add_dataset(data_scope, new_dataset)
|
|
666
|
+
source_asset._list_dataset_scopes
|
|
590
667
|
|
|
591
668
|
if include_events:
|
|
592
669
|
source_events = source_asset.search_events(origin_types=SearchEventOriginType.get_manual_origin_types())
|
|
593
|
-
new_events = []
|
|
594
670
|
for source_event in source_events:
|
|
595
|
-
|
|
596
|
-
|
|
671
|
+
copy_event_from(source_event, destination_client, new_assets=[new_asset])
|
|
672
|
+
|
|
673
|
+
if include_runs:
|
|
674
|
+
source_runs = source_asset.list_runs()
|
|
675
|
+
for source_run in source_runs:
|
|
676
|
+
copy_run_from(source_run, destination_client, new_assets=[new_asset])
|
|
597
677
|
|
|
598
678
|
logger.debug("New asset created: %s (rid: %s)", new_asset, new_asset.rid, extra=log_extras)
|
|
599
679
|
return new_asset
|
|
@@ -6,6 +6,7 @@ import pathlib
|
|
|
6
6
|
from nominal_streaming import NominalDatasetStream
|
|
7
7
|
|
|
8
8
|
from nominal.core._stream.write_stream import DataStream
|
|
9
|
+
from nominal.core._types import PathLike
|
|
9
10
|
from nominal.core.datasource import DataSource
|
|
10
11
|
|
|
11
12
|
|
|
@@ -22,7 +23,7 @@ class RustWriteStream(NominalDatasetStream, DataStream):
|
|
|
22
23
|
datasource_clients: DataSource._Clients,
|
|
23
24
|
batch_size: int,
|
|
24
25
|
max_wait: datetime.timedelta,
|
|
25
|
-
file_fallback:
|
|
26
|
+
file_fallback: PathLike | None = None,
|
|
26
27
|
log_level: str | None = None,
|
|
27
28
|
num_workers: int | None = None,
|
|
28
29
|
) -> RustWriteStream:
|
|
@@ -41,7 +42,7 @@ class RustWriteStream(NominalDatasetStream, DataStream):
|
|
|
41
42
|
).with_core_consumer(datasource_rid)
|
|
42
43
|
|
|
43
44
|
if file_fallback is not None:
|
|
44
|
-
stream = stream.with_file_fallback(file_fallback)
|
|
45
|
+
stream = stream.with_file_fallback(pathlib.Path(file_fallback))
|
|
45
46
|
|
|
46
47
|
if log_level is not None:
|
|
47
48
|
stream = stream.enable_logging(log_level)
|
|
@@ -6,6 +6,7 @@ import shlex
|
|
|
6
6
|
|
|
7
7
|
import ffmpeg
|
|
8
8
|
|
|
9
|
+
from nominal.core._types import PathLike
|
|
9
10
|
from nominal.experimental.video_processing.resolution import (
|
|
10
11
|
AnyResolutionType,
|
|
11
12
|
scale_factor_from_resolution,
|
|
@@ -20,8 +21,8 @@ DEFAULT_KEY_FRAME_INTERVAL_SEC = 2
|
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
def normalize_video(
|
|
23
|
-
input_path:
|
|
24
|
-
output_path:
|
|
24
|
+
input_path: PathLike,
|
|
25
|
+
output_path: PathLike,
|
|
25
26
|
key_frame_interval: int | None = DEFAULT_KEY_FRAME_INTERVAL_SEC,
|
|
26
27
|
force: bool = True,
|
|
27
28
|
resolution: AnyResolutionType | None = None,
|
|
@@ -59,6 +60,8 @@ def normalize_video(
|
|
|
59
60
|
|
|
60
61
|
NOTE: this requires that you have installed ffmpeg on your system with support for H264.
|
|
61
62
|
"""
|
|
63
|
+
input_path = pathlib.Path(input_path)
|
|
64
|
+
output_path = pathlib.Path(output_path)
|
|
62
65
|
assert input_path.exists(), "Input path must exist"
|
|
63
66
|
assert output_path.suffix.lower() in (".mkv", ".mp4")
|
|
64
67
|
|
nominal/nominal.py
CHANGED
|
@@ -24,6 +24,7 @@ from nominal.core import (
|
|
|
24
24
|
poll_until_ingestion_completed,
|
|
25
25
|
)
|
|
26
26
|
from nominal.core._constants import DEFAULT_API_BASE_URL
|
|
27
|
+
from nominal.core._types import PathLike
|
|
27
28
|
from nominal.core.connection import StreamingConnection
|
|
28
29
|
from nominal.core.data_review import DataReview, DataReviewBuilder
|
|
29
30
|
|
|
@@ -102,7 +103,7 @@ def get_user() -> User:
|
|
|
102
103
|
"Use `nominal.thirdparty.tdms.upload_tdms` instead."
|
|
103
104
|
)
|
|
104
105
|
def upload_tdms(
|
|
105
|
-
file:
|
|
106
|
+
file: PathLike,
|
|
106
107
|
name: str | None = None,
|
|
107
108
|
description: str | None = None,
|
|
108
109
|
timestamp_column: str | None = None,
|
|
@@ -213,7 +214,7 @@ def create_dataset(
|
|
|
213
214
|
"Use `nominal.create_dataset` or `nominal.get_dataset`, add data to an existing dataset instead."
|
|
214
215
|
)
|
|
215
216
|
def upload_csv(
|
|
216
|
-
file:
|
|
217
|
+
file: PathLike,
|
|
217
218
|
name: str | None,
|
|
218
219
|
timestamp_column: str,
|
|
219
220
|
timestamp_type: ts._AnyTimestampType,
|
|
@@ -245,7 +246,7 @@ def upload_csv(
|
|
|
245
246
|
|
|
246
247
|
def _upload_csv(
|
|
247
248
|
client: NominalClient,
|
|
248
|
-
file:
|
|
249
|
+
file: PathLike,
|
|
249
250
|
name: str | None,
|
|
250
251
|
timestamp_column: str,
|
|
251
252
|
timestamp_type: ts._AnyTimestampType,
|
|
@@ -317,7 +318,7 @@ def create_run(
|
|
|
317
318
|
f"see {AUTHENTICATION_DOCS_LINK}"
|
|
318
319
|
)
|
|
319
320
|
def create_run_csv(
|
|
320
|
-
file:
|
|
321
|
+
file: PathLike,
|
|
321
322
|
name: str,
|
|
322
323
|
timestamp_column: str,
|
|
323
324
|
timestamp_type: ts._LiteralAbsolute | ts.Iso8601 | ts.Epoch,
|
|
@@ -379,7 +380,7 @@ def search_runs(
|
|
|
379
380
|
f"Use `nominal.NominalClient.create_attachment` instead, see {AUTHENTICATION_DOCS_LINK}"
|
|
380
381
|
)
|
|
381
382
|
def upload_attachment(
|
|
382
|
-
file:
|
|
383
|
+
file: PathLike,
|
|
383
384
|
name: str,
|
|
384
385
|
description: str | None = None,
|
|
385
386
|
) -> Attachment:
|
|
@@ -406,7 +407,7 @@ def get_attachment(rid: str) -> Attachment:
|
|
|
406
407
|
"Use `nominal.NominalClient.get_attachment` and `nominal.core.Attachment.write` instead, "
|
|
407
408
|
f"see {AUTHENTICATION_DOCS_LINK}"
|
|
408
409
|
)
|
|
409
|
-
def download_attachment(rid: str, file:
|
|
410
|
+
def download_attachment(rid: str, file: PathLike) -> None:
|
|
410
411
|
"""Retrieve an attachment from the Nominal platform and save it to `file`."""
|
|
411
412
|
client = _get_default_client()
|
|
412
413
|
attachment = client.get_attachment(rid)
|
|
@@ -418,7 +419,7 @@ def download_attachment(rid: str, file: Path | str) -> None:
|
|
|
418
419
|
f"Use `nominal.NominalClient.create_video` instead, see {AUTHENTICATION_DOCS_LINK}"
|
|
419
420
|
)
|
|
420
421
|
def upload_video(
|
|
421
|
-
file:
|
|
422
|
+
file: PathLike, name: str, start: datetime | str | ts.IntegralNanosecondsUTC, description: str | None = None
|
|
422
423
|
) -> Video:
|
|
423
424
|
"""Upload a video to Nominal from a file."""
|
|
424
425
|
client = _get_default_client()
|
|
@@ -542,7 +543,7 @@ def get_checklist(checklist_rid: str) -> Checklist:
|
|
|
542
543
|
f"Use `nominal.NominalClient.create_mcap_video` instead, see {AUTHENTICATION_DOCS_LINK}"
|
|
543
544
|
)
|
|
544
545
|
def upload_mcap_video(
|
|
545
|
-
file:
|
|
546
|
+
file: PathLike,
|
|
546
547
|
topic: str,
|
|
547
548
|
name: str | None = None,
|
|
548
549
|
description: str | None = None,
|
nominal/thirdparty/tdms/_tdms.py
CHANGED
|
@@ -10,6 +10,7 @@ import pandas as pd
|
|
|
10
10
|
from nptdms import TdmsChannel, TdmsFile, TdmsGroup
|
|
11
11
|
|
|
12
12
|
from nominal import ts
|
|
13
|
+
from nominal.core._types import PathLike
|
|
13
14
|
from nominal.core.client import NominalClient
|
|
14
15
|
from nominal.core.dataset import Dataset
|
|
15
16
|
from nominal.core.dataset_file import DatasetFile
|
|
@@ -19,7 +20,7 @@ logger = logging.getLogger(__name__)
|
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
def _tdms_to_dataframe(
|
|
22
|
-
file:
|
|
23
|
+
file: PathLike,
|
|
23
24
|
timestamp_column: str | None = None,
|
|
24
25
|
timestamp_type: ts._AnyTimestampType | None = None,
|
|
25
26
|
) -> Tuple[str, ts._AnyTimestampType, pd.DataFrame]:
|
|
@@ -46,7 +47,7 @@ def _tdms_to_dataframe(
|
|
|
46
47
|
|
|
47
48
|
def upload_tdms_to_dataset(
|
|
48
49
|
dataset: Dataset,
|
|
49
|
-
file:
|
|
50
|
+
file: PathLike,
|
|
50
51
|
timestamp_column: str | None = None,
|
|
51
52
|
timestamp_type: ts._AnyTimestampType | None = None,
|
|
52
53
|
*,
|
|
@@ -91,7 +92,7 @@ def upload_tdms_to_dataset(
|
|
|
91
92
|
|
|
92
93
|
def upload_tdms(
|
|
93
94
|
client: NominalClient,
|
|
94
|
-
file:
|
|
95
|
+
file: PathLike,
|
|
95
96
|
name: str | None = None,
|
|
96
97
|
description: str | None = None,
|
|
97
98
|
timestamp_column: str | None = None,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
CHANGELOG.md,sha256=
|
|
1
|
+
CHANGELOG.md,sha256=dZLFxX1LixFKq5t-wqt4IkqEptjE12o8iDYP-SFO6L4,86532
|
|
2
2
|
LICENSE,sha256=zEGHG9mjDjaIS3I79O8mweQo-yiTbqx8jJvUPppVAwk,1067
|
|
3
3
|
README.md,sha256=KKe0dxh_pHXCtB7I9G4qWGQYvot_BZU8yW6MJyuyUHM,311
|
|
4
4
|
nominal/__init__.py,sha256=rbraORnXUrNn1hywLXM0XwSQCd9UmQt20PDYlsBalfE,2167
|
|
5
5
|
nominal/__main__.py,sha256=Q0LHBl9ecbFcF6q0ef83f65WEYEQ55q1gfHhp4IUqy0,285
|
|
6
|
-
nominal/nominal.py,sha256=
|
|
6
|
+
nominal/nominal.py,sha256=sbN7clV3OHfsaf72OXnTbP5R54B_WlzZlDUlRzTrPuk,23153
|
|
7
7
|
nominal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
8
|
nominal/_utils/README.md,sha256=1qJE6MSjrHb-JF4L7TJC2r8_OXwnb3gaLvuevXMOOK0,364
|
|
9
9
|
nominal/_utils/__init__.py,sha256=v4WWEY4uC37KX_3xfHEJoF-qfIZ3dftMcuIdGnvtX_8,465
|
|
@@ -31,27 +31,28 @@ nominal/core/__init__.py,sha256=1MiCC44cxHYFofP4hf2fz4EIkepK-OAhDzpPFIzHbWw,2422
|
|
|
31
31
|
nominal/core/_clientsbunch.py,sha256=YwciugX7rQ9AOPHyvKuavG7b9SlX1PURRquP37nvLqE,8458
|
|
32
32
|
nominal/core/_constants.py,sha256=SrxgaSqAEB1MvTSrorgGam3eO29iCmRr6VIdajxX3gI,56
|
|
33
33
|
nominal/core/_event_types.py,sha256=Cq_8x-zv_5EDvRo9UTbaOpenAy92bTfQxlsEuHPOhtE,3706
|
|
34
|
+
nominal/core/_types.py,sha256=FktMmcQ5_rD2rbXv8_p-WISzSo8T2NtO-exsLm-iadU,122
|
|
34
35
|
nominal/core/asset.py,sha256=-hNMGXiU1dPWfrzmOngbab-Hf6vfq2Rm_j0FP-woJ-s,23120
|
|
35
|
-
nominal/core/attachment.py,sha256=
|
|
36
|
+
nominal/core/attachment.py,sha256=yOtDUdkLY5MT_Rk9kUlr1yupIJN7a5pt5sJWx4RLQV8,4355
|
|
36
37
|
nominal/core/bounds.py,sha256=742BWmGL3FBryRAjoiJRg2N6aVinjYkQLxN7kfnJ40Q,581
|
|
37
38
|
nominal/core/channel.py,sha256=dbe8wpfMiWqHu98x66w6GOmC9Ro33Wv9AhBVx2DvtVk,18970
|
|
38
39
|
nominal/core/checklist.py,sha256=rO1RPDYV3o2miPKF7DcCiYpj6bUN-sdtZNhJkXzkfYE,7110
|
|
39
|
-
nominal/core/client.py,sha256=
|
|
40
|
-
nominal/core/connection.py,sha256=
|
|
40
|
+
nominal/core/client.py,sha256=Awt9WPkE-YXBfOwJMTL7Su8AZFJY3UMH7IKp5hI26YQ,68328
|
|
41
|
+
nominal/core/connection.py,sha256=LYllr3a1H2xp8-i4MaX1M7yK8X-HnwuIkciyK9XgLtQ,5175
|
|
41
42
|
nominal/core/containerized_extractors.py,sha256=fUz3-NHoNWYKqOCD15gLwGXDKVfdsW-x_kpXnkOI3BE,10224
|
|
42
43
|
nominal/core/data_review.py,sha256=bEnRsd8LI4x9YOBPcF2H3h5-e12A7Gh8gQfsNUAZmPQ,7922
|
|
43
|
-
nominal/core/dataset.py,sha256=
|
|
44
|
-
nominal/core/dataset_file.py,sha256=
|
|
45
|
-
nominal/core/datasource.py,sha256=
|
|
44
|
+
nominal/core/dataset.py,sha256=NRAKKoO6L3fiwd3voo_U-Ow6FZzW2UrfRXcyF2kPR3o,46636
|
|
45
|
+
nominal/core/dataset_file.py,sha256=8rCW6MO89MFbQ2NH0WtFWmJfRWeTxhmyuoGojuQQ4Qg,16545
|
|
46
|
+
nominal/core/datasource.py,sha256=V5UahbqsCNIdml978kOHiY6boIxKxbp76KscNBpN5xc,16934
|
|
46
47
|
nominal/core/event.py,sha256=8trZXyuAqRlKedgcqSgDIimXAAJBmEfDLyHkOOBwUC0,7762
|
|
47
48
|
nominal/core/exceptions.py,sha256=GUpwXRgdYamLl6684FE8ttCRHkBx6WEhOZ3NPE-ybD4,2671
|
|
48
|
-
nominal/core/filetype.py,sha256=
|
|
49
|
+
nominal/core/filetype.py,sha256=R8goHGW4SP0iO6AoQiUil2tNVuDgaQoHclftRbw44oc,5558
|
|
49
50
|
nominal/core/log.py,sha256=z3hI3CIEyMwpUSWjwBsJ6a3JNGzBbsmrVusSU6uI7CY,3885
|
|
50
51
|
nominal/core/run.py,sha256=IqXCP24UhdHKkss0LbXU_zAhx-7Pf2MIQI-lic_-quw,17987
|
|
51
52
|
nominal/core/secret.py,sha256=Ckq48m60i7rktxL9GY-nxHU5v8gHv9F1-JN7_MSf4bM,2863
|
|
52
53
|
nominal/core/unit.py,sha256=Wa-Bvu0hD-nzxVaQJSnn5YqAfnhUd2kWw2SswXnbMHY,3161
|
|
53
54
|
nominal/core/user.py,sha256=FV333TN4pQzcLh5b2CfxvBnnXyB1TrOP8Ppx1-XdaiE,481
|
|
54
|
-
nominal/core/video.py,sha256=
|
|
55
|
+
nominal/core/video.py,sha256=xQhDAiKzKuLsS3O6orKMvxejWVwDNK1ik56j4phMobE,16113
|
|
55
56
|
nominal/core/video_file.py,sha256=dNAxFNAYwCBUiloCVt62fQymJtI_hK0NGgBXaVWqCXU,5669
|
|
56
57
|
nominal/core/workbook.py,sha256=lJo9ZaYm0TevAyIs239ZA-_1WUriTkj8i1lxvxH9TJw,8902
|
|
57
58
|
nominal/core/workbook_template.py,sha256=PBgQjEDVVQdZMlVea99BbhHdAr_bawknSvNKhNtDAq0,7125
|
|
@@ -86,15 +87,15 @@ nominal/experimental/logging/click_log_handler.py,sha256=ANLf4IGgmh95V0kJlr756wQ
|
|
|
86
87
|
nominal/experimental/logging/nominal_log_handler.py,sha256=hyTxyjsvFnE7vtyrDJpunAqADHmXekNWALwxXPIJGCk,5120
|
|
87
88
|
nominal/experimental/logging/rich_log_handler.py,sha256=8yz_VtxNgJg2oiesnXz2iXoBvQrUP5pAsYkxknOXgXA,1231
|
|
88
89
|
nominal/experimental/migration/__init__.py,sha256=E2IgWJLwJ5bN6jbl8k5nHECKFx5aT11jKAzVYcyXn3o,460
|
|
89
|
-
nominal/experimental/migration/migration_utils.py,sha256=
|
|
90
|
+
nominal/experimental/migration/migration_utils.py,sha256=hCfajIYn91U2_Q9UaXkYhbd5-u8HLEVA6aD7o5_ota8,30223
|
|
90
91
|
nominal/experimental/rust_streaming/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
91
|
-
nominal/experimental/rust_streaming/rust_write_stream.py,sha256=
|
|
92
|
+
nominal/experimental/rust_streaming/rust_write_stream.py,sha256=oQ6ixwm8ct8ZDc_qNB7AucDt8o5-_aBVlW2fFCQ_nmA,1541
|
|
92
93
|
nominal/experimental/stream_v2/__init__.py,sha256=W39vK46pssx5sXvmsImMuJiEPs7iGtwrbYBI0bWnXCY,2313
|
|
93
94
|
nominal/experimental/stream_v2/_serializer.py,sha256=DcGimcY1LsXNeCzOWrel3SwuvoRV4XLdOFjqjM7MgPY,1035
|
|
94
95
|
nominal/experimental/stream_v2/_write_stream.py,sha256=-EncNPXUDYaL1YpFlJFEkuLgcxMdyKEXS5JJzP_2LlI,9981
|
|
95
96
|
nominal/experimental/video_processing/__init__.py,sha256=08ksIeYuv4BbgTRqA5cnetKr8DRsPuuoAas0OTHirt0,654
|
|
96
97
|
nominal/experimental/video_processing/resolution.py,sha256=Z-34DaGDnJUcg-FvnxP-uNuNjXiE-7LvxZRugIvgOqk,3974
|
|
97
|
-
nominal/experimental/video_processing/video_conversion.py,sha256=
|
|
98
|
+
nominal/experimental/video_processing/video_conversion.py,sha256=JU7ELZjyFKQ7ij7nbyrpwwZCOiwYcQ3nrDk_Xh3GULY,6014
|
|
98
99
|
nominal/thirdparty/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
99
100
|
nominal/thirdparty/matlab/__init__.py,sha256=RANJJ6wbpGOrKRohcoFbRlOF8S4sZpSLWswElK954es,113
|
|
100
101
|
nominal/thirdparty/matlab/_matlab.py,sha256=qtrO_wP_o9J2oqiwhriSfL0LYO7rBzssoHygYQJiT30,10400
|
|
@@ -103,10 +104,10 @@ nominal/thirdparty/pandas/_pandas.py,sha256=QcM1tk-Z5W8bKd9oYz5rD6M3qIHZ0N5uUe_U
|
|
|
103
104
|
nominal/thirdparty/polars/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
104
105
|
nominal/thirdparty/polars/polars_export_handler.py,sha256=hGCSwXX9dC4MG01CmmjlTbcajktMYDb-GpNNTwLUsRw,32951
|
|
105
106
|
nominal/thirdparty/tdms/__init__.py,sha256=6n2ImFr2Wiil6JM1P5Q7Mpr0VzLcnDkmup_ftNpPq-s,142
|
|
106
|
-
nominal/thirdparty/tdms/_tdms.py,sha256=
|
|
107
|
+
nominal/thirdparty/tdms/_tdms.py,sha256=m4gxbpxB9MTLi2FuYvGlbUGSyDAZKFxbM3ia2x1wIz0,8746
|
|
107
108
|
nominal/ts/__init__.py,sha256=hmd0ENvDhxRnzDKGLxIub6QG8LpcxCgcyAct029CaEs,21442
|
|
108
|
-
nominal-1.
|
|
109
|
-
nominal-1.
|
|
110
|
-
nominal-1.
|
|
111
|
-
nominal-1.
|
|
112
|
-
nominal-1.
|
|
109
|
+
nominal-1.103.0.dist-info/METADATA,sha256=EigPuLvTA5TUP2SCA0vo49uLWuq4GDil1fN8D118I40,2277
|
|
110
|
+
nominal-1.103.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
111
|
+
nominal-1.103.0.dist-info/entry_points.txt,sha256=-mCLhxgg9R_lm5efT7vW9wuBH12izvY322R0a3TYxbE,66
|
|
112
|
+
nominal-1.103.0.dist-info/licenses/LICENSE,sha256=zEGHG9mjDjaIS3I79O8mweQo-yiTbqx8jJvUPppVAwk,1067
|
|
113
|
+
nominal-1.103.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|