Skip to content

Code Reuse Patterns in Pyvider

Use Standard Python Patterns for Production

๐Ÿค– AI-Generated Content

This documentation was generated with AI assistance and is still being audited. Some, or potentially a lot, of this information may be inaccurate. Learn more.

**For production providers, use these proven approaches:**

- โœ… **Inheritance** - Base classes with shared functionality
- โœ… **Composition** - Helper classes and utilities
- โœ… **Utility Modules** - Shared functions and patterns

**Capabilities are experimental** and not recommended for production use. See [Experimental Capabilities](#experimental-capabilities) below for details.

This guide shows you how to share code and functionality across your Pyvider components using standard Python patterns. These approaches are production-ready and well-tested.

1. Base Class Inheritance

Create shared base classes for common functionality:

class BaseCloudResource(BaseResource):
    """Shared functionality for cloud resources."""

    async def apply_common_tags(self, resource_id: str, tags: dict):
        """Apply standard tags to resource."""
        pass

    async def setup_monitoring(self, resource_id: str):
        """Configure monitoring for resource."""
        pass

@register_resource("server")
class Server(BaseCloudResource):
    """Inherits tagging and monitoring."""
    async def _create_apply(self, ctx: ResourceContext) -> tuple[State | None, None]:
        server = await self.create_server(ctx.config)
        await self.apply_common_tags(server.id, ctx.config.tags)
        await self.setup_monitoring(server.id)
        return State(...), None

2. Composition with Helper Classes

Use composition to share functionality:

class RetryHandler:
    """Reusable retry logic."""

    async def with_retry(self, operation, max_attempts=3):
        for attempt in range(max_attempts):
            try:
                return await operation()
            except RetryableError:
                if attempt == max_attempts - 1:
                    raise
                await asyncio.sleep(2 ** attempt)

@register_resource("server")
class Server(BaseResource):
    def __init__(self):
        super().__init__()
        self.retry_handler = RetryHandler()

    async def _create_apply(self, ctx: ResourceContext) -> tuple[State | None, None]:
        server = await self.retry_handler.with_retry(
            lambda: self.create_server(ctx.config)
        )
        return State(...), None

3. Utility Modules

Create shared utility modules:

# utils/caching.py
class Cache:
    def __init__(self, ttl=300):
        self.cache = {}
        self.ttl = ttl

    async def get(self, key):
        # Cache implementation
        pass

# In your resource
from utils.caching import Cache

@register_resource("server")
class Server(BaseResource):
    def __init__(self):
        super().__init__()
        self.cache = Cache(ttl=600)

Component Bundling

You can package multiple related components together for distribution:

Package Structure

my-pyvider-bundle/
โ”œโ”€โ”€ pyproject.toml
โ”œโ”€โ”€ src/
โ”‚   โ””โ”€โ”€ my_bundle/
โ”‚       โ”œโ”€โ”€ __init__.py
โ”‚       โ”œโ”€โ”€ resources/
โ”‚       โ”œโ”€โ”€ data_sources/
โ”‚       โ””โ”€โ”€ functions/
โ””โ”€โ”€ tests/

Configuration

# pyproject.toml
[project]
name = "my-pyvider-bundle"
version = "0.1.0"
dependencies = [
    "pyvider>=0.0.1000",
]

[project.entry-points."pyvider.components"]
my_bundle = "my_bundle"

Using Bundled Components

# Install the bundle
pip install my-pyvider-bundle

# Components are automatically discovered
# Use them in Terraform configurations

Example: pyvider-components

The pyvider-components repository provides a comprehensive collection of production-ready components:

  • Resources: file_content, local_directory, timed_token
  • Data Sources: env_variables, file_info, http_api, lens_jq
  • Functions: String manipulation, numeric operations, JQ transformations
  • 100+ Working Examples with complete Terraform configurations

Perfect for: - Learning by example - Quick prototyping - Production use - Understanding best practices

Experimental Capabilities

Experimental Feature

This feature is experimental and may change significantly in future releases. Use in production at your own risk.

Stability: โš ๏ธ Experimental Planned Stable: v0.4.0 (Q2 2026)

Feedback welcome! Report issues

What are Capabilities?

Capabilities are a planned composition mechanism that would allow you to create reusable, modular components extending provider functionality. Think of them as mixins or plugins.

Current Status: - โœ… Basic infrastructure implemented (BaseCapability, decorators) - โš ๏ธ Lifecycle hooks partially implemented - ๐Ÿ”ฎ Advanced features planned but not yet available

Planned Features

Capability Lifecycle

Status: Partial implementation

Planned lifecycle hooks: - setup() - Initialize capability - configure() - Configure with provider settings - teardown() - Cleanup on shutdown

Capability Marketplace

Status: Planned for post-1.0

A central hub for discovering and sharing reusable capabilities: - Browse by category - Search by functionality - Community ratings - One-command installation via PyPI

Advanced Composition

Status: Planned

Features under consideration: - Capability dependency management - Composition ordering - Conflict resolution - Dynamic capability loading

Configuration

Capabilities can be configured through provider configuration or environment:

@register_provider("mycloud")
class MyCloudProvider(BaseProvider):
    async def configure(self, config):
        # Configure capabilities
        if hasattr(self, 'capabilities'):
            for cap in self.capabilities.values():
                if hasattr(cap, 'configure'):
                    await cap.configure(config)

Best Practices for Code Reuse

  1. Start Simple: Use inheritance for straightforward shared functionality
  2. Prefer Composition: Use helper classes for complex cross-cutting concerns
  3. Create Utility Modules: Package commonly-used functions in shared modules
  4. Test in Isolation: Test shared code independently from components
  5. Document Well: Provide clear usage examples and docstrings
  6. Version Carefully: Shared code is a dependency - version appropriately
  7. Avoid Over-Abstraction: Don't create abstractions until you need them in 3+ places

Future Plans

See the Roadmap for details on: - Capability marketplace timeline - Advanced composition features - Built-in capability library - Integration with telemetry systems

Contributing

Interested in contributing to the capabilities system?


Note: For production providers, we recommend using well-tested patterns (inheritance, composition, utilities) until the capabilities system reaches 1.0 maturity.