diff --git a/docs/conf.py b/docs/conf.py index 80794e562..5e5d3232d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -42,6 +42,7 @@ # Autodoc settings autodoc_default_options = { "members": None, + "member-order": "bysource", } autodoc_typehints = "description" diff --git a/docs/sdk/async/devbox.rst b/docs/sdk/async/devbox.rst index 35bc9ed58..3698daa17 100644 --- a/docs/sdk/async/devbox.rst +++ b/docs/sdk/async/devbox.rst @@ -4,8 +4,4 @@ Devbox The ``AsyncDevbox`` class provides asynchronous methods for managing and interacting with a devbox instance. .. automodule:: runloop_api_client.sdk.async_devbox - :members: - -.. automodule:: runloop_api_client.sdk.protocols - :members: AsyncCommandInterface, AsyncFileInterface, AsyncNetworkInterface - :undoc-members: \ No newline at end of file + :members: \ No newline at end of file diff --git a/docs/sdk/sync/devbox.rst b/docs/sdk/sync/devbox.rst index 924f96062..19875fc8c 100644 --- a/docs/sdk/sync/devbox.rst +++ b/docs/sdk/sync/devbox.rst @@ -4,8 +4,4 @@ Devbox The ``Devbox`` class provides synchronous methods for managing and interacting with a devbox instance. .. automodule:: runloop_api_client.sdk.devbox - :members: - -.. automodule:: runloop_api_client.sdk.protocols - :members: CommandInterface, FileInterface, NetworkInterface - :undoc-members: \ No newline at end of file + :members: \ No newline at end of file diff --git a/docs/sdk/types.rst b/docs/sdk/types.rst index dabe6680d..4de60fb5e 100644 --- a/docs/sdk/types.rst +++ b/docs/sdk/types.rst @@ -102,8 +102,10 @@ Base API Type Reference :members: :undoc-members: :imported-members: + :member-order: groupwise .. automodule:: runloop_api_client.types :members: :undoc-members: - :imported-members: \ No newline at end of file + :imported-members: + :member-order: groupwise \ No newline at end of file diff --git a/src/runloop_api_client/sdk/async_devbox.py b/src/runloop_api_client/sdk/async_devbox.py index c1794ccf5..1a049b170 100644 --- a/src/runloop_api_client/sdk/async_devbox.py +++ b/src/runloop_api_client/sdk/async_devbox.py @@ -32,7 +32,6 @@ ) from .._client import AsyncRunloop from ._helpers import filter_params -from .protocols import AsyncFileInterface, AsyncCommandInterface, AsyncNetworkInterface from .._streaming import AsyncStream from ..lib.polling import PollingConfig from .async_execution import AsyncExecution, _AsyncStreamingGroup @@ -259,7 +258,7 @@ def cmd(self) -> AsyncCommandInterface: :return: Helper for running shell commands :rtype: AsyncCommandInterface """ - return _AsyncCommandInterface(self) + return AsyncCommandInterface(self) @property def file(self) -> AsyncFileInterface: @@ -268,7 +267,7 @@ def file(self) -> AsyncFileInterface: :return: Helper for reading/writing files :rtype: AsyncFileInterface """ - return _AsyncFileInterface(self) + return AsyncFileInterface(self) @property def net(self) -> AsyncNetworkInterface: @@ -277,7 +276,7 @@ def net(self) -> AsyncNetworkInterface: :return: Helper for SSH keys and tunnels :rtype: AsyncNetworkInterface """ - return _AsyncNetworkInterface(self) + return AsyncNetworkInterface(self) # ------------------------------------------------------------------ # # Internal helpers @@ -357,7 +356,7 @@ async def _stream_worker( logger.exception("error streaming %s logs for devbox %s", name, self._id) -class _AsyncCommandInterface: +class AsyncCommandInterface: """Interface for executing commands on a devbox. Accessed via devbox.cmd property. Provides exec() for synchronous execution @@ -457,7 +456,7 @@ async def exec_async( return AsyncExecution(client, devbox.id, execution, streaming_group) -class _AsyncFileInterface: +class AsyncFileInterface: """Interface for file operations on a devbox. Accessed via devbox.file property. Provides coroutines for reading, writing, @@ -547,7 +546,7 @@ async def upload( ) -class _AsyncNetworkInterface: +class AsyncNetworkInterface: """Interface for networking operations on a devbox. Accessed via devbox.net property. Provides coroutines for SSH access and tunneling. diff --git a/src/runloop_api_client/sdk/devbox.py b/src/runloop_api_client/sdk/devbox.py index 3643afc78..356079a9d 100644 --- a/src/runloop_api_client/sdk/devbox.py +++ b/src/runloop_api_client/sdk/devbox.py @@ -34,7 +34,6 @@ from .._client import Runloop from ._helpers import filter_params from .execution import Execution, _StreamingGroup -from .protocols import FileInterface, CommandInterface, NetworkInterface from .._streaming import Stream from ..lib.polling import PollingConfig from .execution_result import ExecutionResult @@ -261,7 +260,7 @@ def cmd(self) -> CommandInterface: :return: Helper for running shell commands :rtype: CommandInterface """ - return _CommandInterface(self) + return CommandInterface(self) @property def file(self) -> FileInterface: @@ -270,7 +269,7 @@ def file(self) -> FileInterface: :return: Helper for reading/writing files :rtype: FileInterface """ - return _FileInterface(self) + return FileInterface(self) @property def net(self) -> NetworkInterface: @@ -279,7 +278,7 @@ def net(self) -> NetworkInterface: :return: Helper for SSH keys and tunnels :rtype: NetworkInterface """ - return _NetworkInterface(self) + return NetworkInterface(self) # --------------------------------------------------------------------- # # Internal helpers @@ -375,7 +374,7 @@ def worker() -> None: return thread -class _CommandInterface: +class CommandInterface: """Interface for executing commands on a devbox. Accessed via devbox.cmd property. Provides exec() for synchronous execution @@ -465,7 +464,7 @@ def exec_async( return Execution(client, devbox.id, execution, streaming_group) -class _FileInterface: +class FileInterface: """Interface for file operations on a devbox. Accessed via devbox.file property. Provides methods for reading, writing, @@ -555,7 +554,7 @@ def upload( ) -class _NetworkInterface: +class NetworkInterface: """Interface for network operations on a devbox. Accessed via devbox.net property. Provides methods for SSH access and tunneling. diff --git a/src/runloop_api_client/sdk/protocols.py b/src/runloop_api_client/sdk/protocols.py deleted file mode 100644 index af31e49f4..000000000 --- a/src/runloop_api_client/sdk/protocols.py +++ /dev/null @@ -1,201 +0,0 @@ -"""Public protocol interfaces for SDK components. - -This module defines Protocol interfaces that provide clean type hints for SDK -interface classes without exposing private implementation details in documentation. -""" - -from __future__ import annotations - -from typing import Protocol -from typing_extensions import Unpack, runtime_checkable - -from ..types import DevboxTunnelView, DevboxExecutionDetailView, DevboxCreateSSHKeyResponse -from ._types import ( - LongRequestOptions, - SDKDevboxExecuteParams, - SDKDevboxUploadFileParams, - SDKDevboxCreateTunnelParams, - SDKDevboxDownloadFileParams, - SDKDevboxExecuteAsyncParams, - SDKDevboxRemoveTunnelParams, - SDKDevboxReadFileContentsParams, - SDKDevboxWriteFileContentsParams, -) -from .execution import Execution -from .async_execution import AsyncExecution -from .execution_result import ExecutionResult -from .async_execution_result import AsyncExecutionResult - -# ============================================================================== -# Synchronous Interfaces -# ============================================================================== - - -@runtime_checkable -class CommandInterface(Protocol): - """Interface for executing commands on a devbox. - - Accessed via `devbox.cmd` property. Provides `exec()` for synchronous execution - and `exec_async()` for asynchronous process management. - - Important: All streaming callbacks (stdout, stderr, output) must be synchronous - functions, not async functions. - """ - - def exec( - self, - **params: Unpack[SDKDevboxExecuteParams], - ) -> "ExecutionResult": ... - - def exec_async( - self, - **params: Unpack[SDKDevboxExecuteAsyncParams], - ) -> "Execution": ... - - -@runtime_checkable -class FileInterface(Protocol): - """Interface for file operations on a devbox. - - Accessed via `devbox.file` property. Provides methods for reading, writing, - uploading, and downloading files. - """ - - def read( - self, - **params: Unpack[SDKDevboxReadFileContentsParams], - ) -> str: ... - - def write( - self, - **params: Unpack[SDKDevboxWriteFileContentsParams], - ) -> DevboxExecutionDetailView: ... - - def download( - self, - **params: Unpack[SDKDevboxDownloadFileParams], - ) -> bytes: ... - - def upload( - self, - **params: Unpack[SDKDevboxUploadFileParams], - ) -> object: ... - - -@runtime_checkable -class NetworkInterface(Protocol): - """Interface for network operations on a devbox. - - Accessed via `devbox.net` property. Provides methods for managing SSH keys - and network tunnels. - """ - - def create_ssh_key( - self, - **params: Unpack[LongRequestOptions], - ) -> DevboxCreateSSHKeyResponse: ... - - def create_tunnel( - self, - **params: Unpack[SDKDevboxCreateTunnelParams], - ) -> DevboxTunnelView: ... - - def remove_tunnel( - self, - **params: Unpack[SDKDevboxRemoveTunnelParams], - ) -> object: ... - - -# ============================================================================== -# Asynchronous Interfaces -# ============================================================================== - - -@runtime_checkable -class AsyncCommandInterface(Protocol): - """Async interface for executing commands on a devbox. - - Accessed via `devbox.cmd` property. Provides `exec()` and `exec_async()` for - command execution with async/await support. - - Important: All streaming callbacks (stdout, stderr, output) must be synchronous - functions, not async functions. The devbox operations are async, but the callbacks - themselves are called synchronously. - - Examples: - >>> # Async execution (waits for completion) - >>> result = await devbox.cmd.exec(command="ls -la") - >>> print(await result.stdout()) - - >>> # Async non-blocking execution - >>> execution = await devbox.cmd.exec_async(command="npm run dev") - >>> result = await execution.result() # Waits for completion - - >>> # Callbacks must still be synchronous! - >>> def stdout_callback(line: str) -> None: # Not async! - ... print(f">> {line}") - >>> await devbox.cmd.exec(command="tail -f /var/log/app.log", stdout=stdout_callback) - """ - - async def exec( - self, - **params: Unpack[SDKDevboxExecuteParams], - ) -> "AsyncExecutionResult": ... - - async def exec_async( - self, - **params: Unpack[SDKDevboxExecuteAsyncParams], - ) -> "AsyncExecution": ... - - -@runtime_checkable -class AsyncFileInterface(Protocol): - """Async interface for file operations on a devbox. - - Accessed via `devbox.file` property. Provides async methods for reading, writing, - uploading, and downloading files. - """ - - async def read( - self, - **params: Unpack[SDKDevboxReadFileContentsParams], - ) -> str: ... - - async def write( - self, - **params: Unpack[SDKDevboxWriteFileContentsParams], - ) -> DevboxExecutionDetailView: ... - - async def download( - self, - **params: Unpack[SDKDevboxDownloadFileParams], - ) -> bytes: ... - - async def upload( - self, - **params: Unpack[SDKDevboxUploadFileParams], - ) -> object: ... - - -@runtime_checkable -class AsyncNetworkInterface(Protocol): - """Async interface for network operations on a devbox. - - Accessed via `devbox.net` property. Provides async methods for managing SSH keys - and network tunnels. - """ - - async def create_ssh_key( - self, - **params: Unpack[LongRequestOptions], - ) -> DevboxCreateSSHKeyResponse: ... - - async def create_tunnel( - self, - **params: Unpack[SDKDevboxCreateTunnelParams], - ) -> DevboxTunnelView: ... - - async def remove_tunnel( - self, - **params: Unpack[SDKDevboxRemoveTunnelParams], - ) -> object: ... diff --git a/tests/sdk/async_devbox/test_core.py b/tests/sdk/async_devbox/test_core.py index 60dcf7fdc..5d3405c80 100644 --- a/tests/sdk/async_devbox/test_core.py +++ b/tests/sdk/async_devbox/test_core.py @@ -15,9 +15,9 @@ from runloop_api_client.sdk import AsyncDevbox from runloop_api_client.lib.polling import PollingConfig from runloop_api_client.sdk.async_devbox import ( - _AsyncFileInterface, - _AsyncCommandInterface, - _AsyncNetworkInterface, + AsyncFileInterface, + AsyncCommandInterface, + AsyncNetworkInterface, ) @@ -276,19 +276,19 @@ def test_cmd_property(self, mock_async_client: AsyncMock) -> None: """Test cmd property returns AsyncCommandInterface.""" devbox = AsyncDevbox(mock_async_client, "dev_123") cmd = devbox.cmd - assert isinstance(cmd, _AsyncCommandInterface) + assert isinstance(cmd, AsyncCommandInterface) assert cmd._devbox is devbox def test_file_property(self, mock_async_client: AsyncMock) -> None: """Test file property returns AsyncFileInterface.""" devbox = AsyncDevbox(mock_async_client, "dev_123") file_interface = devbox.file - assert isinstance(file_interface, _AsyncFileInterface) + assert isinstance(file_interface, AsyncFileInterface) assert file_interface._devbox is devbox def test_net_property(self, mock_async_client: AsyncMock) -> None: """Test net property returns AsyncNetworkInterface.""" devbox = AsyncDevbox(mock_async_client, "dev_123") net = devbox.net - assert isinstance(net, _AsyncNetworkInterface) + assert isinstance(net, AsyncNetworkInterface) assert net._devbox is devbox diff --git a/tests/sdk/devbox/test_core.py b/tests/sdk/devbox/test_core.py index 4bebd823c..b482e030b 100644 --- a/tests/sdk/devbox/test_core.py +++ b/tests/sdk/devbox/test_core.py @@ -17,9 +17,9 @@ from runloop_api_client.sdk import Devbox from runloop_api_client._types import omit from runloop_api_client.sdk.devbox import ( - _FileInterface, - _CommandInterface, - _NetworkInterface, + FileInterface, + CommandInterface, + NetworkInterface, ) from runloop_api_client.lib.polling import PollingConfig @@ -280,19 +280,19 @@ def test_cmd_property(self, mock_client: Mock) -> None: """Test cmd property returns CommandInterface.""" devbox = Devbox(mock_client, "dev_123") cmd = devbox.cmd - assert isinstance(cmd, _CommandInterface) + assert isinstance(cmd, CommandInterface) assert cmd._devbox is devbox def test_file_property(self, mock_client: Mock) -> None: """Test file property returns FileInterface.""" devbox = Devbox(mock_client, "dev_123") file_interface = devbox.file - assert isinstance(file_interface, _FileInterface) + assert isinstance(file_interface, FileInterface) assert file_interface._devbox is devbox def test_net_property(self, mock_client: Mock) -> None: """Test net property returns NetworkInterface.""" devbox = Devbox(mock_client, "dev_123") net = devbox.net - assert isinstance(net, _NetworkInterface) + assert isinstance(net, NetworkInterface) assert net._devbox is devbox