boxlite 0.1.0.dev0__cp313-cp313-manylinux_2_28_x86_64.whl → 0.1.1.dev0__cp313-cp313-manylinux_2_28_x86_64.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/__init__.py +33 -13
- boxlite/basebox.py +205 -0
- boxlite/bin/boxlite-guest +0 -0
- boxlite/bin/boxlite-runner +0 -0
- boxlite/boxlite.cpython-313-x86_64-linux-gnu.so +0 -0
- boxlite/browserbox.py +104 -0
- boxlite/{boxes/codebox.py → codebox.py} +49 -22
- boxlite-0.1.1.dev0.dist-info/METADATA +9 -0
- boxlite-0.1.1.dev0.dist-info/RECORD +12 -0
- boxlite.libs/{libkrun-a6731198.so.1.15.1 → libkrun-fb5f0f92.so.1.15.1} +0 -0
- boxlite.libs/libkrunfw-6be8cb77.so.4.10.0 +0 -0
- boxlite.libs/libkrunfw.so.4 +0 -0
- boxlite/boxes/__init__.py +0 -11
- boxlite/core/__init__.py +0 -7
- boxlite/core/box.py +0 -93
- boxlite-0.1.0.dev0.dist-info/METADATA +0 -6
- boxlite-0.1.0.dev0.dist-info/RECORD +0 -12
- {boxlite-0.1.0.dev0.dist-info → boxlite-0.1.1.dev0.dist-info}/WHEEL +0 -0
boxlite/__init__.py
CHANGED
|
@@ -5,21 +5,41 @@ Following SQLite philosophy: "BoxLite" for branding, "boxlite" for code APIs.
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import os
|
|
8
|
+
import warnings
|
|
8
9
|
from pathlib import Path
|
|
9
10
|
|
|
10
|
-
#
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
# Import core functionality from Rust extension
|
|
12
|
+
try:
|
|
13
|
+
from .boxlite import (
|
|
14
|
+
Options,
|
|
15
|
+
BoxOptions,
|
|
16
|
+
Boxlite,
|
|
17
|
+
Box,
|
|
18
|
+
BoxInfo,
|
|
19
|
+
)
|
|
16
20
|
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
__all__ = [
|
|
22
|
+
"Options",
|
|
23
|
+
"BoxOptions",
|
|
24
|
+
"Boxlite",
|
|
25
|
+
"Box",
|
|
26
|
+
"BoxInfo",
|
|
27
|
+
]
|
|
28
|
+
except ImportError as e:
|
|
29
|
+
warnings.warn(f"BoxLite native extension not available: {e}", ImportWarning)
|
|
30
|
+
__all__ = []
|
|
19
31
|
|
|
20
|
-
#
|
|
21
|
-
|
|
22
|
-
|
|
32
|
+
# Import Python convenience wrappers
|
|
33
|
+
try:
|
|
34
|
+
from .basebox import BaseBox
|
|
35
|
+
from .codebox import CodeBox
|
|
36
|
+
__all__.extend(["BaseBox", "CodeBox"])
|
|
37
|
+
except ImportError:
|
|
38
|
+
pass
|
|
23
39
|
|
|
24
|
-
|
|
25
|
-
|
|
40
|
+
# Future specialized containers can be added here at top-level
|
|
41
|
+
# Example: BrowserBox (see browserbox.py for implementation template)
|
|
42
|
+
# from .browserbox import BrowserBox
|
|
43
|
+
# __all__.append("BrowserBox")
|
|
44
|
+
|
|
45
|
+
__version__ = "0.1.1-dev"
|
boxlite/basebox.py
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""
|
|
2
|
+
BaseBox - Foundation for specialized container types.
|
|
3
|
+
|
|
4
|
+
Provides common functionality for all specialized boxes (CodeBox, BrowserBox, etc.)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Optional
|
|
8
|
+
import sys
|
|
9
|
+
import os
|
|
10
|
+
import fcntl
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BaseBox:
|
|
14
|
+
"""
|
|
15
|
+
Base class for specialized container types.
|
|
16
|
+
|
|
17
|
+
This class encapsulates the common patterns:
|
|
18
|
+
1. Dual constructor (convenience vs explicit runtime)
|
|
19
|
+
2. Async context manager support
|
|
20
|
+
3. Stdio blocking mode restoration
|
|
21
|
+
4. Runtime lifecycle management
|
|
22
|
+
|
|
23
|
+
Subclasses should override:
|
|
24
|
+
- _create_box_options(): Return BoxOptions for their specific use case
|
|
25
|
+
- Add domain-specific methods (e.g., CodeBox.run(), BrowserBox.navigate())
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
image: str,
|
|
31
|
+
memory_mib: Optional[int] = None,
|
|
32
|
+
cpus: Optional[int] = None,
|
|
33
|
+
home_dir: Optional[str] = None,
|
|
34
|
+
**kwargs
|
|
35
|
+
):
|
|
36
|
+
"""
|
|
37
|
+
Create a specialized box with its own internal runtime.
|
|
38
|
+
|
|
39
|
+
This is the convenience constructor that creates a runtime automatically.
|
|
40
|
+
For better resource management when creating multiple boxes, use create() instead.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
image: Container image to use
|
|
44
|
+
memory_mib: Memory limit in MiB
|
|
45
|
+
cpus: Number of CPU cores
|
|
46
|
+
home_dir: Runtime home directory (optional)
|
|
47
|
+
**kwargs: Additional configuration options
|
|
48
|
+
"""
|
|
49
|
+
try:
|
|
50
|
+
from .boxlite import Boxlite, Options
|
|
51
|
+
except ImportError as e:
|
|
52
|
+
raise ImportError(
|
|
53
|
+
f"BoxLite native extension not found: {e}. "
|
|
54
|
+
"Please install with: pip install boxlite"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Create runtime options
|
|
58
|
+
runtime_opts = Options(
|
|
59
|
+
home_dir=home_dir,
|
|
60
|
+
engine=kwargs.get('engine', 'libkrun')
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Create the runtime (this acquires the lock)
|
|
64
|
+
self._runtime = Boxlite(runtime_opts)
|
|
65
|
+
self._owns_runtime = True
|
|
66
|
+
|
|
67
|
+
# Create box using subclass-defined options
|
|
68
|
+
box_opts = self._create_box_options(image, memory_mib, cpus, **kwargs)
|
|
69
|
+
self._box = self._runtime.create(box_opts)
|
|
70
|
+
|
|
71
|
+
@classmethod
|
|
72
|
+
def create(
|
|
73
|
+
cls,
|
|
74
|
+
runtime, # Type: Boxlite, but avoid import at module level
|
|
75
|
+
image: str,
|
|
76
|
+
memory_mib: Optional[int] = None,
|
|
77
|
+
cpus: Optional[int] = None,
|
|
78
|
+
**kwargs
|
|
79
|
+
):
|
|
80
|
+
"""
|
|
81
|
+
Create a specialized box using an existing runtime.
|
|
82
|
+
|
|
83
|
+
This is the recommended approach when creating multiple boxes,
|
|
84
|
+
as it shares a single runtime instance.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
runtime: Existing Boxlite runtime instance
|
|
88
|
+
image: Container image to use
|
|
89
|
+
memory_mib: Memory limit in MiB
|
|
90
|
+
cpus: Number of CPU cores
|
|
91
|
+
**kwargs: Additional configuration options
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Specialized box instance
|
|
95
|
+
|
|
96
|
+
Example:
|
|
97
|
+
>>> runtime = boxlite.Boxlite(boxlite.Options())
|
|
98
|
+
>>> box1 = SomeBox.create(runtime, image="custom:latest")
|
|
99
|
+
>>> box2 = SomeBox.create(runtime, image="custom:latest", memory_mib=1024)
|
|
100
|
+
"""
|
|
101
|
+
# Create instance without going through __init__
|
|
102
|
+
instance = cls.__new__(cls)
|
|
103
|
+
instance._runtime = runtime
|
|
104
|
+
instance._owns_runtime = False
|
|
105
|
+
|
|
106
|
+
# Create box using subclass-defined options
|
|
107
|
+
box_opts = instance._create_box_options(image, memory_mib, cpus, **kwargs)
|
|
108
|
+
instance._box = runtime.create(box_opts)
|
|
109
|
+
|
|
110
|
+
return instance
|
|
111
|
+
|
|
112
|
+
def _create_box_options(self, image: str, memory_mib: Optional[int], cpus: Optional[int], **kwargs):
|
|
113
|
+
"""
|
|
114
|
+
Create BoxOptions for this specialized box.
|
|
115
|
+
|
|
116
|
+
Subclasses should override this to provide their specific defaults.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
image: Container image
|
|
120
|
+
memory_mib: Memory limit
|
|
121
|
+
cpus: CPU cores
|
|
122
|
+
**kwargs: Additional options
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
BoxOptions instance
|
|
126
|
+
"""
|
|
127
|
+
try:
|
|
128
|
+
from .boxlite import BoxOptions
|
|
129
|
+
except ImportError as e:
|
|
130
|
+
raise ImportError(
|
|
131
|
+
f"BoxLite native extension not found: {e}. "
|
|
132
|
+
"Please install with: pip install boxlite"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
return BoxOptions(
|
|
136
|
+
image=image,
|
|
137
|
+
cpus=cpus,
|
|
138
|
+
memory_mib=memory_mib,
|
|
139
|
+
working_dir=kwargs.get('working_dir'),
|
|
140
|
+
env=kwargs.get('env', [])
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
async def __aenter__(self):
|
|
144
|
+
"""Async context manager entry."""
|
|
145
|
+
self._box.__enter__()
|
|
146
|
+
# Tokio runtime sets stdout/stderr to non-blocking mode
|
|
147
|
+
# Restore blocking mode to prevent BlockingIOError when printing
|
|
148
|
+
self._restore_blocking_mode()
|
|
149
|
+
return self
|
|
150
|
+
|
|
151
|
+
def _restore_blocking_mode(self):
|
|
152
|
+
"""Restore blocking mode on stdout/stderr after Tokio sets them to non-blocking."""
|
|
153
|
+
for fd in [sys.stdout.fileno(), sys.stderr.fileno()]:
|
|
154
|
+
try:
|
|
155
|
+
flags = fcntl.fcntl(fd, fcntl.F_GETFL)
|
|
156
|
+
if flags & os.O_NONBLOCK:
|
|
157
|
+
fcntl.fcntl(fd, fcntl.F_SETFL, flags & ~os.O_NONBLOCK)
|
|
158
|
+
except (OSError, AttributeError):
|
|
159
|
+
# Ignore errors (e.g., when stdout/stderr is not a real file)
|
|
160
|
+
pass
|
|
161
|
+
|
|
162
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
163
|
+
"""Async context manager exit."""
|
|
164
|
+
result = self._box.__exit__(exc_type, exc_val, exc_tb)
|
|
165
|
+
|
|
166
|
+
# Close runtime if we own it
|
|
167
|
+
if self._owns_runtime:
|
|
168
|
+
self._runtime.close()
|
|
169
|
+
|
|
170
|
+
return result
|
|
171
|
+
|
|
172
|
+
@property
|
|
173
|
+
def id(self) -> str:
|
|
174
|
+
"""Get the box ID."""
|
|
175
|
+
return self._box.id
|
|
176
|
+
|
|
177
|
+
def info(self):
|
|
178
|
+
"""Get box information."""
|
|
179
|
+
return self._box.info()
|
|
180
|
+
|
|
181
|
+
async def execute(self, command: str, *args) -> str:
|
|
182
|
+
"""
|
|
183
|
+
Execute a command in the container.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
command: Command to execute
|
|
187
|
+
*args: Command arguments
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
Command output as string
|
|
191
|
+
"""
|
|
192
|
+
result = await self._box.execute(command, list(args) if args else None)
|
|
193
|
+
if isinstance(result, list):
|
|
194
|
+
return '\n'.join(f"[{stream}] {text}" for stream, text in result if text.strip())
|
|
195
|
+
return str(result)
|
|
196
|
+
|
|
197
|
+
def shutdown(self):
|
|
198
|
+
"""
|
|
199
|
+
Shutdown the box and release resources.
|
|
200
|
+
|
|
201
|
+
Note: Usually not needed as context manager handles cleanup.
|
|
202
|
+
"""
|
|
203
|
+
self._box.shutdown()
|
|
204
|
+
if self._owns_runtime:
|
|
205
|
+
self._runtime.close()
|
boxlite/bin/boxlite-guest
CHANGED
|
Binary file
|
boxlite/bin/boxlite-runner
CHANGED
|
Binary file
|
|
Binary file
|
boxlite/browserbox.py
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""
|
|
2
|
+
BrowserBox - Secure web browser container (example).
|
|
3
|
+
|
|
4
|
+
This is a future specialized box showing how to extend BaseBox.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Optional
|
|
8
|
+
from .basebox import BaseBox
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class BrowserBox(BaseBox):
|
|
12
|
+
"""
|
|
13
|
+
Secure container for browser automation (future implementation).
|
|
14
|
+
|
|
15
|
+
Example of how to create specialized boxes by inheriting from BaseBox.
|
|
16
|
+
|
|
17
|
+
Usage:
|
|
18
|
+
>>> runtime = boxlite.Boxlite(boxlite.Options())
|
|
19
|
+
>>> browser = BrowserBox.create(runtime)
|
|
20
|
+
>>> await browser.navigate("https://example.com")
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
image: str = "browserbox:latest",
|
|
26
|
+
memory_mib: Optional[int] = 2048, # Browsers need more memory
|
|
27
|
+
cpus: Optional[int] = None,
|
|
28
|
+
home_dir: Optional[str] = None,
|
|
29
|
+
**kwargs
|
|
30
|
+
):
|
|
31
|
+
"""
|
|
32
|
+
Create a new BrowserBox with its own internal runtime.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
image: Container image with browser (default: browserbox:latest)
|
|
36
|
+
memory_mib: Memory limit in MiB (default: 2048 for browsers)
|
|
37
|
+
cpus: Number of CPU cores
|
|
38
|
+
home_dir: Runtime home directory (optional)
|
|
39
|
+
**kwargs: Additional configuration options
|
|
40
|
+
"""
|
|
41
|
+
super().__init__(image, memory_mib, cpus, home_dir, **kwargs)
|
|
42
|
+
|
|
43
|
+
@classmethod
|
|
44
|
+
def create(
|
|
45
|
+
cls,
|
|
46
|
+
runtime,
|
|
47
|
+
image: str = "browserbox:latest",
|
|
48
|
+
memory_mib: Optional[int] = 2048,
|
|
49
|
+
cpus: Optional[int] = None,
|
|
50
|
+
**kwargs
|
|
51
|
+
):
|
|
52
|
+
"""
|
|
53
|
+
Create a BrowserBox using an existing runtime.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
runtime: Existing Boxlite runtime instance
|
|
57
|
+
image: Container image with browser
|
|
58
|
+
memory_mib: Memory limit in MiB (default: 2048)
|
|
59
|
+
cpus: Number of CPU cores
|
|
60
|
+
**kwargs: Additional configuration options
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
BrowserBox instance
|
|
64
|
+
"""
|
|
65
|
+
return super(BrowserBox, cls).create(runtime, image, memory_mib, cpus, **kwargs)
|
|
66
|
+
|
|
67
|
+
async def navigate(self, url: str) -> str:
|
|
68
|
+
"""
|
|
69
|
+
Navigate browser to URL (example method).
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
url: URL to navigate to
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Page content or status
|
|
76
|
+
"""
|
|
77
|
+
# Example implementation - would use actual browser automation
|
|
78
|
+
return await self.execute("curl", "-L", url)
|
|
79
|
+
|
|
80
|
+
async def screenshot(self, path: str) -> str:
|
|
81
|
+
"""
|
|
82
|
+
Take a screenshot (example method).
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
path: Path to save screenshot
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
Status message
|
|
89
|
+
"""
|
|
90
|
+
# Example implementation - would use actual browser automation
|
|
91
|
+
return await self.execute("echo", "Screenshot would be saved to", path)
|
|
92
|
+
|
|
93
|
+
async def eval_js(self, script: str) -> str:
|
|
94
|
+
"""
|
|
95
|
+
Evaluate JavaScript in browser context (example method).
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
script: JavaScript code to execute
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
Execution result
|
|
102
|
+
"""
|
|
103
|
+
# Example implementation - would use actual browser automation
|
|
104
|
+
return await self.execute("echo", "Would evaluate JS:", script)
|
|
@@ -4,21 +4,25 @@ CodeBox - Secure Python code execution container.
|
|
|
4
4
|
Provides a simple, secure environment for running untrusted Python code.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from typing import Optional
|
|
8
|
-
from
|
|
7
|
+
from typing import Optional
|
|
8
|
+
from .basebox import BaseBox
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
class CodeBox(
|
|
11
|
+
class CodeBox(BaseBox):
|
|
12
12
|
"""
|
|
13
13
|
Secure container for executing Python code.
|
|
14
14
|
|
|
15
15
|
CodeBox provides an isolated environment for running untrusted Python code
|
|
16
16
|
with built-in safety and result formatting.
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
>>>
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
Usage Pattern 1 - Explicit runtime (recommended for multiple boxes):
|
|
19
|
+
>>> runtime = boxlite.Boxlite(boxlite.Options())
|
|
20
|
+
>>> codebox = CodeBox.create(runtime, memory_mib=512)
|
|
21
|
+
>>> result = await codebox.run("print('Hello, World!')")
|
|
22
|
+
|
|
23
|
+
Usage Pattern 2 - Convenience constructor (creates internal runtime):
|
|
24
|
+
>>> async with CodeBox() as cb:
|
|
25
|
+
... result = await cb.run("print('Hello, World!')")
|
|
22
26
|
"""
|
|
23
27
|
|
|
24
28
|
def __init__(
|
|
@@ -26,34 +30,57 @@ class CodeBox(Box):
|
|
|
26
30
|
image: str = "python:slim",
|
|
27
31
|
memory_mib: Optional[int] = None,
|
|
28
32
|
cpus: Optional[int] = None,
|
|
33
|
+
home_dir: Optional[str] = None,
|
|
29
34
|
**kwargs
|
|
30
35
|
):
|
|
31
36
|
"""
|
|
32
|
-
Create a new CodeBox
|
|
37
|
+
Create a new CodeBox with its own internal runtime.
|
|
38
|
+
|
|
39
|
+
This is the convenience constructor that creates a runtime automatically.
|
|
40
|
+
For better resource management when creating multiple boxes, use create() instead.
|
|
33
41
|
|
|
34
42
|
Args:
|
|
35
43
|
image: Container image with Python (default: python:slim)
|
|
36
44
|
memory_mib: Memory limit in MiB (default: system default)
|
|
37
45
|
cpus: Number of CPU cores (default: system default)
|
|
38
|
-
|
|
46
|
+
home_dir: Runtime home directory (optional)
|
|
47
|
+
**kwargs: Additional configuration options
|
|
39
48
|
"""
|
|
40
|
-
|
|
41
|
-
config = {
|
|
42
|
-
'engine': kwargs.get('engine', 'libkrun'),
|
|
43
|
-
'memory_mib': memory_mib,
|
|
44
|
-
'cpus': cpus,
|
|
45
|
-
}
|
|
49
|
+
super().__init__(image, memory_mib, cpus, home_dir, **kwargs)
|
|
46
50
|
|
|
47
|
-
|
|
48
|
-
|
|
51
|
+
@classmethod
|
|
52
|
+
def create(
|
|
53
|
+
cls,
|
|
54
|
+
runtime,
|
|
55
|
+
image: str = "python:slim",
|
|
56
|
+
memory_mib: Optional[int] = None,
|
|
57
|
+
cpus: Optional[int] = None,
|
|
58
|
+
**kwargs
|
|
59
|
+
):
|
|
60
|
+
"""
|
|
61
|
+
Create a CodeBox using an existing runtime.
|
|
49
62
|
|
|
50
|
-
|
|
51
|
-
|
|
63
|
+
This is the recommended approach when creating multiple boxes,
|
|
64
|
+
as it shares a single runtime instance.
|
|
52
65
|
|
|
53
|
-
|
|
54
|
-
|
|
66
|
+
Args:
|
|
67
|
+
runtime: Existing Boxlite runtime instance
|
|
68
|
+
image: Container image with Python (default: python:slim)
|
|
69
|
+
memory_mib: Memory limit in MiB
|
|
70
|
+
cpus: Number of CPU cores
|
|
71
|
+
**kwargs: Additional configuration options
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
CodeBox instance
|
|
75
|
+
|
|
76
|
+
Example:
|
|
77
|
+
>>> runtime = boxlite.Boxlite(boxlite.Options())
|
|
78
|
+
>>> cb1 = CodeBox.create(runtime)
|
|
79
|
+
>>> cb2 = CodeBox.create(runtime, memory_mib=1024)
|
|
80
|
+
"""
|
|
81
|
+
return super(CodeBox, cls).create(runtime, image, memory_mib, cpus, **kwargs)
|
|
55
82
|
|
|
56
|
-
async def run(self, code: str, timeout: Optional[int] = None) ->
|
|
83
|
+
async def run(self, code: str, timeout: Optional[int] = None) -> str:
|
|
57
84
|
"""
|
|
58
85
|
Execute Python code in the secure container.
|
|
59
86
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: boxlite
|
|
3
|
+
Version: 0.1.1.dev0
|
|
4
|
+
Requires-Dist: pytest>=7.0 ; extra == 'dev'
|
|
5
|
+
Requires-Dist: pytest-asyncio>=0.21 ; extra == 'dev'
|
|
6
|
+
Provides-Extra: dev
|
|
7
|
+
Summary: Python bindings for Boxlite runtime
|
|
8
|
+
Author: Dorian Zheng
|
|
9
|
+
Requires-Python: >=3.10
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
boxlite/__init__.py,sha256=ba3Qv2xQPLNPBIGsvj8JEigfg4dUeAEofqcM1n6hc4Y,1028
|
|
2
|
+
boxlite/basebox.py,sha256=KZ6WAqDRrrcWfWFY2FxQGz5mdeeHDNL3CMWYafHawvY,6507
|
|
3
|
+
boxlite/boxlite.cpython-313-x86_64-linux-gnu.so,sha256=tae8LrjCnEFXHxySW6TluUBymC3cGF3ogQeb0HkPdPE,10555929
|
|
4
|
+
boxlite/browserbox.py,sha256=33AkrXebs_nRc70vZauLqWVTkqkzg4_UZWn1b8mhTYo,3077
|
|
5
|
+
boxlite/codebox.py,sha256=COlWcJwqgdxqAti2QoCljreaXF2RC3qxfzgdB05eEXQ,4929
|
|
6
|
+
boxlite/bin/boxlite-guest,sha256=zDK45neGSXikS45k0GjbRQVeawqkPdYk94K16WLFuTs,2702712
|
|
7
|
+
boxlite/bin/boxlite-runner,sha256=8uAND2WajEe3_Z396kiANncQaZ3nO_KEIis_UxDiGJA,3336065
|
|
8
|
+
boxlite.libs/libkrun-fb5f0f92.so.1.15.1,sha256=V_e6pKAizhttRH9qcXtMNiH8ukfAB_ycdF4I64M8HQk,6270745
|
|
9
|
+
boxlite.libs/libkrunfw-6be8cb77.so.4.10.0,sha256=lvCM3V0AoERzdX44V7eZiPyJGgE3_jECBcx8UKwX5vU,19075561
|
|
10
|
+
boxlite-0.1.1.dev0.dist-info/METADATA,sha256=yQHn-IKNbkV7msuDMKAW7yPRSwsl2i3IYcSVuAXhM4c,263
|
|
11
|
+
boxlite-0.1.1.dev0.dist-info/WHEEL,sha256=HMO0puQEz--r12axzxwPwPpTQ-i04eJzIQtJwxCs1kk,109
|
|
12
|
+
boxlite-0.1.1.dev0.dist-info/RECORD,,
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
boxlite/boxes/__init__.py
DELETED
boxlite/core/__init__.py
DELETED
boxlite/core/box.py
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
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")
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
boxlite/__init__.py,sha256=uFUVtHBVkjxpx9NxyyRfezBlLDpunNF_J-NCESPg-bg,726
|
|
2
|
-
boxlite/boxlite.cpython-313-x86_64-linux-gnu.so,sha256=PkKT5IphOTcf4-ra_kRWseJFZ6W0ZuDcS8dZrO837IA,2907672
|
|
3
|
-
boxlite/bin/boxlite-guest,sha256=KC2-jmeNazCWw1yglz39lvDNb_b7XvetOWAVYhiYYkw,2595080
|
|
4
|
-
boxlite/bin/boxlite-runner,sha256=mTzEFYTb9BllvobFy8EF1wvNtpN6Zvix-dcn6_Q3pr0,1918793
|
|
5
|
-
boxlite/boxes/__init__.py,sha256=MYz3-Hgr9Qh2krmyp79amIGn5YovUOspLxZ-7HK7320,223
|
|
6
|
-
boxlite/boxes/codebox.py,sha256=ZBeXNjlRwRN0ywqnqCfqYdHYJmeZA2bqnPDOdeHXR6s,3850
|
|
7
|
-
boxlite/core/__init__.py,sha256=gnumx6utojYT12dd4_kGlJmxwMf0Yidnpc3RsLmOFpc,76
|
|
8
|
-
boxlite/core/box.py,sha256=XwcevOo0opXWvVbUNL98Ozb7lf13Ucg8pPVrTqBqbzk,3223
|
|
9
|
-
boxlite.libs/libkrun-a6731198.so.1.15.1,sha256=mvtQ-5ccDtlgnobBgrl2ErSvTOeYf9Ykqqw6_5LignA,5553409
|
|
10
|
-
boxlite-0.1.0.dev0.dist-info/METADATA,sha256=igHLOzaNtebVZfmMCMpQT9sbawuqEYsnpN_bHaVxDp0,146
|
|
11
|
-
boxlite-0.1.0.dev0.dist-info/WHEEL,sha256=HMO0puQEz--r12axzxwPwPpTQ-i04eJzIQtJwxCs1kk,109
|
|
12
|
-
boxlite-0.1.0.dev0.dist-info/RECORD,,
|
|
File without changes
|