Metadata-Version: 2.4
Name: vextra_utils
Version: 1.0.1
Summary: A collection of Python utilities for timing, concurrency control, thread safety, and design patterns.
Project-URL: Homepage, https://github.com/vnniciusg/vextra_utils
Project-URL: Repository, https://github.com/vnniciusg/vextra_utils
Project-URL: Issues, https://github.com/vnniciusg/vextra_utils/issues
License-File: LICENSE
Requires-Python: >=3.13
Description-Content-Type: text/markdown

# Vextra Utils

I built this because I use the following decorators or singletons in almost all of my code.

## Installation

```bash
uv add vextra_utils
```

## Decorators

### `with_timer`

Measures and logs execution time of sync or async functions.

```python
from vextra_utils import with_timer

@with_timer
def foo():
    return bar # logs: "foo took 1.2345s"

@with_timer
async def foo():
    return bar # logs: "foo took 0.5678s"
```

### `with_semaphore`

Limits concurrent execution of async functions using a semaphore.

```python
import asyncio
from vextra_utils import with_semaphore

semaphore = asyncio.Semaphore(3)  # max 3 concurrent

@with_semaphore(semaphore)
async def fetch_url(url: str) -> str:
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as resp:
            return await resp.text()

# Only 3 requests run concurrently
await asyncio.gather(*[fetch_url(url) for url in urls])
```

### `synchronized`

Makes function execution thread-safe.

```python
from threading import Lock
from vextra_utils import synchronized

# With shared lock
shared_lock = Lock()

@synchronized(shared_lock)
def update_counter():
    # thread-safe critical section
    pass

# Without lock (creates RLock automatically)
@synchronized()
def another_safe_function():
    pass
```

## Metaclasses

### `SingletonMeta`

Thread-safe singleton pattern implementation.

```python
from vextra_utils import SingletonMeta

class Database(metaclass=SingletonMeta):
    def __init__(self, connection_string: str):
        self.connection = connect(connection_string)

db1 = Database("postgres://...")
db2 = Database("different://...")  # ignored, returns same instance

assert db1 is db2  # True
```

Clear instances for testing:

```python
SingletonMeta.clear_instances()
```

## API Reference

| Utility               | Type      | Description                                  |
| --------------------- | --------- | -------------------------------------------- |
| `with_timer`          | Decorator | Logs execution time for sync/async functions |
| `with_semaphore(sem)` | Decorator | Limits async concurrency with semaphore      |
| `synchronized(lock?)` | Decorator | Thread-safe execution with optional lock     |
| `SingletonMeta`       | Metaclass | Thread-safe singleton pattern                |

## Requirements

- Python 3.13+
- No runtime dependencies
- Optional: [loguru](https://github.com/Delgan/loguru) for enhanced logging (falls back to stdlib logging)

## License

[MIT](LICENSE)

---

_Tests generated by Claude xD_
