Metadata-Version: 2.4
Name: ladon-clear-exceptions-n-warnings
Version: 1.2.1
Summary: Structured drop-in extensions for Python's built-in exceptions and warnings. Formatted output, GDPR-safe production mode, hookable logging, zero boilerplate.
Project-URL: Homepage, https://soss.page
Project-URL: Repository, https://github.com/soss-community/ladon_clear_exceptions_n_warnings
Project-URL: Bug Tracker, https://github.com/soss-community/ladon_clear_exceptions_n_warnings/issues
Project-URL: Contact, https://github.com/soss-community
Author: sora7672
License: SOSS Module & Package License (SOSS-MPL) v1.0
        ==============================================
        Copyright (c) 2026 sora7672 & SOSS Community
        Website: https://soss.page
        Organization: https://github.com/soss-community
        
        NOTE: This license may be updated over time.
        Always refer to the latest version at: https://github.com/soss-community
        The version included in this repository was current at time of release.
        
        ---
        
        ACCEPTANCE
        
        By incorporating this package or module into your software, code, or project in any form,
        you confirm that you have read and understood the terms of this license.
        Usage constitutes full acceptance of all conditions stated below.
        
        ---
        
        1. PERMITTED USE
        
           This package/module may be used freely by anyone, for private or commercial purposes,
           subject to the conditions of this license.
        
        2. NO MODIFICATION & DISTRIBUTION
        
           Modification of the original source files is not permitted.
           Modified versions of this package/module may not be redistributed in any form.
        
           Exception:
           You may create child classes or wrapper implementations that extend
           the functionality through inheritance or composition. These must be clearly marked
           as separate works and must not replace or repackage the original.
           This exception exists to allow external verification of behavior, if an issue
           arises in a child class, it can be clearly distinguished from the original.
        
        3. CREDITS & ATTRIBUTION
        
           Any software, project, or codebase that uses this package/module must clearly
           credit it in one of the following locations:
           - A credits section in the README
           - An About screen or page within the software
           - Any other clearly visible location within the project documentation
        
           The credit entry must include:
           - A link to the SOSS community page: https://soss.page
           - The name of the module/package (e.g. "taipan-logger")
           - If multiple SOSS packages/modules are used, all must be listed individually.
        
        4. COMMERCIAL USE
        
           Commercial use of this package/module is explicitly permitted.
           In addition to the credit requirement above, commercial users are strongly encouraged
           to open a discussion on the official GitHub repository with the tag:
           "Using this package/module for my software"
           including the name of their software.
           This will be read and closed by the maintainer.
           This does not replace the credit requirement.
        
        5. ISSUE REPORTING
        
           Issues, bugs, and feature requests are managed exclusively via the official
           GitHub repository of the respective package/module.
           Before reporting an issue, you must have read the README and documentation,
           and must understand the intended purpose of the software.
        
        6. SUPPORT
        
           No official support is provided by the developers or maintainers.
           If you or your organization wish to offer support for this package/module,
           including commercial support, you may apply via the SOSS community page:
           https://soss.page
        
           Approved support providers will be listed there publicly.
           No individual or organization may present themselves as an "official" support
           provider without being listed on the SOSS community page.
        
           Listings may be revoked at any time if quality standards are not maintained.
           The affected party will be notified and the listing updated accordingly.
        
        7. NO MONETIZATION
        
           This package/module is not for sale under any circumstances, regardless of the offer.
        
           Any new maintainer who takes over this project must agree to the same conditions.
           Monetization of this package/module by any maintainer is strictly prohibited.
        
           In the event of a violation, third parties are entitled to pursue legal action
           against the maintainer and to seek the appointment of a new maintainer
           who agrees to these conditions.
        
        8. LEGAL & DISPUTE RESOLUTION
        
           In the event of a dispute arising from improper use or violation of this license,
           all legal costs shall be borne by the party found to be in violation.
           This applies internationally to the extent permitted by applicable law.
        
        9. NO WARRANTY & NO LIABILITY
        
           This package/module is provided "as is", without warranty of any kind.
           The maintainers and contributors make no guarantees regarding functionality,
           fitness for a particular purpose, or absence of errors.
        
           We are human, mistakes can happen. We do our best to keep the code clean and reliable,
           but we accept no liability for any damages, data loss, or other consequences
           arising from the use of this software.
        
        ---
        SOSS-MPL is part of the Sora Open Source Software standard.
        All SOSS-licensed projects must include this license file unchanged.
        Website: https://soss.page
        Organization: https://github.com/soss-community
License-File: LICENSE
Keywords: ansi,builtins,color,debugging,drop-in,error-codes,error-handling,exceptions,fastapi,hook,logging,monitoring,structured-errors,terminal,traceback,type-checking,uvicorn,validation,warnings,zero-boilerplate
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Debuggers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Logging
Requires-Python: >=3.12
Description-Content-Type: text/markdown

# Ladon - clear exceptions 'n warnings

> Structured drop-in extensions for Python's built-in exceptions and warnings. Formatted output, GDPR-safe production mode, hookable logging, zero boilerplate.

![Python](https://img.shields.io/badge/python-3.12%2B-blue)
![License](https://img.shields.io/badge/license-SOSS%20Community-green)
![Version](https://img.shields.io/badge/version-1.1.2-orange)
[![Community](https://img.shields.io/badge/community-soss.page-purple)](https://soss.page)
<img width=75px height=20 src="https://komarev.com/ghpvc/?username=soss-community-ladon&color=763eab&style=flat-round&label=Views:" />

---

In Greek mythology, Ladon is the eternal guardian of the Golden Apples of the Hesperides. Many-headed,  
ever-watchful, standing at the boundary between the mortal and the divine.  
That boundary is exactly what Python's core exception system is:  
something every developer depends on, something almost nobody touches.

This package watches over it. Better output, more context, no silent failures,  
and the ability to hook into anything the runtime raises before it propagates.

Part of the [Sora Open Source Software community](https://soss.page).

---

## The problem

Python's default exception output looks like this:

```
Traceback (most recent call last):
  File "main.py", line 12, in <module>
    process(user_input)
TypeError: unsupported operand type(s) for +: 'int' and 'str'
```

Everything is red. No structure. No context about what value caused the error or what was expected.  
Warnings are silently swallowed by default.  
Logging requires manual setup everywhere.

If you have worked on any Python project longer than a weekend,  
you have probably written your own exception wrappers.  
This package does it once, properly, for everyone.

---

## What this does

**One import, globally available.** One line at the top of your entry module is all it takes.  
Every exception and warning class - including your own subclasses - is automatically registered  
in Python's builtins via `__init_subclass__`. No imports anywhere else in the project, ever.

**Structured, color-coded output.** Exceptions and warnings are visually distinct and readable.  
The final stack frame is highlighted so you see immediately where it broke,  
not just the traceback wall. Colors and styles are fully customizable per element.

**Structured error data.** Every exception carries a `.data` dict with the values that caused the error,  
an `.error_code` for programmatic handling, and a `.message` for human-readable output.

**Automatic logging.** Logging happens before the exception propagates.  
Even caught-and-silenced exceptions leave a log trail when auto-logging is enabled.

**`.check()` on supported classes.** Replaces the `if not isinstance(...): raise` pattern with a single inline call.

**Defense-first validation.** A full set of guard classes for common function entry checks,  
covering types, ranges, string constraints, falsy values, and parameter exclusion rules.

**Warnings that cannot be dropped.** Every warning is either printed to STDERR or written to a logger.  
Nothing is ever silently swallowed.

**Hook system.** Register your own function to be called before any exception is raised or any warning is shown.  
Useful for health monitoring, alerting, or metrics.

**One-time warnings.** Mark any warning subclass as `_one_time = True` and it will only fire once per runtime.

**No dependencies.** Standard library only.

---

## What it _could_ look like

![example_exceptions_n_warnings.png](examples/example_exceptions_n_warnings.png)

> Here you can see the updated output with data & error_code in version 1.1.1

![example_exception_2-0.png](examples/example_exception_2-0.png)

---

## Installation

```bash
pip install ladon_clear_exceptions_n_warnings
```

[ladon-clear-exceptions-n-warnings on PyPI](https://pypi.org/project/ladon-clear-exceptions-n-warnings/)

---

## Usage

Add one line at the top of your entry module, before any other import:

```python
import ladon_clear_exceptions_n_warnings
```

That is it. Every exception and warning class - including any subclass you define yourself -  
is automatically registered in Python's builtins on import via `__init_subclass__`.  
No imports anywhere else in the project. Your IDE gets full type information through the included `.pyi` stub file.

```python
# All of these work unchanged after the import
raise LadonValueError(message="something went wrong")

try:
    do_something()
except LadonTypeError as e:
    handle(e)

class MyError(OOPException):
    pass
```

Two things surface as warnings rather than silently continuing:

`warnings.warn(...)` called with positional string arguments triggers an `OldWarningWarning`  
pointing to the exact call site.

Raising exceptions with positional arguments, for example `raise LadonTypeError("message")`  
instead of `raise LadonTypeError(message="message")`, triggers an `OldExceptionWarning`  
showing the type and the values that were passed.  
Execution still continues normally, the warning is just a signal that the call site should be updated.

If you want to suppress these during a migration before fixing all call sites,  
use `ladon.supress_warning()` temporarily.

> For full working examples see:  
> [examples_no_logger.py](examples/examples_no_logger.py) - basic usage without logger  
> [examples_with_logger.py](examples/examples_with_logger.py) - with a standard Python logger  
> [examples_with_taipan.py](examples/examples_with_taipan.py) - with Taipan Logger for full tracing

---

## IDE setup

Because Ladon registers all classes directly into Python's `builtins` at runtime,
your IDE does not know they exist - it only sees what is defined in its own internal `builtins` stubs.
This means PyCharm and other tools will mark `LadonTypeError`, `OOPException`, and all other Ladon classes
as unresolved references, even though the code runs correctly.

To fix this, Ladon ships a `builtins.pyi` stub file that you place in a `stubs/` directory in your project root.

**PyCharm:**

1. Copy `builtins.pyi` from the package into `stubs/builtins.pyi` in your project root.
2. Go to Settings > Project > Python Interpreter > Show All > select interpreter > Paths tab.
3. Add the `stubs/` directory to the interpreter paths.

Or configure it via `pyrightconfig.json` in your project root:

```json
{
    "stubPath": "stubs"
}
```

**What the stub does:**

The `builtins.pyi` contains full type-annotated signatures for every Ladon class - exceptions, warnings,
and their `.check()` classmethods. Once the stub is loaded, your IDE resolves all Ladon classes
as if they were native builtins, including autocomplete and type checking.

**Custom exception and warning classes you define yourself are NOT covered by the stub.**
If you want your own subclasses to be recognized project-wide without imports, add them to your own
`stubs/builtins.pyi`. The format follows the same pattern as the existing entries:

```python
# stubs/builtins.pyi - your additions at the bottom

class DatabaseConnectionError(OOPException):
    def __init__(
        self,
        *xargs,
        host: str | None = None,
        port: int | None = None,
        message: str | None = None,
        error_code: int | None = None,
        **kwargs,
    ) -> None: ...


class SlowQueryWarning(OOPWarning):
    def __init__(
        self,
        *xargs,
        duration_ms: int | None = None,
        message: str | None = None,
        extra_message: str | None = None,
        **kwargs,
    ) -> None: ...
```

Only the `__init__` signature and any custom classmethods need to be listed.
Base class methods like `.show()` and `.check()` are inherited from the stubs already present.

---

All builtin exception classes in this package follow the `Ladon` prefix convention and extend
their corresponding Python builtin via multiple inheritance. This means existing `except TypeError`
blocks continue to work and will catch `LadonTypeError` - both styles are valid.
The preferred style for raising is always the prefixed name.

Custom exception classes have no prefix and are named by what they guard against,  
for example `NoneValueError`, `StringLengthError`, `MutualExclusionError`.

---

## Structured exceptions

Any keyword argument passed to an exception constructor is stored in `.data` and available after the fact.

```python
raise LadonTypeError(
    message="Invalid input for user_id",
    expected_type=int,
    received_type=type(user_id)
)
```

When caught:

```python
except LadonTypeError as e:
    print(e.message)       # "Invalid input for user_id"
    print(e.error_code)    # numeric code, e.g. 412
    print(e.data)          # {"expected_type": <class 'int'>, "received_type": <class 'str'>}
```

All keyword arguments are stored in `.data` and available for programmatic handling.  
They are intentionally never written to log output - keyword data may contain sensitive values,  
and automatic logging of it could violate privacy regulations such as GDPR.

### Error codes

Every exception class has a numeric error code.  
When a subclass inherits from another, the parent's code is prepended to the child's.  
This means the code itself carries the inheritance chain:  
you can see at a glance whether two errors are related, without inspecting the class hierarchy.

For `CheckableError` subclasses, the code is additionally prefixed with `4` to mark it as a checkable type.

---

## `.check()` - inline validation

The following classes expose a `.check()` classmethod that raises the exception with full context  
already populated if the check fails.

```python
# Type check - skips silently if value is None
LadonTypeError.check(user_id, int)

# Also works with callable
LadonTypeError.check(handler, callable)

# Also works with List, Tuple, Set, Frozenset
LadonTypeError.check(my_list_type_or_str, frozenset({list, tuple, str}))
LadonTypeError.check(my_list_type_or_str, [list, tuple, str])
LadonTypeError.check(my_list_type_or_str, {list, tuple, str})

# None check - raises if value IS None
NoneValueError.check(user_id)

# Falsy check - raises if value is falsy (None, 0, 0.0, "", [], (), {}, set(), frozenset(), dict())
FalsyError.check(user_id)

# Zero division guard
LadonZeroDivisionError.check(divisor)

# Overflow guard
LadonOverflowError.check(value, 1_000_000)

# Value in allowed set
LadonValueError.check(status, {"active", "inactive"})

# Assertion
LadonAssertionError.check(age >= 18, "age >= 18")

# Key exists in dict
LadonKeyError.check("host", config)

# Name exists in scope
LadonNameError.check("my_var", dir())

# Configuration validity - pass any boolean expression
ConfigurationError.check(port > 0, "port")
```

Note: `LadonTypeError.check()` skips `None` silently. Use `NoneValueError.check()` when `None` itself is the error.  
Use `FalsyError.check()` when any falsy value including `None`, `0`, or `""` is unacceptable.

---

## Defense-first validation

Beyond the standard checks, a set of guard classes covers the most common function entry patterns.  
All of them are `CheckableError` subclasses and follow the same `.check()` convention.  
The traceback always points directly to the call site - no internal frames leak through.

### String constraints

```python
# Max length - default is 255
StringLengthError.check(username)
StringLengthError.check(bio, max_length=500)

# Newline count - no default, must be explicit
StringNewlineError.check(comment, max_newlines=5)
StringNewlineError.check(title, max_newlines=0)  # single line only
```

### Number constraints

```python
# Range - min and max are both optional, omit either to disable that bound
NumberRangeError.check(age, min_value=0, max_value=150)
NumberRangeError.check(price, min_value=0)       # only lower bound
NumberRangeError.check(discount, max_value=100)  # only upper bound
```

### Parameter exclusion

```python
# Mutual exclusion - at most one may be set, none is valid
MutualExclusionError.check(file_path, file_url, file_bytes)

# XOR - exactly one must be set, none or multiple both raise
XOrError.check(file_path, file_url, file_bytes)
```

A value counts as set when it is not `None` and not an empty string.  
`False`, `0`, and empty collections count as set because they represent deliberate values,  
including boolean flags.

---

## Custom exceptions and warnings

Use `OOPException` as the base for simple custom exceptions.  
Use `CheckableError` if you want to add a `.check()` classmethod.  
Both are registered in builtins after import, so no additional import is needed in other files.

Since error codes are concatenated up the inheritance chain, your custom code is automatically embedded in the structure.

```python
# No import needed anywhere in your project after the initial package import

class DatabaseConnectionError(OOPException):
    def __init__(self, *, host=None, port=None, message=None, **kwargs):
        self.message = message or (
            f"Could not connect to database at {host}:{port}" if host and port
            else "Could not connect to database"
        )
        self.error_code = 90
        super().__init__(message=self.message, error_code=self.error_code, host=host, port=port, **kwargs)


class PayloadTooLargeError(CheckableError):
    def __init__(self, *, size=None, max_size=None, message=None, **kwargs):
        self.message = message or (
            f"Payload size {size} exceeds maximum of {max_size}" if size and max_size
            else "Payload is too large"
        )
        self.error_code = 91
        super().__init__(message=self.message, error_code=self.error_code, size=size, max_size=max_size, **kwargs)

    @classmethod
    def check(cls, size: int, max_size: int) -> None:
        if size > max_size:
            raise cls(size=size, max_size=max_size, drop_last_stacktrace=True)
```

Use `OOPWarning` as the base for custom warnings. It is also registered in builtins after import.

```python
class SlowQueryWarning(OOPWarning):
    _one_time = False

    def __init__(self, *, duration_ms=None, message=None, extra_message=None, **kwargs):
        self.message = message or f"Query took {duration_ms}ms"
        self.message += (f"\n{extra_message}" if extra_message else "")
        super().__init__(message=self.message, **kwargs)


class MigrationWarning(OOPWarning):
    _one_time = True  # only fires once per runtime

    def __init__(self, *, table=None, message=None, extra_message=None, **kwargs):
        self.message = message or (f"Pending migration on table '{table}'" if table else "Pending migration")
        self.message += (f"\n{extra_message}" if extra_message else "")
        super().__init__(message=self.message, **kwargs)
```

Trigger a warning by instantiating it directly, or conditionally via `.show()`:

```python
SlowQueryWarning(duration_ms=4200)

MigrationWarning.show(pending_migrations > 0)
```

---

## Exceptions & Warnings

Ladon ships with replacements for all overwritable Python builtins plus a set of custom classes  
for the patterns that come up constantly in real projects.  
Every class is registered in builtins on import - no individual imports needed anywhere.

---

### Exceptions

All exceptions inherit from `OOPException` and carry a `.message`, `.error_code`, and `.data` dict.  
`CheckableError` extends that base with a `.check()` classmethod for inline validation without if/raise blocks.

Every Ladon class also extends its corresponding Python builtin via multiple inheritance,
so standard `except` blocks remain fully functional.

#### Builtin extensions

| Class | Extends builtin | Also catchable via |
|---|---|---|
| `LadonAssertionError` | `AssertionError` | `except AssertionError` |
| `LadonTypeError` | `TypeError` | `except TypeError` |
| `LadonValueError` | `ValueError` | `except ValueError` |
| `LadonArithmeticError` | `ArithmeticError` | `except ArithmeticError` |
| `LadonFloatingPointError` | `FloatingPointError` | `except FloatingPointError`, `except ArithmeticError` |
| `LadonOverflowError` | `OverflowError` | `except OverflowError`, `except ArithmeticError` |
| `LadonZeroDivisionError` | `ZeroDivisionError` | `except ZeroDivisionError`, `except ArithmeticError` |
| `LadonLookupError` | `LookupError` | `except LookupError` |
| `LadonKeyError` | `KeyError` | `except KeyError`, `except LookupError` |
| `LadonNameError` | `NameError` | `except NameError` |
| `LadonRuntimeError` | `RuntimeError` | `except RuntimeError` |
| `LadonNotImplementedError` | `NotImplementedError` | `except NotImplementedError`, `except RuntimeError` |
| `LadonBufferError` | `BufferError` | `except BufferError` |
| `LadonEOFError` | `EOFError` | `except EOFError` |
| `LadonMemoryError` | `MemoryError` | `except MemoryError` |
| `LadonReferenceError` | `ReferenceError` | `except ReferenceError` |

#### Custom

These classes cover the patterns that keep showing up at the top of every function.  
All of them support `.check()` for inline use.

| Class | Raises when |
|---|---|
| `NoneValueError` | value is `None` but `None` is not acceptable |
| `FalsyError` | value is falsy: `None`, `0`, `""`, `[]`, `()`, `{}`, `set()`, `frozenset()` |
| `ConfigurationError` | a configuration condition is not met |
| `AttributeReadOnlyError` | a write is attempted on a read-only attribute |
| `StateError` | a method is called on an object in an invalid state |
| `StringLengthError` | a string exceeds the allowed length, default 255 |
| `StringNewlineError` | a string contains more newlines than allowed |
| `NumberRangeError` | an int or float falls outside the allowed range |
| `MutualExclusionError` | more than one mutually exclusive parameter is set |
| `XOrError` | not exactly one of a required set of parameters is set |

---

### Warnings

All warnings inherit from `OOPWarning` and trigger formatted output on instantiation.  
They are never silently swallowed. Two of them fire automatically - everything else  
is raised manually by the developer where it makes sense.

Warnings are instantiated as objects. `extra_message` is available on all warning classes  
to append additional context to the message.

```python
DeprecationWarning(
    message="use new_method() instead",
    old="old_method",
    replacement="new_method",
    since="v1.4"
)
```

`warnings.warn()` is still accepted but surfaces an `OldWarningWarning` pointing to the exact call site.  
The `*args` positional parameter exists on all warning classes for this compatibility reason only.  
Do not pass values through it intentionally.

#### Builtin extensions

| Class | Replaces |
|---|---|
| `UserWarning` | `UserWarning` |
| `DeprecationWarning` | `DeprecationWarning` |
| `PendingDeprecationWarning` | `PendingDeprecationWarning` |
| `RuntimeWarning` | `RuntimeWarning` |
| `SyntaxWarning` | `SyntaxWarning` |
| `ResourceWarning` | `ResourceWarning` |
| `FutureWarning` | `FutureWarning` |
| `ImportWarning` | `ImportWarning` |
| `UnicodeWarning` | `UnicodeWarning` |
| `BytesWarning` | `BytesWarning` |

#### Custom

| Class | Fires | Description |
|---|---|---|
| `OldExceptionWarning` | automatically | exception raised with positional arguments |
| `OldWarningWarning` | automatically | `warnings.warn()` called with positional arguments |
| `PerformanceWarning` | manually | operation is valid but slow or suboptimal |
| `SecurityWarning` | manually | non-fatal security-relevant condition detected |
| `ConfigurationWarning` | manually | configuration accepted but suboptimal |
| `FixedButWrongValueWarning` | manually | wrong input received, fallback applied internally |
| `RetryWarning` | manually | operation failed but is being retried |

### One-time warnings

```python
class MyWarning(UserWarning):
    _one_time = True
```

This warning fires only once per runtime regardless of how many times it is triggered.

---

## Logging

Ladon can route exception and warning output to any logger object that exposes a callable `.error()` method
and a callable `.warning()` method. That is the only requirement - no specific base class is needed.

Set up and connect the logger before enabling log output:

```python
import logging
import ladon_clear_exceptions_n_warnings as ladon

logger = logging.getLogger("my_app")
handler = logging.FileHandler("app.log", encoding="utf-8")
logger.addHandler(handler)

ladon.configure_log_warning_and_exceptions(
    logger_instance=logger,
    write_log_warning=True,
    allow_exception_logging=True,
    suppress_warnings=False,
    code_lines_before_warning=3
)
```

If you use a standard `logging.Logger`:
- use at least one `FileHandler` with `encoding="utf-8"` to avoid encoding errors on Windows
- do not add a `StreamHandler` - it would produce duplicate console output alongside ladon's stderr output

Log output is plain text without ANSI codes. Terminal output is color-coded.  
Both run independently.

The individual setters are also available:

```python
ladon.set_logger(logger)
ladon.enable_log_warning()
ladon.enable_auto_log_exceptions()
ladon.supress_warning()
ladon.set_number_of_prior_warning_code_lines(5)
```

---

## Production mode and GDPR

Every keyword argument passed to an exception constructor ends up in `.data` and is printed
to both terminal and logger output. In development this is useful - you see the exact values
that caused the error. In production this is a problem, because `.data` can contain user IDs,
email addresses, request payloads, or any other personal data. Printing or logging that
violates GDPR and similar regulations.

Enable production mode via `configure_log_warning_and_exceptions` before starting your application:

```python
ladon.configure_log_warning_and_exceptions(
    is_productive=True,
    logger_instance=logger,
    allow_exception_logging=True,
)
```

With production mode on, `.data` is never written anywhere. Instead, both terminal and log output
show a placeholder line:

```
Extra Infos: [hidden in production mode]
```

This line only appears when `.data` is actually populated - if no kwargs were passed to the exception,
nothing is shown at all. This means you can always tell whether an exception was raised with extra
context, without exposing what that context contains.

The message and error code are always shown regardless of production mode. Only `.data` is affected.

> If you want structured, GDPR-safe logging out of the box, use Taipan Logger as the logger instance.
> It is designed for exactly this use case and works with Ladon without any additional configuration.

---

## Using Taipan Logger

[Taipan Logger](https://github.com/soss-community/taipan-logger) is the companion logger from the same ecosystem.  
It is built for structured, GDPR-safe, thread-safe file logging with trace IDs, timestamps, and function context.  
Ladon and Taipan are designed to work together out of the box with zero friction.

When both are active, every exception and warning that Ladon captures is automatically written  
to the Taipan log file with full context. The `@trace` decorator links each exception back  
to the specific function call and trace ID that triggered it, so you can follow the exact path  
through nested calls even in multi-threaded or async code.

### Setup

```python
import ladon_clear_exceptions_n_warnings as ladon
from taipan_logger import taipan, trace

# taipan.configure() must come first
# exception_hook_in_is_set=True tells @trace that ladon's hook will handle
# the full exception log - the decorator then only writes a short marker line
# and does not duplicate the stacktrace in the log
taipan.configure(debug=True, exception_hook_in_is_set=True)

ladon.configure_log_warning_and_exceptions(
    logger_instance=taipan,
    allow_exception_logging=True,
    write_log_warning=True,
)
```

Taipan exposes `.error()` and `.warning()` methods directly on its namespace object,  
which is exactly what Ladon calls when exceptions and warnings fire.  
No adapter, no wrapper, no extra configuration.

### What ends up in the log

For every exception, Taipan writes two entries:

The first comes from `@trace` and includes the trace ID and function name:

```
[2026-05-09 - 12:31:18:609][ERROR][56e58bb3][MainThread][test]LadonTypeError: An exception hook in is configured and should be shown below:
```

The second is the full structured Ladon output routed through Taipan:

```
[2026-05-09 - 12:31:18:610][ERROR][NO TRACEID][MainThread][exception_write]An exception occurred:
    path/to/file.py:17 in function test.
    Error Code: 412
    LadonTypeError: banana
    ╔════════════════════════════════════════╗
    ║ raise LadonTypeError(message="banana") ║
    ╚════════════════════════════════════════╝
```

The first entry gives you the trace ID for correlation. The second gives you the full stacktrace and error context.

For warnings, a single plain-text entry is written with the warning type, message, and surrounding source lines.

### Using `@trace`

Decorate any function with `@trace` to get automatic entry and exit logging with a unique trace ID per call:

```python
@trace
def process_order(order_id: int, user_id: int):
    NoneValueError.check(order_id, "order_id")
    LadonTypeError.check(user_id, int)
    # ... rest of function
```

If an exception propagates out of a traced function, the decorator logs it before re-raising.  
With `exception_hook_in_is_set=True`, the decorator writes the short marker line and Ladon  
writes the full stacktrace - no duplication, clean separation of concerns.

`@trace` works on sync functions, async functions, methods, and dunder methods.

> For a full working example see:  
> [examples_with_taipan.py](examples/examples_with_taipan.py)

---

## Hook system

Register a function that fires before any exception is raised or any warning is shown.  
Useful for monitoring, alerting, or metrics collection.

```python
def on_exception(exc, is_oop_exception):
    # exc is the exception instance
    # is_oop_exception is True for OOPException subclasses, False for non-overwritten builtins
    send_to_monitoring(exc)

def on_warning(warning):
    log_warning_metric(warning)

ladon.set_exceptions_hook_function(on_exception)
ladon.set_warnings_hook_function(on_warning)
```

The exception hook fires on every unhandled exception before stderr output is written.  
The warning hook fires on every warning instantiation, before output is written.

---

## Customizing output

All visual elements are managed through `FormatElements`.  
Each element has a name, a foreground color, a background color, and one or more text styles.  
You can update any element at runtime before exceptions or warnings fire.

```python
from ladon_clear_exceptions_n_warnings import FormatElements

# Change the color of the file path in exception output
FormatElements.get("file").update_fg("bright_green")

# Change the error type badge background
FormatElements.get("error_type").update_bg("blue")

# Inspect what is available
print(FormatElements.get_all_elements())
print(FormatElements.get_all_fg())
print(FormatElements.get_all_bg())
print(FormatElements.get_all_styles())
```

Valid styles are `bold`, `dim`, `italic`, `underline`, `blink`, `inverse`, and `strike`.  
Colors follow the standard 16-color ANSI set with bright variants.

`FormatElements` instances can also be created independently for use in your own terminal output,  
building on the same system the package uses internally.

---

## What is NOT covered

| Class | Reason |
|---|---|
| `BaseException` | Root of the entire exception hierarchy |
| `KeyboardInterrupt` | System-level signal |
| `SystemExit` | Used by `sys.exit()` |
| `GeneratorExit` | Generator and coroutine lifecycle |

The following are not covered because they are raised before or during package import,
so there is no safe point to extend them:

`UnicodeDecodeError`, `UnicodeEncodeError`, `UnicodeTranslateError`, `ImportError`,  
`ModuleNotFoundError`, `SyntaxError`, `IndentationError`, `TabError`, `StopIteration`,  
`StopAsyncIteration`, `UnicodeError`, `RecursionError`, `IndexError`, `OSError` and all its subclasses.

C-level exceptions raised by the Python interpreter itself also remain native.  
This package covers exceptions raised by your own code and any libraries loaded after the import.

---

## Compatibility

Python 3.12 or higher. No dependencies outside the standard library.

### Pydantic

Pydantic triggers a specific warning pattern when used together with FastAPI.  
This package detects and corrects that automatically, no configuration needed.

> This is even documented inline of pydantic & fastapi, that it should not trigger.

### FastAPI / Uvicorn

FastAPI does not work out of the box with this package. Uvicorn spawns worker processes with its own loading mechanics,  
which means the package import in your main module does not carry over into the workers.  
A patch method is included in this package to handle this:

```python
import ladon_clear_exceptions_n_warnings as ladon
fastapi_instance: FastAPI = ...
fastapi_instance = ladon.patch_uvicorn(fastapi_instance)
```

Call this before starting the application server.

---

## License

SOSS Community License.  
If you use this package in a project, include a credit in your README,  
credits section, or anywhere visible, with the package name and a link to [soss.page](https://soss.page).  
Always refer to the latest version at: [github.com/soss-community](https://github.com/soss-community)

---

## Community and contributing

Part of the Sora Open Source Software community.  
Visit [github.com/soss-community](https://github.com/soss-community)  
for contribution guidelines, issue tracking, and discussion.

> **Author:** [sora7672](https://github.com/sora7672)  
> **Organization:** [soss-community](https://github.com/soss-community)  
> **Website:** [soss.page](https://soss.page)