foxglove-sdk 0.5.0__cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl → 0.16.3__cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.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,68 +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
17
  from ._foxglove_py import (
16
- MCAPWriter,
18
+ ChannelDescriptor,
19
+ Context,
17
20
  Schema,
21
+ SinkChannelFilter,
18
22
  open_mcap,
19
23
  )
20
24
  from .channel import Channel, log
21
- from .websocket import (
22
- AssetHandler,
23
- Capability,
24
- ServerListener,
25
- Service,
26
- WebSocketServer,
27
- )
25
+
26
+ # Deprecated. Use foxglove.mcap.MCAPWriter instead.
27
+ from .mcap import MCAPWriter
28
+
29
+ if TYPE_CHECKING:
30
+ from .notebook.notebook_buffer import NotebookBuffer
28
31
 
29
32
  atexit.register(_foxglove.shutdown)
30
33
 
31
34
 
32
- def start_server(
33
- *,
34
- name: Optional[str] = None,
35
- host: Optional[str] = "127.0.0.1",
36
- port: Optional[int] = 8765,
37
- capabilities: Optional[List[Capability]] = None,
38
- server_listener: Optional[ServerListener] = None,
39
- supported_encodings: Optional[List[str]] = None,
40
- services: Optional[List[Service]] = None,
41
- asset_handler: Optional[AssetHandler] = 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
- """
57
- return _foxglove.start_server(
58
- name=name,
59
- host=host,
60
- port=port,
61
- capabilities=capabilities,
62
- server_listener=server_listener,
63
- supported_encodings=supported_encodings,
64
- services=services,
65
- asset_handler=asset_handler,
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,
66
46
  )
67
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
+ )
68
128
 
69
- 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:
70
134
  """
71
135
  Enable SDK logging.
72
136
 
@@ -110,12 +174,72 @@ def _level_names() -> dict[str, int]:
110
174
  }
111
175
 
112
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
180
+ notebooks.
181
+
182
+ The NotebookBuffer object will buffer all data logged to the provided context. When you
183
+ are ready to visualize the data, you can call the :meth:`show` method to display an embedded
184
+ Foxglove visualization widget. The widget provides a fully-featured Foxglove interface
185
+ directly within your Jupyter notebook, allowing you to explore multi-modal robotics data
186
+ including 3D scenes, plots, images, and more.
187
+
188
+ Args:
189
+ context: The Context used to log the messages. If no Context is provided, the global
190
+ context will be used. Logged messages will be buffered.
191
+
192
+ Returns:
193
+ NotebookBuffer: A NotebookBuffer object that can be used to manage the data buffering
194
+ and visualization.
195
+
196
+ Raises:
197
+ Exception: If the notebook extra package is not installed. Install it
198
+ with `pip install foxglove-sdk[notebook]`.
199
+
200
+ Note:
201
+ This function is only available when the `notebook` extra package
202
+ is installed. Install it with `pip install foxglove-sdk[notebook]`.
203
+
204
+ Example:
205
+ >>> import foxglove
206
+ >>>
207
+ >>> # Create a basic viewer using the default context
208
+ >>> nb_buffer = foxglove.init_notebook_buffer()
209
+ >>>
210
+ >>> # Or use a specific context
211
+ >>> nb_buffer = foxglove.init_notebook_buffer(context=my_ctx)
212
+ >>>
213
+ >>> # ... log data as usual ...
214
+ >>>
215
+ >>> # Display the widget in the notebook
216
+ >>> nb_buffer.show()
217
+ """
218
+ try:
219
+ from .notebook.notebook_buffer import NotebookBuffer
220
+
221
+ except ImportError:
222
+ raise Exception(
223
+ "NotebookBuffer is not installed. "
224
+ 'Please install it with `pip install "foxglove-sdk[notebook]"`'
225
+ )
226
+
227
+ return NotebookBuffer(context=context)
228
+
229
+
113
230
  __all__ = [
114
231
  "Channel",
232
+ "ChannelDescriptor",
233
+ "Context",
115
234
  "MCAPWriter",
116
235
  "Schema",
236
+ "SinkChannelFilter",
237
+ "CloudSink",
238
+ "CloudSinkListener",
239
+ "start_cloud_sink",
117
240
  "log",
118
241
  "open_mcap",
119
242
  "set_log_level",
120
243
  "start_server",
244
+ "init_notebook_buffer",
121
245
  ]
@@ -1,53 +1,97 @@
1
1
  from pathlib import Path
2
- from typing import Any, List, Optional, Tuple
2
+ from typing import Any, BinaryIO, Callable, Protocol
3
3
 
4
- from .websocket import AssetHandler, Capability, Service, WebSocketServer
4
+ from foxglove.websocket import AssetHandler
5
5
 
6
- class MCAPWriter:
7
- """
8
- A writer for logging messages to an MCAP file.
9
-
10
- Obtain an instance by calling :py:func:`open_mcap`.
11
-
12
- This class may be used as a context manager, in which case the writer will
13
- be closed when you exit the context.
6
+ class McapWritable(Protocol):
7
+ """A writable and seekable file-like object.
14
8
 
15
- If the writer is not closed by the time it is garbage collected, it will be
16
- closed automatically, and any errors will be logged.
9
+ This protocol defines the minimal interface required for writing MCAP data.
17
10
  """
18
11
 
19
- def __new__(cls) -> "MCAPWriter": ...
20
- def __enter__(self) -> "MCAPWriter": ...
21
- def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None: ...
22
- def close(self) -> None:
23
- """
24
- Close the writer explicitly.
12
+ def write(self, data: bytes | bytearray) -> int:
13
+ """Write data and return the number of bytes written."""
14
+ ...
25
15
 
26
- You may call this to explicitly close the writer. Note that the writer
27
- will be automatically closed when it is garbage-collected, or when
28
- exiting the context manager.
29
- """
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."""
30
22
  ...
31
23
 
24
+ from .cloud import CloudSink
25
+ from .mcap import MCAPWriteOptions, MCAPWriter
26
+ from .websocket import Capability, Service, WebSocketServer
27
+
32
28
  class BaseChannel:
33
29
  """
34
30
  A channel for logging messages.
35
31
  """
36
32
 
37
- def __new__(
38
- cls,
33
+ def __init__(
34
+ self,
39
35
  topic: str,
40
36
  message_encoding: str,
41
- schema: Optional["Schema"] = None,
42
- metadata: Optional[List[Tuple[str, str]]] = None,
43
- ) -> "BaseChannel": ...
37
+ schema: "Schema" | None = None,
38
+ metadata: dict[str, str] | None = None,
39
+ ) -> None: ...
40
+ def id(self) -> int:
41
+ """The unique ID of the channel"""
42
+ ...
43
+
44
+ def topic(self) -> str:
45
+ """The topic name of the channel"""
46
+ ...
47
+
48
+ @property
49
+ def message_encoding(self) -> str:
50
+ """The message encoding for the channel"""
51
+ ...
52
+
53
+ def metadata(self) -> dict[str, str]:
54
+ """
55
+ Returns a copy of the channel's metadata.
56
+
57
+ Note that changes made to the returned dictionary will not be applied to
58
+ the channel's metadata.
59
+ """
60
+ ...
61
+
62
+ def schema(self) -> "Schema" | None:
63
+ """
64
+ Returns a copy of the channel's schema.
65
+
66
+ Note that changes made to the returned object will not be applied to
67
+ the channel's schema.
68
+ """
69
+ ...
70
+
71
+ def schema_name(self) -> str | None:
72
+ """The name of the schema for the channel"""
73
+ ...
74
+
75
+ def has_sinks(self) -> bool:
76
+ """Returns true if at least one sink is subscribed to this channel"""
77
+ ...
78
+
44
79
  def log(
45
80
  self,
46
81
  msg: bytes,
47
- publish_time: Optional[int] = None,
48
- log_time: Optional[int] = None,
49
- sequence: Optional[int] = None,
50
- ) -> None: ...
82
+ log_time: int | None = None,
83
+ sink_id: int | None = None,
84
+ ) -> None:
85
+ """
86
+ Log a message to the channel.
87
+
88
+ :param msg: The message to log.
89
+ :param log_time: The optional time the message was logged.
90
+ :param sink_id: The sink ID to log the message to. If not provided, the message will be
91
+ sent to all sinks.
92
+ """
93
+ ...
94
+
51
95
  def close(self) -> None: ...
52
96
 
53
97
  class Schema:
@@ -59,30 +103,89 @@ class Schema:
59
103
  encoding: str
60
104
  data: bytes
61
105
 
62
- def __new__(
63
- cls,
106
+ def __init__(
107
+ self,
64
108
  *,
65
109
  name: str,
66
110
  encoding: str,
67
111
  data: bytes,
68
- ) -> "Schema": ...
112
+ ) -> None: ...
113
+
114
+ class Context:
115
+ """
116
+ A context for logging messages.
117
+
118
+ A context is the binding between channels and sinks. By default, the SDK will use a single
119
+ global context for logging, but you can create multiple contexts in order to log to different
120
+ topics to different sinks or servers. To do so, associate the context by passing it to the
121
+ channel constructor and to :py:func:`open_mcap` or :py:func:`start_server`.
122
+ """
123
+
124
+ def __init__(self) -> None: ...
125
+ def _create_channel(
126
+ self,
127
+ topic: str,
128
+ message_encoding: str,
129
+ schema: Schema | None = None,
130
+ metadata: list[tuple[str, str]] | None = None,
131
+ ) -> "BaseChannel":
132
+ """
133
+ Instead of calling this method, pass a context to a channel constructor.
134
+ """
135
+ ...
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]
69
156
 
70
157
  def start_server(
71
158
  *,
72
- name: Optional[str] = None,
73
- host: Optional[str] = "127.0.0.1",
74
- port: Optional[int] = 8765,
75
- 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,
76
163
  server_listener: Any = None,
77
- supported_encodings: Optional[List[str]] = None,
78
- services: Optional[List["Service"]] = None,
79
- asset_handler: Optional["AssetHandler"] = 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,
80
171
  ) -> WebSocketServer:
81
172
  """
82
173
  Start a websocket server for live visualization.
83
174
  """
84
175
  ...
85
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
+
86
189
  def enable_logging(level: int) -> None:
87
190
  """
88
191
  Forward SDK logs to python's logging facility.
@@ -101,17 +204,29 @@ def shutdown() -> None:
101
204
  """
102
205
  ...
103
206
 
104
- def open_mcap(path: str | Path, allow_overwrite: bool = False) -> MCAPWriter:
207
+ def open_mcap(
208
+ path: str | Path | BinaryIO | McapWritable,
209
+ *,
210
+ allow_overwrite: bool = False,
211
+ context: Context | None = None,
212
+ channel_filter: SinkChannelFilter | None = None,
213
+ writer_options: MCAPWriteOptions | None = None,
214
+ ) -> MCAPWriter:
105
215
  """
106
- 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.
221
+
222
+ If a context is provided, the MCAP file will be associated with that context. Otherwise, the
223
+ global context will be used.
107
224
 
108
- :param path: The path to the MCAP file. This file will be created and must not already exist.
109
- :param allow_overwrite: Set this flag in order to overwrite an existing file at this path.
110
- :rtype: :py:class:`MCAPWriter`
225
+ You must close the writer with close() or the with statement to ensure the file is correctly finished.
111
226
  """
112
227
  ...
113
228
 
114
- def get_channel_for_topic(topic: str) -> BaseChannel:
229
+ def get_channel_for_topic(topic: str) -> BaseChannel | None:
115
230
  """
116
231
  Get a previously-registered channel.
117
232
  """