Metadata-Version: 2.4
Name: unter
Version: 0.1.0a1
Summary: The antonym of super(): let a base class hijack a derived class's overridden method
Project-URL: Homepage, https://github.com/whisperity/Unter
Project-URL: Repository, https://github.com/whisperity/Unter
Project-URL: Issues, https://github.com/whisperity/Unter/issues
Author-email: Whisperity <whisperity-packages@protonmail.com>
License: MIT License
        
        Copyright (c) 2026– by Whisperity and contributors
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to
        deal in the Software without restriction, including without limitation the
        rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
        sell copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in
        all copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
        FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
        IN THE SOFTWARE.
License-File: LICENSE.txt
Keywords: decorator,inheritance,metaclass,oop,override,super
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.12
Provides-Extra: dev
Requires-Dist: mypy>=1.8; extra == 'dev'
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff>=0.5; extra == 'dev'
Description-Content-Type: text/markdown

# 📟🔃 Unter

[![PyPI](https://img.shields.io/pypi/v/unter.svg?logo=python&logoColor=white&cacheSeconds=3600)](https://pypi.org/project/unter/)
[![Licence](https://img.shields.io/pypi/l/unter.svg?cacheSeconds=3600)](LICENSE.txt)

A Python module of decorators to allow _base classes_ to selectively enforce their primacy even in the face of _derived classes_ overriding implementations.

## Overview

The **unter** module provides a set of decorators that can be applied to the base classes and its methods in order to ensure that calls of a method overridden in a derived class nevertheless invoke the base class implementation instead.

Normal Python method resolution order resolves the most-overridden method first when a call is made, and the ability for base classes to provide a common frame around an implementation customisable by derived classes is only achievable through either the explicit use of `super()`, or via template functions.

With **unter**, the base class can break the normal method resolution order and enforce its own implementation to be called first, while leaving the derived class to visually appear as-if to override the base class method (instead of `_*_impl`-style methods).

## Installation

```bash
pip install unter
```

## Quick-start

### Current, Pythonic way of doing things

```python3
class Base:
    def method(self):
        print("Base.method() called")
        self._method()  # Calls the derived class method
        print("Base.method() over!")

    def _method(self):
        print("Base._method() - not overridden")

class Derived(Base):
    def _method(self):
        print("Derived._method() - overridden")

    # Whoops, should never do this by accident, otherwise, the implementation
    # would break!
    # def method(self):
    #     ...


>>> Base().method()
Base.method() called
Base._method() - not overridden
Base.method() over!

>>> Derived().method()
Base.method() called
Derived._method() - overridden
Base.method() over!
```

### Basic decoration and primacy

```python3
from unter import unter

@unter.enable
class Base:
    @unter.attorney
    def method(self) -> int:
        print("Base.method() called")
        i = 1 + unter().method()
        print("Base.method() over!", i)
        return i

class Derived:
    def method(self) -> int:
        print("Derived.method() called")
        i = 4
        print("Derived.method() over!", i)
        return i

>>> print(Derived().method())
Base.method() called
Derived.method() called
Derived.method() over! 4
Base.method() over! 5
5
```

## Why?

**unter** is a highly specialised tool to invert the normal method resolution behaviour of class hierarchies.
Specialised tools are things that one needs rarely, but when they are needed, they are needed badly.
