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