Metadata-Version: 2.3
Name: py-generichost
Version: 0.1.0
Summary: Modern Generic Host and ApplicationBuilder for Python inspired by .NET 10
Keywords: hosting,application-builder,generic-host,dependency-injection,dynaconf,pydantic,dotnet,background-service,asyncio
Author: Christofer Toledo Luna
Author-email: Christofer Toledo Luna <t_christofer@hotmail.com>
License: MIT
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Dist: dependency-injector>=4.49.0
Requires-Dist: dynaconf>=3.2.13
Requires-Dist: pydantic>=2.10.0
Requires-Dist: structlog>=24.0.0 ; extra == 'structlog'
Requires-Python: >=3.10
Project-URL: Homepage, https://github.com/cristozz/pyappbuilder
Project-URL: Repository, https://github.com/cristozz/pyappbuilder
Project-URL: Issues, https://github.com/cristozz/pyappbuilder/issues
Provides-Extra: structlog
Description-Content-Type: text/markdown

# py-generichost

**A modern Python Generic Host and ApplicationBuilder inspired by .NET 10.**

`py-generichost` brings the power, ergonomics, and lifecycle management of the .NET Generic Host (`Host.CreateApplicationBuilder` / `Microsoft.Extensions.Hosting`) to Python. It is designed to be fully asynchronous (using `asyncio`) and provides a unified, production-grade architecture:

- **Dependency Injection**: Seamless, auto-wiring DI powered by `python-dependency-injector` (zero decorators required).
- **Configuration**: Settings management via `dynaconf`, bound directly to `pydantic` models with automatic environment overlays (`appsettings.{Environment}.json`).
- **Application Lifecycle**: Managed background services (`BackgroundService` / `IHostedService`) with cancellation tokens (`CancellationToken`) and OS signal trapping (`SIGINT` / `SIGTERM`) for graceful shutdown.
- **Logging**: Smart class-level logger injection (`ILogger<T>` style) with late-bound JSON configuration and optional `structlog` integration.
- **Hosting Environment**: `HostEnvironment` with intelligent `ContentRootPath` detection and environment status flags (`is_development`, `is_production`).
- **Convention over Configuration**: Automatic discovery of `appsettings.json`, automatic logging setup, and customizable via `HostApplicationBuilderSettings`.

---

## Installation

You can install `py-generichost` using `uv` (or `pip`):

```bash
uv add py-generichost
```

With optional `structlog` structured logging:

```bash
uv add py-generichost[structlog]
```

---

## Quickstart

```python
import asyncio
from py_generichost import Host

async def main():
    # 1. Create builder (loads defaults, appsettings.json, and environment)
    builder = Host.create_application_builder()

    # 2. Register your services (Transient, Scoped, Singleton)
    # builder.services.add_singleton(MyService)
    # builder.services.add_singleton(MyWorkerDaemon)

    # 3. Build & Run
    host = builder.build()
    await host.run_async()

if __name__ == "__main__":
    asyncio.run(main())
```

---

## Core Features & Patterns

### 1. The Application Builder

You can initialize the builder either using `HostApplicationBuilder()` or the `.NET`-style `Host.create_application_builder()`.

```python
import asyncio
from py_generichost import HostApplicationBuilder

async def main():
    builder = HostApplicationBuilder()
    
    # Custom minimum log level (optional, defaults to INFO or JSON config)
    builder.logging.set_minimum_level(10)
    
    # Build container and run
    host = builder.build()
    await host.run_async()

if __name__ == "__main__":
    asyncio.run(main())
```

---

### 2. Dependency Injection (Zero Decorators!)

In standard `python-dependency-injector`, you often have to define explicit containers and use `@inject` / `Provide[]` decorators. `py-generichost` uses reflection-based auto-wiring.

Just register your classes, and `py-generichost` resolves dependencies dynamically based on standard Python type annotations:

```python
class DatabaseConnection:
    def execute(self, query: str):
        return f"Executed: {query}"

class OrderService:
    # DatabaseConnection is automatically injected by type annotation!
    def __init__(self, db: DatabaseConnection):
        self.db = db

# Register services
builder.services.add_singleton(DatabaseConnection)
builder.services.add_transient(OrderService)
```

**Supported Lifetimes:**
- `add_singleton(ServiceType)`: One shared instance across the entire application.
- `add_scoped(ServiceType)`: Unique instance per `contextvars` execution context.
- `add_transient(ServiceType)`: A fresh instance is created on every resolution.

---

### 3. Configuration & Environments (The .NET Way)

`py-generichost` automatically discovers `appsettings.json` in your `ContentRootPath`. If an environment-specific file exists (e.g. `appsettings.Development.json`), it is automatically merged on top.

#### `appsettings.json`
```json
{
  "Database": {
    "connection_string": "sqlite:///:memory:",
    "timeout": 30
  }
}
```

#### `appsettings.Development.json`
```json
{
  "Database": {
    "timeout": 120
  }
}
```

#### Python Binding:
```python
from pydantic import BaseModel
from py_generichost import HostEnvironment

class DatabaseConfig(BaseModel):
    connection_string: str
    timeout: int = 30

# Automatically binds the "Database" JSON section to the Pydantic model
# and registers it as a Singleton in DI
builder.configuration.configure(builder.services, DatabaseConfig, "Database")

class DatabaseService:
    def __init__(self, config: DatabaseConfig, env: HostEnvironment):
        self.config = config
        if env.is_development:
            print(f"DEV Mode: Using timeout of {config.timeout}s")
```

---

### 4. Background Services (`IHostedService` & Graceful Shutdown)

Create long-running daemons or periodic tasks by inheriting from `BackgroundService`. When the host receives `SIGINT` (Ctrl+C) or `SIGTERM`, it signals cancellation and waits gracefully for services to finish.

```python
import asyncio
import logging
from py_generichost import BackgroundService, CancellationToken

class HealthCheckWorker(BackgroundService):
    def __init__(self, logger: logging.Logger):
        super().__init__()
        self.logger = logger

    async def execute_async(self, cancellation_token: CancellationToken):
        self.logger.info("HealthCheck Worker started.")
        try:
            while not cancellation_token.is_cancellation_requested:
                self.logger.debug("System healthy...")
                await asyncio.sleep(5)
        except asyncio.CancelledError:
            self.logger.warning("Worker received shutdown signal.")
            
        self.logger.info("Worker stopped gracefully.")

# Register as hosted service (Singleton)
builder.services.add_singleton(HealthCheckWorker)
```

---

### 5. Class-specific Loggers (`ILogger<T>`)

If your class needs a logger, simply add `logger: logging.Logger` to `__init__`. `py-generichost` automatically creates and injects a logger named after your class's module and class name (`module_name.ClassName`).

```python
import logging

class OrderProcessor:
    def __init__(self, logger: logging.Logger):
        self.logger = logger

    def process(self):
        # Automatically emits as `[your_app.OrderProcessor] Processing...`
        self.logger.info("Processing order #1234")
```

---

### 6. Logging via `appsettings.json` (Late-Bound)

You can manage your entire logging setup (console, rotating file handlers, formatters, log levels) directly inside `appsettings.json` using Python's standard `dictConfig` format. The builder automatically applies it at `build()` time:

```json
{
  "Logging": {
    "version": 1,
    "disable_existing_loggers": false,
    "formatters": {
      "detailed": {
        "format": "%(asctime)s | %(name)-25s | [%(levelname)s] | %(message)s"
      }
    },
    "handlers": {
      "console": {
        "class": "logging.StreamHandler",
        "formatter": "detailed"
      },
      "file": {
        "class": "logging.handlers.RotatingFileHandler",
        "filename": "app.log",
        "maxBytes": 1048576,
        "backupCount": 3,
        "formatter": "detailed"
      }
    },
    "root": {
      "level": "INFO",
      "handlers": ["console", "file"]
    }
  }
}
```

---

### 7. Custom Settings & `ContentRootPath`

Use `HostApplicationBuilderSettings` to customize initialization, force environments, or set an explicit `ContentRootPath`:

```python
from py_generichost import HostApplicationBuilder, HostApplicationBuilderSettings

settings = HostApplicationBuilderSettings(
    environment_name="Testing",
    content_root_path="/custom/project/root",
    disable_defaults=False
)

builder = HostApplicationBuilder(settings)
```

- **`ContentRootPath`**: Defaults to the directory containing the running entrypoint script (`sys.argv[0]`).
- **`disable_defaults`**: If `True`, disables automatic file discovery and default logging.

---

## Design Principles

1. **Idiomatic Python with .NET Ergonomics**: Enjoy the clean architecture of .NET 10 (`Host.CreateApplicationBuilder`) without losing Python's simplicity and async-first nature.
2. **True Auto-Wiring**: No need to wrap every function or class with `@inject` decorators.
3. **Graceful Teardown**: Async background services receive cancellation tokens and clean shutdown signals.
4. **Environment Isolation**: Native configuration inheritance and overriding for Development, Staging, and Production.
