Skip to main content

Pure Python parser for Confetti configuration language

Project description

🎉 PyConfetti 🎉

Python 3.11+ License: Unlicense

"Because config files should be as fun as throwing confetti!" 🎊

A pure Python parser for the Confetti configuration language - a simple, flexible, and expressive configuration syntax that makes your config files look like a party! 🥳

✨ Features

  • 🚀 Zero dependencies - just plain Python!
  • 🧰 uv compatible - works perfectly with modern Python tooling!
  • 🔍 Full parsing support for directives, arguments, subdirectives, and comments
  • 🛡️ Robust error handling with helpful error messages
  • 🧩 Two convenient APIs: parse and walk
  • 🔄 Bidirectional support for reading AND writing configs
  • 📝 Triple-quoted strings for multiline values
  • 💪 Well-typed with complete type annotations

🚀 Installation

uv pip install pyconfetti

# Or install from source
git clone https://github.com/yourusername/pyconfetti.git
cd pyconfetti
uv pip install -e .

🎯 Quick Start

Parsing a Confetti Configuration

from pyconfetti import parse, pretty_print

# Example configuration
config = """
# This is a comment
server {
    host localhost
    port 8080

    ssl {
        enabled true
        cert "/path/to/cert.pem"
    }
}
"""

# Parse the configuration
unit = parse(config)

# Access the parsed data
server_directive = unit.root.subdirectives[0]

# Iterate through arguments in subdirectives
host_value = None
port_value = None

for subdir in server_directive.subdirectives:
    for i, arg in enumerate(subdir.arguments):
        if arg.value == "host" and i+1 < len(subdir.arguments):
            host_value = subdir.arguments[i+1].value  # "localhost"
        if arg.value == "port" and i+1 < len(subdir.arguments):
            port_value = subdir.arguments[i+1].value  # "8080"

print(f"Host: {host_value}, Port: {port_value}")

# Pretty print the parsed configuration
pretty_print(unit)

Walking Through a Configuration

from pyconfetti import walk, ElementType

def callback(element_type, arguments, comment):
    if element_type == ElementType.COMMENT:
        print(f"Found comment: {comment.text}")
    elif element_type == ElementType.DIRECTIVE:
        print(f"Found directive with arguments: {[arg.value for arg in arguments]}")
    elif element_type == ElementType.BLOCK_ENTER:
        print("Entering block")
    elif element_type == ElementType.BLOCK_LEAVE:
        print("Leaving block")
    return True  # Continue walking

# Walk through the configuration
walk(config, callback)

🎭 Why Confetti?

Confetti is a configuration language that looks like NGINX config but is more flexible. Based on the original C implementation, this pure Python parser offers full compatibility with the Confetti specification. It's perfect when:

  • YAML is too space-sensitive for you 😬
  • JSON doesn't support comments 🙄
  • TOML feels too INI-like 😴
  • XML makes you want to cry 😭

Confetti strikes the perfect balance between readability, expressiveness, and simplicity! ✨

🧩 Syntax Example

For more detailed information about the Confetti syntax, check out the official Confetti specification

# This is a comment

# Basic directives
server_name "my-awesome-app.com";
port 8080;

# Blocks with subdirectives
database {
    url "postgres://user:password@localhost:5432/mydb"
    pool_size 10
    timeout 30
}

# Multiple arguments
allowed_origins "http://localhost:3000" "https://example.com";

# Triple-quoted strings for multiline content
description """
    This is a multi-line description
    that spans several lines
    without needing escapes
""";

# Nested blocks
logging {
    level "info"

    file {
        path "/var/log/app.log"
        rotate true
        max_size "10MB"
    }
}

🗺️ Using the Mapper

PyConfetti's mapper module provides a simple way to map between Confetti configurations and Python dataclasses:

from typing import Optional
from pyconfetti import confetti, load_confetti, dump_confetti

# Define your classes with type annotations
@confetti
class Database:
    host: str
    port: int
    username: Optional[str] = None
    password: Optional[str] = None

@confetti
class WebServer:
    host: str
    dbname: str
    port: int = 8080

@confetti
class Config:
    database: Database
    server: WebServer

# Example Confetti configuration
config_text = """
config {
    database {
        host localhost
        port 5432
        username admin
    }

    server {
        host 127.0.0.1
        dbname myapp
    }
}
"""

# Load the configuration into a Config object
config = load_confetti(config_text, Config)

# Access the configuration as regular Python objects
print(f"Database: {config.database.host}:{config.database.port}")
print(f"Server: {config.server.host}:{config.server.port}")

# Create a new configuration programmatically
new_config = Config(
    database=Database(host="localhost", port=5433, username="postgres"),
    server=WebServer(host="0.0.0.0", dbname="newapp", port=9000),
)

# Convert back to Confetti
new_config_text = dump_confetti(new_config)

🛠️ Advanced Usage

Custom Configuration Options

from pyconfetti import parse, ConfettiOptions

# Create custom options
options = ConfettiOptions(
    max_depth=50,               # Maximum nesting depth
    allow_bidi=True,            # Allow bidirectional Unicode control characters
    c_style_comments=True,      # Support C-style comments (/* ... */)
    expression_arguments=True   # Enable expression evaluation in arguments
)

# Parse with custom options
unit = parse(config_text, options)

Building Configurations Programmatically

from pyconfetti import Directive, Argument, ConfettiUnit, pretty_print

# Create directives and arguments
server = Directive()
server.arguments.append(Argument(value="server", offset=0, length=6))

host = Directive()
host.arguments.append(Argument(value="host", offset=0, length=4))
host.arguments.append(Argument(value="localhost", offset=0, length=9))

port = Directive()
port.arguments.append(Argument(value="port", offset=0, length=4))
port.arguments.append(Argument(value="8080", offset=0, length=4))

# Add subdirectives
server.subdirectives.extend([host, port])

# Create a configuration unit
unit = ConfettiUnit()
unit.root.subdirectives.append(server)

# Pretty print the configuration
pretty_print(unit)

🔍 Error Handling

PyConfetti provides detailed error messages to help you debug configuration issues:

from pyconfetti import parse, ConfettiError

try:
    unit = parse(invalid_config)
except ConfettiError as e:
    print(f"Configuration error: {e}")
    # Handle the error appropriately

💻 Development

PyConfetti has no runtime dependencies, so you can start developing right away!

# Clone the repository
git clone https://github.com/yourusername/pyconfetti.git
cd pyconfetti

# Install development dependencies
uv pip install -e ".[dev]"

# Run tests
python run_test_suite.py

🤝 Contributing

Contributions are welcome! Feel free to:

  • 🐛 Report bugs
  • 💡 Suggest features
  • 🔧 Submit pull requests
  • 📖 Improve documentation

For questions and discussions about the Confetti format itself, please visit the original Confetti repository.

📄 License

This project is licensed under the Unlicense - dedicated to the public domain. See the LICENSE file for details.


Made with ❤️ and a handful of confetti! 🎊

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyconfetti-0.1.0.tar.gz (14.8 kB view details)

Uploaded Source

File details

Details for the file pyconfetti-0.1.0.tar.gz.

File metadata

  • Download URL: pyconfetti-0.1.0.tar.gz
  • Upload date:
  • Size: 14.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.6.10

File hashes

Hashes for pyconfetti-0.1.0.tar.gz
Algorithm Hash digest
SHA256 ec3efc8639027b42a2272adb3d9bd7f0ab07616dd2befe0e988af5fc4bdb869b
MD5 d0cff6d6766f97db44a589add24f17a5
BLAKE2b-256 80517f5a2704cbe80f36b605c766f2a66d95080a93a7b82bdeb211a7c30d07e5

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page