🎉 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! 🎊
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
|