Metadata-Version: 2.4
Name: vcti-error
Version: 1.0.4
Summary: Structured exception handling and error classification for Python
Author: Visual Collaboration Technologies Inc.
Requires-Python: <3.15,>=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Requires-Dist: pytest-cov; extra == "test"
Provides-Extra: lint
Requires-Dist: ruff; extra == "lint"
Dynamic: license-file

# Error Framework

## Purpose

VCollab Python scripts are typically invoked by partner programs or
external applications. When a script finishes, the invoking application
needs to know what happened — did it succeed, fail due to a missing
file, a license issue, or something unexpected?

In standard subprocess semantics, the **exit code is the only reliable
contract** between caller and callee. The `vcti-error` package
formalizes this contract by ensuring that every known exception type
maps to a stable, unique integer exit code — consistent across all
VCollab applications.

This package has **zero external dependencies**.

### When to use this package

Use `vcti-error` when your Python script is invoked as a **subprocess** by
a partner application (e.g., VCollab Presenter, an external scheduler, or
a CI pipeline) and the caller interprets the process exit code to decide
what happened. If your code runs inside a larger Python process where
exceptions propagate normally, you likely don't need this package.

---

## Installation

```bash
# Latest main branch
pip install git+https://github.com/vcollab/vcti-python-error.git

# Specific version
pip install git+https://github.com/vcollab/vcti-python-error.git@v1.0.4
```

### From a GitHub Release

Download the wheel from the
[Releases](https://github.com/vcollab/vcti-python-error/releases)
page and install directly:

```bash
pip install vcti_error-1.0.4-py3-none-any.whl
```

### In `requirements.txt`

```
vcti-error @ git+https://github.com/vcollab/vcti-python-error.git@v1.0.4
```

### In `pyproject.toml` dependencies

```toml
dependencies = [
    "vcti-error @ git+https://github.com/vcollab/vcti-python-error.git@v1.0.4",
]
```

---

## Quick Start

### The main() pattern

All CLI entry points follow this structure:

```python
import logging
import traceback
from vcti.error import error_code

logger = logging.getLogger(__name__)

def main() -> int:
    try:
        cli_app()
        return 0
    except Exception as error:
        logger.error("Error: %s", error)
        traceback.print_exc()
        return error_code(type(error))
```

- `0` — success
- `!= 0` — failure (category encoded in exit code)
- `error_code(type(error))` — resolves exception class to exit code
- Unmapped exceptions fall back to `UNSPECIFIED` (exit code 1)

### Look up exit codes

```python
from vcti.error import error_code

error_code(FileNotFoundError)   # 2
error_code(KeyError)            # 4
error_code(RuntimeError)        # 1 (UNSPECIFIED fallback)
```

### Resolve exception class by name

```python
from vcti.error import exception_class

exception_class("file_not_found")       # FileNotFoundError
exception_class("variable_undefined")   # VariableUndefined
exception_class("unknown_name")         # Exception (fallback)
```

### Create system errors with errno

```python
from vcti.error import system_error

err = system_error(FileNotFoundError, "path/to/file")
# FileNotFoundError(errno.ENOENT, "No such file or directory", "path/to/file")
raise err
```

---

## Registered Exception Types

| Exception type | Exit code | Python class | Description |
|----------------|-----------|-------------|-------------|
| `UNSPECIFIED` | 1 | `Exception` | Fallback for unmapped exceptions |
| `FILE_NOT_FOUND` | 2 | `FileNotFoundError` | Expected file not found |
| `LICENSE_ERROR` | 3 | `LicenseError` | License validation failed |
| `KEY_ERROR` | 4 | `KeyError` | Missing key or identifier |
| `VARIABLE_UNDEFINED` | 5 | `VariableUndefined` | Undefined workflow variable |

**Stability guarantee:** Exit code values are part of the external
contract with partner applications. Once assigned, a value must never
be changed or reused.

---

## Public API

| Function | Purpose |
|----------|---------|
| `error_code(exception_class)` | Returns exit code for an exception class |
| `exception_class(name)` | Resolves exception type name to Python class (case-insensitive) |
| `system_error(exception_class, *args)` | Creates exception with appropriate errno |

---

## Documentation

- [Changelog](CHANGELOG.md) — Release history and versioning policy
- [Design](docs/design.md) — Architecture decisions and mapping pipeline
- [Source Guide](docs/source-guide.md) — File descriptions and execution flow traces
- [Extension Guide](docs/extending.md) — How to add new exception types
- [API Reference](docs/api.md) — Autodoc for all modules
