Metadata-Version: 2.4
Name: embdes
Version: 1.0.2
Summary: Professional build automation for EmbedDesire Python applications
Home-page: https://github.com/Qarvexium-Ops/embdes
Author: Qarvexium Team
Author-email: Qarvexium <Qarvexium@proton.me>
Keywords: embdes,embeddesire,pyinstaller,build,assets,resources,embedding,automation
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Build Tools
Classifier: Topic :: Utilities
Requires-Python: >=3.7
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: author
Dynamic: home-page
Dynamic: license-file
Dynamic: requires-python

# EmbedDesire - Professional Python Asset Embedding

**embdes** is a production-ready tool for embedding static resources (images, sounds, text files, folders) directly into Python applications and building standalone executables with PyInstaller.

## Features

- **Embed resources** into Python applications with optional compression
- **Automatic extraction** with built-in caching (100% cache hit rate)
- **Development/Runtime modes** — swap between reading from disk and reading from embedded data with one call
- **PyInstaller integration** — automated build process via `embdes` CLI
- **Persistent storage** — save/load embedded data manually if needed
- **Production-ready** — comprehensive logging, validation, error handling
- **Cross-platform** — Windows, macOS, Linux support
- **Global CLI** — callable from any directory as `embdes` command

---

## Installation

```bash
pip install embdes
```

---

## How It Works

`embed()` always returns the original file path. What changes between modes is what happens under the hood:

| Mode | `set_runtime_mode(...)` | `embed()` behavior |
|---|---|---|
| **Runtime mode** | `True` | Skips embedding — just returns the path so you read from disk as normal |
| **Embedded mode** | `False` | Reads and stores the file in memory — the returned path is used to access embedded data in the built exe |

This means your app code stays identical in both modes — you always just `open(embed(...))`.

---

## Basic Usage

```python
from embeddesire import embed, set_runtime_mode

set_runtime_mode(True)  # Read from disk (development)

embedded = embed(".\\hello.txt", id="hello", compress=True)

with open(embedded, "r") as f:
    print(f.read())
```

Switch to `set_runtime_mode(False)` when you want to work with embedded data (e.g. inside a built exe where the original files no longer exist on disk).

---

## Building a Standalone Exe

### Using `embdes` (Recommended)

`embdes` automates everything. Write your app normally, then build with one command.

**app.py**

```python
from embeddesire import embed, set_runtime_mode

set_runtime_mode(True)  # During dev, reads from disk

config  = embed("config.json",    id="config",  compress=False)
sprites = embed("assets/sprites/",id="sprites", compress=True)
sounds  = embed("assets/sounds/", id="sounds",  compress=True)

with open(config, "r") as f:
    print(f.read())

# Use sprites and sounds paths however your app needs them
```

**Build**

```bash
embdes app.py
```

`embdes` will:
1. Validate all paths passed to `embed()` exist on disk
2. Execute your script to collect all `embed()` calls
3. Save the embedded data to `embedded_data.pkl`
4. Call PyInstaller with the pickle bundled in automatically
5. Clean up temporary files

The resulting exe in `dist/` has all your assets baked in — no external files needed.

---

### Manual Build (Full Control)

Use this if you have an existing PyInstaller setup, a `.spec` file, or prefer not to use `embdes`.

**Step 1 — Generate the pickle**

Run a small preparation script once before building:

```python
# prepare.py
from embeddesire import embed, save_embedded, set_runtime_mode

set_runtime_mode(False)  # Must be False so embed() actually stores the data

embed("config.json",    id="config",  compress=False)
embed("assets/sprites/",id="sprites", compress=True)
embed("assets/sounds/", id="sounds",  compress=True)

save_embedded("embedded_data.pkl")
```

```bash
python prepare.py
```

**Step 2 — Pass the pickle to PyInstaller yourself**

```bash
pyinstaller app.py --onefile --add-data "embedded_data.pkl;."
```

**Step 3 — Load it inside the exe**

```python
# app.py
import sys, os
from embeddesire import embed, set_runtime_mode, load_embedded, extract

if getattr(sys, "frozen", False):
    # Inside the exe — load the bundled pickle, then use extract() to get files out
    load_embedded(os.path.join(sys._MEIPASS, "embedded_data.pkl"))
    config_dir = extract("config")
    config_path = os.path.join(config_dir, "config.json")
    with open(config_path, "r") as f:
        print(f.read())
else:
    # Development — read from disk as normal
    set_runtime_mode(True)
    config = embed("config.json", id="config", compress=False)
    with open(config, "r") as f:
        print(f.read())
```

> **Note:** `extract()` only works on data already in memory from `load_embedded()`. It does not read from disk on its own.

---

## Embedding Options

### Disable compression for pre-compressed formats

Compression (`compress=True`) is on by default. Turn it off for files already compressed to avoid wasted CPU and larger output:

```python
embed("photo.jpg",   id="photo",   compress=False)
embed("music.mp3",   id="music",   compress=False)
embed("archive.zip", id="archive", compress=False)
```

### Extract to a specific location (manual builds)

```python
path = extract("sprites", output_path="./runtime/sprites")
```

---

## Managing Embedded Resources

```python
from embeddesire import get_embedded_resources, remove_embedded_resource, get_cache_stats, clear_cache

# See all IDs currently embedded in memory
print(get_embedded_resources())
# ['config', 'sprites', 'sounds']

# Remove one to free memory
remove_embedded_resource("sounds")

# How many extract() calls were served from cache vs freshly written to disk
print(get_cache_stats())
# {'hits': 8, 'misses': 3, 'current_size': 0}

# Delete all temp extraction directories (next extract() call will re-extract)
clear_cache()
```

---

## Error Handling

```python
from embeddesire import embed, extract, load_embedded
from embeddesire import EmbedError, ExtractError, PersistenceError, ValidationError

try:
    embed("nonexistent/path/file.txt", id="data")
except EmbedError as e:
    print(f"Embed failed: {e}")

try:
    extract("id_that_was_never_embedded")
except ExtractError as e:
    print(f"Extract failed: {e}")

try:
    load_embedded("missing_or_corrupt.pkl")
except PersistenceError as e:
    print(f"Load failed: {e}")
```

---

## Logging

```python
from embeddesire import configure_logging, LogLevel

configure_logging(LogLevel.DEBUG)    # Full trace — sizes, cache hits, paths
configure_logging(LogLevel.WARNING)  # Quiet — only problems reported
configure_logging(LogLevel.INFO, format_string="%(levelname)s | %(message)s")
```

---

## CLI Reference

```bash
embdes <script.py> [pyinstaller_args...]
```

Arguments after the script are passed directly to PyInstaller:

```bash
embdes app.py                           # Basic build
embdes app.py --windowed                # No console window
embdes app.py --windowed --icon=app.ico # With custom icon
embdes app.py --name=MyApp              # Custom exe name
embdes app.py --add-data "extra.db;."   # Bundle extra files too
```

---

## API Reference

| Function | Description |
|---|---|
| `embed(path, id, compress=True)` | Embed a file or folder; always returns the original path |
| `extract(id, output_path=None)` | Extract an in-memory resource to disk (cached) |
| `set_runtime_mode(value)` | `True` = read from disk, `False` = read from embedded data |
| `save_embedded(file_path)` | Save all embedded data to a pickle file |
| `load_embedded(file_path)` | Load embedded data from a pickle file into memory |
| `clear_cache()` | Delete all temp extraction directories |
| `get_cache_stats()` | Return `{hits, misses, current_size}` dict |
| `get_embedded_resources()` | List all currently embedded IDs |
| `remove_embedded_resource(id)` | Remove a resource from memory |
| `configure_logging(level, format_string)` | Set log level and format |

---

## Configuration

```bash
export EMBEDDESIRE_COMPRESSION_LEVEL=9      # zlib level 0-9 (default 6)
export EMBEDDESIRE_MAX_FILE_SIZE=50000000   # bytes (default 100 MB)
export EMBEDDESIRE_MAX_CACHE_SIZE=250000000 # bytes (default 500 MB)
```

---

## Requirements

- Python 3.7+
- PyInstaller (required only for `embdes` CLI builds)

## License

MIT License — See LICENSE file
