dora-rs 0.3.12rc0__cp37-abi3-manylinux_2_28_i686.whl → 0.3.13__cp37-abi3-manylinux_2_28_i686.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.
Potentially problematic release.
This version of dora-rs might be problematic. Click here for more details.
- dora/__init__.pyi +279 -169
- dora/builder.py +166 -0
- dora/dora.abi3.so +0 -0
- dora/py.typed +0 -0
- {dora_rs-0.3.12rc0.dist-info → dora_rs-0.3.13.dist-info}/METADATA +2 -3
- dora_rs-0.3.13.dist-info/RECORD +9 -0
- {dora_rs-0.3.12rc0.dist-info → dora_rs-0.3.13.dist-info}/WHEEL +1 -1
- dora_rs-0.3.12rc0.dist-info/RECORD +0 -7
dora/__init__.pyi
CHANGED
|
@@ -1,100 +1,160 @@
|
|
|
1
|
-
import typing
|
|
2
|
-
|
|
3
|
-
import pyarrow
|
|
4
|
-
|
|
5
1
|
import dora
|
|
2
|
+
import pyarrow
|
|
3
|
+
import typing
|
|
6
4
|
|
|
7
5
|
@typing.final
|
|
8
6
|
class Enum:
|
|
9
|
-
"""
|
|
7
|
+
"""Create a collection of name/value pairs.
|
|
8
|
+
|
|
9
|
+
Example enumeration:
|
|
10
|
+
|
|
11
|
+
>>> class Color(Enum):
|
|
12
|
+
... RED = 1
|
|
13
|
+
... BLUE = 2
|
|
14
|
+
... GREEN = 3
|
|
15
|
+
|
|
16
|
+
Access them by:
|
|
17
|
+
|
|
18
|
+
- attribute access:
|
|
19
|
+
|
|
20
|
+
>>> Color.RED
|
|
21
|
+
<Color.RED: 1>
|
|
22
|
+
|
|
23
|
+
- value lookup:
|
|
24
|
+
|
|
25
|
+
>>> Color(1)
|
|
26
|
+
<Color.RED: 1>
|
|
10
27
|
|
|
11
|
-
|
|
12
|
-
"""
|
|
28
|
+
- name lookup:
|
|
13
29
|
|
|
30
|
+
>>> Color['RED']
|
|
31
|
+
<Color.RED: 1>
|
|
32
|
+
|
|
33
|
+
Enumerations can be iterated over, and know how many members they have:
|
|
34
|
+
|
|
35
|
+
>>> len(Color)
|
|
36
|
+
3
|
|
37
|
+
|
|
38
|
+
>>> list(Color)
|
|
39
|
+
[<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]
|
|
40
|
+
|
|
41
|
+
Methods can be added to enumerations, and members can have their own
|
|
42
|
+
attributes -- see the documentation for details."""
|
|
43
|
+
|
|
44
|
+
@staticmethod
|
|
45
|
+
def __contains__(value: typing.Any) -> bool:
|
|
46
|
+
"""Return True if `value` is in `cls`.
|
|
47
|
+
|
|
48
|
+
`value` is in `cls` if:
|
|
49
|
+
1) `value` is a member of `cls`, or
|
|
50
|
+
2) `value` is the value of one of the `cls`'s members."""
|
|
51
|
+
|
|
52
|
+
@staticmethod
|
|
53
|
+
def __getitem__(name: typing.Any) -> typing.Any:
|
|
54
|
+
"""Return the member matching `name`."""
|
|
55
|
+
|
|
56
|
+
@staticmethod
|
|
57
|
+
def __iter__() -> typing.Any:
|
|
58
|
+
"""Return members in definition order."""
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def __len__() -> int:
|
|
62
|
+
"""Return the number of members (no aliases)"""
|
|
14
63
|
__members__: mappingproxy = ...
|
|
64
|
+
__name__: str = ...
|
|
65
|
+
__qualname__: str = ...
|
|
15
66
|
|
|
16
67
|
@typing.final
|
|
17
68
|
class Node:
|
|
18
69
|
"""The custom node API lets you integrate `dora` into your application.
|
|
19
|
-
|
|
20
|
-
It allows you to retrieve input and send output in any fashion you want.
|
|
70
|
+
It allows you to retrieve input and send output in any fashion you want.
|
|
21
71
|
|
|
22
|
-
|
|
72
|
+
Use with:
|
|
23
73
|
|
|
24
|
-
|
|
25
|
-
|
|
74
|
+
```python
|
|
75
|
+
from dora import Node
|
|
26
76
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"""
|
|
77
|
+
node = Node()
|
|
78
|
+
```"""
|
|
30
79
|
|
|
31
80
|
def __init__(self, node_id: str=None) -> None:
|
|
32
|
-
"""
|
|
81
|
+
"""The custom node API lets you integrate `dora` into your application.
|
|
82
|
+
It allows you to retrieve input and send output in any fashion you want.
|
|
33
83
|
|
|
34
|
-
|
|
84
|
+
Use with:
|
|
35
85
|
|
|
36
|
-
|
|
86
|
+
```python
|
|
87
|
+
from dora import Node
|
|
37
88
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
node = Node()
|
|
42
|
-
```
|
|
43
|
-
"""
|
|
89
|
+
node = Node()
|
|
90
|
+
```"""
|
|
44
91
|
|
|
45
92
|
def dataflow_descriptor(self) -> dict:
|
|
46
|
-
"""
|
|
93
|
+
"""Returns the full dataflow descriptor that this node is part of.
|
|
47
94
|
|
|
48
|
-
|
|
49
|
-
"""
|
|
95
|
+
This method returns the parsed dataflow YAML file."""
|
|
50
96
|
|
|
51
97
|
def dataflow_id(self) -> str:
|
|
52
|
-
"""
|
|
98
|
+
"""Returns the dataflow id."""
|
|
53
99
|
|
|
54
100
|
def merge_external_events(self, subscription: dora.Ros2Subscription) -> None:
|
|
55
101
|
"""Merge an external event stream with dora main loop.
|
|
56
|
-
|
|
57
|
-
This currently only work with ROS2.
|
|
58
|
-
"""
|
|
102
|
+
This currently only work with ROS2."""
|
|
59
103
|
|
|
60
104
|
def next(self, timeout: float=None) -> dict:
|
|
61
105
|
"""`.next()` gives you the next input that the node has received.
|
|
106
|
+
It blocks until the next event becomes available.
|
|
107
|
+
You can use timeout in seconds to return if no input is available.
|
|
108
|
+
It will return `None` when all senders has been dropped.
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
event = node.next()
|
|
112
|
+
```
|
|
62
113
|
|
|
63
|
-
|
|
64
|
-
You can use timeout in seconds to return if no input is available.
|
|
65
|
-
It will return `None` when all senders has been dropped.
|
|
114
|
+
You can also iterate over the event stream with a loop
|
|
66
115
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
116
|
+
```python
|
|
117
|
+
for event in node:
|
|
118
|
+
match event["type"]:
|
|
119
|
+
case "INPUT":
|
|
120
|
+
match event["id"]:
|
|
121
|
+
case "image":
|
|
122
|
+
```"""
|
|
70
123
|
|
|
71
|
-
|
|
124
|
+
def node_config(self) -> dict:
|
|
125
|
+
"""Returns the node configuration."""
|
|
72
126
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
127
|
+
def recv_async(self, timeout: float=None) -> dict:
|
|
128
|
+
"""`.recv_async()` gives you the next input that the node has received asynchronously.
|
|
129
|
+
It does not blocks until the next event becomes available.
|
|
130
|
+
You can use timeout in seconds to return if no input is available.
|
|
131
|
+
It will return an Error if the timeout is reached.
|
|
132
|
+
It will return `None` when all senders has been dropped.
|
|
133
|
+
|
|
134
|
+
warning::
|
|
135
|
+
This feature is experimental as pyo3 async (rust-python FFI) is still in development.
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
event = await node.recv_async()
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
You can also iterate over the event stream with a loop"""
|
|
81
142
|
|
|
82
143
|
def send_output(self, output_id: str, data: pyarrow.Array, metadata: dict=None) -> None:
|
|
83
144
|
"""`send_output` send data from the node.
|
|
84
145
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
146
|
+
```python
|
|
147
|
+
Args:
|
|
148
|
+
output_id: str,
|
|
149
|
+
data: pyarrow.Array,
|
|
150
|
+
metadata: Option[Dict],
|
|
151
|
+
```
|
|
91
152
|
|
|
92
|
-
|
|
153
|
+
ex:
|
|
93
154
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
"""
|
|
155
|
+
```python
|
|
156
|
+
node.send_output("string", b"string", {"open_telemetry_context": "7632e76"})
|
|
157
|
+
```"""
|
|
98
158
|
|
|
99
159
|
def __iter__(self) -> typing.Any:
|
|
100
160
|
"""Implement iter(self)."""
|
|
@@ -102,70 +162,79 @@ class Node:
|
|
|
102
162
|
def __next__(self) -> typing.Any:
|
|
103
163
|
"""Implement next(self)."""
|
|
104
164
|
|
|
165
|
+
def __repr__(self) -> str:
|
|
166
|
+
"""Return repr(self)."""
|
|
167
|
+
|
|
168
|
+
def __str__(self) -> str:
|
|
169
|
+
"""Return str(self)."""
|
|
170
|
+
|
|
105
171
|
@typing.final
|
|
106
172
|
class Ros2Context:
|
|
107
173
|
"""ROS2 Context holding all messages definition for receiving and sending messages to ROS2.
|
|
108
174
|
|
|
109
|
-
|
|
175
|
+
By default, Ros2Context will use env `AMENT_PREFIX_PATH` to search for message definition.
|
|
110
176
|
|
|
111
|
-
|
|
177
|
+
AMENT_PREFIX_PATH folder structure should be the following:
|
|
112
178
|
|
|
113
|
-
|
|
114
|
-
|
|
179
|
+
- For messages: <namespace>/msg/<name>.msg
|
|
180
|
+
- For services: <namespace>/srv/<name>.srv
|
|
115
181
|
|
|
116
|
-
|
|
182
|
+
You can also use `ros_paths` if you don't want to use env variable.
|
|
117
183
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
184
|
+
warning::
|
|
185
|
+
dora Ros2 bridge functionality is considered **unstable**. It may be changed
|
|
186
|
+
at any point without it being considered a breaking change.
|
|
121
187
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
"""
|
|
188
|
+
```python
|
|
189
|
+
context = Ros2Context()
|
|
190
|
+
```"""
|
|
126
191
|
|
|
127
|
-
def __init__(self, ros_paths:
|
|
192
|
+
def __init__(self, ros_paths: typing.List[str]=None) -> None:
|
|
128
193
|
"""ROS2 Context holding all messages definition for receiving and sending messages to ROS2.
|
|
129
194
|
|
|
130
|
-
|
|
195
|
+
By default, Ros2Context will use env `AMENT_PREFIX_PATH` to search for message definition.
|
|
131
196
|
|
|
132
|
-
|
|
197
|
+
AMENT_PREFIX_PATH folder structure should be the following:
|
|
133
198
|
|
|
134
|
-
|
|
135
|
-
|
|
199
|
+
- For messages: <namespace>/msg/<name>.msg
|
|
200
|
+
- For services: <namespace>/srv/<name>.srv
|
|
136
201
|
|
|
137
|
-
|
|
202
|
+
You can also use `ros_paths` if you don't want to use env variable.
|
|
138
203
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
204
|
+
warning::
|
|
205
|
+
dora Ros2 bridge functionality is considered **unstable**. It may be changed
|
|
206
|
+
at any point without it being considered a breaking change.
|
|
142
207
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
"""
|
|
208
|
+
```python
|
|
209
|
+
context = Ros2Context()
|
|
210
|
+
```"""
|
|
147
211
|
|
|
148
212
|
def new_node(self, name: str, namespace: str, options: dora.Ros2NodeOptions) -> dora.Ros2Node:
|
|
149
|
-
"""Create a new ROS2 node
|
|
213
|
+
"""Create a new ROS2 node
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
ros2_node = ros2_context.new_node(
|
|
217
|
+
"turtle_teleop",
|
|
218
|
+
"/ros2_demo",
|
|
219
|
+
Ros2NodeOptions(rosout=True),
|
|
220
|
+
)
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
warning::
|
|
224
|
+
dora Ros2 bridge functionality is considered **unstable**. It may be changed
|
|
225
|
+
at any point without it being considered a breaking change."""
|
|
150
226
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
"turtle_teleop",
|
|
154
|
-
"/ros2_demo",
|
|
155
|
-
Ros2NodeOptions(rosout=True),
|
|
156
|
-
)
|
|
157
|
-
```
|
|
227
|
+
def __repr__(self) -> str:
|
|
228
|
+
"""Return repr(self)."""
|
|
158
229
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
at any point without it being considered a breaking change.
|
|
162
|
-
"""
|
|
230
|
+
def __str__(self) -> str:
|
|
231
|
+
"""Return str(self)."""
|
|
163
232
|
|
|
164
233
|
@typing.final
|
|
165
234
|
class Ros2Durability:
|
|
166
|
-
"""DDS 2.2.3.4 DURABILITY
|
|
235
|
+
"""DDS 2.2.3.4 DURABILITY"""
|
|
167
236
|
|
|
168
|
-
def __eq__(self, value:
|
|
237
|
+
def __eq__(self, value: typing.Any) -> bool:
|
|
169
238
|
"""Return self==value."""
|
|
170
239
|
|
|
171
240
|
def __ge__(self, value: typing.Any) -> bool:
|
|
@@ -175,7 +244,7 @@ class Ros2Durability:
|
|
|
175
244
|
"""Return self>value."""
|
|
176
245
|
|
|
177
246
|
def __int__(self) -> None:
|
|
178
|
-
"""int(self)
|
|
247
|
+
"""int(self)"""
|
|
179
248
|
|
|
180
249
|
def __le__(self, value: typing.Any) -> bool:
|
|
181
250
|
"""Return self<=value."""
|
|
@@ -183,9 +252,14 @@ class Ros2Durability:
|
|
|
183
252
|
def __lt__(self, value: typing.Any) -> bool:
|
|
184
253
|
"""Return self<value."""
|
|
185
254
|
|
|
186
|
-
def __ne__(self, value:
|
|
255
|
+
def __ne__(self, value: typing.Any) -> bool:
|
|
187
256
|
"""Return self!=value."""
|
|
188
257
|
|
|
258
|
+
def __repr__(self) -> str:
|
|
259
|
+
"""Return repr(self)."""
|
|
260
|
+
|
|
261
|
+
def __str__(self) -> str:
|
|
262
|
+
"""Return str(self)."""
|
|
189
263
|
Persistent: Ros2Durability = ...
|
|
190
264
|
Transient: Ros2Durability = ...
|
|
191
265
|
TransientLocal: Ros2Durability = ...
|
|
@@ -193,9 +267,9 @@ class Ros2Durability:
|
|
|
193
267
|
|
|
194
268
|
@typing.final
|
|
195
269
|
class Ros2Liveliness:
|
|
196
|
-
"""DDS 2.2.3.11 LIVELINESS
|
|
270
|
+
"""DDS 2.2.3.11 LIVELINESS"""
|
|
197
271
|
|
|
198
|
-
def __eq__(self, value:
|
|
272
|
+
def __eq__(self, value: typing.Any) -> bool:
|
|
199
273
|
"""Return self==value."""
|
|
200
274
|
|
|
201
275
|
def __ge__(self, value: typing.Any) -> bool:
|
|
@@ -205,7 +279,7 @@ class Ros2Liveliness:
|
|
|
205
279
|
"""Return self>value."""
|
|
206
280
|
|
|
207
281
|
def __int__(self) -> None:
|
|
208
|
-
"""int(self)
|
|
282
|
+
"""int(self)"""
|
|
209
283
|
|
|
210
284
|
def __le__(self, value: typing.Any) -> bool:
|
|
211
285
|
"""Return self<=value."""
|
|
@@ -213,123 +287,159 @@ class Ros2Liveliness:
|
|
|
213
287
|
def __lt__(self, value: typing.Any) -> bool:
|
|
214
288
|
"""Return self<value."""
|
|
215
289
|
|
|
216
|
-
def __ne__(self, value:
|
|
290
|
+
def __ne__(self, value: typing.Any) -> bool:
|
|
217
291
|
"""Return self!=value."""
|
|
218
292
|
|
|
293
|
+
def __repr__(self) -> str:
|
|
294
|
+
"""Return repr(self)."""
|
|
295
|
+
|
|
296
|
+
def __str__(self) -> str:
|
|
297
|
+
"""Return str(self)."""
|
|
219
298
|
Automatic: Ros2Liveliness = ...
|
|
220
299
|
ManualByParticipant: Ros2Liveliness = ...
|
|
221
300
|
ManualByTopic: Ros2Liveliness = ...
|
|
222
301
|
|
|
223
302
|
@typing.final
|
|
224
303
|
class Ros2Node:
|
|
225
|
-
"""ROS2 Node
|
|
304
|
+
"""ROS2 Node
|
|
226
305
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
"""
|
|
306
|
+
warnings::
|
|
307
|
+
- dora Ros2 bridge functionality is considered **unstable**. It may be changed
|
|
308
|
+
at any point without it being considered a breaking change.
|
|
309
|
+
- There's a known issue about ROS2 nodes not being discoverable by ROS2
|
|
310
|
+
See: https://github.com/jhelovuo/ros2-client/issues/4"""
|
|
233
311
|
|
|
234
312
|
def create_publisher(self, topic: dora.Ros2Topic, qos: dora.Ros2QosPolicies=None) -> dora.Ros2Publisher:
|
|
235
|
-
"""Create a ROS2 publisher
|
|
313
|
+
"""Create a ROS2 publisher
|
|
236
314
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
"""
|
|
315
|
+
```python
|
|
316
|
+
pose_publisher = ros2_node.create_publisher(turtle_pose_topic)
|
|
317
|
+
```
|
|
318
|
+
warnings:
|
|
319
|
+
- dora Ros2 bridge functionality is considered **unstable**. It may be changed
|
|
320
|
+
at any point without it being considered a breaking change."""
|
|
244
321
|
|
|
245
322
|
def create_subscription(self, topic: dora.Ros2Topic, qos: dora.Ros2QosPolicies=None) -> dora.Ros2Subscription:
|
|
246
|
-
"""Create a ROS2 subscription
|
|
247
|
-
|
|
248
|
-
```python
|
|
249
|
-
pose_reader = ros2_node.create_subscription(turtle_pose_topic)
|
|
250
|
-
```
|
|
323
|
+
"""Create a ROS2 subscription
|
|
251
324
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
325
|
+
```python
|
|
326
|
+
pose_reader = ros2_node.create_subscription(turtle_pose_topic)
|
|
327
|
+
```
|
|
255
328
|
|
|
256
|
-
|
|
329
|
+
warnings:
|
|
330
|
+
- dora Ros2 bridge functionality is considered **unstable**. It may be changed
|
|
331
|
+
at any point without it being considered a breaking change."""
|
|
257
332
|
|
|
258
333
|
def create_topic(self, name: str, message_type: str, qos: dora.Ros2QosPolicies) -> dora.Ros2Topic:
|
|
259
334
|
"""Create a ROS2 topic to connect to.
|
|
260
335
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
336
|
+
```python
|
|
337
|
+
turtle_twist_topic = ros2_node.create_topic(
|
|
338
|
+
"/turtle1/cmd_vel", "geometry_msgs/Twist", topic_qos
|
|
339
|
+
)
|
|
340
|
+
```"""
|
|
341
|
+
|
|
342
|
+
def __repr__(self) -> str:
|
|
343
|
+
"""Return repr(self)."""
|
|
344
|
+
|
|
345
|
+
def __str__(self) -> str:
|
|
346
|
+
"""Return str(self)."""
|
|
267
347
|
|
|
268
348
|
@typing.final
|
|
269
349
|
class Ros2NodeOptions:
|
|
270
|
-
"""ROS2 Node Options
|
|
350
|
+
"""ROS2 Node Options"""
|
|
271
351
|
|
|
272
352
|
def __init__(self, rosout: bool=None) -> None:
|
|
273
|
-
"""ROS2 Node Options
|
|
353
|
+
"""ROS2 Node Options"""
|
|
354
|
+
|
|
355
|
+
def __repr__(self) -> str:
|
|
356
|
+
"""Return repr(self)."""
|
|
357
|
+
|
|
358
|
+
def __str__(self) -> str:
|
|
359
|
+
"""Return str(self)."""
|
|
274
360
|
|
|
275
361
|
@typing.final
|
|
276
362
|
class Ros2Publisher:
|
|
277
|
-
"""ROS2 Publisher
|
|
278
|
-
|
|
279
|
-
Warnings:
|
|
280
|
-
- dora Ros2 bridge functionality is considered **unstable**. It may be changed
|
|
281
|
-
at any point without it being considered a breaking change.
|
|
363
|
+
"""ROS2 Publisher
|
|
282
364
|
|
|
283
|
-
|
|
365
|
+
warnings:
|
|
366
|
+
- dora Ros2 bridge functionality is considered **unstable**. It may be changed
|
|
367
|
+
at any point without it being considered a breaking change."""
|
|
284
368
|
|
|
285
369
|
def publish(self, data: pyarrow.Array) -> None:
|
|
286
370
|
"""Publish a message into ROS2 topic.
|
|
287
371
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
372
|
+
Remember that the data format should respect the structure of the ROS2 message using an arrow Structure.
|
|
373
|
+
|
|
374
|
+
ex:
|
|
375
|
+
```python
|
|
376
|
+
gripper_command.publish(
|
|
377
|
+
pa.array(
|
|
378
|
+
[
|
|
379
|
+
{
|
|
380
|
+
"name": "gripper",
|
|
381
|
+
"cmd": np.float32(5),
|
|
382
|
+
}
|
|
383
|
+
]
|
|
384
|
+
),
|
|
385
|
+
)
|
|
386
|
+
```"""
|
|
387
|
+
|
|
388
|
+
def __repr__(self) -> str:
|
|
389
|
+
"""Return repr(self)."""
|
|
390
|
+
|
|
391
|
+
def __str__(self) -> str:
|
|
392
|
+
"""Return str(self)."""
|
|
304
393
|
|
|
305
394
|
@typing.final
|
|
306
395
|
class Ros2QosPolicies:
|
|
307
|
-
"""ROS2 QoS Policy
|
|
396
|
+
"""ROS2 QoS Policy"""
|
|
308
397
|
|
|
309
398
|
def __init__(self, durability: dora.Ros2Durability=None, liveliness: dora.Ros2Liveliness=None, reliable: bool=None, keep_all: bool=None, lease_duration: float=None, max_blocking_time: float=None, keep_last: int=None) -> dora.Ros2QoSPolicies:
|
|
310
|
-
"""ROS2 QoS Policy
|
|
399
|
+
"""ROS2 QoS Policy"""
|
|
400
|
+
|
|
401
|
+
def __repr__(self) -> str:
|
|
402
|
+
"""Return repr(self)."""
|
|
403
|
+
|
|
404
|
+
def __str__(self) -> str:
|
|
405
|
+
"""Return str(self)."""
|
|
311
406
|
|
|
312
407
|
@typing.final
|
|
313
408
|
class Ros2Subscription:
|
|
314
|
-
"""ROS2 Subscription
|
|
409
|
+
"""ROS2 Subscription
|
|
315
410
|
|
|
316
|
-
Warnings:
|
|
317
|
-
- dora Ros2 bridge functionality is considered **unstable**. It may be changed
|
|
318
|
-
at any point without it being considered a breaking change.
|
|
319
411
|
|
|
320
|
-
|
|
412
|
+
warnings:
|
|
413
|
+
- dora Ros2 bridge functionality is considered **unstable**. It may be changed
|
|
414
|
+
at any point without it being considered a breaking change."""
|
|
321
415
|
|
|
322
416
|
def next(self):...
|
|
323
417
|
|
|
418
|
+
def __repr__(self) -> str:
|
|
419
|
+
"""Return repr(self)."""
|
|
420
|
+
|
|
421
|
+
def __str__(self) -> str:
|
|
422
|
+
"""Return str(self)."""
|
|
423
|
+
|
|
324
424
|
@typing.final
|
|
325
425
|
class Ros2Topic:
|
|
326
|
-
"""ROS2 Topic
|
|
426
|
+
"""ROS2 Topic
|
|
427
|
+
|
|
428
|
+
warnings:
|
|
429
|
+
- dora Ros2 bridge functionality is considered **unstable**. It may be changed
|
|
430
|
+
at any point without it being considered a breaking change."""
|
|
431
|
+
|
|
432
|
+
def __repr__(self) -> str:
|
|
433
|
+
"""Return repr(self)."""
|
|
434
|
+
|
|
435
|
+
def __str__(self) -> str:
|
|
436
|
+
"""Return str(self)."""
|
|
327
437
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
at any point without it being considered a breaking change.
|
|
438
|
+
def build(dataflow_path: str, uv: bool=None, coordinator_addr: str=None, coordinator_port: int=None, force_local: bool=False) -> None:
|
|
439
|
+
"""Build a Dataflow, exactly the same way as `dora build` command line tool."""
|
|
331
440
|
|
|
332
|
-
|
|
441
|
+
def run(dataflow_path: str, uv: bool=None) -> None:
|
|
442
|
+
"""Run a Dataflow, exactly the same way as `dora run` command line tool."""
|
|
333
443
|
|
|
334
444
|
def start_runtime() -> None:
|
|
335
|
-
"""Start a runtime for Operators
|
|
445
|
+
"""Start a runtime for Operators"""
|
dora/builder.py
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import yaml
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DataflowBuilder:
|
|
7
|
+
"""A dora dataflow."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, name: str = "dora-dataflow"):
|
|
10
|
+
self.name = name
|
|
11
|
+
self.nodes = []
|
|
12
|
+
|
|
13
|
+
def add_node(self, id: str, **kwargs) -> Node:
|
|
14
|
+
"""Adds a new node to the dataflow."""
|
|
15
|
+
node = Node(id, **kwargs)
|
|
16
|
+
self.nodes.append(node)
|
|
17
|
+
return node
|
|
18
|
+
|
|
19
|
+
def to_yaml(self, path: str = None) -> str | None:
|
|
20
|
+
"""Generates the YAML representation of the dataflow."""
|
|
21
|
+
dataflow_spec = {"nodes": [node.to_dict() for node in self.nodes]}
|
|
22
|
+
if path:
|
|
23
|
+
with open(path, "w") as f:
|
|
24
|
+
yaml.dump(dataflow_spec, f)
|
|
25
|
+
return None
|
|
26
|
+
else:
|
|
27
|
+
return yaml.dump(dataflow_spec)
|
|
28
|
+
|
|
29
|
+
def __enter__(self):
|
|
30
|
+
return self
|
|
31
|
+
|
|
32
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Output:
|
|
37
|
+
"""Represents an output from a node."""
|
|
38
|
+
|
|
39
|
+
def __init__(self, node: Node, output_id: str):
|
|
40
|
+
self.node = node
|
|
41
|
+
self.output_id = output_id
|
|
42
|
+
|
|
43
|
+
def __str__(self) -> str:
|
|
44
|
+
return f"{self.node.id}/{self.output_id}"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class Node:
|
|
48
|
+
"""A node in a dora dataflow."""
|
|
49
|
+
|
|
50
|
+
def __init__(self, id: str, **kwargs):
|
|
51
|
+
self.id = id
|
|
52
|
+
self.config = kwargs
|
|
53
|
+
self.config["id"] = id
|
|
54
|
+
self.operators = []
|
|
55
|
+
|
|
56
|
+
def path(self, path: str) -> Node:
|
|
57
|
+
"""Sets the path to the executable or script."""
|
|
58
|
+
self.config["path"] = path
|
|
59
|
+
return self
|
|
60
|
+
|
|
61
|
+
def args(self, args: str) -> Node:
|
|
62
|
+
"""Sets the command-line arguments for the node."""
|
|
63
|
+
self.config["args"] = args
|
|
64
|
+
return self
|
|
65
|
+
|
|
66
|
+
def env(self, env: dict) -> Node:
|
|
67
|
+
"""Sets the environment variables for the node."""
|
|
68
|
+
self.config["env"] = env
|
|
69
|
+
return self
|
|
70
|
+
|
|
71
|
+
def build(self, build_command: str) -> Node:
|
|
72
|
+
"""Sets the build command for the node."""
|
|
73
|
+
self.config["build"] = build_command
|
|
74
|
+
return self
|
|
75
|
+
|
|
76
|
+
def git(
|
|
77
|
+
self, url: str, branch: str = None, tag: str = None, rev: str = None
|
|
78
|
+
) -> Node:
|
|
79
|
+
"""Sets the Git repository for the node."""
|
|
80
|
+
self.config["git"] = url
|
|
81
|
+
if branch:
|
|
82
|
+
self.config["branch"] = branch
|
|
83
|
+
if tag:
|
|
84
|
+
self.config["tag"] = tag
|
|
85
|
+
if rev:
|
|
86
|
+
self.config["rev"] = rev
|
|
87
|
+
return self
|
|
88
|
+
|
|
89
|
+
def add_operator(self, operator: Operator) -> Node:
|
|
90
|
+
"""Adds an operator to this node."""
|
|
91
|
+
self.operators.append(operator)
|
|
92
|
+
return self
|
|
93
|
+
|
|
94
|
+
def add_output(self, output_id: str) -> Output:
|
|
95
|
+
"""Adds an output to the node and returns an Output object."""
|
|
96
|
+
if "outputs" not in self.config:
|
|
97
|
+
self.config["outputs"] = []
|
|
98
|
+
if output_id not in self.config["outputs"]:
|
|
99
|
+
self.config["outputs"].append(output_id)
|
|
100
|
+
return Output(self, output_id)
|
|
101
|
+
|
|
102
|
+
def add_input(
|
|
103
|
+
self, input_id: str, source: str | Output, queue_size: int = None
|
|
104
|
+
) -> Node:
|
|
105
|
+
"""Adds a user-defined input to the node. Source can be a string or an Output object."""
|
|
106
|
+
if "inputs" not in self.config:
|
|
107
|
+
self.config["inputs"] = {}
|
|
108
|
+
|
|
109
|
+
if isinstance(source, Output):
|
|
110
|
+
source_str = str(source)
|
|
111
|
+
if queue_size is not None:
|
|
112
|
+
self.config["inputs"][input_id] = {
|
|
113
|
+
"source": source_str,
|
|
114
|
+
"queue_size": queue_size,
|
|
115
|
+
}
|
|
116
|
+
else:
|
|
117
|
+
self.config["inputs"][input_id] = source_str
|
|
118
|
+
else:
|
|
119
|
+
if queue_size is not None:
|
|
120
|
+
self.config["inputs"][input_id] = {
|
|
121
|
+
"source": source,
|
|
122
|
+
"queue_size": queue_size,
|
|
123
|
+
}
|
|
124
|
+
else:
|
|
125
|
+
self.config["inputs"][input_id] = source
|
|
126
|
+
return self
|
|
127
|
+
|
|
128
|
+
def to_dict(self) -> dict:
|
|
129
|
+
"""Returns the dictionary representation of the node."""
|
|
130
|
+
config = self.config.copy()
|
|
131
|
+
if self.operators:
|
|
132
|
+
config["operators"] = [op.to_dict() for op in self.operators]
|
|
133
|
+
return config
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class Operator:
|
|
137
|
+
"""An operator in a dora dataflow."""
|
|
138
|
+
|
|
139
|
+
def __init__(
|
|
140
|
+
self,
|
|
141
|
+
id: str,
|
|
142
|
+
name: str = None,
|
|
143
|
+
description: str = None,
|
|
144
|
+
build: str = None,
|
|
145
|
+
python: str = None,
|
|
146
|
+
shared_library: str = None,
|
|
147
|
+
send_stdout_as: str = None,
|
|
148
|
+
):
|
|
149
|
+
self.id = id
|
|
150
|
+
self.config = {"id": id}
|
|
151
|
+
if name:
|
|
152
|
+
self.config["name"] = name
|
|
153
|
+
if description:
|
|
154
|
+
self.config["description"] = description
|
|
155
|
+
if build:
|
|
156
|
+
self.config["build"] = build
|
|
157
|
+
if python:
|
|
158
|
+
self.config["python"] = python
|
|
159
|
+
if shared_library:
|
|
160
|
+
self.config["shared-library"] = shared_library
|
|
161
|
+
if send_stdout_as:
|
|
162
|
+
self.config["send_stdout_as"] = send_stdout_as
|
|
163
|
+
|
|
164
|
+
def to_dict(self) -> dict:
|
|
165
|
+
"""Returns the dictionary representation of the operator."""
|
|
166
|
+
return self.config
|
dora/dora.abi3.so
CHANGED
|
Binary file
|
dora/py.typed
ADDED
|
File without changes
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dora-rs
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.13
|
|
4
4
|
Requires-Dist: pyarrow
|
|
5
|
-
|
|
5
|
+
Requires-Dist: pyyaml>=6.0
|
|
6
6
|
License: MIT
|
|
7
7
|
Requires-Python: >=3.7
|
|
8
8
|
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
9
|
-
Project-URL: Source Code, https://github.com/dora-rs/dora/
|
|
10
9
|
|
|
11
10
|
This crate corresponds to the Node API for Dora.
|
|
12
11
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
dora/__init__.py,sha256=mogA09uYFBIwnBnnMfeQtD52ZJbVbWmWo1h70GZj2aU,709
|
|
2
|
+
dora/__init__.pyi,sha256=DfrAGQUkAmtNEjLrDWOL13Q4qYlF9ttDAUeRTIgwQQ4,11633
|
|
3
|
+
dora/builder.py,sha256=WqYuhtm8tCOlxEbGvsRiTfW0Ji84P1MmA4FIRfpbWQo,4956
|
|
4
|
+
dora/cuda.py,sha256=tsp76SLAeR1gOXoQTTMv4oVHFoSzkXeevEQG41U9xFU,2736
|
|
5
|
+
dora/dora.abi3.so,sha256=WOpfBpFZY0q6Anh2DUdYfKYeLl5Rpua4E_Z5hO4rWLs,44035772
|
|
6
|
+
dora/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
dora_rs-0.3.13.dist-info/METADATA,sha256=bTlAHhLQ1Oo-zE_XK3c52gmC9ILh2GIthOqhGTWNODc,509
|
|
8
|
+
dora_rs-0.3.13.dist-info/WHEEL,sha256=MDuN54W-NrOAXVUrFjEH4u4pzh6sT_9fY94iqfNhmAI,104
|
|
9
|
+
dora_rs-0.3.13.dist-info/RECORD,,
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
dora/__init__.py,sha256=mogA09uYFBIwnBnnMfeQtD52ZJbVbWmWo1h70GZj2aU,709
|
|
2
|
-
dora/__init__.pyi,sha256=-jnLcWmFIi8ygYv44-VeYmq4e4I9a5zBHdz5q2Kae-E,9148
|
|
3
|
-
dora/cuda.py,sha256=tsp76SLAeR1gOXoQTTMv4oVHFoSzkXeevEQG41U9xFU,2736
|
|
4
|
-
dora/dora.abi3.so,sha256=FKUuyFKh6UO7B5aN6AU-qEdjuC53k-xF2MD4od3D-mg,42870152
|
|
5
|
-
dora_rs-0.3.12rc0.dist-info/METADATA,sha256=IRWdC8KToEgCWTBNAf2Y2MA0d5XZejPmsnpcSRmShMg,628
|
|
6
|
-
dora_rs-0.3.12rc0.dist-info/WHEEL,sha256=g8Fs8T9aIikGw5UItkARvmZKB1G5uuxP5zfX8DfD8D8,104
|
|
7
|
-
dora_rs-0.3.12rc0.dist-info/RECORD,,
|