foxglove-sdk 0.9.0__pp310-pypy310_pp73-musllinux_1_2_i686.whl → 0.15.3__pp310-pypy310_pp73-musllinux_1_2_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 foxglove-sdk might be problematic. Click here for more details.

foxglove/__init__.py CHANGED
@@ -5,70 +5,128 @@ See :py:mod:`foxglove.schemas` and :py:mod:`foxglove.channels` for working with
5
5
  schemas.
6
6
  """
7
7
 
8
+ from __future__ import annotations
9
+
8
10
  import atexit
9
11
  import logging
10
- from typing import List, Optional, Union
12
+ from typing import TYPE_CHECKING
11
13
 
12
14
  from . import _foxglove_py as _foxglove
13
15
 
14
16
  # Re-export these imports
15
- from ._foxglove_py import Context, Schema, open_mcap
17
+ from ._foxglove_py import (
18
+ ChannelDescriptor,
19
+ Context,
20
+ Schema,
21
+ SinkChannelFilter,
22
+ open_mcap,
23
+ )
16
24
  from .channel import Channel, log
17
25
 
18
26
  # Deprecated. Use foxglove.mcap.MCAPWriter instead.
19
27
  from .mcap import MCAPWriter
20
- from .websocket import (
21
- AssetHandler,
22
- Capability,
23
- ServerListener,
24
- Service,
25
- WebSocketServer,
26
- )
28
+
29
+ if TYPE_CHECKING:
30
+ from .notebook.notebook_buffer import NotebookBuffer
27
31
 
28
32
  atexit.register(_foxglove.shutdown)
29
33
 
30
34
 
31
- def start_server(
32
- *,
33
- name: Optional[str] = None,
34
- host: Optional[str] = "127.0.0.1",
35
- port: Optional[int] = 8765,
36
- capabilities: Optional[List[Capability]] = None,
37
- server_listener: Optional[ServerListener] = None,
38
- supported_encodings: Optional[List[str]] = None,
39
- services: Optional[List[Service]] = None,
40
- asset_handler: Optional[AssetHandler] = None,
41
- context: Optional[Context] = None,
42
- ) -> WebSocketServer:
43
- """
44
- Start a websocket server for live visualization.
45
-
46
- :param name: The name of the server.
47
- :param host: The host to bind to.
48
- :param port: The port to bind to.
49
- :param capabilities: A list of capabilities to advertise to clients.
50
- :param server_listener: A Python object that implements the :py:class:`websocket.ServerListener`
51
- protocol.
52
- :param supported_encodings: A list of encodings to advertise to clients.
53
- :param services: A list of services to advertise to clients.
54
- :param asset_handler: A callback function that returns the asset for a given URI, or None if
55
- it doesn't exist.
56
- :param context: The context to use for logging. If None, the global context is used.
57
- """
58
- return _foxglove.start_server(
59
- name=name,
60
- host=host,
61
- port=port,
62
- capabilities=capabilities,
63
- server_listener=server_listener,
64
- supported_encodings=supported_encodings,
65
- services=services,
66
- asset_handler=asset_handler,
67
- context=context,
35
+ try:
36
+ from ._foxglove_py.cloud import CloudSink
37
+
38
+ # from ._foxglove_py.cloud import start_cloud_sink as _start_cloud_sink
39
+ from .cloud import CloudSinkListener
40
+ from .websocket import (
41
+ AssetHandler,
42
+ Capability,
43
+ ServerListener,
44
+ Service,
45
+ WebSocketServer,
68
46
  )
69
47
 
48
+ def start_server(
49
+ *,
50
+ name: str | None = None,
51
+ host: str | None = "127.0.0.1",
52
+ port: int | None = 8765,
53
+ capabilities: list[Capability] | None = None,
54
+ server_listener: ServerListener | None = None,
55
+ supported_encodings: list[str] | None = None,
56
+ services: list[Service] | None = None,
57
+ asset_handler: AssetHandler | None = None,
58
+ context: Context | None = None,
59
+ session_id: str | None = None,
60
+ channel_filter: SinkChannelFilter | None = None,
61
+ ) -> WebSocketServer:
62
+ """
63
+ Start a websocket server for live visualization.
64
+
65
+ :param name: The name of the server.
66
+ :param host: The host to bind to.
67
+ :param port: The port to bind to.
68
+ :param capabilities: A list of capabilities to advertise to clients.
69
+ :param server_listener: A Python object that implements the
70
+ :py:class:`websocket.ServerListener` protocol.
71
+ :param supported_encodings: A list of encodings to advertise to clients.
72
+ :param services: A list of services to advertise to clients.
73
+ :param asset_handler: A callback function that returns the asset for a given URI, or None if
74
+ it doesn't exist.
75
+ :param context: The context to use for logging. If None, the global context is used.
76
+ :param session_id: An ID which allows the client to understand if the connection is a
77
+ re-connection or a new server instance. If None, then an ID is generated based on the
78
+ current time.
79
+ :param channel_filter: A `Callable` that determines whether a channel should be logged to.
80
+ Return `True` to log the channel, or `False` to skip it. By default, all channels
81
+ will be logged.
82
+ """
83
+ return _foxglove.start_server(
84
+ name=name,
85
+ host=host,
86
+ port=port,
87
+ capabilities=capabilities,
88
+ server_listener=server_listener,
89
+ supported_encodings=supported_encodings,
90
+ services=services,
91
+ asset_handler=asset_handler,
92
+ context=context,
93
+ session_id=session_id,
94
+ channel_filter=channel_filter,
95
+ )
96
+
97
+ def start_cloud_sink(
98
+ *,
99
+ listener: CloudSinkListener | None = None,
100
+ supported_encodings: list[str] | None = None,
101
+ context: Context | None = None,
102
+ session_id: str | None = None,
103
+ ) -> CloudSink:
104
+ """
105
+ Connect to Foxglove Agent for live visualization and teleop.
106
+
107
+ Foxglove Agent must be running on the same host for this to work.
108
+
109
+ :param capabilities: A list of capabilities to advertise to the agent.
110
+ :param listener: A Python object that implements the
111
+ :py:class:`cloud.CloudSinkListener` protocol.
112
+ :param supported_encodings: A list of encodings to advertise to the agent.
113
+ :param context: The context to use for logging. If None, the global context is used.
114
+ :param session_id: An ID which allows the agent to understand if the connection is a
115
+ re-connection or a new connection instance. If None, then an ID is generated based on
116
+ the current time.
117
+ """
118
+ return _foxglove.start_cloud_sink(
119
+ listener=listener,
120
+ supported_encodings=supported_encodings,
121
+ context=context,
122
+ session_id=session_id,
123
+ )
70
124
 
71
- def set_log_level(level: Union[int, str] = "INFO") -> None:
125
+ except ImportError:
126
+ pass
127
+
128
+
129
+ def set_log_level(level: int | str = "INFO") -> None:
72
130
  """
73
131
  Enable SDK logging.
74
132
 
@@ -112,13 +170,72 @@ def _level_names() -> dict[str, int]:
112
170
  }
113
171
 
114
172
 
173
+ def init_notebook_buffer(context: Context | None = None) -> NotebookBuffer:
174
+ """
175
+ Create a NotebookBuffer object to manage data buffering and visualization in Jupyter
176
+ notebooks.
177
+
178
+ The NotebookBuffer object will buffer all data logged to the provided context. When you
179
+ are ready to visualize the data, you can call the :meth:`show` method to display an embedded
180
+ Foxglove visualization widget. The widget provides a fully-featured Foxglove interface
181
+ directly within your Jupyter notebook, allowing you to explore multi-modal robotics data
182
+ including 3D scenes, plots, images, and more.
183
+
184
+ Args:
185
+ context: The Context used to log the messages. If no Context is provided, the global
186
+ context will be used. Logged messages will be buffered.
187
+
188
+ Returns:
189
+ NotebookBuffer: A NotebookBuffer object that can be used to manage the data buffering
190
+ and visualization.
191
+
192
+ Raises:
193
+ Exception: If the notebook extra package is not installed. Install it
194
+ with `pip install foxglove-sdk[notebook]`.
195
+
196
+ Note:
197
+ This function is only available when the `notebook` extra package
198
+ is installed. Install it with `pip install foxglove-sdk[notebook]`.
199
+
200
+ Example:
201
+ >>> import foxglove
202
+ >>>
203
+ >>> # Create a basic viewer using the default context
204
+ >>> nb_buffer = foxglove.init_notebook_buffer()
205
+ >>>
206
+ >>> # Or use a specific context
207
+ >>> nb_buffer = foxglove.init_notebook_buffer(context=my_ctx)
208
+ >>>
209
+ >>> # ... log data as usual ...
210
+ >>>
211
+ >>> # Display the widget in the notebook
212
+ >>> nb_buffer.show()
213
+ """
214
+ try:
215
+ from .notebook.notebook_buffer import NotebookBuffer
216
+
217
+ except ImportError:
218
+ raise Exception(
219
+ "NotebookBuffer is not installed. "
220
+ 'Please install it with `pip install "foxglove-sdk[notebook]"`'
221
+ )
222
+
223
+ return NotebookBuffer(context=context)
224
+
225
+
115
226
  __all__ = [
116
227
  "Channel",
228
+ "ChannelDescriptor",
117
229
  "Context",
118
230
  "MCAPWriter",
119
231
  "Schema",
232
+ "SinkChannelFilter",
233
+ "CloudSink",
234
+ "CloudSinkListener",
235
+ "start_cloud_sink",
120
236
  "log",
121
237
  "open_mcap",
122
238
  "set_log_level",
123
239
  "start_server",
240
+ "init_notebook_buffer",
124
241
  ]
@@ -1,21 +1,24 @@
1
1
  from pathlib import Path
2
- from typing import Any, Dict, List, Optional, Tuple
2
+ from typing import Any, Callable
3
3
 
4
+ from foxglove.websocket import AssetHandler
5
+
6
+ from .cloud import CloudSink
4
7
  from .mcap import MCAPWriteOptions, MCAPWriter
5
- from .websocket import AssetHandler, Capability, Service, WebSocketServer
8
+ from .websocket import Capability, Service, WebSocketServer
6
9
 
7
10
  class BaseChannel:
8
11
  """
9
12
  A channel for logging messages.
10
13
  """
11
14
 
12
- def __new__(
13
- cls,
15
+ def __init__(
16
+ self,
14
17
  topic: str,
15
18
  message_encoding: str,
16
- schema: Optional["Schema"] = None,
17
- metadata: Optional[List[Tuple[str, str]]] = None,
18
- ) -> "BaseChannel": ...
19
+ schema: "Schema" | None = None,
20
+ metadata: dict[str, str] | None = None,
21
+ ) -> None: ...
19
22
  def id(self) -> int:
20
23
  """The unique ID of the channel"""
21
24
  ...
@@ -29,7 +32,7 @@ class BaseChannel:
29
32
  """The message encoding for the channel"""
30
33
  ...
31
34
 
32
- def metadata(self) -> Dict[str, str]:
35
+ def metadata(self) -> dict[str, str]:
33
36
  """
34
37
  Returns a copy of the channel's metadata.
35
38
 
@@ -38,7 +41,7 @@ class BaseChannel:
38
41
  """
39
42
  ...
40
43
 
41
- def schema(self) -> Optional["Schema"]:
44
+ def schema(self) -> "Schema" | None:
42
45
  """
43
46
  Returns a copy of the channel's schema.
44
47
 
@@ -47,7 +50,7 @@ class BaseChannel:
47
50
  """
48
51
  ...
49
52
 
50
- def schema_name(self) -> Optional[str]:
53
+ def schema_name(self) -> str | None:
51
54
  """The name of the schema for the channel"""
52
55
  ...
53
56
 
@@ -58,13 +61,16 @@ class BaseChannel:
58
61
  def log(
59
62
  self,
60
63
  msg: bytes,
61
- log_time: Optional[int] = None,
64
+ log_time: int | None = None,
65
+ sink_id: int | None = None,
62
66
  ) -> None:
63
67
  """
64
68
  Log a message to the channel.
65
69
 
66
70
  :param msg: The message to log.
67
71
  :param log_time: The optional time the message was logged.
72
+ :param sink_id: The sink ID to log the message to. If not provided, the message will be
73
+ sent to all sinks.
68
74
  """
69
75
  ...
70
76
 
@@ -79,13 +85,13 @@ class Schema:
79
85
  encoding: str
80
86
  data: bytes
81
87
 
82
- def __new__(
83
- cls,
88
+ def __init__(
89
+ self,
84
90
  *,
85
91
  name: str,
86
92
  encoding: str,
87
93
  data: bytes,
88
- ) -> "Schema": ...
94
+ ) -> None: ...
89
95
 
90
96
  class Context:
91
97
  """
@@ -97,36 +103,70 @@ class Context:
97
103
  channel constructor and to :py:func:`open_mcap` or :py:func:`start_server`.
98
104
  """
99
105
 
100
- def __new__(cls) -> "Context": ...
106
+ def __init__(self) -> None: ...
101
107
  def _create_channel(
102
108
  self,
103
109
  topic: str,
104
110
  message_encoding: str,
105
- schema: Optional["Schema"] = None,
106
- metadata: Optional[List[Tuple[str, str]]] = None,
111
+ schema: Schema | None = None,
112
+ metadata: list[tuple[str, str]] | None = None,
107
113
  ) -> "BaseChannel":
108
114
  """
109
115
  Instead of calling this method, pass a context to a channel constructor.
110
116
  """
111
117
  ...
112
118
 
119
+ @staticmethod
120
+ def default() -> "Context":
121
+ """
122
+ Returns the default context.
123
+ """
124
+ ...
125
+
126
+ class ChannelDescriptor:
127
+ """
128
+ Information about a channel
129
+ """
130
+
131
+ id: int
132
+ topic: str
133
+ message_encoding: str
134
+ metadata: dict[str, str]
135
+ schema: "Schema" | None
136
+
137
+ SinkChannelFilter = Callable[[ChannelDescriptor], bool]
138
+
113
139
  def start_server(
114
140
  *,
115
- name: Optional[str] = None,
116
- host: Optional[str] = "127.0.0.1",
117
- port: Optional[int] = 8765,
118
- capabilities: Optional[List[Capability]] = None,
141
+ name: str | None = None,
142
+ host: str | None = "127.0.0.1",
143
+ port: int | None = 8765,
144
+ capabilities: list[Capability] | None = None,
119
145
  server_listener: Any = None,
120
- supported_encodings: Optional[List[str]] = None,
121
- services: Optional[List["Service"]] = None,
122
- asset_handler: Optional["AssetHandler"] = None,
123
- context: Optional["Context"] = None,
146
+ supported_encodings: list[str] | None = None,
147
+ services: list[Service] | None = None,
148
+ asset_handler: AssetHandler | None = None,
149
+ context: Context | None = None,
150
+ session_id: str | None = None,
151
+ channel_filter: SinkChannelFilter | None = None,
124
152
  ) -> WebSocketServer:
125
153
  """
126
154
  Start a websocket server for live visualization.
127
155
  """
128
156
  ...
129
157
 
158
+ def start_cloud_sink(
159
+ *,
160
+ listener: Any = None,
161
+ supported_encodings: list[str] | None = None,
162
+ context: Context | None = None,
163
+ session_id: str | None = None,
164
+ ) -> CloudSink:
165
+ """
166
+ Connect to Foxglove Agent for remote visualization and teleop.
167
+ """
168
+ ...
169
+
130
170
  def enable_logging(level: int) -> None:
131
171
  """
132
172
  Forward SDK logs to python's logging facility.
@@ -149,18 +189,21 @@ def open_mcap(
149
189
  path: str | Path,
150
190
  *,
151
191
  allow_overwrite: bool = False,
152
- context: Optional["Context"] = None,
153
- writer_options: Optional[MCAPWriteOptions] = None,
192
+ context: Context | None = None,
193
+ channel_filter: SinkChannelFilter | None = None,
194
+ writer_options: MCAPWriteOptions | None = None,
154
195
  ) -> MCAPWriter:
155
196
  """
156
197
  Creates a new MCAP file for recording.
157
198
 
158
199
  If a context is provided, the MCAP file will be associated with that context. Otherwise, the
159
200
  global context will be used.
201
+
202
+ You must close the writer with close() or the with statement to ensure the file is correctly finished.
160
203
  """
161
204
  ...
162
205
 
163
- def get_channel_for_topic(topic: str) -> Optional[BaseChannel]:
206
+ def get_channel_for_topic(topic: str) -> BaseChannel | None:
164
207
  """
165
208
  Get a previously-registered channel.
166
209
  """