User Tutorial: From Installation to Your First Plugin

[!NOTE] This guide walks you step-by-step from installing Structum to creating your first custom plugin.


Prerequisites

  • Python 3.11+ installed

  • Git installed

  • uv (modern package manager) - Installation


Part 1: Installation

[!IMPORTANT] Structum is in alpha phase (v0.1.0). Installation is from source only.

Step 1.1: Clone the Repository

git clone https://github.com/PythonWoods/structum.git
cd structum

Step 1.2: Install with uv

uv sync --extra dev

This command:

  • Creates a virtual environment automatically

  • Installs all workspace packages in editable mode

  • Installs development tools (pytest, mypy, ruff)

Step 1.3: Verify Installation

# Verify packages are installed
python -c "import structum; print('✓ Structum installed!')"

# Run tests (optional)
pytest packages/core/tests -v

Part 2: First Usage

Step 2.1: Create a Test Project

mkdir ~/my-structum-app
cd ~/my-structum-app

Step 2.2: Create the Config Structure

mkdir -p config/app

Step 2.3: Create a Configuration File

File: config/app/database.toml

[default]
host = "localhost"
port = 5432
name = "myapp"

[production]
host = "db.production.example.com"

Step 2.4: Use the Configuration

File: main.py

from structum.config import get_config, set_config_provider
from structum.plugins.dynaconf import DynaconfConfigProvider

# Setup provider
provider = DynaconfConfigProvider(root_path=".", env_prefix="MYAPP")
provider.auto_discover()
set_config_provider(provider)

# Use the configuration
config = get_config()
db_host = config.get("database.host", default="localhost")
db_port = config.get("database.port", default=5432)
db_name = config.get("database.name", default="app")

print(f"Database: {db_host}:{db_port}/{db_name}")

Step 2.5: Run

python main.py
# Output: Database: localhost:5432/myapp

# With a different environment
STRUCTUM_ENV=production python main.py
# Output: Database: db.production.example.com:5432/myapp

Part 3: Create Your First Plugin

Step 3.1: Plugin Structure

mkdir -p my_plugin
touch my_plugin/__init__.py my_plugin/provider.py

Step 3.2: Define the Interface (optional)

File: my_plugin/__init__.py

"""My Custom Plugin for Structum."""
from .provider import SlackNotificationProvider

__all__ = ["SlackNotificationProvider"]

Step 3.3: Implement the Provider

File: my_plugin/provider.py

from typing import Protocol
from dataclasses import dataclass

# 1. Define the protocol (interface)
class NotificationProviderInterface(Protocol):
    """Interface for notification providers."""
    
    def send(self, to: str, message: str) -> bool:
        """Send a notification."""
        ...

# 2. Implement the provider
@dataclass
class SlackNotificationProvider:
    """Slack implementation of NotificationProvider."""
    
    webhook_url: str
    channel: str = "#general"
    
    def send(self, to: str, message: str) -> bool:
        """Send notification to Slack."""
        import requests
        
        payload = {
            "channel": self.channel,
            "text": f"@{to}: {message}"
        }
        
        try:
            response = requests.post(self.webhook_url, json=payload)
            return response.status_code == 200
        except Exception:
            return False
    
    @classmethod
    def from_config(cls) -> "SlackNotificationProvider":
        """Create from Structum config."""
        from structum.config import get_config
        
        config = get_config()
        
        return cls(
            webhook_url=config.get("notifications.slack.webhook_url"),
            channel=config.get("notifications.slack.channel", default="#general")
        )

Step 3.4: Configure the Plugin

File: config/app/notifications.toml

[default.slack]
webhook_url = "https://hooks.slack.com/services/xxx"
channel = "#alerts"

Step 3.5: Use the Plugin

File: main.py

from my_plugin import SlackNotificationProvider

# Initialize from config
notifier = SlackNotificationProvider.from_config()

# Send notification
success = notifier.send("admin", "Deployment completed!")

if success:
    print("✓ Notification sent!")
else:
    print("✗ Notification failed")

Part 4: Advanced Patterns

4.1: Register the Plugin Globally

# In your app's __init__.py
from structum.config import set_config_provider
from structum.plugins.dynaconf import DynaconfConfigProvider

from my_plugin import SlackNotificationProvider

# Global singleton
_notifier: SlackNotificationProvider | None = None

def get_notifier() -> SlackNotificationProvider:
    """Get the global notifier instance."""
    global _notifier
    if _notifier is None:
        _notifier = SlackNotificationProvider.from_config()
    return _notifier

4.2: Use Dependency Injection

from structum.plugins.di import StructumContainer
from dependency_injector import providers

class AppContainer(StructumContainer):
    """Application container with notification provider."""
    
    notifier = providers.Singleton(
        SlackNotificationProvider.from_config
    )

# Usage
container = AppContainer()
notifier = container.notifier()
notifier.send("user", "Hello!")

Next Steps