Skip to content

Attributes

Attributes define the individual fields in your provider's schemas. Pyvider uses factory functions to create attributes with proper typing and validation.

๐Ÿค– 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.

Overview

Attributes represent simple and complex values in Terraform configurations: - Simple types: strings, numbers, booleans - Collection types: lists, maps, sets - Complex types: objects, tuples - Special types: dynamic, unknown, null

Factory Functions

Pyvider provides a_* factory functions to create attributes:

from pyvider.schema import (
    a_str, a_num, a_bool,          # Simple types
    a_list, a_map, a_set, a_tuple, # Collections
    a_obj,                          # Complex objects
    a_dyn,                          # Dynamic type
)

Simple Types

String Attributes

Use a_str() for text values:

from pyvider.schema import a_str, s_resource

@classmethod
def get_schema(cls):
    return s_resource({
        "name": a_str(
            required=True,
            description="Resource name"
        ),
        "region": a_str(
            default="us-east-1",
            description="AWS region"
        ),
        "password": a_str(
            required=True,
            sensitive=True,  # Masked in logs
            description="Admin password"
        ),
    })

Number Attributes

Use a_num() for numeric values (integers or floats):

port = a_num(
    default=8080,
    description="Port number",
    validators=[
        lambda x: 1 <= x <= 65535 or "Port must be between 1 and 65535"
    ]
)

timeout = a_num(
    default=30.5,
    description="Timeout in seconds"
)

Boolean Attributes

Use a_bool() for true/false values:

enabled = a_bool(
    default=True,
    description="Whether the feature is enabled"
)

debug_mode = a_bool(
    default=False,
    description="Enable debug logging"
)

Collection Types

Lists

Use a_list() for ordered collections:

# List of strings
tags = a_list(
    a_str(),
    default=[],
    description="Resource tags"
)

# List of numbers
ports = a_list(
    a_num(),
    description="Allowed ports"
)

# List of objects
rules = a_list(
    a_obj({
        "port": a_num(required=True),
        "protocol": a_str(required=True),
    }),
    description="Firewall rules"
)

Maps

Use a_map() for key-value pairs:

# Map of strings
labels = a_map(
    a_str(),
    default={},
    description="Label key-value pairs"
)

# Map of numbers
quotas = a_map(
    a_num(),
    description="Resource quotas by type"
)

Sets

Use a_set() for unordered unique collections:

# Set of strings
allowed_ips = a_set(
    a_str(),
    description="Allowed IP addresses"
)

Tuples

Use a_tuple() for fixed-length ordered collections with different types:

# Tuple with specific types for each element
coordinates = a_tuple(
    [a_num(), a_num()],  # [latitude, longitude]
    description="Geographic coordinates"
)

# Mixed types
metadata = a_tuple(
    [a_str(), a_num(), a_bool()],  # [name, count, active]
    description="Resource metadata tuple"
)

Complex Types

Objects

Use a_obj() for nested structures:

config = a_obj({
    "timeout": a_num(default=30),
    "retries": a_num(default=3),
    "endpoint": a_str(required=True),
    "tls_enabled": a_bool(default=True),
}, description="Connection configuration")

# Nested objects
server_config = a_obj({
    "host": a_str(required=True),
    "port": a_num(default=443),
    "auth": a_obj({
        "username": a_str(required=True),
        "password": a_str(required=True, sensitive=True),
    }, description="Authentication credentials"),
}, description="Server configuration")

Attribute Properties

Required vs Optional

# Required attribute (must be provided)
name = a_str(
    required=True,
    description="Required resource name"
)

# Optional attribute with default
region = a_str(
    default="us-east-1",
    description="Optional AWS region"
)

# Optional attribute without default (can be null)
description = a_str(
    description="Optional description"
)

Computed Attributes

Computed attributes are set by the provider, not by users:

@classmethod
def get_schema(cls):
    return s_resource({
        # User provides this
        "name": a_str(required=True, description="Server name"),

        # Provider computes these
        "id": a_str(computed=True, description="Unique identifier"),
        "ip_address": a_str(computed=True, description="Assigned IP"),
        "created_at": a_str(computed=True, description="Creation timestamp"),
    })

Sensitive Attributes

Sensitive attributes are masked in logs and outputs:

api_key = a_str(
    required=True,
    sensitive=True,  # Value will be masked
    description="API key for authentication"
)

credentials = a_obj({
    "username": a_str(required=True),
    "password": a_str(required=True, sensitive=True),
}, description="Login credentials")

Default Values

# Simple defaults
port = a_num(default=8080)
enabled = a_bool(default=True)
region = a_str(default="us-east-1")

# Collection defaults
tags = a_list(a_str(), default=[])
labels = a_map(a_str(), default={})

# Object defaults
config = a_obj({
    "timeout": a_num(default=30),
    "retries": a_num(default=3),
}, description="Configuration with defaults")

Validators

Add validation logic to attributes:

from pyvider.schema import a_str, a_num

# Single validator
port = a_num(
    validators=[
        lambda x: 1 <= x <= 65535 or "Port must be between 1 and 65535"
    ]
)

# Multiple validators
username = a_str(
    required=True,
    validators=[
        lambda x: len(x) >= 3 or "Username must be at least 3 characters",
        lambda x: x.isalnum() or "Username must be alphanumeric",
        lambda x: not x.startswith("_") or "Username cannot start with underscore",
    ]
)

# Validators for collections
tags = a_list(
    a_str(),
    validators=[
        lambda x: len(x) <= 10 or "Maximum 10 tags allowed",
        lambda x: all(len(tag) <= 50 for tag in x) or "Tag length must be <= 50 chars",
    ]
)

Special Types

Dynamic Type

Use a_dyn() when the type is not known until runtime:

metadata = a_dyn(
    description="Arbitrary metadata (type determined at runtime)"
)

Unknown and Null Values

For testing and advanced scenarios:

from pyvider.schema import a_unknown, a_null, a_str

# Create unknown values
unknown_string = a_unknown(a_str())

# Create null values
null_number = a_null(a_num())

Complete Example

Here's a comprehensive resource schema using various attribute types:

from pyvider.schema import (
    s_resource,
    a_str, a_num, a_bool,
    a_list, a_map, a_obj,
)

@classmethod
def get_schema(cls):
    return s_resource({
        # Required simple types
        "name": a_str(
            required=True,
            description="Server name"
        ),
        "instance_type": a_str(
            required=True,
            description="Instance type (e.g., t2.micro)"
        ),

        # Optional with defaults
        "port": a_num(
            default=8080,
            description="Port number",
            validators=[lambda x: 1 <= x <= 65535 or "Invalid port"]
        ),
        "enabled": a_bool(
            default=True,
            description="Whether server is enabled"
        ),

        # Sensitive
        "admin_password": a_str(
            required=True,
            sensitive=True,
            description="Administrator password"
        ),

        # Computed
        "id": a_str(
            computed=True,
            description="Unique identifier"
        ),
        "ip_address": a_str(
            computed=True,
            description="Assigned IP address"
        ),

        # Collections
        "tags": a_list(
            a_str(),
            default=[],
            description="Resource tags"
        ),
        "labels": a_map(
            a_str(),
            default={},
            description="Key-value labels"
        ),

        # Complex objects
        "config": a_obj({
            "timeout": a_num(default=30),
            "retries": a_num(default=3),
            "endpoints": a_list(a_str(), default=[]),
        }, description="Server configuration"),

        # Nested complex structure
        "monitoring": a_obj({
            "enabled": a_bool(default=True),
            "interval": a_num(default=60),
            "alerts": a_list(
                a_obj({
                    "type": a_str(required=True),
                    "threshold": a_num(required=True),
                    "email": a_str(required=True),
                }),
                default=[]
            ),
        }, description="Monitoring configuration"),
    })

Corresponding Terraform Configuration

The schema above would be used in Terraform like this:

resource "mycloud_server" "web" {
  name          = "web-server"
  instance_type = "t2.micro"
  port          = 8080
  enabled       = true
  admin_password = "secure-password"  # Sensitive

  tags = ["production", "web"]

  labels = {
    environment = "prod"
    team        = "platform"
  }

  config {
    timeout   = 60
    retries   = 5
    endpoints = ["https://api.example.com"]
  }

  monitoring {
    enabled  = true
    interval = 120

    alerts {
      type      = "cpu"
      threshold = 80
      email     = "[email protected]"
    }

    alerts {
      type      = "memory"
      threshold = 90
      email     = "[email protected]"
    }
  }
}

See Also