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.
- 🔄 CTY Integration - Parses HCL directly into
CtyValueobjects for pyvider compatibility - 🎯 Simplified API - Clean interface for parsing HCL and creating Terraform structures
- 🔍 Automatic Type Inference - Infer
CtyTypefrom 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
Note: pyvider-hcl is in pre-release (v0.x.x). APIs and features may change before 1.0 release.
- Install:
uv add pyvider-hcl - Read the Getting Started guide.
- Try the examples in examples/README.md.
- User Guide: Detailed usage examples and patterns
- API Reference: Complete API documentation
- Architecture: System design and data flow diagrams
- Contributing: Guidelines for contributors
- Changelog: Version history and release notes
# 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 commandsSee CLAUDE.md for detailed development instructions and architecture information.
For contribution guidelines, see CONTRIBUTING.md.
uv run ruff format .
uv run ruff check .See CONTRIBUTING.md for contribution guidelines.
Apache-2.0 License - see LICENSE for details.
To install pyvider-hcl, you can use uv:
uv add pyvider-hclHere'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)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)Here are some more complex examples of how to use pyvider-hcl with pyvider.cty:
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)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)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)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)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).
Not yet. The library currently parses static HCL data. Expression evaluation (variables, functions, conditionals) is not implemented.
parse_hcl_to_cty(): Returns aCtyValueobject 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.
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 failsYes! 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.
The library uses python-hcl2 which supports HCL 2.x (the version used by Terraform 0.12+).
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 setYou need to parse each file individually. For multi-file Terraform projects, parse each file separately and combine the results as needed.
When no schema is provided, the library automatically infers:
string→CtyStringnumber(int/float) →CtyNumberbool→CtyBoollist→CtyList(CtyDynamic())object→CtyObjectwith inferred field types
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
- pyvider-cty: CTY type system for Python
- pyvider: Terraform provider framework for Python
- provide-foundation: Foundation services and utilities
Copyright (c) provide.io LLC.