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.
Files changed (89) hide show
  1. {current_data_py-0.1.6 → current_data_py-0.1.9}/MANIFEST.in +5 -4
  2. current_data_py-0.1.9/PKG-INFO +269 -0
  3. current_data_py-0.1.9/README.md +255 -0
  4. current_data_py-0.1.9/__init__.py +17 -0
  5. current_data_py-0.1.9/current_data_py.egg-info/PKG-INFO +269 -0
  6. {current_data_py-0.1.6 → current_data_py-0.1.9}/current_data_py.egg-info/SOURCES.txt +3 -0
  7. {current_data_py-0.1.6 → current_data_py-0.1.9}/current_data_py.egg-info/requires.txt +4 -0
  8. current_data_py-0.1.9/decoding.py +187 -0
  9. {current_data_py-0.1.6 → current_data_py-0.1.9}/examples/__init__.py +1 -1
  10. {current_data_py-0.1.6 → current_data_py-0.1.9}/examples/list_schemas.py +7 -7
  11. {current_data_py-0.1.6 → current_data_py-0.1.9}/examples/read_remote_td.py +54 -54
  12. {current_data_py-0.1.6 → current_data_py-0.1.9}/examples/read_td.py +30 -30
  13. {current_data_py-0.1.6 → current_data_py-0.1.9}/examples/write_td.py +30 -30
  14. {current_data_py-0.1.6 → current_data_py-0.1.9}/management.py +118 -114
  15. {current_data_py-0.1.6 → current_data_py-0.1.9}/oss.py +182 -182
  16. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/management/metadata/metadata_pb2.py +62 -62
  17. current_data_py-0.1.9/proto_gen/python/management/service/service_pb2.py +69 -0
  18. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/management/service/service_pb2_grpc.py +330 -330
  19. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/builtin_interfaces/duration_pb2.py +25 -25
  20. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/builtin_interfaces/time_pb2.py +25 -25
  21. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/custom_msgs/joint_current_pb2.py +26 -26
  22. current_data_py-0.1.9/proto_gen/python/msg/custom_msgs/pose_rpy_pb2.py +26 -0
  23. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/custom_msgs/slam_quality_pb2.py +25 -25
  24. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/custom_msgs/sonic_frame_pb2.py +25 -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
  26. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/foxglove/compressed_audio_pb2.py +26 -26
  27. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/foxglove/compressed_video_pb2.py +26 -26
  28. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/point32_pb2.py +25 -25
  29. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/point_pb2.py +25 -25
  30. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/pose_pb2.py +27 -27
  31. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/pose_stamped_pb2.py +27 -27
  32. {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
  33. {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
  34. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/quaternion_pb2.py +25 -25
  35. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/transform_pb2.py +27 -27
  36. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/transform_stamped_pb2.py +27 -27
  37. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/twist_pb2.py +26 -26
  38. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/twist_stamped_pb2.py +27 -27
  39. {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
  40. {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
  41. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/vector3_pb2.py +25 -25
  42. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/wrench_pb2.py +26 -26
  43. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/geometry_msgs/wrench_stamped_pb2.py +27 -27
  44. {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
  45. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/nav_msgs/occupancy_grid_pb2.py +27 -27
  46. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/nav_msgs/odometry_pb2.py +28 -28
  47. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/nav_msgs/path_pb2.py +27 -27
  48. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/sensor_msgs/battery_state_pb2.py +32 -32
  49. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/sensor_msgs/camera_info_pb2.py +27 -27
  50. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/sensor_msgs/compressed_image_pb2.py +26 -26
  51. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/sensor_msgs/image_pb2.py +26 -26
  52. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/sensor_msgs/imu_pb2.py +28 -28
  53. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/sensor_msgs/joint_state_pb2.py +26 -26
  54. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/sensor_msgs/laser_scan_pb2.py +26 -26
  55. {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
  56. {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
  57. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/sensor_msgs/point_cloud2_pb2.py +27 -27
  58. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/sensor_msgs/point_field_pb2.py +27 -27
  59. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/sensor_msgs/range_pb2.py +28 -28
  60. {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
  61. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/std_msgs/bool_pb2.py +25 -25
  62. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/std_msgs/color_rgba_pb2.py +25 -25
  63. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/std_msgs/float32_pb2.py +25 -25
  64. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/std_msgs/float64_pb2.py +25 -25
  65. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/std_msgs/header_pb2.py +26 -26
  66. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/std_msgs/int32_pb2.py +25 -25
  67. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/std_msgs/string_pb2.py +25 -25
  68. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/std_msgs/uint32_pb2.py +25 -25
  69. {current_data_py-0.1.6 → current_data_py-0.1.9}/proto_gen/python/msg/tf2_msgs/tf_message_pb2.py +26 -26
  70. {current_data_py-0.1.6 → current_data_py-0.1.9}/pyproject.toml +24 -21
  71. {current_data_py-0.1.6 → current_data_py-0.1.9}/remote/__init__.py +7 -7
  72. {current_data_py-0.1.6 → current_data_py-0.1.9}/remote/client.py +89 -83
  73. {current_data_py-0.1.6 → current_data_py-0.1.9}/remote/source.py +98 -98
  74. {current_data_py-0.1.6 → current_data_py-0.1.9}/schema/__init__.py +107 -105
  75. {current_data_py-0.1.6 → current_data_py-0.1.9}/schema/message_metadata.py +30 -30
  76. {current_data_py-0.1.6 → current_data_py-0.1.9}/setup.py +60 -60
  77. current_data_py-0.1.9/tests/check_environment.py +79 -0
  78. {current_data_py-0.1.6 → current_data_py-0.1.9}/tests/check_management.py +255 -253
  79. {current_data_py-0.1.6 → current_data_py-0.1.9}/tests/check_oss.py +317 -317
  80. {current_data_py-0.1.6 → current_data_py-0.1.9}/tests/check_package.py +43 -43
  81. {current_data_py-0.1.6 → current_data_py-0.1.9}/tests/check_remote.py +291 -289
  82. current_data_py-0.1.6/PKG-INFO +0 -136
  83. current_data_py-0.1.6/README.md +0 -125
  84. current_data_py-0.1.6/__init__.py +0 -8
  85. current_data_py-0.1.6/current_data_py.egg-info/PKG-INFO +0 -136
  86. current_data_py-0.1.6/proto_gen/python/management/service/service_pb2.py +0 -64
  87. {current_data_py-0.1.6 → current_data_py-0.1.9}/current_data_py.egg-info/dependency_links.txt +0 -0
  88. {current_data_py-0.1.6 → current_data_py-0.1.9}/current_data_py.egg-info/top_level.txt +0 -0
  89. {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/check_remote.py
3
- include tests/check_management.py
4
- include tests/check_oss.py
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"]