Skip to main content
▗▄▄▄▖    ▗▄▄▄▖▄▄▄▄  ▗▞▀▘▗▞▀▜▌▄▄▄▄     ■  ▗▞▀▜▌   ■  ▗▞▀▚▖▄▄▄▄  
▐▌         █  █   █ ▝▚▄▖▝▚▄▟▌█   █ ▗▄▟▙▄▖▝▚▄▟▌▗▄▟▙▄▖▐▛▀▀▘█ █ █ 
▐▛▀▀▘      █  █   █          █   █   ▐▌         ▐▌  ▝▚▄▄▖█   █ 
▐▌       ▗▄█▄▖                       ▐▌         ▐▌             
                                     ▐▌         ▐▌             

License Last commit Repo size PyPI version Python versions

Python is honest, but not generous.

When code breaks, it hands you an exception type and a message. You see where it failed, but the reasoning that led there is scattered across stack frames, source files, and the half-forgotten state sitting in locals. The error itself is real. The understanding is not.

F. Incantatem reconstructs that understanding. It reads the exception in full context, including the stack trace, the relevant source, the local state at the point of rupture, and produces an explanation of why it happened, what caused it, and what to do about it. The difference between knowing a crash occurred and knowing why it occurred is the difference between debugging and guessing.

It integrates without ceremony: decorator, CLI, or IPython extension. Add it where you want. Leave the rest alone.

Features

F. Incantatem The tool displays a condensed traceback, identifies the root cause, and provides actionable fixes — all automatically. You can even chat with your exception.

  • Contextual Analysis — Examines stack traces, source code, and local variables to produce reasoned explanations
  • Multiple Integration Points — Use as a decorator, command-line tool, or IPython extension
  • Inference Flexibility — OpenRouter, OpenAI, or any OpenAI-compatible API; support for local inference via Ollama or vLLM
  • Rich Output — Optional Markdown formatting and interactive chat for follow-up questions
  • Zero Core Dependencies — The library itself is lightweight; optional features remain modular
  • Security Conscious — Optional cautious mode to automatically redact secrets and PII before transmission

Table of Contents

Installation

The library installs with zero dependencies:

pip install fincantatem

Or with uv:

uv add fincantatem

Optional Features

For rich Markdown output in your terminal:

pip install "fincantatem[pretty]"

For automatic redaction of secrets and PII:

pip install "fincantatem[cautious]"
python -m spacy download en_core_web_lg

Usage

As a Decorator

Wrap any function to get AI-powered error analysis when exceptions occur:

from fincantatem import finite

@finite
def process_data(value: int):
    return 100 / value

process_data(0)  # Exception triggers analysis

Decorator Options

@finite(
    preset="openrouter",  # "openrouter" or "openai" (default: "openrouter")
    snippets=True,        # Send code snippets or full source (default: True)
    chat=False,           # Enable interactive chat after analysis (default: False)
    cautious=False,       # Redact secrets and PII (default: False)
)
def my_function():
    pass

From the Command Line

Run any Python script with automatic error analysis:

python -m fincantatem script.py

Options

python -m fincantatem script.py \
  --preset openrouter \
  --snippets true \
  --cautious false
  • -p, --preset — Inference preset to use (default: openrouter)
  • -s, --snippets — Show code snippets instead of full source (default: true)
  • -c, --cautious — Enable secret and PII redaction (default: false)

In Jupyter/IPython

Load as an extension to get automatic error analysis on cell failures:

%load_ext fincantatem

Every exception can be analyzed automatically without (onerous) modification to your code.

Examples

1. The "Silent API Change"

Scenario: You are consuming a third-party API. The code has worked for months. Suddenly, it breaks with a KeyError, but the API status code is still 200.

@finite
def sync_user_data(user_id: str):
    response = requests.get(f"https://api.vendor.com/users/{user_id}")
    payload = response.json()
    
    # CRASH: KeyError: 'active_subscription'
    if payload["data"]["active_subscription"]["status"] == "active":
        update_local_db(user_id)

Why this is hard: Standard debugging requires you to add print statements to dump payload, rerun the script, and inspect the JSON structure. But here's the insidious part: you might not be able to reproduce it because the API behavior depends on the specific user_id, time of day, or rate limit state.

F. Incantatem Insight: It captures the actual response body that caused the crash—no reproduction needed.

The Explanation:

"The code expects payload['data']['active_subscription']['status']. However, the actual payload captured during the crash contains {'error': 'RateLimitExceeded', 'retry_after': 60, 'request_id': 'req_8x2k9'}.

The API vendor is returning application-level errors with HTTP 200 status codes—a common but poor API design pattern. Your code assumes response.status_code == 200 means success, but you need defensive key checking or schema validation. The request_id in the payload suggests you can report this specific failure to their support."


2. The "Mutable Default Argument" Trap

Scenario: A classic Python foot-gun that manifests as "data bleeding" between requests in a long-running process.

@finite
def add_audit_log(event, _buffer=[]):  # The bug is here
    _buffer.append(event)
    if len(_buffer) >= 3:
        flush_to_disk(_buffer)
        return True
    return False

# Imagine this running in a server context
add_audit_log("login") 
add_audit_log("logout")
add_audit_log("view_page")  # Flushes
add_audit_log("login_admin")  # CRASH or security leak

Why this is hard: This is a Heisenbug—it disappears when you try to debug it with unit tests because each test starts fresh. The failure only emerges in production after the process has been running long enough. You might see symptoms like: duplicate database entries, security logs showing events from different users mixed together, or memory growing unbounded.

F. Incantatem Insight: It shows you the identity of the buffer object and its complete contents at crash time.

The Explanation:

"The local variable _buffer contains ['login', 'logout', 'view_page', 'login_admin']—4 items when you expected 1.

This is the mutable default argument trap. Your function signature is def add_audit_log(event, _buffer=[]). The list contains events from previous function calls because Python evaluates default arguments once at definition time, then reuses the same object. You can verify this yourself by adding print(id(_buffer)) at the start of the function—you'll see the same memory address across calls. In this case, the presence of 4 accumulated items instead of 1 is the smoking gun."


3. The "Unicode Normalization Bomb"

Scenario: A web application that's been running fine suddenly starts having bizarre duplicate key violations in your database, but only for certain users with international names.

@finite
def create_user(username: str, email: str):
    # CRASH: IntegrityError - duplicate key value violates unique constraint "users_username_key"
    # But you just checked and the username doesn't exist!
    user = User.objects.create(username=username, email=email)
    return user

Why this is hard: Looking at your database, you see one entry for "José" and you're trying to insert "José"—they look identical. Your uniqueness check passes because Python's == operator says they're different. The bug appears random because it only affects certain character combinations.

F. Incantatem Insight: It shows you the actual byte representation of the username variable.

The Explanation:

"The variable username contains the string 'José' which appears as 'Jos\\u00e9' (5 characters, with a single codepoint é). However, your database already contains a user with username 'Jose\\u0301' (5 characters, with e + combining acute accent).

These are two different Unicode representations of the same visual character—NFC (composed) vs. NFD (decomposed) normalization. Your form input is receiving NFD-normalized text (common on macOS), while your database contains NFC (common on Windows/Linux). Python's User.objects.filter(username=username) doesn't find a match because you're comparing different byte sequences. The fix: Apply unicodedata.normalize('NFC', username) before any database operation. This affects approximately 2,184 Unicode characters with multiple representations."


Optional Features

For rich Markdown output in your terminal:

pip install "fincantatem[pretty]"

For automatic redaction of secrets and PII:

pip install "fincantatem[cautious]"

Or with uv:

uv add "fincantatem[pretty]"
uv add "fincantatem[cautious]"

Configuration

Environment Variables

Variable Default Description
FI_PRESET openrouter Preset name or custom identifier
FI_API_KEY None API key for the inference service
FI_URL https://openrouter.ai/api/v1/chat/completions OpenAI-compatible API endpoint
FI_MODEL google/gemini-2.5-flash Model identifier (overrides preset)

Presets

OpenRouter (default)

FI_API_KEY=sk_your_key python -m fincantatem script.py

OpenAI

FI_PRESET=openai FI_API_KEY=sk_your_key python -m fincantatem script.py

Local Inference (e.g., Ollama)

FI_PRESET=local \
  FI_URL=http://localhost:11434/v1/chat/completions \
  FI_MODEL=llama2 \
  python -m fincantatem script.py

Data Risks

By default, the following is transmitted to the inference API:

  • Source code and file paths
  • Full stack trace
  • Exception messages
  • Local variables in the call stack
  • Any values or secrets present in your code

This may be undesirable in environments handling sensitive information. F. Incantatem offers several mitigation strategies.

Cautious Mode

When enabled, cautious mode attempts to automatically redact secrets and personally identifiable information from source code and local variables before transmission. Enable it with:

@finite(cautious=True)
def my_function():
    pass

Or via the CLI:

python -m fincantatem script.py --cautious true

How It Works

Cautious mode employs two complementary detection mechanisms:

1. Secret Detection — Uses pattern matching and entropy analysis (via detect-secrets) to identify API keys, tokens, private keys, and other cryptographic material. Detected secrets are replaced with a deterministic placeholder derived from their SHA-256 hash, allowing correlation without exposing the actual value.

2. PII Detection — Uses Presidio, a Microsoft library trained to recognize personally identifiable information including names, email addresses, phone numbers, credit card numbers, and more. Detected entities are anonymized in-place.

Important Caveats

Cautious mode is a best-effort mechanism, not a guarantee. Users should understand its limitations:

  • Incompleteness — Detection patterns may miss secrets in unusual formats or custom PII that doesn't match known patterns. A secret hidden in a comment, a name disguised as a variable, or a partially exposed credential may slip through.

  • False Positives — Legitimate values (e.g., a string containing "password") may be incorrectly flagged as sensitive. This can result in unnecessary redaction of benign data.

  • Heuristic Nature — Both detect-secrets and Presidio rely on heuristics rather than definitive signatures. They are probabilistic tools designed for high recall at the cost of some precision.

  • No Guarantee — Cautious mode reduces risk but does not eliminate it. Never enable cautious mode as a substitute for data governance. If transmitting source code to any external service is unacceptable in your environment, cautious mode is insufficient—use local inference instead.

  • Performance Cost — Secret and PII scanning adds latency to exception processing, particularly on large codebases or with complex stack traces.

Recommendation: Cautious mode is suitable for development workflows where occasional false redactions are tolerable and where the code does not contain highly sensitive information by design. For production systems handling financial data, credentials, or protected health information, consider local inference or private endpoints instead.

Alternative Mitigation Strategies

  1. Local Inference — Run a model locally via Ollama or vLLM, keeping all data on your machine
  2. Private API — Route requests through a trusted organizational endpoint with appropriate access controls and data handling policies
  3. Selective Decoration — Use @finite only on non-sensitive functions, or on functions unlikely to encounter data exposure

Performance Considerations

We have not benchmarked FI meaningfully yet, but we can make a very reliable estimate of its performance characterstics at runtime. There are two performance profiles:

Case Performance Characteristics
No exception If the wrapped function does microseconds or more of work (I/O, JSON, allocations, network, DB, nontrivial Python logic), this overhead is usually negligible.

If the wrapped function is a tiny hot-loop primitive (simple arithmetic, attribute access) called millions of times, @finite can be a noticeable slowdown (often tens of percent or more) because the baseline call is already extremely small.
Exception All of the characteristics of the no exception case, plus the additional overhead of the exception handling, analysis and LLM call. You're likely to notice a 2-3s delay in wind-down at exception time.

Key Features

Basic Debugging

from fincantatem import finite

@finite
def divide(a: int, b: int):
    return a / b

divide(10, 0)  # Receives AI analysis of ZeroDivisionError

Interactive Chat

@finite(chat=True)
def load_config(path: str):
    with open(path) as f:
        return json.loads(f.read())

# After analysis, continue conversing with the AI
load_config("missing.json")

Cautious Mode

@finite(cautious=True)
def query_api(token: str):
    response = requests.get("https://api.example.com", headers={
        "Authorization": f"Bearer {token}"
    })
    return response.json()

# Token will be redacted before transmission
query_api("secret_key_123")

Requirements

  • Python 3.10+
  • Optional: rich for formatted output
  • Optional: presidio-analyzer, presidio-anonymizer, spacy, detect-secrets, bip-utils for cautious mode

License

Apache License 2.0

Acknowledgements

The pipe utility is lifted from the toolz library.

Status

F. Incantatem is actively maintained and used in production environments.

Release files for fincantatem 0.2.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fincantatem 0.2.3
File Size Uploaded
fincantatem-0.2.3.tar.gz 490.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fincantatem 0.2.3
File Interpreter ABI Platform
fincantatem-0.2.3-py3-none-any.whl Python 3 none any Details

Total release size: 523.5 kB

Release files / fincantatem-0.2.3.tar.gz

Download URL fincantatem-0.2.3.tar.gz
Size 490.0 kB
Tags Source
SHA-256 checksum
How to use checksums
5b14aee4faa9b8c55078926061d0f7d5a62720ca46e96bbdc2ef35fa6f177458
BLAKE2b-256 checksum
How to use checksums
02990dfb250177d05fbc058e46ab798899a2affd9a400765300e99d84a0ce27f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Dec 16, 2025.

Transparency log

Release files / fincantatem-0.2.3-py3-none-any.whl

Download URL fincantatem-0.2.3-py3-none-any.whl
Size 33.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9fcaf8f67f967b399bb6eaf8ec1956c7f8b635310e062ec8a9f1bcb14860bc0a
BLAKE2b-256 checksum
How to use checksums
41be101f5912278b7bc1827c96dcbf498091d365c5ad5cfcf7ca4af7a8bf0d58
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Dec 16, 2025.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.3 This release

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page