Integration Examples
Flask
Flask is a microframework for Python web applications. It’s flexible and extensible. Although there are best practices and traditions, Flask doesn’t really enforce the only one way to do it.
If you are working on a small project the chances are that you are using some Flask extensions like Flask-Mail or Flask-WTF. The extensions of the past are often got configured through environment variables only. It makes the use of Piny cumbersome. In mid-sized and large Flask projects though, you usually avoid using extra dependencies whenever possible. In such a case you can fit your code to use Piny pretty easy.
Here is an example of a simple Flask application. Configuration file is loaded with Piny and validated with Pydantic.
from flask import Flask
from flask.logging import default_handler
from piny import YamlLoader, StrictMatcher, PydanticV2Validator
from pydantic import BaseModel, validator
from typing import Any, Dict, Optional
from werkzeug.serving import run_simple
import logging
import sys
# Watch out!
# Pydantic V2 deprecated some model's methods:
# https://docs.pydantic.dev/2.0/migration/
#
# For Pydantic v2 use `PydanticV2Validator`
# For Pydantic v1 use `PydanticValidator`
#
# Validation
#
class AppSettings(BaseModel):
company: str
secret: str
max_content_len: Optional[int] = None
debug: bool = False
testing: bool = False
class LoggingSettings(BaseModel):
fmt: str
date_fmt: str
level: str
@validator("level")
def validate_name(cls, value):
upper = value.upper()
if upper not in logging._nameToLevel:
raise ValueError("Invalid logging level")
return upper
class Configuration(BaseModel):
app: AppSettings
logging: LoggingSettings
#
# Helpers
#
def configure_app(app: Flask, configuration: Dict[str, Any]) -> None:
"""
Apply configs to application
"""
app.settings = configuration
app.secret_key = app.settings["app"]["secret"].encode("utf-8")
def configure_logging(app: Flask) -> None:
"""
Configure app's logging
"""
app.logger.removeHandler(default_handler)
log_formatter = logging.Formatter(
fmt=app.settings["logging"]["fmt"], datefmt=app.settings["logging"]["date_fmt"]
)
log_handler = logging.StreamHandler()
log_handler.setFormatter(log_formatter)
log_handler.setLevel(app.settings["logging"]["level"])
app.logger.addHandler(log_handler)
#
# Factory
#
def create_app(path: str) -> Flask:
"""
Application factory
"""
# Get and validate config
config = YamlLoader(
path=path,
matcher=StrictMatcher,
validator=PydanticV2Validator,
schema=Configuration,
).load()
# Initialize app
app = Flask(__name__)
# Configure app
configure_app(app, config)
configure_logging(app)
return app
if __name__ == "__main__":
app = create_app(sys.argv[1])
@app.route("/")
def hello():
return "Hello World!"
# Run application:
# $ python flask_integration.py your-config.yaml
run_simple(hostname="localhost", port=5000, application=app)
You can use the same pattern with application factory in other frameworks, like aiohttp or sanic.
Command line
There are many possible applications for Piny CLI utility. For example, you can use it for Kubernetes deployment automation in your CI/CD pipeline.
Piny command line tool works both with standard input/output and files.
Standard input and output
$ export PASSWORD=mySecretPassword
$ echo "db: \${PASSWORD}" | piny
db: mySecretPassword
Files
$ piny config.template config.yaml
Or you can substitute environment variables in place:
$ piny production.yaml production.yaml