boxlite 0.1.0__cp311-cp311-macosx_11_0_arm64.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 boxlite might be problematic. Click here for more details.
- boxlite/.dylibs/libkrun.1.15.1.dylib +0 -0
- boxlite/.dylibs/libkrunfw.4.dylib +0 -0
- boxlite/__init__.py +25 -0
- boxlite/bin/boxlite-guest +0 -0
- boxlite/bin/boxlite-runner +0 -0
- boxlite/boxes/__init__.py +11 -0
- boxlite/boxes/codebox.py +126 -0
- boxlite/boxlite.cpython-311-darwin.so +0 -0
- boxlite/core/__init__.py +7 -0
- boxlite/core/box.py +93 -0
- boxlite-0.1.0.dist-info/METADATA +6 -0
- boxlite-0.1.0.dist-info/RECORD +13 -0
- boxlite-0.1.0.dist-info/WHEEL +4 -0
|
Binary file
|
|
Binary file
|
boxlite/__init__.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
BoxLite - Lightweight, secure containerization for any environment.
|
|
3
|
+
|
|
4
|
+
Following SQLite philosophy: "BoxLite" for branding, "boxlite" for code APIs.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
# Set BOXLITE_BIN_DIR if not already set, to help Rust find binaries
|
|
11
|
+
if "BOXLITE_BIN_DIR" not in os.environ:
|
|
12
|
+
# Check if binaries are bundled with the package
|
|
13
|
+
package_bin_dir = Path(__file__).parent / "bin"
|
|
14
|
+
if package_bin_dir.exists():
|
|
15
|
+
os.environ["BOXLITE_BIN_DIR"] = str(package_bin_dir)
|
|
16
|
+
|
|
17
|
+
from .core.box import Box
|
|
18
|
+
from .boxes import CodeBox
|
|
19
|
+
|
|
20
|
+
# Future specialized containers
|
|
21
|
+
# from .boxes.browser import BrowserBox
|
|
22
|
+
# from .boxes.claude_code import ClaudeCodeBox
|
|
23
|
+
|
|
24
|
+
__version__ = "0.1.0"
|
|
25
|
+
__all__ = ["Box", "CodeBox"]
|
|
Binary file
|
|
Binary file
|
boxlite/boxes/codebox.py
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CodeBox - Secure Python code execution container.
|
|
3
|
+
|
|
4
|
+
Provides a simple, secure environment for running untrusted Python code.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Optional, Dict, Any, Union
|
|
8
|
+
from ..core.box import Box
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CodeBox(Box):
|
|
12
|
+
"""
|
|
13
|
+
Secure container for executing Python code.
|
|
14
|
+
|
|
15
|
+
CodeBox provides an isolated environment for running untrusted Python code
|
|
16
|
+
with built-in safety and result formatting.
|
|
17
|
+
|
|
18
|
+
Example:
|
|
19
|
+
>>> with CodeBox() as codebox:
|
|
20
|
+
... result = codebox.run("print('Hello, World!')")
|
|
21
|
+
... print(result)
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
image: str = "python:slim",
|
|
27
|
+
memory_mib: Optional[int] = None,
|
|
28
|
+
cpus: Optional[int] = None,
|
|
29
|
+
**kwargs
|
|
30
|
+
):
|
|
31
|
+
"""
|
|
32
|
+
Create a new CodeBox for Python code execution.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
image: Container image with Python (default: python:slim)
|
|
36
|
+
memory_mib: Memory limit in MiB (default: system default)
|
|
37
|
+
cpus: Number of CPU cores (default: system default)
|
|
38
|
+
**kwargs: Additional Box configuration options
|
|
39
|
+
"""
|
|
40
|
+
# Set up Python-specific defaults
|
|
41
|
+
config = {
|
|
42
|
+
'engine': kwargs.get('engine', 'libkrun'),
|
|
43
|
+
'memory_mib': memory_mib,
|
|
44
|
+
'cpus': cpus,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
# Filter out None values
|
|
48
|
+
config = {k: v for k, v in config.items() if v is not None}
|
|
49
|
+
|
|
50
|
+
# Merge with any additional kwargs
|
|
51
|
+
config.update({k: v for k, v in kwargs.items() if k not in config})
|
|
52
|
+
|
|
53
|
+
# Initialize the base Box with Python image
|
|
54
|
+
super().__init__(image=image, **config)
|
|
55
|
+
|
|
56
|
+
async def run(self, code: str, timeout: Optional[int] = None) -> Union[str, Dict[str, Any]]:
|
|
57
|
+
"""
|
|
58
|
+
Execute Python code in the secure container.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
code: Python code to execute
|
|
62
|
+
timeout: Execution timeout in seconds (not yet implemented)
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
Execution output as a string
|
|
66
|
+
|
|
67
|
+
Example:
|
|
68
|
+
>>> async with CodeBox() as cb:
|
|
69
|
+
... result = await cb.run("print('Hello, World!')")
|
|
70
|
+
... print(result)
|
|
71
|
+
[stdout] Hello, World!
|
|
72
|
+
|
|
73
|
+
Note:
|
|
74
|
+
Uses python3 from the container image.
|
|
75
|
+
For custom Python paths, use execute() directly:
|
|
76
|
+
await cb.execute("/path/to/python", "-c", code)
|
|
77
|
+
"""
|
|
78
|
+
# Execute Python code using python3 -c
|
|
79
|
+
return await self.execute("/usr/local/bin/python", "-c", code)
|
|
80
|
+
|
|
81
|
+
async def run_script(self, script_path: str) -> str:
|
|
82
|
+
"""
|
|
83
|
+
Execute a Python script file in the container.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
script_path: Path to the Python script on the host
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
Execution output as a string
|
|
90
|
+
"""
|
|
91
|
+
with open(script_path, 'r') as f:
|
|
92
|
+
code = f.read()
|
|
93
|
+
return await self.run(code)
|
|
94
|
+
|
|
95
|
+
async def install_package(self, package: str) -> str:
|
|
96
|
+
"""
|
|
97
|
+
Install a Python package in the container using pip.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
package: Package name (e.g., 'requests', 'numpy==1.24.0')
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Installation output
|
|
104
|
+
|
|
105
|
+
Example:
|
|
106
|
+
>>> async with CodeBox() as cb:
|
|
107
|
+
... await cb.install_package("requests")
|
|
108
|
+
... result = await cb.run("import requests; print(requests.__version__)")
|
|
109
|
+
"""
|
|
110
|
+
return await self.execute("pip", "install", package)
|
|
111
|
+
|
|
112
|
+
async def install_packages(self, *packages: str) -> str:
|
|
113
|
+
"""
|
|
114
|
+
Install multiple Python packages.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
*packages: Package names to install
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
Installation output
|
|
121
|
+
|
|
122
|
+
Example:
|
|
123
|
+
>>> async with CodeBox() as cb:
|
|
124
|
+
... await cb.install_packages("requests", "numpy", "pandas")
|
|
125
|
+
"""
|
|
126
|
+
return await self.execute("pip", "install", *packages)
|
|
Binary file
|
boxlite/core/__init__.py
ADDED
boxlite/core/box.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base Box class for BoxLite containers.
|
|
3
|
+
|
|
4
|
+
This is the main user-facing API that wraps the Rust extension.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Optional, Dict, Any, List, Tuple
|
|
8
|
+
import sys
|
|
9
|
+
import os
|
|
10
|
+
import fcntl
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Box:
|
|
14
|
+
"""
|
|
15
|
+
Base class for all BoxLite containers.
|
|
16
|
+
|
|
17
|
+
Provides secure, lightweight containerization using VM technology.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, image: str = "alpine:latest", **kwargs):
|
|
21
|
+
"""
|
|
22
|
+
Create a new BoxLite container.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
image: Container image to use (default: alpine:latest)
|
|
26
|
+
**kwargs: Additional configuration options
|
|
27
|
+
- engine: VM engine to use ('libkrun', 'firecracker')
|
|
28
|
+
- memory_mib: Memory limit in MiB
|
|
29
|
+
- cpus: Number of CPU cores
|
|
30
|
+
- env: Environment variables as list of (key, value) tuples
|
|
31
|
+
"""
|
|
32
|
+
# Import the Rust extension - the native Box class
|
|
33
|
+
try:
|
|
34
|
+
from ..boxlite import Box as NativeBox
|
|
35
|
+
# Use the native implementation directly
|
|
36
|
+
self._native_box = NativeBox({
|
|
37
|
+
'engine': kwargs.get('engine', 'libkrun'),
|
|
38
|
+
'image': image,
|
|
39
|
+
**{k: v for k, v in kwargs.items() if k != 'engine'}
|
|
40
|
+
})
|
|
41
|
+
self._use_native = True
|
|
42
|
+
return
|
|
43
|
+
except ImportError as e:
|
|
44
|
+
raise ImportError(
|
|
45
|
+
f"BoxLite native extension not found: {e}. "
|
|
46
|
+
"Please install with: pip install boxlite"
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
async def __aenter__(self):
|
|
51
|
+
"""Async context manager entry."""
|
|
52
|
+
if self._use_native:
|
|
53
|
+
self._native_box.__enter__()
|
|
54
|
+
# Tokio runtime sets stdout/stderr to non-blocking mode
|
|
55
|
+
# Restore blocking mode to prevent BlockingIOError when printing
|
|
56
|
+
self._restore_blocking_mode()
|
|
57
|
+
return self
|
|
58
|
+
|
|
59
|
+
def _restore_blocking_mode(self):
|
|
60
|
+
"""Restore blocking mode on stdout/stderr after Tokio sets them to non-blocking."""
|
|
61
|
+
for fd in [sys.stdout.fileno(), sys.stderr.fileno()]:
|
|
62
|
+
try:
|
|
63
|
+
flags = fcntl.fcntl(fd, fcntl.F_GETFL)
|
|
64
|
+
if flags & os.O_NONBLOCK:
|
|
65
|
+
fcntl.fcntl(fd, fcntl.F_SETFL, flags & ~os.O_NONBLOCK)
|
|
66
|
+
except (OSError, AttributeError):
|
|
67
|
+
# Ignore errors (e.g., when stdout/stderr is not a real file)
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
71
|
+
"""Async context manager exit."""
|
|
72
|
+
if self._use_native:
|
|
73
|
+
return self._native_box.__exit__(exc_type, exc_val, exc_tb)
|
|
74
|
+
return None
|
|
75
|
+
|
|
76
|
+
async def execute(self, command: str, *args) -> str:
|
|
77
|
+
"""
|
|
78
|
+
Execute a command in the container.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
command: Command to execute
|
|
82
|
+
*args: Command arguments
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
Command output as string
|
|
86
|
+
"""
|
|
87
|
+
if self._use_native:
|
|
88
|
+
result = await self._native_box.run_command(command, list(args) if args else None)
|
|
89
|
+
if isinstance(result, list):
|
|
90
|
+
return '\n'.join(f"[{stream}] {text}" for stream, text in result if text.strip())
|
|
91
|
+
return str(result)
|
|
92
|
+
|
|
93
|
+
raise RuntimeError("Container not initialized")
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
boxlite-0.1.0.dist-info/RECORD,,
|
|
2
|
+
boxlite-0.1.0.dist-info/WHEEL,sha256=dewdenAwp3PDth0u4HpQhcjieEs1_hiwRbm3WvCuoaI,104
|
|
3
|
+
boxlite-0.1.0.dist-info/METADATA,sha256=7-eRZ_Kg6fNA_C3jaVWfJnjKLx1_JRYGkZAQyAEXT6E,156
|
|
4
|
+
boxlite/__init__.py,sha256=uFUVtHBVkjxpx9NxyyRfezBlLDpunNF_J-NCESPg-bg,726
|
|
5
|
+
boxlite/boxlite.cpython-311-darwin.so,sha256=2ersq6_ezNXrYhEgM6n5dR-_vxjWAhtvxRZ9p8x913s,2445664
|
|
6
|
+
boxlite/core/box.py,sha256=XwcevOo0opXWvVbUNL98Ozb7lf13Ucg8pPVrTqBqbzk,3223
|
|
7
|
+
boxlite/core/__init__.py,sha256=gnumx6utojYT12dd4_kGlJmxwMf0Yidnpc3RsLmOFpc,76
|
|
8
|
+
boxlite/bin/boxlite-runner,sha256=Q03LG8X5hDjjiR0fmdUy9Uc7Gv_8CGGaJFkc1LG-G6U,1640672
|
|
9
|
+
boxlite/bin/boxlite-guest,sha256=P8THYIum1PqfB5aDSnw9pnChURGwrUgKi1JSSP4Gjj8,2640464
|
|
10
|
+
boxlite/.dylibs/libkrunfw.4.dylib,sha256=_0skZ63p91-aH5cstSdJIJjAgffWSUjYtOJgOm42Euo,21979472
|
|
11
|
+
boxlite/.dylibs/libkrun.1.15.1.dylib,sha256=SlWoMO0mUm4oQmisLfkaNVmoKZe68KMJeYUgcyZVncc,4176176
|
|
12
|
+
boxlite/boxes/__init__.py,sha256=MYz3-Hgr9Qh2krmyp79amIGn5YovUOspLxZ-7HK7320,223
|
|
13
|
+
boxlite/boxes/codebox.py,sha256=ZBeXNjlRwRN0ywqnqCfqYdHYJmeZA2bqnPDOdeHXR6s,3850
|