bunnyshell


Namebunnyshell JSON
Version 0.1.7 PyPI version JSON
download
home_pageNone
SummaryOfficial Python SDK for Bunnyshell Sandboxes
upload_time2025-11-05 22:31:50
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseMIT
keywords bunnyshell cloud containers microvm sandbox vm
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Bunnyshell Python SDK

[![PyPI version](https://badge.fury.io/py/bunnyshell.svg)](https://pypi.org/project/bunnyshell/)
[![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

Official Python SDK for Bunnyshell Sandboxes - Execute code safely in isolated microVM environments.

## Features

- 🚀 **47/47 Features** - 100% API coverage
- 🔒 **Type Safe** - Full Pydantic models with auto-complete
- ⚡ **Async Support** - Native async/await for all operations
- 🌊 **WebSocket Streaming** - Real-time code execution, terminal, file watching
- 🎨 **Rich Outputs** - Automatic capture of plots, images, DataFrames
- 🔐 **Security First** - Environment variables for secrets, execution timeouts
- 📦 **Context Managers** - Automatic cleanup with `with` statement
- 🐍 **Pythonic API** - Follows Python best practices

## Installation

```bash
pip install bunnyshell
```

For WebSocket features (optional):
```bash
pip install bunnyshell[websockets]
```

## Quick Start

```python
from bunnyshell import Sandbox

# Create sandbox
with Sandbox.create(template="code-interpreter", api_key="your-api-key") as sandbox:
    # Execute Python code
    result = sandbox.run_code("""
import pandas as pd
df = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]})
print(df.sum())
    """)
    
    print(result.stdout)
    # Output: a    6
    #         b   15
```

## Environment Variables (IMPORTANT!)

**✅ ALWAYS pass secrets via environment variables:**

```python
# ✅ GOOD: Secrets via environment variables
result = sandbox.run_code(
    """
import os
api_key = os.getenv('OPENAI_API_KEY')
# Use api_key safely...
    """,
    env={"OPENAI_API_KEY": "sk-..."}
)

# ❌ BAD: Never hardcode secrets
# result = sandbox.run_code('api_key = "sk-..."')  # DON'T DO THIS!
```

## Core Features

### Code Execution

```python
# Synchronous execution
result = sandbox.run_code("print('Hello')")

# With environment variables
result = sandbox.run_code(
    "import os; print(os.getenv('MY_VAR'))",
    env={"MY_VAR": "value"}
)

# Async execution (non-blocking)
execution_id = sandbox.run_code_async("import time; time.sleep(10)")

# Background execution (fire-and-forget)
execution_id = sandbox.run_code_background("# long task...")

# IPython/Jupyter-style
result = sandbox.run_ipython("df.describe()")
```

### Real-time Streaming (WebSocket)

```python
import asyncio

async def stream():
    async for message in sandbox.run_code_stream("""
import time
for i in range(5):
    print(f"Step {i+1}")
    time.sleep(1)
    """):
        if message['type'] == 'stdout':
            print(message['data'], end='')

asyncio.run(stream())
```

### File Operations

```python
# Write file
sandbox.files.write('/workspace/data.txt', 'Hello, World!')

# Read file
content = sandbox.files.read('/workspace/data.txt')

# List directory
files = sandbox.files.list('/workspace')
for file in files:
    print(f"{file.name} ({file.size_kb:.2f}KB)")

# Upload local file
sandbox.files.upload('./local.txt', '/workspace/remote.txt')

# Download remote file
sandbox.files.download('/workspace/remote.txt', './local.txt')

# Check existence
if sandbox.files.exists('/workspace/data.txt'):
    print("File exists!")

# Remove file/directory
sandbox.files.remove('/workspace/data.txt')

# Create directory
sandbox.files.mkdir('/workspace/mydir')

# Watch filesystem (WebSocket)
async for event in sandbox.files.watch('/workspace'):
    print(f"{event['event']}: {event['path']}")
```

### Commands

```python
# Run shell command
result = sandbox.commands.run('ls -la /workspace')
print(result.stdout)

# With environment variables
result = sandbox.commands.run(
    'echo "Key: $API_KEY"',
    env={'API_KEY': 'secret'}
)

# Custom working directory
result = sandbox.commands.run(
    'pwd',
    working_dir='/workspace/myproject'
)
```

### Environment Variables

```python
# Get all
env_vars = sandbox.env.get_all()

# Set all (replace)
sandbox.env.set_all({'API_KEY': 'sk-...', 'DEBUG': 'true'})

# Update (merge)
sandbox.env.update({'NEW_VAR': 'value'})

# Delete
sandbox.env.delete(['VAR_TO_DELETE'])

# Get single
value = sandbox.env.get('API_KEY')

# Set single
sandbox.env.set('API_KEY', 'sk-...')
```

### Process Management

```python
# List processes
processes = sandbox.list_processes()
for proc in processes:
    print(f"{proc.pid}: {proc.name} (CPU: {proc.cpu_percent}%)")

# Kill process
sandbox.kill_process(1234)
```

### Metrics & Health

```python
# Get metrics
metrics = sandbox.get_metrics_snapshot()
print(f"Total executions: {metrics.total_executions}")
print(f"Uptime: {metrics.uptime_seconds}s")

# Health check
health = sandbox.get_health()
print(f"Status: {health.status}")

# Cache management
cache_stats = sandbox.cache.stats()
print(f"Cache size: {cache_stats.cache.size}")

sandbox.cache.clear()
```

### Desktop Automation

```python
# Note: Requires 'desktop' template
sandbox = Sandbox.create(template='desktop', api_key='...')

# Mouse operations
sandbox.desktop.mouse_click(x=500, y=300, button='left')
sandbox.desktop.mouse_move(x=600, y=400)
sandbox.desktop.drag_drop(from_x=100, from_y=100, to_x=300, to_y=300)

# Keyboard
sandbox.desktop.keyboard_type('Hello, World!')
sandbox.desktop.keyboard_press('Return')
sandbox.desktop.keyboard_press('Control_L+c')  # Ctrl+C

# Clipboard
sandbox.desktop.clipboard_set('text to copy')
content = sandbox.desktop.clipboard_get()

# Screenshots
screenshot = sandbox.desktop.screenshot()
with open('screenshot.png', 'wb') as f:
    f.write(screenshot)

# OCR (text recognition)
text = sandbox.desktop.ocr()
print(f"Found text: {text}")

# Find elements
element = sandbox.desktop.find_element(text='Button')
if element:
    sandbox.desktop.mouse_click(x=element['x'], y=element['y'])

# VNC connection
vnc_info = sandbox.desktop.get_vnc_url()
print(f"VNC URL: {vnc_info['url']}")
```

### Interactive Terminal (WebSocket)

```python
import asyncio

async def terminal_session():
    async with sandbox.terminal.connect() as term:
        # Send commands
        await sandbox.terminal.send_input(term, 'ls -la\n')
        
        # Receive output
        async for message in sandbox.terminal.iter_output(term):
            if message['type'] == 'output':
                print(message['data'], end='')

asyncio.run(terminal_session())
```

## Advanced Configuration

```python
sandbox = Sandbox.create(
    template='code-interpreter',
    api_key='your-api-key',
    vcpu=4,              # 4 vCPUs
    memory_mb=4096,      # 4GB RAM
    disk_gb=20,          # 20GB disk
    region='us-west-2',  # Specific region
    timeout=600,         # 10 minute timeout
    env_vars={           # Pre-set environment variables
        'DATABASE_URL': 'postgres://...',
        'API_KEY': 'sk-...'
    }
)
```

## Error Handling

```python
from bunnyshell.errors import (
    BunnyshellError,
    AuthenticationError,
    NotFoundError,
    FileNotFoundError,
    CodeExecutionError,
    RateLimitError,
    ServerError
)

try:
    with Sandbox.create(template='code-interpreter', api_key='...') as sandbox:
        result = sandbox.run_code("print('Hello')")
        
except AuthenticationError as e:
    print(f"Auth failed: {e.message}")
    print(f"Request ID: {e.request_id}")
    
except FileNotFoundError as e:
    print(f"File not found: {e.path}")
    
except CodeExecutionError as e:
    print(f"Code execution failed: {e.message}")
    print(f"Exit code: {e.exit_code}")
    
except RateLimitError as e:
    print(f"Rate limited: {e.message}")
    
except BunnyshellError as e:
    print(f"API error: {e.message}")
    print(f"Status code: {e.status_code}")
```

## Best Practices

### 1. Always Use Context Managers

```python
# ✅ GOOD: Automatic cleanup
with Sandbox.create(...) as sandbox:
    # Work here
    pass
# Sandbox automatically deleted

# ❌ BAD: Manual cleanup (easy to forget)
sandbox = Sandbox.create(...)
# ... work ...
sandbox.kill()  # Easy to forget if error occurs!
```

### 2. Set Timeouts

```python
# Prevent infinite execution
result = sandbox.run_code(code, timeout=30)
```

### 3. Optimize Performance

```python
# Set environment variables once
sandbox.env.set_all({'API_KEY': 'sk-...', 'DB_URL': '...'})

# Now available in all executions
for i in range(100):
    sandbox.run_code('...')  # Env vars already set
```

## Use Cases

### OpenAI: ChatGPT Code Interpreter

```python
def execute_user_code(user_code: str):
    with Sandbox.create(template="code-interpreter") as sandbox:
        result = sandbox.run_code(user_code, timeout=30)
        return {
            "output": result.stdout,
            "error": result.stderr,
            "rich_outputs": result.rich_outputs  # Images, plots
        }
```

### Stripe: Payment Reports

```python
with Sandbox.create(template="code-interpreter") as sandbox:
    sandbox.env.set('STRIPE_SECRET_KEY', os.getenv('STRIPE_SECRET_KEY'))
    
    result = sandbox.run_code("""
import stripe
stripe.api_key = os.environ['STRIPE_SECRET_KEY']
charges = stripe.Charge.list(limit=100)
# Generate report...
    """)
```

## Documentation

- **Cookbook**: See [cookbook/python](../../cookbook/python/) for 10 complete examples
- **API Reference**: [docs.bunnyshell.com](https://docs.bunnyshell.com)
- **GitHub**: [github.com/bunnyshell/sandbox-sdks](https://github.com/bunnyshell/sandbox-sdks)

## License

MIT License - See [LICENSE](../LICENSE) file for details.

## Support

- **Issues**: [github.com/bunnyshell/sandbox-sdks/issues](https://github.com/bunnyshell/sandbox-sdks/issues)
- **Email**: support@bunnyshell.com
- **Documentation**: [docs.bunnyshell.com](https://docs.bunnyshell.com)

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "bunnyshell",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "bunnyshell, cloud, containers, microvm, sandbox, vm",
    "author": null,
    "author_email": "Bunnyshell <support@bunnyshell.com>",
    "download_url": "https://files.pythonhosted.org/packages/51/e6/3962b6235692c36a94ed5e4f4f9b30122b856b5c27e2bace891c046225fb/bunnyshell-0.1.7.tar.gz",
    "platform": null,
    "description": "# Bunnyshell Python SDK\n\n[![PyPI version](https://badge.fury.io/py/bunnyshell.svg)](https://pypi.org/project/bunnyshell/)\n[![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nOfficial Python SDK for Bunnyshell Sandboxes - Execute code safely in isolated microVM environments.\n\n## Features\n\n- \ud83d\ude80 **47/47 Features** - 100% API coverage\n- \ud83d\udd12 **Type Safe** - Full Pydantic models with auto-complete\n- \u26a1 **Async Support** - Native async/await for all operations\n- \ud83c\udf0a **WebSocket Streaming** - Real-time code execution, terminal, file watching\n- \ud83c\udfa8 **Rich Outputs** - Automatic capture of plots, images, DataFrames\n- \ud83d\udd10 **Security First** - Environment variables for secrets, execution timeouts\n- \ud83d\udce6 **Context Managers** - Automatic cleanup with `with` statement\n- \ud83d\udc0d **Pythonic API** - Follows Python best practices\n\n## Installation\n\n```bash\npip install bunnyshell\n```\n\nFor WebSocket features (optional):\n```bash\npip install bunnyshell[websockets]\n```\n\n## Quick Start\n\n```python\nfrom bunnyshell import Sandbox\n\n# Create sandbox\nwith Sandbox.create(template=\"code-interpreter\", api_key=\"your-api-key\") as sandbox:\n    # Execute Python code\n    result = sandbox.run_code(\"\"\"\nimport pandas as pd\ndf = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]})\nprint(df.sum())\n    \"\"\")\n    \n    print(result.stdout)\n    # Output: a    6\n    #         b   15\n```\n\n## Environment Variables (IMPORTANT!)\n\n**\u2705 ALWAYS pass secrets via environment variables:**\n\n```python\n# \u2705 GOOD: Secrets via environment variables\nresult = sandbox.run_code(\n    \"\"\"\nimport os\napi_key = os.getenv('OPENAI_API_KEY')\n# Use api_key safely...\n    \"\"\",\n    env={\"OPENAI_API_KEY\": \"sk-...\"}\n)\n\n# \u274c BAD: Never hardcode secrets\n# result = sandbox.run_code('api_key = \"sk-...\"')  # DON'T DO THIS!\n```\n\n## Core Features\n\n### Code Execution\n\n```python\n# Synchronous execution\nresult = sandbox.run_code(\"print('Hello')\")\n\n# With environment variables\nresult = sandbox.run_code(\n    \"import os; print(os.getenv('MY_VAR'))\",\n    env={\"MY_VAR\": \"value\"}\n)\n\n# Async execution (non-blocking)\nexecution_id = sandbox.run_code_async(\"import time; time.sleep(10)\")\n\n# Background execution (fire-and-forget)\nexecution_id = sandbox.run_code_background(\"# long task...\")\n\n# IPython/Jupyter-style\nresult = sandbox.run_ipython(\"df.describe()\")\n```\n\n### Real-time Streaming (WebSocket)\n\n```python\nimport asyncio\n\nasync def stream():\n    async for message in sandbox.run_code_stream(\"\"\"\nimport time\nfor i in range(5):\n    print(f\"Step {i+1}\")\n    time.sleep(1)\n    \"\"\"):\n        if message['type'] == 'stdout':\n            print(message['data'], end='')\n\nasyncio.run(stream())\n```\n\n### File Operations\n\n```python\n# Write file\nsandbox.files.write('/workspace/data.txt', 'Hello, World!')\n\n# Read file\ncontent = sandbox.files.read('/workspace/data.txt')\n\n# List directory\nfiles = sandbox.files.list('/workspace')\nfor file in files:\n    print(f\"{file.name} ({file.size_kb:.2f}KB)\")\n\n# Upload local file\nsandbox.files.upload('./local.txt', '/workspace/remote.txt')\n\n# Download remote file\nsandbox.files.download('/workspace/remote.txt', './local.txt')\n\n# Check existence\nif sandbox.files.exists('/workspace/data.txt'):\n    print(\"File exists!\")\n\n# Remove file/directory\nsandbox.files.remove('/workspace/data.txt')\n\n# Create directory\nsandbox.files.mkdir('/workspace/mydir')\n\n# Watch filesystem (WebSocket)\nasync for event in sandbox.files.watch('/workspace'):\n    print(f\"{event['event']}: {event['path']}\")\n```\n\n### Commands\n\n```python\n# Run shell command\nresult = sandbox.commands.run('ls -la /workspace')\nprint(result.stdout)\n\n# With environment variables\nresult = sandbox.commands.run(\n    'echo \"Key: $API_KEY\"',\n    env={'API_KEY': 'secret'}\n)\n\n# Custom working directory\nresult = sandbox.commands.run(\n    'pwd',\n    working_dir='/workspace/myproject'\n)\n```\n\n### Environment Variables\n\n```python\n# Get all\nenv_vars = sandbox.env.get_all()\n\n# Set all (replace)\nsandbox.env.set_all({'API_KEY': 'sk-...', 'DEBUG': 'true'})\n\n# Update (merge)\nsandbox.env.update({'NEW_VAR': 'value'})\n\n# Delete\nsandbox.env.delete(['VAR_TO_DELETE'])\n\n# Get single\nvalue = sandbox.env.get('API_KEY')\n\n# Set single\nsandbox.env.set('API_KEY', 'sk-...')\n```\n\n### Process Management\n\n```python\n# List processes\nprocesses = sandbox.list_processes()\nfor proc in processes:\n    print(f\"{proc.pid}: {proc.name} (CPU: {proc.cpu_percent}%)\")\n\n# Kill process\nsandbox.kill_process(1234)\n```\n\n### Metrics & Health\n\n```python\n# Get metrics\nmetrics = sandbox.get_metrics_snapshot()\nprint(f\"Total executions: {metrics.total_executions}\")\nprint(f\"Uptime: {metrics.uptime_seconds}s\")\n\n# Health check\nhealth = sandbox.get_health()\nprint(f\"Status: {health.status}\")\n\n# Cache management\ncache_stats = sandbox.cache.stats()\nprint(f\"Cache size: {cache_stats.cache.size}\")\n\nsandbox.cache.clear()\n```\n\n### Desktop Automation\n\n```python\n# Note: Requires 'desktop' template\nsandbox = Sandbox.create(template='desktop', api_key='...')\n\n# Mouse operations\nsandbox.desktop.mouse_click(x=500, y=300, button='left')\nsandbox.desktop.mouse_move(x=600, y=400)\nsandbox.desktop.drag_drop(from_x=100, from_y=100, to_x=300, to_y=300)\n\n# Keyboard\nsandbox.desktop.keyboard_type('Hello, World!')\nsandbox.desktop.keyboard_press('Return')\nsandbox.desktop.keyboard_press('Control_L+c')  # Ctrl+C\n\n# Clipboard\nsandbox.desktop.clipboard_set('text to copy')\ncontent = sandbox.desktop.clipboard_get()\n\n# Screenshots\nscreenshot = sandbox.desktop.screenshot()\nwith open('screenshot.png', 'wb') as f:\n    f.write(screenshot)\n\n# OCR (text recognition)\ntext = sandbox.desktop.ocr()\nprint(f\"Found text: {text}\")\n\n# Find elements\nelement = sandbox.desktop.find_element(text='Button')\nif element:\n    sandbox.desktop.mouse_click(x=element['x'], y=element['y'])\n\n# VNC connection\nvnc_info = sandbox.desktop.get_vnc_url()\nprint(f\"VNC URL: {vnc_info['url']}\")\n```\n\n### Interactive Terminal (WebSocket)\n\n```python\nimport asyncio\n\nasync def terminal_session():\n    async with sandbox.terminal.connect() as term:\n        # Send commands\n        await sandbox.terminal.send_input(term, 'ls -la\\n')\n        \n        # Receive output\n        async for message in sandbox.terminal.iter_output(term):\n            if message['type'] == 'output':\n                print(message['data'], end='')\n\nasyncio.run(terminal_session())\n```\n\n## Advanced Configuration\n\n```python\nsandbox = Sandbox.create(\n    template='code-interpreter',\n    api_key='your-api-key',\n    vcpu=4,              # 4 vCPUs\n    memory_mb=4096,      # 4GB RAM\n    disk_gb=20,          # 20GB disk\n    region='us-west-2',  # Specific region\n    timeout=600,         # 10 minute timeout\n    env_vars={           # Pre-set environment variables\n        'DATABASE_URL': 'postgres://...',\n        'API_KEY': 'sk-...'\n    }\n)\n```\n\n## Error Handling\n\n```python\nfrom bunnyshell.errors import (\n    BunnyshellError,\n    AuthenticationError,\n    NotFoundError,\n    FileNotFoundError,\n    CodeExecutionError,\n    RateLimitError,\n    ServerError\n)\n\ntry:\n    with Sandbox.create(template='code-interpreter', api_key='...') as sandbox:\n        result = sandbox.run_code(\"print('Hello')\")\n        \nexcept AuthenticationError as e:\n    print(f\"Auth failed: {e.message}\")\n    print(f\"Request ID: {e.request_id}\")\n    \nexcept FileNotFoundError as e:\n    print(f\"File not found: {e.path}\")\n    \nexcept CodeExecutionError as e:\n    print(f\"Code execution failed: {e.message}\")\n    print(f\"Exit code: {e.exit_code}\")\n    \nexcept RateLimitError as e:\n    print(f\"Rate limited: {e.message}\")\n    \nexcept BunnyshellError as e:\n    print(f\"API error: {e.message}\")\n    print(f\"Status code: {e.status_code}\")\n```\n\n## Best Practices\n\n### 1. Always Use Context Managers\n\n```python\n# \u2705 GOOD: Automatic cleanup\nwith Sandbox.create(...) as sandbox:\n    # Work here\n    pass\n# Sandbox automatically deleted\n\n# \u274c BAD: Manual cleanup (easy to forget)\nsandbox = Sandbox.create(...)\n# ... work ...\nsandbox.kill()  # Easy to forget if error occurs!\n```\n\n### 2. Set Timeouts\n\n```python\n# Prevent infinite execution\nresult = sandbox.run_code(code, timeout=30)\n```\n\n### 3. Optimize Performance\n\n```python\n# Set environment variables once\nsandbox.env.set_all({'API_KEY': 'sk-...', 'DB_URL': '...'})\n\n# Now available in all executions\nfor i in range(100):\n    sandbox.run_code('...')  # Env vars already set\n```\n\n## Use Cases\n\n### OpenAI: ChatGPT Code Interpreter\n\n```python\ndef execute_user_code(user_code: str):\n    with Sandbox.create(template=\"code-interpreter\") as sandbox:\n        result = sandbox.run_code(user_code, timeout=30)\n        return {\n            \"output\": result.stdout,\n            \"error\": result.stderr,\n            \"rich_outputs\": result.rich_outputs  # Images, plots\n        }\n```\n\n### Stripe: Payment Reports\n\n```python\nwith Sandbox.create(template=\"code-interpreter\") as sandbox:\n    sandbox.env.set('STRIPE_SECRET_KEY', os.getenv('STRIPE_SECRET_KEY'))\n    \n    result = sandbox.run_code(\"\"\"\nimport stripe\nstripe.api_key = os.environ['STRIPE_SECRET_KEY']\ncharges = stripe.Charge.list(limit=100)\n# Generate report...\n    \"\"\")\n```\n\n## Documentation\n\n- **Cookbook**: See [cookbook/python](../../cookbook/python/) for 10 complete examples\n- **API Reference**: [docs.bunnyshell.com](https://docs.bunnyshell.com)\n- **GitHub**: [github.com/bunnyshell/sandbox-sdks](https://github.com/bunnyshell/sandbox-sdks)\n\n## License\n\nMIT License - See [LICENSE](../LICENSE) file for details.\n\n## Support\n\n- **Issues**: [github.com/bunnyshell/sandbox-sdks/issues](https://github.com/bunnyshell/sandbox-sdks/issues)\n- **Email**: support@bunnyshell.com\n- **Documentation**: [docs.bunnyshell.com](https://docs.bunnyshell.com)\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Official Python SDK for Bunnyshell Sandboxes",
    "version": "0.1.7",
    "project_urls": {
        "Documentation": "https://docs.bunnyshell.com",
        "Homepage": "https://bunnyshell.com",
        "Issues": "https://github.com/bunnyshell/bunnyshell-python/issues",
        "Repository": "https://github.com/bunnyshell/bunnyshell-python"
    },
    "split_keywords": [
        "bunnyshell",
        " cloud",
        " containers",
        " microvm",
        " sandbox",
        " vm"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "ebb8610c0cc87a6186f8bca3e9b151f2699c9ec737d8753f309bf7b940583363",
                "md5": "8cabcb35fbf2b27c9576b0eeea3612bb",
                "sha256": "f279e834600bd0c080c3b083abdb6d2d4252245eb4a667c42b9486b6c00e2b6c"
            },
            "downloads": -1,
            "filename": "bunnyshell-0.1.7-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "8cabcb35fbf2b27c9576b0eeea3612bb",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 66718,
            "upload_time": "2025-11-05T22:31:48",
            "upload_time_iso_8601": "2025-11-05T22:31:48.320375Z",
            "url": "https://files.pythonhosted.org/packages/eb/b8/610c0cc87a6186f8bca3e9b151f2699c9ec737d8753f309bf7b940583363/bunnyshell-0.1.7-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "51e63962b6235692c36a94ed5e4f4f9b30122b856b5c27e2bace891c046225fb",
                "md5": "96a1ef3b72be866e3a4b3c79d8602c20",
                "sha256": "837419eb971fe7e30d2c29977f43091454e4096e31f27c87c35b3d638d0bf0d2"
            },
            "downloads": -1,
            "filename": "bunnyshell-0.1.7.tar.gz",
            "has_sig": false,
            "md5_digest": "96a1ef3b72be866e3a4b3c79d8602c20",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 119582,
            "upload_time": "2025-11-05T22:31:50",
            "upload_time_iso_8601": "2025-11-05T22:31:50.018289Z",
            "url": "https://files.pythonhosted.org/packages/51/e6/3962b6235692c36a94ed5e4f4f9b30122b856b5c27e2bace891c046225fb/bunnyshell-0.1.7.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-11-05 22:31:50",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "bunnyshell",
    "github_project": "bunnyshell-python",
    "github_not_found": true,
    "lcname": "bunnyshell"
}
        
Elapsed time: 3.41880s