Metadata-Version: 2.4
Name: isopodlib
Version: 1.1.0
Summary: A collection of useful Python utilities and types.
Author-email: Arlo <arlorolypoly67@gmail.com>
Requires-Python: >=3.14
Description-Content-Type: text/markdown
Requires-Dist: bcrypt
Requires-Dist: cryptography

# isopodlib 🐛

A small Python utility library containing decorators, random utilities, authentication helpers, validators, custom errors, and useful type aliases.

## Features

* 🔁 Function retrying with configurable exceptions, delays, and fallbacks
* ⏱️ Function execution benchmarking
* ℹ️ Function call information and result reporting
* 🎲 Random and cryptographically secure random string generation
* 🔐 Password hashing and Fernet encryption
* ✅ Common type and string validators
* ⚠️ Custom error types

## Installation

Install it with pip:

```bash
pip install isopodlib
```

## Quick Start

```python
from isopodlib import Randomizer, Auth, retry

r = Randomizer()

print(r.random(10, 'str+int'))

auth = Auth()

password = auth.hashPassword('my-password')
print(auth.checkPassword('my-password', password))
```

## Modules

### `decorators`

Provides useful decorators for working with functions.

#### `retry`

Retries a function when a specified exception occurs.

```python
from isopodlib import retry

@retry(amount=3, catch=ValueError)
def test():
    raise ValueError('no')

test()
```

By default, `retry()` attempts a function three times and catches `Exception`.

A delay can be specified between attempts:

```python
@retry(amount=5, delay=1)
def test():
    ...
```

Retries can also return a fallback instead of raising an error:

```python
@retry(
    amount=3,
    raise_on_fail=False,
    fallback='failed'
)
def test():
    raise ValueError('no')

print(test())
```

If no fallback is supplied, `FAILED` can be returned when `raise_on_fail=False`.

```python
from isopodlib.decorators import FAILED, retry

@retry(raise_on_fail=False)
def test():
    raise ValueError('no')

if test() is FAILED:
    print('The function failed.')
```

#### `benchmark`

Measures how long a function takes to execute.

```python
from isopodlib import benchmark

@benchmark
def test():
    return sum(range(1000))

test()
```

Example output:

```text
test took 0.000012s
```

The original return value is preserved.

#### `info`

Returns information about a function call.

```python
from isopodlib import info

@info
def test(a, b):
    return a + b

print(test(1, 1))
```

Example result:

```python
{
    'qualname': 'test',
    'name': 'test',
    'module': '__main__',
    'arguments': {
        'keyword': {},
        'positional': [1, 1]
    },
    'result': 2
}
```

---

### `polyrandom`

Provides the `Randomizer` class.

```python
from isopodlib import Randomizer

r = Randomizer()
```

#### `random()`

Generates a random string.

```python
r.random(15, 'str')
r.random(15, 'str+int')
r.random(15, 'int')
r.random(15, 'all')
```

Supported types:

| Type      | Characters                        |
| --------- | --------------------------------- |
| `str`     | Letters                           |
| `str+int` | Letters and numbers               |
| `int`     | Numbers                           |
| `all`     | Letters, numbers, and punctuation |

The default length is `15`.

#### `secureRandom()`

Generates a cryptographically secure random string using Python's `secrets` module.

```python
r.secureRandom(32)
```

The default length is `32`.

---

### `polyauth`

Provides the `Auth` class for password hashing and Fernet encryption.

```python
from isopodlib import Auth

auth = Auth()
```

#### Password hashing

```python
hashed = auth.hashPassword('password')

print(auth.checkPassword('password', hashed))
```

#### Generate passwords

`Auth().genPassword()` uses `Randomizer().secureRandom()`

```python
password = auth.genPassword(32)
```

#### Encryption

```python
encrypted = auth.encrypt('Hello, World!')
decrypted = auth.decrypt(encrypted)

print(decrypted)
```

A Fernet key can also be supplied when creating an `Auth` instance:

```python
key = Auth.genKey()
auth = Auth(key)
```

---

### `validators`

Provides simple validation functions.

```python
from isopodlib.validators import (
    is_str,
    is_int,
    is_float,
    is_num,
    is_dict,
    is_list,
    is_json,
    is_email,
    is_url
)
```

Examples:

```python
is_str('hello')
is_int(123)
is_float(1.5)
is_num(123)
is_dict({})
is_list([])
is_json({})
is_email('user@example.com')
is_url('https://example.com')
```

`is_int()` and `is_float()` do not consider `bool` to be a valid integer or float.

---

### `errors`

Provides custom exceptions.

```python
from isopodlib import InvalidTypeError, InvalidLengthError
```

Available errors:

```text
PolyError
├── InvalidTypeError
└── InvalidLengthError
```

`InvalidTypeError` is used when a value has an invalid type.

`InvalidLengthError` is used when a value has an invalid length.

## Types

`isopodlib.types` contains reusable type aliases, including:

```python
type StrInt = str | int
type DictList = dict | list
type NoStr = str | None
type NoStrInt = StrInt | None
type NoDictList = DictList | None
type NoBool = bool | None
type No = Never
type ErrorDescriptor = type[BaseException] | tuple[ErrorDescriptor, ...]
```

`ErrorDescriptor` is used by `retry()` to describe accepted exception types.

## Project Structure

```text
isopodlib/
├── isopodlib/
│   ├── __init__.py
│   ├── decorators.py
│   ├── errors.py
│   ├── polyauth.py
│   ├── polyrandom.py
│   ├── types.py
│   └── validators.py
├── README.md
└── ...
```

## Package Information

```text
Name:     isopodlib
Version:  1.0.0
Author:   Arlo
License:  MIT
```

## Requirements

Python 3.14+

`polyauth` additionally uses:

* `bcrypt`
* `cryptography`

## License

This project is licensed under the MIT License.
