Skip to content

Repository files navigation

📄⚙️ Pyvider HCL

License Python 3.11+ uv Ruff CI

Python library for parsing HCL into pyvider.cty types

pyvider-hcl provides a simple and intuitive way to work with HCL (HashiCorp Configuration Language) data in your Python applications, with seamless integration into the pyvider ecosystem.

✨ Key Features

  • 🔄 CTY Integration - Parses HCL directly into CtyValue objects for pyvider compatibility
  • 🎯 Simplified API - Clean interface for parsing HCL and creating Terraform structures
  • 🔍 Automatic Type Inference - Infer CtyType from HCL data without explicit schemas
  • Schema Validation - Validate HCL data against CTY type schemas
  • 🏭 Factory Functions - Create Terraform variables and resources programmatically
  • 🖨️ Pretty Printing - Debug-friendly output for CTY values

Quick Start

Note: pyvider-hcl is in pre-release (v0.x.x). APIs and features may change before 1.0 release.

  1. Install: uv add pyvider-hcl
  2. Read the Getting Started guide.
  3. Try the examples in examples/README.md.

Documentation

Development

Quick Start

# Set up environment
uv sync

# Run common tasks
we run test       # Run tests
we run lint       # Check code
we run format     # Format code
we tasks          # See all available commands

See CLAUDE.md for detailed development instructions and architecture information.

For contribution guidelines, see CONTRIBUTING.md.

Format and lint

uv run ruff format .
uv run ruff check .

Contributing

See CONTRIBUTING.md for contribution guidelines.

License

Apache-2.0 License - see LICENSE for details.

Installation

To install pyvider-hcl, you can use uv:

uv add pyvider-hcl

Usage

Here's a simple example of how to use pyvider-hcl to parse an HCL string:

from pyvider.hcl import parse_hcl_to_cty, pretty_print_cty
from pyvider.cty import CtyString

hcl_string = """
  name = "Jules"
  age = 30
"""

cty_value = parse_hcl_to_cty(hcl_string)

pretty_print_cty(cty_value)

You can also parse an HCL file:

from pyvider.hcl import parse_hcl_to_cty, pretty_print_cty

with open("my_config.hcl", "r") as f:
    hcl_content = f.read()
    cty_value = parse_hcl_to_cty(hcl_content)
    pretty_print_cty(cty_value)

Schema Validation

You can validate HCL data against a CtyType schema:

from pyvider.hcl import parse_hcl_to_cty, pretty_print_cty
from pyvider.cty import CtyObject, CtyString, CtyNumber

schema = CtyObject({
    "name": CtyString(),
    "age": CtyNumber(),
})

hcl_string = """
  name = "Jules"
  age = "thirty" # Invalid type
"""

try:
    cty_value = parse_hcl_to_cty(hcl_string, schema=schema)
except Exception as e:
    print(e)

Complex Cty Integration Examples

Here are some more complex examples of how to use pyvider-hcl with pyvider.cty:

Parsing a list of objects

from pyvider.hcl import parse_hcl_to_cty, pretty_print_cty
from pyvider.cty import CtyObject, CtyList, CtyString, CtyNumber

hcl_string = """
  users = [
    {
      name = "Jules"
      age  = 30
    },
    {
      name = "Vincent"
      age  = 40
    }
  ]
"""

schema = CtyObject({
    "users": CtyList(
        element_type=CtyObject({
            "name": CtyString(),
            "age": CtyNumber(),
        })
    )
})

cty_value = parse_hcl_to_cty(hcl_string, schema=schema)

pretty_print_cty(cty_value)

Parsing nested objects

from pyvider.hcl import parse_hcl_to_cty, pretty_print_cty
from pyvider.cty import CtyObject, CtyString, CtyNumber

hcl_string = """
  config = {
    server = {
      host = "localhost"
      port = 8080
    }
    database = {
      host = "localhost"
      port = 5432
    }
  }
"""

schema = CtyObject({
    "config": CtyObject({
        "server": CtyObject({
            "host": CtyString(),
            "port": CtyNumber(),
        }),
        "database": CtyObject({
            "host": CtyString(),
            "port": CtyNumber(),
        }),
    })
})

cty_value = parse_hcl_to_cty(hcl_string, schema=schema)

pretty_print_cty(cty_value)

Creating Terraform Variables and Resources

You can use the factory functions to create CtyValue objects for Terraform variables and resources:

from pyvider.hcl import (
    parse_hcl_to_cty,
    pretty_print_cty,
    create_variable_cty,
    create_resource_cty,
)

# Create a variable
variable_cty = create_variable_cty(
    name="my_variable",
    type_str="string",
    default_py="my_default_value",
)

pretty_print_cty(variable_cty)

# Create a resource
resource_cty = create_resource_cty(
    r_type="my_resource",
    r_name="my_instance",
    attributes_py={
        "name": "my_resource_name",
        "value": 123,
    },
)

pretty_print_cty(resource_cty)

FAQ

How do I parse an HCL file?

Currently, you need to read the file manually and pass the content to parse_hcl_to_cty():

from pathlib import Path
from pyvider.hcl import parse_hcl_to_cty

hcl_content = Path("config.hcl").read_text()
result = parse_hcl_to_cty(hcl_content)

Can this library generate HCL output?

Yes. cty_to_hcl() renders an object- or map-typed CtyValue back into formatted HCL text:

from pyvider.hcl import cty_to_hcl, parse_hcl_to_cty

print(cty_to_hcl(parse_hcl_to_cty('name = "example"\nport = 8080\n')))

Everything is emitted as an attribute — a CtyValue carries no notion of HCL blocks, so block structure cannot be recovered from one. Unknown values and marked (e.g. sensitive) values are refused rather than rendered.

For a human-readable rendering rather than HCL, use format_cty() (returns a string) or pretty_print_cty() (prints it).

Does this support HCL expressions like var.name or length(list)?

Not yet. The library currently parses static HCL data. Expression evaluation (variables, functions, conditionals) is not implemented.

What's the difference between parse_hcl_to_cty() and parse_with_context()?

  • parse_hcl_to_cty(): Returns a CtyValue object with full type information. Use this for most cases.
  • parse_with_context(): Returns raw Python dict/list from the parser. Use this when you need the raw data structure or want enhanced error context without CTY conversion.

How do I validate HCL against a specific structure?

Pass a CTY schema to parse_hcl_to_cty():

from pyvider.hcl import parse_hcl_to_cty
from pyvider.cty import CtyObject, CtyString, CtyNumber

schema = CtyObject({
    "name": CtyString(),
    "port": CtyNumber(),
})

result = parse_hcl_to_cty(hcl_content, schema=schema)
# Raises HclParsingError if validation fails

Can I use this with Terraform configurations?

Yes! The library parses HCL syntax used by Terraform. The create_variable_cty() and create_resource_cty() factory functions help create Terraform-specific structures. Full Terraform-specific validation (provider blocks, module blocks, etc.) is limited.

What HCL version is supported?

The library uses python-hcl2 which supports HCL 2.x (the version used by Terraform 0.12+).

How do I handle parsing errors?

Wrap your parsing calls in a try/except block:

from pyvider.hcl import parse_hcl_to_cty, HclParsingError

try:
    result = parse_hcl_to_cty(hcl_content)
except HclParsingError as e:
    print(f"Parsing failed: {e}")
    # e.source_file, e.line, e.column available if set

Can I parse multiple HCL files at once?

You need to parse each file individually. For multi-file Terraform projects, parse each file separately and combine the results as needed.

What types can be automatically inferred?

When no schema is provided, the library automatically infers:

  • stringCtyString
  • number (int/float) → CtyNumber
  • boolCtyBool
  • listCtyList(CtyDynamic())
  • objectCtyObject with inferred field types

How do I contribute or report bugs?

See CONTRIBUTING.md for contribution guidelines. For bugs, please open an issue on the GitHub repository with:

  • The HCL content that fails
  • The error message
  • Expected vs. actual behavior

Related Projects

Copyright (c) provide.io LLC.

About

No description, website, or topics provided.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages