🎉 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! 🎊
Metadata
Release files for pyconfetti 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| pyconfetti-0.1.0.tar.gz | 14.8 kB | Without provenance | Details for pyconfetti-0.1.0.tar.gz |
Release files / pyconfetti-0.1.0.tar.gz
| Download URL | pyconfetti-0.1.0.tar.gz |
|---|---|
| Size | 14.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ec3efc8639027b42a2272adb3d9bd7f0ab07616dd2befe0e988af5fc4bdb869b
|
|
BLAKE2b-256 checksum How to use checksums |
80517f5a2704cbe80f36b605c766f2a66d95080a93a7b82bdeb211a7c30d07e5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.6.10
|
Security for pyconfetti 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.