Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion crates/bashkit-python/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ doc = false # Python extension, no Rust docs needed

[dependencies]
# Bashkit core
bashkit = { path = "../bashkit", features = ["scripted_tool"] }
bashkit = { path = "../bashkit", features = ["scripted_tool", "python"] }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The built wheel is ~3.5 MB with the python feature enabled. I think the size/build-time impact is small enough that it is not worth the added complexity of a conditional Cargo feature, but happy to revisit if the maintainer feels differently.


# PyO3 native extension
pyo3 = { workspace = true }
Expand All @@ -28,3 +28,6 @@ tokio = { workspace = true, features = ["rt-multi-thread"] }

# Serialization
serde_json = { workspace = true }

# Big-integer support for py_to_monty BigInt extraction
num-bigint = "^0.4.6" # must be compatible with the version pulled in by bashkit core
29 changes: 27 additions & 2 deletions crates/bashkit-python/bashkit/_bashkit.pyi
Original file line number Diff line number Diff line change
@@ -1,18 +1,38 @@
"""Type stubs for bashkit native module."""

from typing import Any, Callable
from collections.abc import Callable
from typing import Any, Protocol

class ExternalHandler(Protocol):
"""Protocol for the external function handler passed to Bash.

Called when Monty Python code invokes a registered external function.
Must be an async callable with this exact signature.
"""

async def __call__(self, fn_name: str, args: list[Any], kwargs: dict[str, Any]) -> Any: ...

class Bash:
"""Core bash interpreter with virtual filesystem.

State persists between calls — files created in one execute() are
available in subsequent calls.

Example:
Example (basic):
>>> bash = Bash()
>>> result = await bash.execute("echo 'Hello!'")
>>> print(result.stdout)
Hello!

Example (Python execution with external function handler):
>>> async def handler(fn_name: str, args: list, kwargs: dict) -> Any:
... return await tool_executor.call(fn_name, kwargs)
>>> bash = Bash(
... python=True,
... external_functions=["api_request"],
... external_handler=handler,
... )
>>> result = await bash.execute("python3 -c 'print(api_request(url=\"/data\"))'")
"""

def __init__(
Expand All @@ -21,9 +41,13 @@ class Bash:
hostname: str | None = None,
max_commands: int | None = None,
max_loop_iterations: int | None = None,
python: bool = False,
external_functions: list[str] | None = None,
external_handler: ExternalHandler | None = None,
) -> None: ...
async def execute(self, commands: str) -> ExecResult: ...
def execute_sync(self, commands: str) -> ExecResult: ...
def cancel(self) -> None: ...
def reset(self) -> None: ...
Comment on lines 48 to 51
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added — cancel() was already implemented on BashTool in Rust, just missing from the stub. Drive-by fix.


class ExecResult:
Expand Down Expand Up @@ -64,6 +88,7 @@ class BashTool:
) -> None: ...
async def execute(self, commands: str) -> ExecResult: ...
def execute_sync(self, commands: str) -> ExecResult: ...
def cancel(self) -> None: ...
def description(self) -> str: ...
def help(self) -> str: ...
def system_prompt(self) -> str: ...
Expand Down
Loading
Loading