Metadata-Version: 2.4
Name: singleton-base
Version: 1.2.7
Summary: A type-friendly, thread-safe singleton base class for Python 3.10+. Very simple to use and test-friendly.
Author-email: chaz <bright.lid5647@fastmail.com>
Requires-Python: >=3.12
Description-Content-Type: text/markdown

# singleton-base

A type-friendly, thread-safe singleton base class for Python 3.12+. Very simple to use and test-friendly.

## Installation

### Using pip

```bash
pip install singleton-base
```

### Using Uv

```bash
uv add singleton-base
```

### Using Poetry

```bash
poetry add singleton-base
```

## Features

- Thread-safe: Multiple threads can safely create instances
- Type-friendly: Full type hint support, including `Self` returns and PEP 695 generic syntax
- Test-friendly: Easy instance management for testing scenarios
- Zero runtime dependencies

## Usage

### Usage Example

Subclass using SingletonBase to make your class a singleton. All returned instances will now be the same object.

```python
from singleton_base import SingletonBase


class MySingleton(SingletonBase):
    """A singleton class example that holds an integer value."""

    def __init__(self, value: int) -> None:
        self.value = value


class AnotherSingleton(SingletonBase):
    """Another singleton class example that holds a string value."""

    def __init__(self, value: str) -> None:
        self.value = value


# Initially, no instance exists
print(MySingleton.has_instance())  # False

# Two equivalent ways to create the singleton:
instance = MySingleton(42)
# or
instance = MySingleton.get_instance(value=42)

print(MySingleton.has_instance())  # True
print(instance.value)              # 42

# Subsequent calls return the same instance.
# Note: any args passed after the first construction are ignored.
instance2 = MySingleton.get_instance(value=99)
print(instance is instance2)       # True
print(instance2.value)             # 42

# Other singleton classes are independent — each maintains its own instance:
another = AnotherSingleton(value="Hello")
print(instance is another)         # False

# Reset to allow re-initialization (handy for tests):
MySingleton.reset_instance()
print(MySingleton.has_instance())  # False

# After a reset, the next call constructs a fresh instance:
instance3 = MySingleton.get_instance(value=9001)
print(instance3.value)             # 9001
```

### Available Methods

| Method                          | Description                                                                              |
| ------------------------------- | ---------------------------------------------------------------------------------------- |
| `get_instance(*args, **kwargs)` | Returns the singleton instance, constructing it with `*args, **kwargs` if it does not yet exist. |
| `has_instance()`                | Returns `True` if the singleton instance has been created.                               |
| `reset_instance()`              | Destroys the current instance, allowing re-initialization.                               |

## Wrapper API: SingletonWrap

When subclassing isn't an option (third-party classes, dataclasses, etc.), use `SingletonWrap` to wrap an existing class instead:

```python
from singleton_base import SingletonWrap


class Service:
    def __init__(self, host: str, port: int) -> None:
        self.host = host
        self.port = port


service_singleton = SingletonWrap(Service, "localhost", 8080)

service = service_singleton.get()      # constructs on first access
same_service = service_singleton.get() # returns the same instance
print(service is same_service)         # True

service_singleton.reset_instance()
print(service_singleton.has_instance())  # False
```

`SingletonWrap` exposes the same `has_instance()` / `reset_instance()` lifecycle as `SingletonBase`, plus `get()` / `get_instance()` (aliases) for retrieval.

## Python Version Compatibility

Supports Python 3.12–3.14 with full test coverage across every version. No version-conditional code, no runtime dependencies.
