current-data-py 0.1.6__tar.gz → 0.1.9__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.
- {current_data_py-0.1.6 → current_data_py-0.1.9}/MANIFEST.in +5 -4
- current_data_py-0.1.9/PKG-INFO +269 -0
- current_data_py-0.1.9/README.md +255 -0
- current_data_py-0.1.9/__init__.py +17 -0
- current_data_py-0.1.9/current_data_py.egg-info/PKG-INFO +269 -0
- {current_data_py-0.1.6 → current_data_py-0.1.9}/current_data_py.egg-info/SOURCES.txt +3 -0
- {current_data_py-0.1.6 → current_data_py-0.1.9}/current_data_py.egg-info/requires.txt +4 -0
- current_data_py-0.1.9/decoding.py +187 -0
- {current_data_py-0.1.6 → current_data_py-0.1.9}/examples/__init__.py +1 -1
- {current_data_py-0.1.6 → current_data_py-0.1.9}/examples/list_schemas.py +7 -7
- {current_data_py-0.1.6 → current_data_py-0.1.9}/examples/read_remote_td.py +54 -54
- {current_data_py-0.1.6 → current_data_py-0.1.9}/examples/read_td.py +30 -30
- {current_data_py-0.1.6 → current_data_py-0.1.9}/examples/write_td.py +30 -30
- {current_data_py-0.1.6 → current_data_py-0.1.9}/management.py +118 -114
- {current_data_py-0.1.6 → current_data_py-0.1.9}/oss.py +182 -182
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/management/metadata/metadata_pb2.py +62 -62
- current_data_py-0.1.9/proto_gen/python/management/service/service_pb2.py +69 -0
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/management/service/service_pb2_grpc.py +330 -330
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/builtin_interfaces/duration_pb2.py +25 -25
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/builtin_interfaces/time_pb2.py +25 -25
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/custom_msgs/joint_current_pb2.py +26 -26
- current_data_py-0.1.9/proto_gen/python/msg/custom_msgs/pose_rpy_pb2.py +26 -0
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/custom_msgs/slam_quality_pb2.py +25 -25
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/custom_msgs/sonic_frame_pb2.py +25 -25
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/custom_msgs/target_quality_pb2.py +25 -25
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/foxglove/compressed_audio_pb2.py +26 -26
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/foxglove/compressed_video_pb2.py +26 -26
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/point32_pb2.py +25 -25
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/point_pb2.py +25 -25
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/pose_pb2.py +27 -27
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/pose_stamped_pb2.py +27 -27
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/pose_with_covariance_pb2.py +26 -26
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/pose_with_covariance_stamped_pb2.py +27 -27
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/quaternion_pb2.py +25 -25
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/transform_pb2.py +27 -27
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/transform_stamped_pb2.py +27 -27
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/twist_pb2.py +26 -26
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/twist_stamped_pb2.py +27 -27
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/twist_with_covariance_pb2.py +26 -26
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/twist_with_covariance_stamped_pb2.py +27 -27
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/vector3_pb2.py +25 -25
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/wrench_pb2.py +26 -26
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/wrench_stamped_pb2.py +27 -27
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/nav_msgs/map_meta_data_pb2.py +27 -27
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/nav_msgs/occupancy_grid_pb2.py +27 -27
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/nav_msgs/odometry_pb2.py +28 -28
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/nav_msgs/path_pb2.py +27 -27
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/sensor_msgs/battery_state_pb2.py +32 -32
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/sensor_msgs/camera_info_pb2.py +27 -27
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/sensor_msgs/compressed_image_pb2.py +26 -26
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/sensor_msgs/image_pb2.py +26 -26
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/sensor_msgs/imu_pb2.py +28 -28
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/sensor_msgs/joint_state_pb2.py +26 -26
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/sensor_msgs/laser_scan_pb2.py +26 -26
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/sensor_msgs/nav_sat_fix_pb2.py +29 -29
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/sensor_msgs/nav_sat_status_pb2.py +27 -27
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/sensor_msgs/point_cloud2_pb2.py +27 -27
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/sensor_msgs/point_field_pb2.py +27 -27
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/sensor_msgs/range_pb2.py +28 -28
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/sensor_msgs/region_of_interest_pb2.py +25 -25
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/std_msgs/bool_pb2.py +25 -25
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/std_msgs/color_rgba_pb2.py +25 -25
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/std_msgs/float32_pb2.py +25 -25
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/std_msgs/float64_pb2.py +25 -25
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/std_msgs/header_pb2.py +26 -26
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/std_msgs/int32_pb2.py +25 -25
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/std_msgs/string_pb2.py +25 -25
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/std_msgs/uint32_pb2.py +25 -25
- {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/tf2_msgs/tf_message_pb2.py +26 -26
- {current_data_py-0.1.6 → current_data_py-0.1.9}/pyproject.toml +24 -21
- {current_data_py-0.1.6 → current_data_py-0.1.9}/remote/__init__.py +7 -7
- {current_data_py-0.1.6 → current_data_py-0.1.9}/remote/client.py +89 -83
- {current_data_py-0.1.6 → current_data_py-0.1.9}/remote/source.py +98 -98
- {current_data_py-0.1.6 → current_data_py-0.1.9}/schema/__init__.py +107 -105
- {current_data_py-0.1.6 → current_data_py-0.1.9}/schema/message_metadata.py +30 -30
- {current_data_py-0.1.6 → current_data_py-0.1.9}/setup.py +60 -60
- current_data_py-0.1.9/tests/check_environment.py +79 -0
- {current_data_py-0.1.6 → current_data_py-0.1.9}/tests/check_management.py +255 -253
- {current_data_py-0.1.6 → current_data_py-0.1.9}/tests/check_oss.py +317 -317
- {current_data_py-0.1.6 → current_data_py-0.1.9}/tests/check_package.py +43 -43
- {current_data_py-0.1.6 → current_data_py-0.1.9}/tests/check_remote.py +291 -289
- current_data_py-0.1.6/PKG-INFO +0 -136
- current_data_py-0.1.6/README.md +0 -125
- current_data_py-0.1.6/__init__.py +0 -8
- current_data_py-0.1.6/current_data_py.egg-info/PKG-INFO +0 -136
- current_data_py-0.1.6/proto_gen/python/management/service/service_pb2.py +0 -64
- {current_data_py-0.1.6 → current_data_py-0.1.9}/current_data_py.egg-info/dependency_links.txt +0 -0
- {current_data_py-0.1.6 → current_data_py-0.1.9}/current_data_py.egg-info/top_level.txt +0 -0
- {current_data_py-0.1.6 → current_data_py-0.1.9}/setup.cfg +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
include tests/check_package.py
|
|
2
|
-
include tests/
|
|
3
|
-
include tests/
|
|
4
|
-
include tests/
|
|
1
|
+
include tests/check_package.py
|
|
2
|
+
include tests/check_environment.py
|
|
3
|
+
include tests/check_remote.py
|
|
4
|
+
include tests/check_management.py
|
|
5
|
+
include tests/check_oss.py
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: current-data-py
|
|
3
|
+
Version: 0.1.9
|
|
4
|
+
Summary: Current Data protobuf messages and schema metadata
|
|
5
|
+
Requires-Python: >=3.9
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: protobuf>=3.20
|
|
8
|
+
Requires-Dist: turbodata==0.1.0
|
|
9
|
+
Requires-Dist: grpcio>=1.51.1
|
|
10
|
+
Requires-Dist: alibabacloud-oss-v2<2,>=1.4.0
|
|
11
|
+
Provides-Extra: decode
|
|
12
|
+
Requires-Dist: av>=12; extra == "decode"
|
|
13
|
+
Requires-Dist: pillow>=9; extra == "decode"
|
|
14
|
+
|
|
15
|
+
# current-data-py
|
|
16
|
+
|
|
17
|
+
## Environment
|
|
18
|
+
|
|
19
|
+
Set `ENV=test` before importing the SDK to use the test service. `ENV=prod`,
|
|
20
|
+
an unset `ENV`, or an empty value selects production. Other values raise
|
|
21
|
+
`ValueError` at import time.
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
ENV=test python -m current_data_py.examples.read_remote_td
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
In Python, set the environment before the first SDK import:
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
import os
|
|
31
|
+
os.environ["ENV"] = "test"
|
|
32
|
+
|
|
33
|
+
from current_data_py.management import ManagementClient
|
|
34
|
+
from current_data_py.remote import DataClient
|
|
35
|
+
|
|
36
|
+
with ManagementClient() as management, DataClient() as data:
|
|
37
|
+
pass # Both clients use the test service over TLS.
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The environment initializes `current_data_py.DEFAULT_ADDRESS` once at import.
|
|
41
|
+
Changing `ENV` after import does not switch clients. An explicit `address=`
|
|
42
|
+
overrides the default; a caller-provided channel takes precedence over both.
|
|
43
|
+
Changing `current_data_py.DEFAULT_ADDRESS` still affects newly created clients.
|
|
44
|
+
OSS uploads and reads use credentials from the selected management service;
|
|
45
|
+
explicit bucket arguments remain unchanged.
|
|
46
|
+
|
|
47
|
+
## Management
|
|
48
|
+
|
|
49
|
+
`ManagementClient` connects over TLS. Pass `address=` to configure the service,
|
|
50
|
+
or supply your own gRPC channel.
|
|
51
|
+
|
|
52
|
+
The examples below read deployment-specific values from environment variables.
|
|
53
|
+
Set `MANAGEMENT_ADDRESS` to your service address and `UPLOAD_DATA_TYPE` to a valid
|
|
54
|
+
integer enum value supplied by your deployment administrator. Set the bucket and
|
|
55
|
+
object-key variables to resources you are authorized to access. These variables
|
|
56
|
+
are read explicitly by the examples; they are not automatic SDK configuration.
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
import os
|
|
60
|
+
|
|
61
|
+
from current_data_py.management import ManagementClient, Metadata, MetadataFilter
|
|
62
|
+
|
|
63
|
+
with ManagementClient(address=os.environ["MANAGEMENT_ADDRESS"]) as client:
|
|
64
|
+
upload = client.get_upload_credentials(data_type=int(os.environ["UPLOAD_DATA_TYPE"]))
|
|
65
|
+
read = client.get_read_credentials(bucket=os.environ["READ_BUCKET"])
|
|
66
|
+
|
|
67
|
+
# Register an already uploaded file; replace the example file details.
|
|
68
|
+
record = client.register_data(Metadata(
|
|
69
|
+
name="recording",
|
|
70
|
+
data_type=int(os.environ["UPLOAD_DATA_TYPE"]),
|
|
71
|
+
storages=[{"bucket": os.environ["UPLOADED_BUCKET"], "object_key": os.environ["UPLOADED_OBJECT_KEY"]}],
|
|
72
|
+
file_info={"size_bytes": 1234, "checksum": "sha256:<file-hash>"},
|
|
73
|
+
))
|
|
74
|
+
for item in client.stream_data_references(
|
|
75
|
+
MetadataFilter(name_prefix="recording"),
|
|
76
|
+
return_fields=["name", "data_type", "recording_info.task.name", "recording_info.recording_time"],
|
|
77
|
+
):
|
|
78
|
+
print(item.id, item.name, item.data_type, item.storages, item.recording_info)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`Metadata`, `MetadataFilter`, and responses are Protobuf objects exported by the
|
|
82
|
+
SDK. Registration only saves metadata; it does not upload files. Credential
|
|
83
|
+
calls return fresh credentials without caching. Calls accept `timeout=` and
|
|
84
|
+
`metadata=` for gRPC headers.
|
|
85
|
+
|
|
86
|
+
## Remote TD reads
|
|
87
|
+
|
|
88
|
+
### Payload decoding
|
|
89
|
+
|
|
90
|
+
> [!IMPORTANT]
|
|
91
|
+
> **Video decoding requires PyAV, Pillow and the FFmpeg native libraries used by
|
|
92
|
+
> PyAV.** `open_td()` enables decoding by default, but a base `current-data-py`
|
|
93
|
+
> installation does not install the video dependencies. Install the `[decode]`
|
|
94
|
+
> extra in every environment that decodes video, including DataLoader workers.
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
python -m pip install --only-binary=av,pillow 'current-data-py[decode]'
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- **Prebuilt wheels:** PyAV wheels bundle FFmpeg libraries on supported platforms;
|
|
101
|
+
no separate FFmpeg system installation is normally needed. The command above
|
|
102
|
+
fails if compatible PyAV/Pillow wheels are unavailable instead of silently
|
|
103
|
+
compiling them. Wheel availability depends on Python, OS and architecture.
|
|
104
|
+
- **Source builds:** install a compiler, Python development headers, `pkg-config`
|
|
105
|
+
and compatible FFmpeg development libraries: `libavcodec`, `libavformat`,
|
|
106
|
+
`libavutil`, `libavdevice`, `libavfilter`, `libswscale` and `libswresample`.
|
|
107
|
+
Runtime shared libraries must also remain available in the deployed environment.
|
|
108
|
+
Follow the requirements for your chosen [PyAV version](https://pyav.basswood.io/docs/stable/overview/installation.html)
|
|
109
|
+
and [Pillow build](https://pillow.readthedocs.io/en/stable/installation/building-from-source.html).
|
|
110
|
+
- **The `ffmpeg` executable alone is insufficient:** this SDK calls PyAV's native
|
|
111
|
+
library bindings, not a command-line subprocess. Decoding uses the CPU and does
|
|
112
|
+
not require CUDA or a GPU.
|
|
113
|
+
- **Protobuf-only decoding and raw reads** (`enable_decode=False`) do not require
|
|
114
|
+
PyAV, Pillow or FFmpeg for decoding.
|
|
115
|
+
|
|
116
|
+
Check the video dependencies in the environment that will run the reader:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
python -c "import av; from PIL import Image; av.CodecContext.create('h264', 'r'); print(av.library_versions)"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Decoding applies to both sequential reads and time sampling; it does not depend
|
|
123
|
+
on a robot dataset format.
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
# Read all messages in an inclusive time range, without constructing sample queries.
|
|
127
|
+
with client.open_td(reference, bucket=bucket) as reader:
|
|
128
|
+
messages = reader.read_messages(
|
|
129
|
+
topic_names=topics,
|
|
130
|
+
start_timestamp=start_ns,
|
|
131
|
+
end_timestamp=end_ns,
|
|
132
|
+
)
|
|
133
|
+
try:
|
|
134
|
+
for result in messages:
|
|
135
|
+
print(result.topic_name, result.timestamp, type(result.value))
|
|
136
|
+
finally:
|
|
137
|
+
messages.close() # Also release iterator state when stopping early.
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Sequential results are `DecodedMessage(topic_name, timestamp, value)`. Reads are
|
|
141
|
+
lazy: all selected topics share one raw read iterator, with a separate video decoder
|
|
142
|
+
per topic. Results are yielded as decoding completes; delayed video frames can
|
|
143
|
+
arrive after newer messages from other topics. Use each result's timestamp for alignment.
|
|
144
|
+
GOP prefix frames are consumed for decoding but excluded from returned
|
|
145
|
+
results outside the requested time bounds. Reverse-order decoded reads are not
|
|
146
|
+
supported; use `raw` for reverse encoded reads.
|
|
147
|
+
|
|
148
|
+
To request particular timestamps instead:
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
from turbodata import SampleQuery
|
|
152
|
+
|
|
153
|
+
# reference, bucket, topic and timestamps_ns come from your selected recording.
|
|
154
|
+
with client.open_td(reference, bucket=bucket) as reader:
|
|
155
|
+
samples_by_topic = reader.sample([SampleQuery(topic, timestamps_ns)])
|
|
156
|
+
for sample in samples_by_topic[0]:
|
|
157
|
+
if sample.found:
|
|
158
|
+
print(sample.timestamp, type(sample.value))
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
`value` is a Protobuf message, or an RGB PIL image
|
|
162
|
+
for H.264 video. Missing samples have `found=False` and `value=None`. Timestamps are
|
|
163
|
+
actual floor-match times, not necessarily the requested times. Repeated video
|
|
164
|
+
samples may share the same image object; copy it before modifying it in place.
|
|
165
|
+
Decoding errors propagate; no normalization, resizing or alignment tolerance is
|
|
166
|
+
applied. Only schemas registered in the SDK are decoded. An unregistered schema
|
|
167
|
+
raises `KeyError` with the schema name, even if the TD metadata
|
|
168
|
+
includes a descriptor. Unsupported video codecs also raise errors.
|
|
169
|
+
|
|
170
|
+
Decoding is enabled by default. Set `enable_decode=False` to return the original
|
|
171
|
+
TurboData Reader when your code needs encoded payloads in `.data`.
|
|
172
|
+
The decoded reader exposes `summary()`, `read_messages()`, `sample()` and `raw`;
|
|
173
|
+
use `raw` for encoded reads and other low-level APIs. Video sampling automatically requests
|
|
174
|
+
GOP dependencies. Keep all reads inside the `open_td()` context.
|
|
175
|
+
|
|
176
|
+
For a local Reader, use `current_data_py.decoding.DecodedReader(reader)`. The same
|
|
177
|
+
module exports `decode_messages(payloads, schema)` and `decode_video(results)` for
|
|
178
|
+
already-read payloads. Supply a complete ordered row from
|
|
179
|
+
`Reader.sample(video_decodable=True)` to `decode_video`; decoder state is local
|
|
180
|
+
to each call.
|
|
181
|
+
|
|
182
|
+
### Storage and credentials
|
|
183
|
+
|
|
184
|
+
`DataClient.open_td()` accepts an optional `bucket` argument. Without it, reads
|
|
185
|
+
use the management service's `OSS_READ_BUCKET` and `OSS_READ_PREFIX` defaults.
|
|
186
|
+
With it, the SDK requests whole-bucket read credentials, limited by the server's
|
|
187
|
+
RAM role, and selects the first matching storage in the reference. An explicit
|
|
188
|
+
bucket does not inherit the default prefix. Default and per-bucket credentials
|
|
189
|
+
are cached separately and refreshed automatically.
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
import os
|
|
193
|
+
|
|
194
|
+
from current_data_py.remote import DataClient
|
|
195
|
+
|
|
196
|
+
with DataClient(address=os.environ["MANAGEMENT_ADDRESS"]) as client:
|
|
197
|
+
for reference in client.iter_data():
|
|
198
|
+
# Use the service's default bucket and prefix.
|
|
199
|
+
with client.open_td(reference) as reader:
|
|
200
|
+
print(reader.summary())
|
|
201
|
+
|
|
202
|
+
# Request read access to the selected bucket.
|
|
203
|
+
with client.open_td(reference, bucket=os.environ["READ_BUCKET"]) as reader:
|
|
204
|
+
print(reader.summary())
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
The explicit bucket must contain a storage listed in the reference. This uses
|
|
208
|
+
the existing bucket-aware management API; no protocol changes are required.
|
|
209
|
+
|
|
210
|
+
## OSS
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
import os
|
|
214
|
+
|
|
215
|
+
from current_data_py.management import ManagementClient
|
|
216
|
+
from current_data_py.oss import OSSClient
|
|
217
|
+
|
|
218
|
+
with ManagementClient(address=os.environ["MANAGEMENT_ADDRESS"]) as management:
|
|
219
|
+
with OSSClient(management, permission="read", bucket=os.environ["READ_BUCKET"]) as source:
|
|
220
|
+
source.download_file(os.environ["SOURCE_OBJECT_KEY"], "/tmp/input.h5")
|
|
221
|
+
|
|
222
|
+
# Convert the input into /tmp/output.td before uploading.
|
|
223
|
+
with OSSClient(management, permission="upload", data_type=int(os.environ["UPLOAD_DATA_TYPE"])) as destination:
|
|
224
|
+
key = destination.object_prefix + "recording/output.td"
|
|
225
|
+
destination.upload_file(key, "/tmp/output.td")
|
|
226
|
+
print(destination.bucket, key) # Use these values when registering metadata.
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
- Read access covers the requested bucket, subject to server permissions.
|
|
230
|
+
The server chooses the upload bucket and prefix based on the requested data type.
|
|
231
|
+
- Credentials refresh automatically. Expired tokens trigger one refresh and retry.
|
|
232
|
+
- Uploads reject overwrites by default; set `forbid_overwrite=False` to replace.
|
|
233
|
+
Downloads replace the local file only on success; its parent directory must exist.
|
|
234
|
+
- Uploads use a single PutObject request ([maximum 5 GiB](https://www.alibabacloud.com/help/en/oss/developer-reference/upload-files-using-oss-sdk-for-python-v2/)); multipart upload is not supported.
|
|
235
|
+
- Supply `region=` for custom upload endpoints. Deploy the current management
|
|
236
|
+
credential API before using the new bucket and data-type arguments.
|
|
237
|
+
|
|
238
|
+
## 打包
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
cd current_data_py # 从仓库根目录执行
|
|
242
|
+
# 发布新版本前,更新 pyproject.toml 中的 version
|
|
243
|
+
python -m pip install build twine
|
|
244
|
+
rm -rf build dist
|
|
245
|
+
python -m build
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## 测试
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
python -m twine check dist/*
|
|
252
|
+
python tests/check_distribution.py
|
|
253
|
+
python -m pip install --force-reinstall dist/*.whl
|
|
254
|
+
python -I tests/check_package.py
|
|
255
|
+
python -I tests/check_environment.py
|
|
256
|
+
python -I tests/check_management.py
|
|
257
|
+
python -I tests/check_remote.py
|
|
258
|
+
python -I tests/check_oss.py
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## 推送
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
# TestPyPI
|
|
265
|
+
python -m twine upload --repository testpypi dist/*
|
|
266
|
+
|
|
267
|
+
# PyPI
|
|
268
|
+
python -m twine upload dist/*
|
|
269
|
+
```
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# current-data-py
|
|
2
|
+
|
|
3
|
+
## Environment
|
|
4
|
+
|
|
5
|
+
Set `ENV=test` before importing the SDK to use the test service. `ENV=prod`,
|
|
6
|
+
an unset `ENV`, or an empty value selects production. Other values raise
|
|
7
|
+
`ValueError` at import time.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
ENV=test python -m current_data_py.examples.read_remote_td
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
In Python, set the environment before the first SDK import:
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
import os
|
|
17
|
+
os.environ["ENV"] = "test"
|
|
18
|
+
|
|
19
|
+
from current_data_py.management import ManagementClient
|
|
20
|
+
from current_data_py.remote import DataClient
|
|
21
|
+
|
|
22
|
+
with ManagementClient() as management, DataClient() as data:
|
|
23
|
+
pass # Both clients use the test service over TLS.
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The environment initializes `current_data_py.DEFAULT_ADDRESS` once at import.
|
|
27
|
+
Changing `ENV` after import does not switch clients. An explicit `address=`
|
|
28
|
+
overrides the default; a caller-provided channel takes precedence over both.
|
|
29
|
+
Changing `current_data_py.DEFAULT_ADDRESS` still affects newly created clients.
|
|
30
|
+
OSS uploads and reads use credentials from the selected management service;
|
|
31
|
+
explicit bucket arguments remain unchanged.
|
|
32
|
+
|
|
33
|
+
## Management
|
|
34
|
+
|
|
35
|
+
`ManagementClient` connects over TLS. Pass `address=` to configure the service,
|
|
36
|
+
or supply your own gRPC channel.
|
|
37
|
+
|
|
38
|
+
The examples below read deployment-specific values from environment variables.
|
|
39
|
+
Set `MANAGEMENT_ADDRESS` to your service address and `UPLOAD_DATA_TYPE` to a valid
|
|
40
|
+
integer enum value supplied by your deployment administrator. Set the bucket and
|
|
41
|
+
object-key variables to resources you are authorized to access. These variables
|
|
42
|
+
are read explicitly by the examples; they are not automatic SDK configuration.
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
import os
|
|
46
|
+
|
|
47
|
+
from current_data_py.management import ManagementClient, Metadata, MetadataFilter
|
|
48
|
+
|
|
49
|
+
with ManagementClient(address=os.environ["MANAGEMENT_ADDRESS"]) as client:
|
|
50
|
+
upload = client.get_upload_credentials(data_type=int(os.environ["UPLOAD_DATA_TYPE"]))
|
|
51
|
+
read = client.get_read_credentials(bucket=os.environ["READ_BUCKET"])
|
|
52
|
+
|
|
53
|
+
# Register an already uploaded file; replace the example file details.
|
|
54
|
+
record = client.register_data(Metadata(
|
|
55
|
+
name="recording",
|
|
56
|
+
data_type=int(os.environ["UPLOAD_DATA_TYPE"]),
|
|
57
|
+
storages=[{"bucket": os.environ["UPLOADED_BUCKET"], "object_key": os.environ["UPLOADED_OBJECT_KEY"]}],
|
|
58
|
+
file_info={"size_bytes": 1234, "checksum": "sha256:<file-hash>"},
|
|
59
|
+
))
|
|
60
|
+
for item in client.stream_data_references(
|
|
61
|
+
MetadataFilter(name_prefix="recording"),
|
|
62
|
+
return_fields=["name", "data_type", "recording_info.task.name", "recording_info.recording_time"],
|
|
63
|
+
):
|
|
64
|
+
print(item.id, item.name, item.data_type, item.storages, item.recording_info)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`Metadata`, `MetadataFilter`, and responses are Protobuf objects exported by the
|
|
68
|
+
SDK. Registration only saves metadata; it does not upload files. Credential
|
|
69
|
+
calls return fresh credentials without caching. Calls accept `timeout=` and
|
|
70
|
+
`metadata=` for gRPC headers.
|
|
71
|
+
|
|
72
|
+
## Remote TD reads
|
|
73
|
+
|
|
74
|
+
### Payload decoding
|
|
75
|
+
|
|
76
|
+
> [!IMPORTANT]
|
|
77
|
+
> **Video decoding requires PyAV, Pillow and the FFmpeg native libraries used by
|
|
78
|
+
> PyAV.** `open_td()` enables decoding by default, but a base `current-data-py`
|
|
79
|
+
> installation does not install the video dependencies. Install the `[decode]`
|
|
80
|
+
> extra in every environment that decodes video, including DataLoader workers.
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
python -m pip install --only-binary=av,pillow 'current-data-py[decode]'
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
- **Prebuilt wheels:** PyAV wheels bundle FFmpeg libraries on supported platforms;
|
|
87
|
+
no separate FFmpeg system installation is normally needed. The command above
|
|
88
|
+
fails if compatible PyAV/Pillow wheels are unavailable instead of silently
|
|
89
|
+
compiling them. Wheel availability depends on Python, OS and architecture.
|
|
90
|
+
- **Source builds:** install a compiler, Python development headers, `pkg-config`
|
|
91
|
+
and compatible FFmpeg development libraries: `libavcodec`, `libavformat`,
|
|
92
|
+
`libavutil`, `libavdevice`, `libavfilter`, `libswscale` and `libswresample`.
|
|
93
|
+
Runtime shared libraries must also remain available in the deployed environment.
|
|
94
|
+
Follow the requirements for your chosen [PyAV version](https://pyav.basswood.io/docs/stable/overview/installation.html)
|
|
95
|
+
and [Pillow build](https://pillow.readthedocs.io/en/stable/installation/building-from-source.html).
|
|
96
|
+
- **The `ffmpeg` executable alone is insufficient:** this SDK calls PyAV's native
|
|
97
|
+
library bindings, not a command-line subprocess. Decoding uses the CPU and does
|
|
98
|
+
not require CUDA or a GPU.
|
|
99
|
+
- **Protobuf-only decoding and raw reads** (`enable_decode=False`) do not require
|
|
100
|
+
PyAV, Pillow or FFmpeg for decoding.
|
|
101
|
+
|
|
102
|
+
Check the video dependencies in the environment that will run the reader:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
python -c "import av; from PIL import Image; av.CodecContext.create('h264', 'r'); print(av.library_versions)"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Decoding applies to both sequential reads and time sampling; it does not depend
|
|
109
|
+
on a robot dataset format.
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
# Read all messages in an inclusive time range, without constructing sample queries.
|
|
113
|
+
with client.open_td(reference, bucket=bucket) as reader:
|
|
114
|
+
messages = reader.read_messages(
|
|
115
|
+
topic_names=topics,
|
|
116
|
+
start_timestamp=start_ns,
|
|
117
|
+
end_timestamp=end_ns,
|
|
118
|
+
)
|
|
119
|
+
try:
|
|
120
|
+
for result in messages:
|
|
121
|
+
print(result.topic_name, result.timestamp, type(result.value))
|
|
122
|
+
finally:
|
|
123
|
+
messages.close() # Also release iterator state when stopping early.
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Sequential results are `DecodedMessage(topic_name, timestamp, value)`. Reads are
|
|
127
|
+
lazy: all selected topics share one raw read iterator, with a separate video decoder
|
|
128
|
+
per topic. Results are yielded as decoding completes; delayed video frames can
|
|
129
|
+
arrive after newer messages from other topics. Use each result's timestamp for alignment.
|
|
130
|
+
GOP prefix frames are consumed for decoding but excluded from returned
|
|
131
|
+
results outside the requested time bounds. Reverse-order decoded reads are not
|
|
132
|
+
supported; use `raw` for reverse encoded reads.
|
|
133
|
+
|
|
134
|
+
To request particular timestamps instead:
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from turbodata import SampleQuery
|
|
138
|
+
|
|
139
|
+
# reference, bucket, topic and timestamps_ns come from your selected recording.
|
|
140
|
+
with client.open_td(reference, bucket=bucket) as reader:
|
|
141
|
+
samples_by_topic = reader.sample([SampleQuery(topic, timestamps_ns)])
|
|
142
|
+
for sample in samples_by_topic[0]:
|
|
143
|
+
if sample.found:
|
|
144
|
+
print(sample.timestamp, type(sample.value))
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
`value` is a Protobuf message, or an RGB PIL image
|
|
148
|
+
for H.264 video. Missing samples have `found=False` and `value=None`. Timestamps are
|
|
149
|
+
actual floor-match times, not necessarily the requested times. Repeated video
|
|
150
|
+
samples may share the same image object; copy it before modifying it in place.
|
|
151
|
+
Decoding errors propagate; no normalization, resizing or alignment tolerance is
|
|
152
|
+
applied. Only schemas registered in the SDK are decoded. An unregistered schema
|
|
153
|
+
raises `KeyError` with the schema name, even if the TD metadata
|
|
154
|
+
includes a descriptor. Unsupported video codecs also raise errors.
|
|
155
|
+
|
|
156
|
+
Decoding is enabled by default. Set `enable_decode=False` to return the original
|
|
157
|
+
TurboData Reader when your code needs encoded payloads in `.data`.
|
|
158
|
+
The decoded reader exposes `summary()`, `read_messages()`, `sample()` and `raw`;
|
|
159
|
+
use `raw` for encoded reads and other low-level APIs. Video sampling automatically requests
|
|
160
|
+
GOP dependencies. Keep all reads inside the `open_td()` context.
|
|
161
|
+
|
|
162
|
+
For a local Reader, use `current_data_py.decoding.DecodedReader(reader)`. The same
|
|
163
|
+
module exports `decode_messages(payloads, schema)` and `decode_video(results)` for
|
|
164
|
+
already-read payloads. Supply a complete ordered row from
|
|
165
|
+
`Reader.sample(video_decodable=True)` to `decode_video`; decoder state is local
|
|
166
|
+
to each call.
|
|
167
|
+
|
|
168
|
+
### Storage and credentials
|
|
169
|
+
|
|
170
|
+
`DataClient.open_td()` accepts an optional `bucket` argument. Without it, reads
|
|
171
|
+
use the management service's `OSS_READ_BUCKET` and `OSS_READ_PREFIX` defaults.
|
|
172
|
+
With it, the SDK requests whole-bucket read credentials, limited by the server's
|
|
173
|
+
RAM role, and selects the first matching storage in the reference. An explicit
|
|
174
|
+
bucket does not inherit the default prefix. Default and per-bucket credentials
|
|
175
|
+
are cached separately and refreshed automatically.
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
import os
|
|
179
|
+
|
|
180
|
+
from current_data_py.remote import DataClient
|
|
181
|
+
|
|
182
|
+
with DataClient(address=os.environ["MANAGEMENT_ADDRESS"]) as client:
|
|
183
|
+
for reference in client.iter_data():
|
|
184
|
+
# Use the service's default bucket and prefix.
|
|
185
|
+
with client.open_td(reference) as reader:
|
|
186
|
+
print(reader.summary())
|
|
187
|
+
|
|
188
|
+
# Request read access to the selected bucket.
|
|
189
|
+
with client.open_td(reference, bucket=os.environ["READ_BUCKET"]) as reader:
|
|
190
|
+
print(reader.summary())
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The explicit bucket must contain a storage listed in the reference. This uses
|
|
194
|
+
the existing bucket-aware management API; no protocol changes are required.
|
|
195
|
+
|
|
196
|
+
## OSS
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
import os
|
|
200
|
+
|
|
201
|
+
from current_data_py.management import ManagementClient
|
|
202
|
+
from current_data_py.oss import OSSClient
|
|
203
|
+
|
|
204
|
+
with ManagementClient(address=os.environ["MANAGEMENT_ADDRESS"]) as management:
|
|
205
|
+
with OSSClient(management, permission="read", bucket=os.environ["READ_BUCKET"]) as source:
|
|
206
|
+
source.download_file(os.environ["SOURCE_OBJECT_KEY"], "/tmp/input.h5")
|
|
207
|
+
|
|
208
|
+
# Convert the input into /tmp/output.td before uploading.
|
|
209
|
+
with OSSClient(management, permission="upload", data_type=int(os.environ["UPLOAD_DATA_TYPE"])) as destination:
|
|
210
|
+
key = destination.object_prefix + "recording/output.td"
|
|
211
|
+
destination.upload_file(key, "/tmp/output.td")
|
|
212
|
+
print(destination.bucket, key) # Use these values when registering metadata.
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
- Read access covers the requested bucket, subject to server permissions.
|
|
216
|
+
The server chooses the upload bucket and prefix based on the requested data type.
|
|
217
|
+
- Credentials refresh automatically. Expired tokens trigger one refresh and retry.
|
|
218
|
+
- Uploads reject overwrites by default; set `forbid_overwrite=False` to replace.
|
|
219
|
+
Downloads replace the local file only on success; its parent directory must exist.
|
|
220
|
+
- Uploads use a single PutObject request ([maximum 5 GiB](https://www.alibabacloud.com/help/en/oss/developer-reference/upload-files-using-oss-sdk-for-python-v2/)); multipart upload is not supported.
|
|
221
|
+
- Supply `region=` for custom upload endpoints. Deploy the current management
|
|
222
|
+
credential API before using the new bucket and data-type arguments.
|
|
223
|
+
|
|
224
|
+
## 打包
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
cd current_data_py # 从仓库根目录执行
|
|
228
|
+
# 发布新版本前,更新 pyproject.toml 中的 version
|
|
229
|
+
python -m pip install build twine
|
|
230
|
+
rm -rf build dist
|
|
231
|
+
python -m build
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## 测试
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
python -m twine check dist/*
|
|
238
|
+
python tests/check_distribution.py
|
|
239
|
+
python -m pip install --force-reinstall dist/*.whl
|
|
240
|
+
python -I tests/check_package.py
|
|
241
|
+
python -I tests/check_environment.py
|
|
242
|
+
python -I tests/check_management.py
|
|
243
|
+
python -I tests/check_remote.py
|
|
244
|
+
python -I tests/check_oss.py
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## 推送
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
# TestPyPI
|
|
251
|
+
python -m twine upload --repository testpypi dist/*
|
|
252
|
+
|
|
253
|
+
# PyPI
|
|
254
|
+
python -m twine upload dist/*
|
|
255
|
+
```
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Protobuf message schemas and metadata for Current Data."""
|
|
2
|
+
|
|
3
|
+
import os as _os
|
|
4
|
+
|
|
5
|
+
from .schema.message_metadata import descriptor_set, topic_metadata
|
|
6
|
+
from .schema import SCHEMA_TO_CLASS
|
|
7
|
+
|
|
8
|
+
_environment = _os.environ.get("ENV") or "prod"
|
|
9
|
+
if _environment not in ("prod", "test"):
|
|
10
|
+
raise ValueError("ENV must be prod or test")
|
|
11
|
+
|
|
12
|
+
DEFAULT_ADDRESS = (
|
|
13
|
+
"data-test.current-robotics.work:443"
|
|
14
|
+
if _environment == "test" else "data.current-robotics.work:443"
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = ["DEFAULT_ADDRESS", "SCHEMA_TO_CLASS", "descriptor_set", "topic_metadata"]
|