Skip to main content

flasklet

A few nice things for Flask: typed configuration, flash messages with delivery types, convention-based blueprint discovery, route imports, and template rendering helpers.

Quick start

Install with uv add flasklet. Create these files:

myapp/
  __init__.py
  admin/
    __init__.py
    routes.py
  templates/
    index.html
    components.html
    partial.html

myapp/__init__.py:

from enum import StrEnum

from flask import Flask
from flasklet import config, init_template_helpers, register_blueprints


class Mode(StrEnum):
	DEVELOPMENT = "development"
	PRODUCTION = "production"


class Settings(config.Config, prefix="MYAPP_"):
	secret_key: str = config.from_env()
	mode: Mode = config.from_env(default=Mode.DEVELOPMENT)


def create_app() -> Flask:
	app = Flask(__name__)
	settings = config.load(Settings)
	config.init_app(app, settings, flask_keys={"SECRET_KEY": "secret_key"})
	init_template_helpers(app)
	register_blueprints(app, auto_import_routes=True)
	return app

myapp/admin/__init__.py:

from flask import Blueprint

bp = Blueprint("admin", __name__)

myapp/admin/routes.py:

from flask import Response, redirect, render_template, url_for
from flasklet import config, flash_message, parse_flashed_messages

from myapp import Settings
from . import bp


@bp.get("/")
def index() -> str:
	return render_template(
		"index.html",
		mode=config.read_config(Settings).mode.value,
		messages=parse_flashed_messages(),
	)


@bp.post("/notice")
def notice() -> Response:
	flash_message("Saved", type="toast", category="success")
	return redirect(url_for("admin.index"))

myapp/templates/index.html:

{{ render_macro("components.html", "badge", value=mode) }}
{{ render_block("components.html", "notice", text="Welcome") }}
{{ render_partial("partial.html", text="Rendered separately") }}
{% for message in messages %}
  <p data-type="{{ message.type }}" class="{{ message.category }}">{{ message.text }}</p>
{% endfor %}
<form action="{{ url_for('admin.notice') }}" method="post">
  <button type="submit">Flash a message</button>
</form>

myapp/templates/components.html:

{% macro badge(value) %}<strong>{{ value }}</strong>{% endmacro %}
{% block notice %}<p>{{ text }}</p>{% endblock %}

myapp/templates/partial.html:

<aside>{{ text }}</aside>

Run MYAPP_SECRET_KEY=local-example-only uv run flask --app myapp:create_app run, then open http://127.0.0.1:5000/.

Typed configuration

config.load(Settings) reads environment variables when called. A field's name defaults to its uppercased attribute name, preceded by the class prefix. Pass a name to from_env to override it. load(Settings, prefix=...) adds a prefix in front of the class prefix.

Supported types are str, int, float, bool, Path, Enum subclasses, Optional[T], list[T], and NewType wrappers. Enum variables match the string representation of a member's value exactly. For example, MYAPP_MODE=production selects Mode.PRODUCTION. An invalid value reports the environment variable and allowed values. Booleans accept only true and false. Lists are comma-separated; an empty variable gives an empty list. For Optional[T], an empty variable gives None.

A field without a default is required. Use default_factory for values that must be created per instance. Mutable list, dict, and set defaults are deep-copied. Derived fields are computed once, in declaration order:

from pathlib import Path


class Storage(config.Config, prefix="MYAPP_"):
	data_dir: Path | None = config.from_env(default=None)
	base_dir: Path = config.derived(lambda s: (s.data_dir or Path.cwd()).resolve())

config.init_app stores the settings on the Flask app. flask_keys copies selected attributes into Flask's own config, which is needed for values such as SECRET_KEY. In an app context, config.read_config(Settings) returns the registered instance. Outside an app context, it loads a new instance from the current environment on every call.

Blueprint and route discovery

register_blueprints(app) scans subpackages of the app package for a module attribute named bp. It registers each discovered blueprint on its parent blueprint or on the app. A subpackage without bp and its children are skipped. Call it during app creation, before handling requests. The same imported blueprints can be registered on separate app instances.

Pass auto_import_routes=True to import modules next to each discovered blueprint and in its routes/ directory before registration. This is opt-in because importing a module runs its top-level code. The quick start uses this option.

import_routes(__file__, __name__) imports every Python module in the current package directory and its routes/ directory, in sorted order. Route decorators run as those modules are imported. It returns None. Use it in a package's __init__.py when you want explicit route imports instead.

Flash messages

flash_message(text, type="toast", category="success") adds a delivery type to Flask's ordinary flash category. Read messages with parse_flashed_messages(); each result has text, type, and category. Messages from Flask's own flash() are returned with type="message". Use parse_flashed_messages() when mixing the two functions.

Template helpers

init_template_helpers(app) registers render_block, render_macro, render_macro_unsafe, and render_partial as Jinja globals. Call it in the app factory before rendering templates. You can also register individual functions with app.add_template_global.

render_partial renders a complete template. render_block renders a block defined in the named template; it does not resolve a block that exists only in a parent template, and the selected block cannot use super().

render_macro renders only the selected macro. Other top-level expressions in the source template are not evaluated. Its keyword arguments serve two roles: arguments with names declared by the macro are passed to it, and all arguments are available as template context. A misspelled or unused keyword argument has no effect, so check argument names when output is missing.

render_macro follows the app's Jinja autoescaping. render_macro_unsafe disables autoescaping and returns an ordinary string. Use it only when the macro values are trusted HTML. When calling it from a Jinja template, the result needs |safe to avoid escaping the rendered HTML.

To include a file's source without Jinja processing, import IncludeRawExtension from flasklet, register it with app.jinja_env.add_extension(IncludeRawExtension), and use {% include_raw "path/to/file" %} in a template.

Metadata

Release files for flasklet 1.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for flasklet 1.3.0
File Size Uploaded
flasklet-1.3.0.tar.gz 13.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for flasklet 1.3.0
File Interpreter ABI Platform
flasklet-1.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 30.5 kB

Release files / flasklet-1.3.0.tar.gz

Download URL flasklet-1.3.0.tar.gz
Size 13.4 kB
Tags Source
SHA-256 checksum
How to use checksums
a825a871619ac7241512ec91579b6492ae1b8f5f4bc51301ffc3e77cf1b1ff95
BLAKE2b-256 checksum
How to use checksums
c47b016366d97bc04e56bfa071377b487a56e9040b2e8ee4c5e59c1b05eb5a68
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / flasklet-1.3.0-py3-none-any.whl

Download URL flasklet-1.3.0-py3-none-any.whl
Size 17.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b733bd108c444a83ca78d7f3b8d67b01f847805fd2b5ec66c394f0864b8b3685
BLAKE2b-256 checksum
How to use checksums
d830401908e824cdec28579eca326174bff9928ef4d1186e77ddaa4bb91e358f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

3.0.1

2 release files

3.0.0

2 release files

2.0.1

2 release files

2.0.0

2 release files

This release

1.3.0 This release

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.1.1

2 release files

1.0.0

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page