Pure Python parser for Confetti configuration language
Project description
🎉 PyConfetti 🎉
"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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ec3efc8639027b42a2272adb3d9bd7f0ab07616dd2befe0e988af5fc4bdb869b
|
|
| MD5 |
d0cff6d6766f97db44a589add24f17a5
|
|
| BLAKE2b-256 |
80517f5a2704cbe80f36b605c766f2a66d95080a93a7b82bdeb211a7c30d07e5
|