Full Code of miguelgrinberg/aioflask for AI

main 7f447e79c81c cached
60 files
89.0 KB
23.7k tokens
150 symbols
1 requests
Download .txt
Repository: miguelgrinberg/aioflask
Branch: main
Commit: 7f447e79c81c
Files: 60
Total size: 89.0 KB

Directory structure:
gitextract_sepaq9a1/

├── .github/
│   └── workflows/
│       └── tests.yml
├── .gitignore
├── .readthedocs.yaml
├── CHANGES.md
├── LICENSE
├── README.md
├── examples/
│   ├── AsyncProgressBar/
│   │   ├── README.md
│   │   ├── progress_bar.py
│   │   └── requirements.txt
│   ├── aioflaskr/
│   │   ├── .flaskenv
│   │   ├── LICENSE
│   │   ├── README.md
│   │   ├── flaskr/
│   │   │   ├── __init__.py
│   │   │   ├── auth.py
│   │   │   ├── blog.py
│   │   │   ├── models.py
│   │   │   ├── static/
│   │   │   │   └── style.css
│   │   │   └── templates/
│   │   │       ├── auth/
│   │   │       │   ├── login.html
│   │   │       │   └── register.html
│   │   │       ├── base.html
│   │   │       └── blog/
│   │   │           ├── create.html
│   │   │           ├── index.html
│   │   │           └── update.html
│   │   ├── requirements.txt
│   │   └── tests/
│   │       ├── __init__.py
│   │       ├── conftest.py
│   │       ├── test_auth.py
│   │       ├── test_blog.py
│   │       └── test_init.py
│   ├── g/
│   │   └── app.py
│   ├── hello_world/
│   │   ├── app.py
│   │   └── templates/
│   │       └── index.html
│   ├── login/
│   │   └── app.py
│   ├── quotes-aiohttp/
│   │   ├── README.md
│   │   └── quotes.py
│   └── quotes-requests/
│       ├── README.md
│       ├── quotes.py
│       └── quotes_app.py
├── pyproject.toml
├── setup.cfg
├── setup.py
├── src/
│   └── aioflask/
│       ├── __init__.py
│       ├── app.py
│       ├── asgi.py
│       ├── cli.py
│       ├── ctx.py
│       ├── patch.py
│       ├── patched/
│       │   ├── __init__.py
│       │   └── flask_login/
│       │       └── __init__.py
│       ├── templating.py
│       └── testing.py
├── tests/
│   ├── __init__.py
│   ├── templates/
│   │   └── template.html
│   ├── test_app.py
│   ├── test_cli.py
│   ├── test_ctx.py
│   ├── test_patch.py
│   ├── test_templating.py
│   └── utils.py
└── tox.ini

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/tests.yml
================================================
name: build
on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
jobs:
  lint:
    name: lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-python@v2
      - run: python -m pip install --upgrade pip wheel
      - run: pip install tox tox-gh-actions
      - run: tox -eflake8
  tests:
    name: tests
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        python: ['3.7', '3.8', '3.9', '3.10']
      fail-fast: false
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-python@v2
        with:
          python-version: ${{ matrix.python }}
      - run: python -m pip install --upgrade pip wheel
      - run: pip install tox tox-gh-actions
      - run: tox
  coverage:
    name: coverage
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-python@v2
      - run: python -m pip install --upgrade pip wheel
      - run: pip install tox tox-gh-actions codecov
      - run: tox
      - run: codecov


================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
.python-version

# pipenv
#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
#   However, in case of collaboration, if having platform-specific dependencies or dependencies
#   having no cross-platform support, pipenv may install dependencies that don't work, or not
#   install all needed dependencies.
#Pipfile.lock

# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/


================================================
FILE: .readthedocs.yaml
================================================
version: 2

build:
  os: ubuntu-22.04
  tools:
    python: "3.11"

sphinx:
  configuration: docs/conf.py

python:
  install:
    - method: pip
      path: .
      extra_requirements:
        - docs


================================================
FILE: CHANGES.md
================================================
# aioflask change log

**Release 0.4.0** - 2021-08-18

- Support for app factory functions with uvicorn ([commit](https://github.com/miguelgrinberg/aioflask/commit/7f51ca835a5b581b28915b9818428ea09f720081))
- Make app context async ([commit](https://github.com/miguelgrinberg/aioflask/commit/d07ea5449389ae58b286ceff389386e9481e6715))
- Make request context async ([commit](https://github.com/miguelgrinberg/aioflask/commit/4232b2819ad7cf3ed386578a679ba2cbc75f91b0))
- Make test and cli runner clients async ([commit](https://github.com/miguelgrinberg/aioflask/commit/12408cb1d6018fabac8d2749607687164fb1da50))
- Patcher for 3rd party decorators without async view support ([commit](https://github.com/miguelgrinberg/aioflask/commit/b7d4433acd153c43463bd047ddfa19b8c2087078))
- Flask-Login support ([commit #1](https://github.com/miguelgrinberg/aioflask/commit/cbe8abcc0d890bc03787b75ba3c7cb78d5333f38)) ([commit #2](https://github.com/miguelgrinberg/aioflask/commit/e0ab0e0fe1a3b51c3dc3b35abc47b21002f034c3))
- Fix handling of application context ([commit](https://github.com/miguelgrinberg/aioflask/commit/f0b14856b58bd1e85b2b054cd6b3028da0f89091)) ([commit #3](https://github.com/miguelgrinberg/aioflask/commit/a6f5a67a1d9eaa4046d075c1417f5c042dd30c38))
- More unit tests ([commit](https://github.com/miguelgrinberg/aioflask/commit/cf061caa60c9c32975db7560de6c6e8dbe746e7d)) ([commit](https://github.com/miguelgrinberg/aioflask/commit/28f6bd5d62baa8857310aedd2ba728ab3e7322b6)) ([commit](https://github.com/miguelgrinberg/aioflask/commit/0b5f9e7bb0ba98f3c291dd5aa2c2e55ebce4aa61)) ([commit](https://github.com/miguelgrinberg/aioflask/commit/d4275e15474906c30acb80acac0a41766ef1d5d7))
- Update example documentation to use `flask aiorun` ([commit](https://github.com/miguelgrinberg/aioflask/commit/634ee10b7cbc934fa70d512a66334e78ddc39b3a))

**Release 0.3.0** - 2021-06-07

- Test client support, and some more unit tests ([commit](https://github.com/miguelgrinberg/aioflask/commit/c765e12f6382d685bbec1861dac062c13d63aea3))
- Started a change log ([commit](https://github.com/miguelgrinberg/aioflask/commit/e6f4c3a87964fb2e5ea3f4464853c2a1d5ecfc29))
- Improved example code ([commit](https://github.com/miguelgrinberg/aioflask/commit/496ce73f3f0ecb1bdbdd25bd957ce08e6742c191))
- One more example ([commit](https://github.com/miguelgrinberg/aioflask/commit/7edab525809f7ba19562f67bb363a033563d6158))

**Release 0.2.0** - 2021-05-15

- Flask 2.x changes ([commit](https://github.com/miguelgrinberg/aioflask/commit/52aef31fb9a7f8fe6a54b156fe257db1300c0ca6))
- Update README.md ([commit](https://github.com/miguelgrinberg/aioflask/commit/c232561ff3e1c954c49ab362be030da854ceb8ba))
- codecov.io integration ([commit](https://github.com/miguelgrinberg/aioflask/commit/d558dfde5f0717dc6f9b6ff0cedec142ffe60335))
- github actions build ([commit](https://github.com/miguelgrinberg/aioflask/commit/c5f43dacae3d8c73ac0c55a7a58b7c9ac985195a))

**Release 0.1** - 2020-11-07

- async render_template and CLI commands ([commit](https://github.com/miguelgrinberg/aioflask/commit/2e6944c111bd581e1c0eb345ffe88cb1ec014140))
- travis builds ([commit](https://github.com/miguelgrinberg/aioflask/commit/5834c8526fffe424bccfcbe62aa03e33c81b3018))
- app.run implementation and debug mode fixes ([commit](https://github.com/miguelgrinberg/aioflask/commit/2dc8426b5e5e52309639aa31db1f845c44226259))
- add note about the experimental nature of this thing ([commit](https://github.com/miguelgrinberg/aioflask/commit/f027e5ba95cc16ed3c513525d88a197c22001784))
- initial version ([commit](https://github.com/miguelgrinberg/aioflask/commit/4f1d1a343642fa88f76a4cc064f1d7268c9d7dc7))
- Initial commit ([commit](https://github.com/miguelgrinberg/aioflask/commit/b02c360cae72d2f7dd479c93e0cd7517d4dce259))


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2020 Miguel Grinberg

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# aioflask

![Build status](https://github.com/miguelgrinberg/aioflask/workflows/build/badge.svg) [![codecov](https://codecov.io/gh/miguelgrinberg/aioflask/branch/main/graph/badge.svg?token=CDMKF3L0ID)](https://codecov.io/gh/miguelgrinberg/aioflask)

Flask 2.x running on asyncio!

Is there a purpose for this, now that Flask 2.0 is out with support for async
views? Yes! Flask's own support for async handlers is very limited, as the
application still runs inside a WSGI web server, which severely limits
scalability. With aioflask you get a true ASGI application, running in a 100%
async environment.

WARNING: This is an experiment at this point. Not at all production ready!

## Quick start

To use async view functions and other handlers, use the `aioflask` package
instead of `flask`.

The `aioflask.Flask` class is a subclass of `flask.Flask` that changes a few
minor things to help the application run properly under the asyncio loop. In
particular, it overrides the following aspects of the application instance:

- The `route`, `before_request`, `before_first_request`, `after_request`, 
  `teardown_request`, `teardown_appcontext`, `errorhandler` and `cli.command`
  decorators accept coroutines as well as regular functions. The handlers all
  run inside an asyncio loop, so when using regular functions, care must be
  taken to not block.
- The WSGI callable entry point is replaced with an ASGI equivalent.
- The `run()` method uses uvicorn as web server.

There are also changes outside of the `Flask` class:

- The `flask aiorun` command starts an ASGI application using the uvicorn web
  server.
- The `render_template()` and `render_template_string()` functions are
  asynchronous and must be awaited.
- The context managers for the Flask application and request contexts are
  async.
- The test client and test CLI runner use coroutines.

## Example

```python
import asyncio
from aioflask import Flask, render_template

app = Flask(__name__)

@app.route('/')
async def index():
    await asyncio.sleep(1)
    return await render_template('index.html')
```


================================================
FILE: examples/AsyncProgressBar/README.md
================================================
AsyncProgressBar
================

This is the *AsyncProgressBar* from Quart ported to Flask. You need to have a
Redis server running on localhost:6379 for this example to run.


================================================
FILE: examples/AsyncProgressBar/progress_bar.py
================================================
import asyncio
import random
import aioredis
import redis
from aioflask import Flask, request, url_for, jsonify

app = Flask(__name__)

sr = redis.StrictRedis(host='localhost', port=6379)
sr.execute_command('FLUSHDB')


async def some_work():
    global aredis
    await aredis.set('state', 'running')
    work_to_do = range(1, 26)
    await aredis.set('length_of_work', len(work_to_do))
    for i in work_to_do:
        await aredis.set('processed', i)
        await asyncio.sleep(random.random())
    await aredis.set('state', 'ready')
    await aredis.set('percent', 100)


@app.route('/check_status/')
async def check_status():
    global aredis, sr
    status = dict()
    try:
        if await aredis.get('state') == b'running':
            if await aredis.get('processed') != await aredis.get('lastProcessed'):
                await aredis.set('percent', round(
                    int(await aredis.get('processed')) / int(await aredis.get('length_of_work')) * 100, 2))
                await aredis.set('lastProcessed', str(await aredis.get('processed')))
    except:
        pass

    try:
        status['state'] = sr.get('state').decode()
        status['processed'] = sr.get('processed').decode()
        status['length_of_work'] = sr.get('length_of_work').decode()
        status['percent_complete'] = sr.get('percent').decode()
    except:
        status['state'] = sr.get('state')
        status['processed'] = sr.get('processed')
        status['length_of_work'] = sr.get('length_of_work')
        status['percent_complete'] = sr.get('percent')

    status['hint'] = 'refresh me.'

    return jsonify(status)


@app.route('/progress/')
async def progress():
    return """
    <!doctype html>
    <html lang="en">
    <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Asyncio Progress Bar Demo</title>
    <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
    <link rel="stylesheet" href="/resources/demos/style.css">
    <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
    <script>
    var percent;
    
    function checkStatus() {
        $.getJSON('""" + url_for('check_status') + """', function (data) {
            console.log(data);
            percent = parseFloat(data.percent_complete);
            update_bar(percent);
            update_text(percent);
          });
        if (percent != 100) {
            setTimeout(checkStatus, 1000); 
        }
    }
    
    function update_bar(val) {
        if (val.length <= 0) {
            val = 0;
        }
        $( "#progressBar" ).progressbar({
            value: val
        });
    };
    
    function update_text(val) {
        if (val != 100) {
            document.getElementById("progressData").innerHTML = "&nbsp;<center>"+percent+"%</center>";
        } else {
            document.getElementById("progressData").innerHTML = "&nbsp;<center>Done!</center>";
        }
    }
    
    checkStatus();
    </script>
    </head>
    <body>
    <center><h2>Progress of work is shown below</h2></center>
    <div id="progressBar"></div>
    <div id="progressData" name="progressData"><center></center></div>
    </body>
    </html>"""


@app.route('/')
async def index():
    return 'This is the index page. Try the following to <a href="' + url_for(
        'start_work') + '">start some test work</a> with a progress indicator.'


@app.route('/start_work/')
async def start_work():
    global aredis
    loop = asyncio.get_event_loop()
    aredis = await aioredis.create_redis('redis://localhost', loop=loop)

    if await aredis.get('state') == b'running':
        return "<center>Please wait for current work to finish.</center>"
    else:
        await aredis.set('state', 'ready')

    if await aredis.get('state') == b'ready':
        loop.create_task(some_work())
        body = '''
        <center>
        work started!
        </center>
        <script type="text/javascript">
            window.location = "''' + url_for('progress') + '''";
        </script>'''
        return body


if __name__ == "__main__":
    app.run('localhost', port=5000, debug=True)


================================================
FILE: examples/AsyncProgressBar/requirements.txt
================================================
aioflask
aioredis==1.3.1
async-timeout==3.0.1
click==7.1.2
Flask==1.1.2
greenlet==0.4.16
greenletio
h11==0.9.0
hiredis==1.1.0
httptools==0.1.1
itsdangerous==1.1.0
Jinja2==2.11.2
MarkupSafe==1.1.1
redis==3.5.3
uvicorn==0.11.6
uvloop==0.14.0
websockets==8.1
Werkzeug==1.0.1


================================================
FILE: examples/aioflaskr/.flaskenv
================================================
FLASK_APP=flaskr:create_app()


================================================
FILE: examples/aioflaskr/LICENSE
================================================
Copyright 2010 Pallets (original version)
Copyright 2021 Miguel Grinberg (this version)

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

1.  Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.

2.  Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.

3.  Neither the name of the copyright holder nor the names of its
    contributors may be used to endorse or promote products derived from
    this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: examples/aioflaskr/README.md
================================================
aioflaskr
=========

This is the "Flaskr" application from the tutorial section of the Flask
documentation, adapted to work as an asyncio application with aioflask as the
web framework and Alchemical for its database. The Flask-Login extension is
used to maintain the logged in state of the user.

Install
-------
```bash
# clone the repository
$ git clone https://github.com/miguelgrinberg/aioflask
$ cd aioflask/examples/aioflaskr
```

Create a virtualenv and activate it:

```bash
$ python3 -m venv venv
$ . venv/bin/activate
```

Or on Windows cmd:

```text
$ py -3 -m venv venv
$ venv\Scripts\activate.bat
```

Install the requirements

```bash
$ pip install -r requirements.txt
```

Run
---

```bash
flask init-db
flask aiorun
```

Open http://127.0.0.1:5000 in a browser.

Test
----

```bash
$ pip install pytest pytest-asyncio
$ python -m pytest
```

Run with coverage report:

```bash
$ pip install pytest-cov
$ python -m pytest --cov=flaskr --cov-branch --cov-report=term-missing
```


================================================
FILE: examples/aioflaskr/flaskr/__init__.py
================================================
import os

import click
from aioflask import Flask
from aioflask.cli import with_appcontext
from alchemical.aioflask import Alchemical
from aioflask.patched.flask_login import LoginManager

db = Alchemical()
login = LoginManager()
login.login_view = 'auth.login'


def create_app(test_config=None):
    """Create and configure an instance of the Flask application."""
    app = Flask(__name__, instance_relative_config=True)

    # some deploy systems set the database url in the environ
    db_url = os.environ.get("DATABASE_URL")

    if db_url is None:
        # default to a sqlite database in the instance folder
        db_path = os.path.join(app.instance_path, "flaskr.sqlite")
        db_url = f"sqlite:///{db_path}"
        # ensure the instance folder exists
        os.makedirs(app.instance_path, exist_ok=True)

    app.config.from_mapping(
        SECRET_KEY=os.environ.get("SECRET_KEY", "dev"),
        ALCHEMICAL_DATABASE_URL=db_url,
    )

    if test_config is None:
        # load the instance config, if it exists, when not testing
        app.config.from_pyfile("config.py", silent=True)
    else:
        # load the test config if passed in
        app.config.update(test_config)

    # initialize Flask-Alchemical and the init-db command
    db.init_app(app)
    app.cli.add_command(init_db_command)

    # initialize Flask-Login
    login.init_app(app)

    # apply the blueprints to the app
    from flaskr import auth, blog

    app.register_blueprint(auth.bp)
    app.register_blueprint(blog.bp)

    # make "index" point at "/", which is handled by "blog.index"
    app.add_url_rule("/", endpoint="index")

    return app


async def init_db():
    await db.drop_all()
    await db.create_all()


@click.command("init-db")
@with_appcontext
async def init_db_command():
    """Clear existing data and create new tables."""
    await init_db()
    click.echo("Initialized the database.")


================================================
FILE: examples/aioflaskr/flaskr/auth.py
================================================
from aioflask import Blueprint
from aioflask import flash
from aioflask import redirect
from aioflask import render_template
from aioflask import request
from aioflask import url_for
from aioflask.patched.flask_login import login_user
from aioflask.patched.flask_login import logout_user
from sqlalchemy.exc import IntegrityError

from flaskr import db, login
from flaskr.models import User

bp = Blueprint("auth", __name__, url_prefix="/auth")


@login.user_loader
async def load_user(id):
    return await db.session.get(User, int(id))


@bp.route("/register", methods=("GET", "POST"))
async def register():
    """Register a new user.
    Validates that the username is not already taken. Hashes the
    password for security.
    """
    if request.method == "POST":
        username = request.form["username"]
        password = request.form["password"]
        error = None

        if not username:
            error = "Username is required."
        elif not password:
            error = "Password is required."

        if error is None:
            try:
                db.session.add(User(username=username, password=password))
                await db.session.commit()
            except IntegrityError:
                # The username was already taken, which caused the
                # commit to fail. Show a validation error.
                error = f"User {username} is already registered."
            else:
                # Success, go to the login page.
                return redirect(url_for("auth.login"))

        flash(error)

    return await render_template("auth/register.html")


@bp.route("/login", methods=("GET", "POST"))
async def login():
    """Log in a registered user by adding the user id to the session."""
    if request.method == "POST":
        username = request.form["username"]
        password = request.form["password"]
        error = None

        query = User.select().filter_by(username=username)
        user = await db.session.scalar(query)

        if user is None:
            error = "Incorrect username."
        elif not user.check_password(password):
            error = "Incorrect password."

        if error is None:
            # store the user id in a new session and return to the index
            login_user(user)
            return redirect(url_for("index"))

        flash(error)

    return await render_template("auth/login.html")


@bp.route("/logout")
async def logout():
    """Clear the current session, including the stored user id."""
    logout_user()
    return redirect(url_for("index"))


================================================
FILE: examples/aioflaskr/flaskr/blog.py
================================================
from aioflask import Blueprint
from aioflask import flash
from aioflask import redirect
from aioflask import render_template
from aioflask import request
from aioflask import url_for
from werkzeug.exceptions import abort
from aioflask.patched.flask_login import current_user
from aioflask.patched.flask_login import login_required

from flaskr import db
from flaskr.models import Post

bp = Blueprint("blog", __name__)


@bp.route("/")
async def index():
    """Show all the posts, most recent first."""
    posts = (await db.session.scalars(Post.select())).all()
    return await render_template("blog/index.html", posts=posts)


async def get_post(id, check_author=True):
    """Get a post and its author by id.
    Checks that the id exists and optionally that the current user is
    the author.
    :param id: id of post to get
    :param check_author: require the current user to be the author
    :return: the post with author information
    :raise 404: if a post with the given id doesn't exist
    :raise 403: if the current user isn't the author
    """
    post = await db.session.get(Post, id)

    if post is None:
        abort(404, f"Post id {id} doesn't exist.")

    if check_author and post.author != current_user:
        abort(403)

    return post


@bp.route("/create", methods=("GET", "POST"))
@login_required
async def create():
    """Create a new post for the current user."""
    if request.method == "POST":
        title = request.form["title"]
        body = request.form["body"]
        error = None

        if not title:
            error = "Title is required."

        if error is not None:
            flash(error)
        else:
            db.session.add(Post(title=title, body=body, author=current_user))
            await db.session.commit()
            return redirect(url_for("blog.index"))

    return await render_template("blog/create.html")


@bp.route("/<int:id>/update", methods=("GET", "POST"))
@login_required
async def update(id):
    """Update a post if the current user is the author."""
    post = await get_post(id)

    if request.method == "POST":
        title = request.form["title"]
        body = request.form["body"]
        error = None

        if not title:
            error = "Title is required."

        if error is not None:
            flash(error)
        else:
            post.title = title
            post.body = body
            await db.session.commit()
            return redirect(url_for("blog.index"))

    return await render_template("blog/update.html", post=post)


@bp.route("/<int:id>/delete", methods=("POST",))
@login_required
async def delete(id):
    """Delete a post.
    Ensures that the post exists and that the logged in user is the
    author of the post.
    """
    post = await get_post(id)
    await db.session.delete(post)
    await db.session.commit()
    return redirect(url_for("blog.index"))


================================================
FILE: examples/aioflaskr/flaskr/models.py
================================================
from werkzeug.security import check_password_hash
from werkzeug.security import generate_password_hash
from aioflask import url_for
from aioflask.patched.flask_login import UserMixin
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, func
from sqlalchemy.orm import relationship

from flaskr import db


class User(UserMixin, db.Model):
    id = Column(Integer, primary_key=True)
    username = Column(String, unique=True, nullable=False)
    password_hash = Column(String, nullable=False)

    @property
    def password(self):
        raise RuntimeError('Cannot get user passwords!')

    @password.setter
    def password(self, value):
        """Store the password as a hash for security."""
        self.password_hash = generate_password_hash(value)

    def check_password(self, value):
        return check_password_hash(self.password_hash, value)


class Post(db.Model):
    id = Column(Integer, primary_key=True)
    author_id = Column(ForeignKey(User.id), nullable=False)
    created = Column(
        DateTime, nullable=False, server_default=func.current_timestamp()
    )
    title = Column(String, nullable=False)
    body = Column(String, nullable=False)

    # User object backed by author_id
    # lazy="joined" means the user is returned with the post in one query
    author = relationship(User, lazy="joined", backref="posts")

    @property
    def update_url(self):
        return url_for("blog.update", id=self.id)

    @property
    def delete_url(self):
        return url_for("blog.delete", id=self.id)


================================================
FILE: examples/aioflaskr/flaskr/static/style.css
================================================
html {
  font-family: sans-serif;
  background: #eee;
  padding: 1rem;
}

body {
  max-width: 960px;
  margin: 0 auto;
  background: white;
}

h1, h2, h3, h4, h5, h6 {
  font-family: serif;
  color: #377ba8;
  margin: 1rem 0;
}

a {
  color: #377ba8;
}

hr {
  border: none;
  border-top: 1px solid lightgray;
}

nav {
  background: lightgray;
  display: flex;
  align-items: center;
  padding: 0 0.5rem;
}

nav h1 {
  flex: auto;
  margin: 0;
}

nav h1 a {
  text-decoration: none;
  padding: 0.25rem 0.5rem;
}

nav ul  {
  display: flex;
  list-style: none;
  margin: 0;
  padding: 0;
}

nav ul li a, nav ul li span, header .action {
  display: block;
  padding: 0.5rem;
}

.content {
  padding: 0 1rem 1rem;
}

.content > header {
  border-bottom: 1px solid lightgray;
  display: flex;
  align-items: flex-end;
}

.content > header h1 {
  flex: auto;
  margin: 1rem 0 0.25rem 0;
}

.flash {
  margin: 1em 0;
  padding: 1em;
  background: #cae6f6;
  border: 1px solid #377ba8;
}

.post > header {
  display: flex;
  align-items: flex-end;
  font-size: 0.85em;
}

.post > header > div:first-of-type {
  flex: auto;
}

.post > header h1 {
  font-size: 1.5em;
  margin-bottom: 0;
}

.post .about {
  color: slategray;
  font-style: italic;
}

.post .body {
  white-space: pre-line;
}

.content:last-child {
  margin-bottom: 0;
}

.content form {
  margin: 1em 0;
  display: flex;
  flex-direction: column;
}

.content label {
  font-weight: bold;
  margin-bottom: 0.5em;
}

.content input, .content textarea {
  margin-bottom: 1em;
}

.content textarea {
  min-height: 12em;
  resize: vertical;
}

input.danger {
  color: #cc2f2e;
}

input[type=submit] {
  align-self: start;
  min-width: 10em;
}


================================================
FILE: examples/aioflaskr/flaskr/templates/auth/login.html
================================================
{% extends 'base.html' %}

{% block header %}
  <h1>{% block title %}Log In{% endblock %}</h1>
{% endblock %}

{% block content %}
  <form method="post">
    <label for="username">Username</label>
    <input name="username" id="username" required>
    <label for="password">Password</label>
    <input type="password" name="password" id="password" required>
    <input type="submit" value="Log In">
  </form>
{% endblock %}


================================================
FILE: examples/aioflaskr/flaskr/templates/auth/register.html
================================================
{% extends 'base.html' %}

{% block header %}
  <h1>{% block title %}Register{% endblock %}</h1>
{% endblock %}

{% block content %}
  <form method="post">
    <label for="username">Username</label>
    <input name="username" id="username" required>
    <label for="password">Password</label>
    <input type="password" name="password" id="password" required>
    <input type="submit" value="Register">
  </form>
{% endblock %}


================================================
FILE: examples/aioflaskr/flaskr/templates/base.html
================================================
<!doctype html>
<title>{% block title %}{% endblock %} - Flaskr</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<nav>
  <h1><a href="{{ url_for('index') }}">Flaskr</a></h1>
  <ul>
    {% if current_user.is_authenticated %}
      <li><span>{{ current_user.username }}</span>
      <li><a href="{{ url_for('auth.logout') }}">Log Out</a>
    {% else %}
      <li><a href="{{ url_for('auth.register') }}">Register</a>
      <li><a href="{{ url_for('auth.login') }}">Log In</a>
    {% endif %}
  </ul>
</nav>
<section class="content">
  <header>
    {% block header %}{% endblock %}
  </header>
  {% for message in get_flashed_messages() %}
    <div class="flash">{{ message }}</div>
  {% endfor %}
  {% block content %}{% endblock %}
</section>


================================================
FILE: examples/aioflaskr/flaskr/templates/blog/create.html
================================================
{% extends 'base.html' %}

{% block header %}
  <h1>{% block title %}New Post{% endblock %}</h1>
{% endblock %}

{% block content %}
  <form method="post">
    <label for="title">Title</label>
    <input name="title" id="title" value="{{ request.form['title'] }}" required>
    <label for="body">Body</label>
    <textarea name="body" id="body">{{ request.form['body'] }}</textarea>
    <input type="submit" value="Save">
  </form>
{% endblock %}


================================================
FILE: examples/aioflaskr/flaskr/templates/blog/index.html
================================================
{% extends 'base.html' %}

{% block header %}
  <h1>{% block title %}Posts{% endblock %}</h1>
  {% if current_user.is_authenticated %}
    <a class="action" href="{{ url_for('blog.create') }}">New</a>
  {% endif %}
{% endblock %}

{% block content %}
  {% for post in posts %}
    <article class="post">
      <header>
        <div>
          <h1>{{ post.title }}</h1>
          <div class="about">by {{ post.author.username }} on {{ post.created.strftime('%Y-%m-%d') }}</div>
        </div>
        {% if current_user == post.author %}
          <a class="action" href="{{ post.update_url }}">Edit</a>
        {% endif %}
      </header>
      <p class="body">{{ post.body }}</p>
    </article>
    {% if not loop.last %}
      <hr>
    {% endif %}
  {% endfor %}
{% endblock %}


================================================
FILE: examples/aioflaskr/flaskr/templates/blog/update.html
================================================
{% extends 'base.html' %}

{% block header %}
  <h1>{% block title %}Edit "{{ post['title'] }}"{% endblock %}</h1>
{% endblock %}

{% block content %}
  <form method="post">
    <label for="title">Title</label>
    <input name="title" id="title" value="{{ request.form['title'] or post['title'] }}" required>
    <label for="body">Body</label>
    <textarea name="body" id="body">{{ request.form['body'] or post['body'] }}</textarea>
    <input type="submit" value="Save">
  </form>
  <hr>
  <form action="{{ post.delete_url }}" method="post">
    <input class="danger" type="submit" value="Delete" onclick="return confirm('Are you sure?');">
  </form>
{% endblock %}


================================================
FILE: examples/aioflaskr/requirements.txt
================================================
aioflask
aiosqlite==0.17.0
alchemical
asgiref==3.4.1
click==8.0.1
Flask==2.0.1
Flask-Login==0.5.0
greenlet==1.1.1
greenletio
h11==0.12.0
importlib-metadata==4.6.3
itsdangerous==2.0.1
Jinja2==3.0.1
MarkupSafe==2.0.1
python-dotenv==0.19.0
SQLAlchemy==1.4.25
typing-extensions==3.10.0.0
uvicorn==0.14.0
Werkzeug==2.0.1
zipp==3.5.0


================================================
FILE: examples/aioflaskr/tests/__init__.py
================================================


================================================
FILE: examples/aioflaskr/tests/conftest.py
================================================
from datetime import datetime

import pytest

from flaskr import create_app
from flaskr import db
from flaskr import init_db
from flaskr.models import User
from flaskr.models import Post


@pytest.fixture
async def app():
    """Create and configure a new app instance for each test."""
    # create the app with common test config
    app = create_app({"TESTING": True,
                      "ALCHEMICAL_DATABASE_URL": "sqlite:///:memory:"})

    # create the database and load test data
    async with app.app_context():
        await init_db()
        user = User(username="test", password="test")
        db.session.add_all(
            (
                user,
                User(username="other", password="other"),
                Post(
                    title="test title",
                    body="test\nbody",
                    author=user,
                    created=datetime(2018, 1, 1),
                ),
            )
        )
        await db.session.commit()

    yield app


@pytest.fixture
def client(app):
    """A test client for the app."""
    return app.test_client()


@pytest.fixture
def runner(app):
    """A test runner for the app's Click commands."""
    return app.test_cli_runner()


class AuthActions:
    def __init__(self, client):
        self._client = client

    async def login(self, username="test", password="test"):
        return await self._client.post(
            "/auth/login", data={"username": username, "password": password}
        )

    async def logout(self):
        return await self._client.get("/auth/logout")


@pytest.fixture
def auth(client):
    return AuthActions(client)


================================================
FILE: examples/aioflaskr/tests/test_auth.py
================================================
import pytest

from flaskr import db
from flaskr.models import User


@pytest.mark.asyncio
async def test_register(client, app):
    # test that viewing the page renders without template errors
    assert (await client.get("/auth/register")).status_code == 200

    # test that successful registration redirects to the login page
    response = await client.post("/auth/register",
                                 data={"username": "a", "password": "a"})
    assert "/auth/login" == response.headers["Location"]

    # test that the user was inserted into the database
    async with app.app_context():
        query = User.select().filter_by(username="a")
        assert await db.session.scalar(query) is not None


def test_user_password(app):
    user = User(username="a", password="a")
    assert user.password_hash != "a"
    assert user.check_password("a")


@pytest.mark.asyncio
@pytest.mark.parametrize(
    ("username", "password", "message"),
    (
        ("", "", b"Username is required."),
        ("a", "", b"Password is required."),
        ("test", "test", b"already registered"),
    ),
)
async def test_register_validate_input(client, username, password, message):
    response = await client.post(
        "/auth/register", data={"username": username, "password": password}
    )
    assert message in response.data


@pytest.mark.asyncio
async def test_login(client, auth):
    # test that viewing the page renders without template errors
    assert (await client.get("/auth/login")).status_code == 200

    # test that successful login redirects to the index page
    response = await auth.login()
    assert response.headers["Location"] == "/"

    # login request set the user_id in the session
    # check that the user is loaded from the session
    async with client:
        response = await client.get("/")
        assert b"<span>test</span>" in response.data


@pytest.mark.asyncio
@pytest.mark.parametrize(
    ("username", "password", "message"),
    (("a", "test", b"Incorrect username."),
     ("test", "a", b"Incorrect password.")),
)
async def test_login_validate_input(auth, username, password, message):
    response = await auth.login(username, password)
    assert message in response.data


@pytest.mark.asyncio
async def test_logout(client, auth):
    await auth.login()

    async with client:
        await auth.logout()
        response = await client.get("/")
        assert b"Log In" in response.data


================================================
FILE: examples/aioflaskr/tests/test_blog.py
================================================
import pytest
from sqlalchemy import func
from sqlalchemy import select

from flaskr import db
from flaskr.models import User
from flaskr.models import Post


@pytest.mark.asyncio
async def test_index(client, auth):
    response = await client.get("/")
    assert b"Log In" in response.data
    assert b"Register" in response.data

    await auth.login()
    response = await client.get("/")
    assert b"test title" in response.data
    assert b"by test on 2018-01-01" in response.data
    assert b"test\nbody" in response.data
    assert b'href="/1/update"' in response.data


@pytest.mark.asyncio
@pytest.mark.parametrize("path", ("/create", "/1/update", "/1/delete"))
async def test_login_required(client, path):
    response = await client.post(path)
    assert response.headers["Location"].startswith("/auth/login?next=")


@pytest.mark.asyncio
async def test_author_required(app, client, auth):
    # change the post author to another user
    async with app.app_context():
        (await db.session.get(Post, 1)).author = await db.session.get(User, 2)
        await db.session.commit()

    await auth.login()
    # current user can't modify other user's post
    assert (await client.post("/1/update")).status_code == 403
    assert (await client.post("/1/delete")).status_code == 403
    # current user doesn't see edit link
    assert b'href="/1/update"' not in (await client.get("/")).data


@pytest.mark.asyncio
@pytest.mark.parametrize("path", ("/2/update", "/2/delete"))
async def test_exists_required(client, auth, path):
    await auth.login()
    assert (await client.post(path)).status_code == 404


@pytest.mark.asyncio
async def test_create(client, auth, app):
    await auth.login()
    assert (await client.get("/create")).status_code == 200
    await client.post("/create", data={"title": "created", "body": ""})

    async with app.app_context():
        query = select(func.count()).select_from(Post)
        assert await db.session.scalar(query) == 2


@pytest.mark.asyncio
async def test_update(client, auth, app):
    await auth.login()
    assert (await client.get("/1/update")).status_code == 200
    await client.post("/1/update", data={"title": "updated", "body": ""})

    async with app.app_context():
        assert (await db.session.get(Post, 1)).title == "updated"


@pytest.mark.asyncio
@pytest.mark.parametrize("path", ("/create", "/1/update"))
async def test_create_update_validate(client, auth, path):
    await auth.login()
    response = await client.post(path, data={"title": "", "body": ""})
    assert b"Title is required." in response.data


@pytest.mark.asyncio
async def test_delete(client, auth, app):
    await auth.login()
    response = await client.post("/1/delete")
    assert response.headers["Location"] == "/"

    async with app.app_context():
        assert (await db.session.get(Post, 1)) is None


================================================
FILE: examples/aioflaskr/tests/test_init.py
================================================
import pytest
from flaskr import create_app


def test_config():
    """Test create_app without passing test config."""
    assert not create_app().testing
    assert create_app({"TESTING": True}).testing


def test_db_url_environ(monkeypatch):
    """Test DATABASE_URL environment variable."""
    monkeypatch.setenv("DATABASE_URL", "sqlite:///environ")
    app = create_app()
    assert app.config["ALCHEMICAL_DATABASE_URL"] == "sqlite:///environ"


@pytest.mark.asyncio
async def test_init_db_command(runner, monkeypatch):
    class Recorder:
        called = False

    async def fake_init_db():
        Recorder.called = True

    monkeypatch.setattr("flaskr.init_db", fake_init_db)
    result = await runner.invoke(args=["init-db"])
    assert "Initialized" in result.output
    assert Recorder.called


================================================
FILE: examples/g/app.py
================================================
from aioflask import Flask, g
import aiohttp

app = Flask(__name__)


@app.before_request
async def before_request():
    g.session = aiohttp.ClientSession()


@app.teardown_appcontext
async def teardown_appcontext(exc):
    await g.session.close()


@app.route('/')
async def index():
    response = await g.session.get('https://api.quotable.io/random')
    return (await response.json())['content']


================================================
FILE: examples/hello_world/app.py
================================================
from aioflask import Flask, render_template

app = Flask(__name__)


@app.route('/')
async def index():
    return await render_template('index.html')


@app.cli.command()
async def hello():
    """Example async CLI handler."""
    print('hello!')


================================================
FILE: examples/hello_world/templates/index.html
================================================
<!doctype html>
<html>
  <head>
    <title>Hello (async) world!</title>
  </head>
  <body>
    <h1>Hello (async) world!</h1>
  </body>
</html>


================================================
FILE: examples/login/app.py
================================================
from aioflask import Flask, request, redirect
from aioflask.patched.flask_login import LoginManager, login_required, UserMixin, login_user, logout_user, current_user
import aiohttp

app = Flask(__name__)
app.secret_key = 'top-secret!'
login = LoginManager(app)
login.login_view = 'login'


class User(UserMixin):
    def __init__(self, user_id):
        self.id = user_id


@login.user_loader
async def load_user(user_id):
    return User(user_id)


@app.route('/')
@login_required
async def index():
    return f'''
<html>
  <body>
    <p>Logged in user: {current_user.id}</p>
    <form method="POST" action="/logout">
      <input type="submit" value="Logout">
    </form>
  </body>
</html>'''


@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return '''
<html>
  <body>
    <form method="POST" action="">
      <input type="text" name="username">
      <input type="submit" value="Login">
    </form>
  </body>
</html>'''
    else:
        login_user(User(request.form['username']))
        return redirect(request.args.get('next', '/'))


@app.route('/logout', methods=['POST'])
def logout():
    logout_user()
    return redirect('/')


================================================
FILE: examples/quotes-aiohttp/README.md
================================================
Quotes
======

Returns 10 famous quotes each time the page is refreshed. Quotes are obtained
by sending concurrent HTTP requests to a Quotes API with the aiohttp
asynchronous client.

To run this example, set `FLASK_APP=quotes.py` in your environment and then use
the standard `flask aiorun` command to start the server.


================================================
FILE: examples/quotes-aiohttp/quotes.py
================================================
import asyncio
import aiohttp
from aioflask import Flask, render_template_string

app = Flask(__name__)
template = '''<!doctype html>
<html>
  <head>
    <title>Quotes</title>
  </head>
  <body>
    <h1>Quotes</h1>
    {% for quote in quotes %}
    <p><i>"{{ quote.content }}"</i> &mdash; {{ quote.author }}</p>
    {% endfor %}
  </body>
</html>'''


async def get_quote(session):
    response = await session.get('https://api.quotable.io/random')
    return await response.json()


@app.route('/')
async def index():
    async with aiohttp.ClientSession() as session:
        tasks = [get_quote(session) for _ in range(10)]
        quotes = await asyncio.gather(*tasks)
    return await render_template_string(template, quotes=quotes)


================================================
FILE: examples/quotes-requests/README.md
================================================
Quotes
======

Returns 10 famous quotes each time the page is refreshed. Quotes are obtained
by sending concurrent HTTP requests to a Quotes API with the requests client.

This example shows how you can incorporate blocking code into your aioflask
application without blocking the asyncio loop. 

To run this example, set `FLASK_APP=quotes.py` in your environment and then use
the standard `flask aiorun` command to start the server.


================================================
FILE: examples/quotes-requests/quotes.py
================================================
import greenletio

# import the application with blocking functions monkey patched
with greenletio.patch_blocking():
    from quotes_app import app


================================================
FILE: examples/quotes-requests/quotes_app.py
================================================
import asyncio
from aioflask import Flask, render_template_string
from greenletio import async_
import requests

app = Flask(__name__)
template = '''<!doctype html>
<html>
  <head>
    <title>Quotes</title>
  </head>
  <body>
    <h1>Quotes</h1>
    {% for quote in quotes %}
    <p><i>"{{ quote.content }}"</i> &mdash; {{ quote.author }}</p>
    {% endfor %}
  </body>
</html>'''


# this is a blocking function that is converted to asynchronous with
# greenletio's @async_ decorator. For this to work, all the low-level I/O
# functions started from this function must be asynchronous, which can be
# achieved with greenletio's monkey patching feature.
@async_
def get_quote():
    response = requests.get('https://api.quotable.io/random')
    return response.json()


@app.route('/')
async def index():
    tasks = [get_quote() for _ in range(10)]
    quotes = await asyncio.gather(*tasks)
    return await render_template_string(template, quotes=quotes)


================================================
FILE: pyproject.toml
================================================
[build-system]
requires = [
    "setuptools>=42",
    "wheel"
]
build-backend = "setuptools.build_meta"


================================================
FILE: setup.cfg
================================================
[metadata]
name = aioflask
version = 0.4.1.dev0
author = Miguel Grinberg
author_email = miguel.grinberg@gmail.com
description = Flask running on asyncio.
long_description = file: README.md
long_description_content_type = text/markdown
url = https://github.com/miguelgrinberg/aioflask
project_urls =
    Bug Tracker = https://github.com/miguelgrinberg/aioflask/issues
classifiers =
    Intended Audience :: Developers
    Programming Language :: Python :: 3
    License :: OSI Approved :: MIT License
    Operating System :: OS Independent

[options]
zip_safe = False
include_package_data = True
package_dir =
    = src
packages = find:
python_requires = >=3.6
install_requires =
    greenletio
    flask >= 2
    uvicorn

[options.packages.find]
where = src

[options.entry_points]
flask.commands = 
    aiorun = aioflask.cli:run_command

[options.extras_require]
docs =
    sphinx


================================================
FILE: setup.py
================================================
import setuptools

setuptools.setup()


================================================
FILE: src/aioflask/__init__.py
================================================
from flask import *
from .app import Flask
from .templating import render_template, render_template_string
from .testing import FlaskClient


================================================
FILE: src/aioflask/app.py
================================================
import asyncio
from functools import wraps
from inspect import iscoroutinefunction
import os
from flask.app import *
from flask.app import Flask as OriginalFlask
from flask import cli
from flask.globals import _app_ctx_stack, _request_ctx_stack
from flask.helpers import get_debug_flag, get_env, get_load_dotenv
from greenletio import await_
import uvicorn
from .asgi import WsgiToAsgiInstance
from .cli import show_server_banner, AppGroup
from .ctx import AppContext, RequestContext
from .testing import FlaskClient, FlaskCliRunner


class Flask(OriginalFlask):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.cli = AppGroup()
        self.jinja_options['enable_async'] = True
        self.test_client_class = FlaskClient
        self.test_cli_runner_class = FlaskCliRunner
        self.async_fixed = False

    def ensure_sync(self, func):
        if not iscoroutinefunction(func):
            return func

        def wrapped(*args, **kwargs):
            appctx = _app_ctx_stack.top
            reqctx = _request_ctx_stack.top

            async def _coro():
                # app context is push internally to avoid changing reference
                # counts and emitting duplicate signals
                _app_ctx_stack.push(appctx)
                if reqctx:
                    _request_ctx_stack.push(reqctx)
                ret = await func(*args, **kwargs)
                if reqctx:
                    _request_ctx_stack.pop()
                _app_ctx_stack.pop()
                return ret

            return await_(_coro())

        return wrapped

    def app_context(self):
        return AppContext(self)

    def request_context(self, environ):
        return RequestContext(self, environ)

    def _fix_async(self):  # pragma: no cover
        self.async_fixed = True

        if os.environ.get('AIOFLASK_USE_DEBUGGER') == 'true':
            os.environ['WERKZEUG_RUN_MAIN'] = 'true'
            from werkzeug.debug import DebuggedApplication
            self.wsgi_app = DebuggedApplication(self.wsgi_app, evalex=True)

    async def asgi_app(self, scope, receive, send):  # pragma: no cover
        if not self.async_fixed:
            self._fix_async()
        return await WsgiToAsgiInstance(self.wsgi_app)(scope, receive, send)

    async def __call__(self, scope, receive, send=None):  # pragma: no cover
        if send is None:
            # we were called with two arguments, so this is likely a WSGI app
            raise RuntimeError('The WSGI interface is not supported by '
                               'aioflask, use an ASGI web server instead.')
        return await self.asgi_app(scope, receive, send)

    def run(self, host=None, port=None, debug=None, load_dotenv=True,
            **options):

        if get_load_dotenv(load_dotenv):
            cli.load_dotenv()

            # if set, let env vars override previous values
            if "FLASK_ENV" in os.environ:
                self.env = get_env()
                self.debug = get_debug_flag()
            elif "FLASK_DEBUG" in os.environ:
                self.debug = get_debug_flag()

        # debug passed to method overrides all other sources
        if debug is not None:
            self.debug = bool(debug)

        server_name = self.config.get("SERVER_NAME")
        sn_host = sn_port = None

        if server_name:
            sn_host, _, sn_port = server_name.partition(":")

        if not host:
            if sn_host:
                host = sn_host
            else:
                host = "127.0.0.1"

        if port or port == 0:
            port = int(port)
        elif sn_port:
            port = int(sn_port)
        else:
            port = 5000

        options.setdefault("use_reloader", self.debug)
        options.setdefault("use_debugger", self.debug)
        options.setdefault("threaded", True)
        options.setdefault("workers", 1)

        certfile = None
        keyfile = None
        cert = options.get('ssl_context')
        if cert is not None and len(cert) == 2:
            certfile = cert[0]
            keyfile = cert[1]
        elif cert == 'adhoc':
            raise RuntimeError(
                'Aad-hoc certificates are not supported by aioflask.')

        if debug:
            os.environ['FLASK_DEBUG'] = 'true'

        if options['use_debugger']:
            os.environ['AIOFLASK_USE_DEBUGGER'] = 'true'

        show_server_banner(self.env, self.debug, self.name, False)

        uvicorn.run(
            self.import_name + ':app',
            host=host,
            port=port,
            reload=options['use_reloader'],
            workers=options['workers'],
            log_level='debug' if self.debug else 'info',
            ssl_certfile=certfile,
            ssl_keyfile=keyfile,
        )


================================================
FILE: src/aioflask/asgi.py
================================================
import sys
from tempfile import SpooledTemporaryFile
from greenletio import async_, await_


class wsgi_to_asgi:  # pragma: no cover
    """Wraps a WSGI application to make it into an ASGI application."""

    def __init__(self, wsgi_application):
        self.wsgi_application = wsgi_application

    async def __call__(self, scope, receive, send):
        """ASGI application instantiation point.

        We return a new WsgiToAsgiInstance here with the WSGI app
        and the scope, ready to respond when it is __call__ed.
        """
        await WsgiToAsgiInstance(self.wsgi_application)(scope, receive, send)


class WsgiToAsgiInstance:  # pragma: no cover
    """Per-socket instance of a wrapped WSGI application"""

    def __init__(self, wsgi_application):
        self.wsgi_application = wsgi_application
        self.response_started = False

    async def __call__(self, scope, receive, send):
        if scope["type"] != "http":
            raise ValueError("WSGI wrapper received a non-HTTP scope")
        self.scope = scope
        with SpooledTemporaryFile(max_size=65536) as body:
            # Alright, wait for the http.request messages
            while True:
                message = await receive()
                if message["type"] != "http.request":
                    raise ValueError(
                        "WSGI wrapper received a non-HTTP-request message")
                body.write(message.get("body", b""))
                if not message.get("more_body"):
                    break
            body.seek(0)

            # Wrap send so it can be called from the subthread
            self.sync_send = await_(send)
            # Call the WSGI app
            await self.run_wsgi_app(body)

    def build_environ(self, scope, body):
        """Builds a scope and request body into a WSGI environ object."""
        environ = {
            "REQUEST_METHOD": scope["method"],
            "SCRIPT_NAME": scope.get("root_path", ""),
            "PATH_INFO": scope["path"],
            "QUERY_STRING": scope["query_string"].decode("ascii"),
            "SERVER_PROTOCOL": "HTTP/%s" % scope["http_version"],
            "wsgi.version": (1, 0),
            "wsgi.url_scheme": scope.get("scheme", "http"),
            "wsgi.input": body,
            "wsgi.errors": sys.stderr,
            "wsgi.multithread": True,
            "wsgi.multiprocess": True,
            "wsgi.run_once": False,
        }
        # Get server name and port - required in WSGI, not in ASGI
        if "server" in scope:
            environ["SERVER_NAME"] = scope["server"][0]
            environ["SERVER_PORT"] = str(scope["server"][1])
        else:
            environ["SERVER_NAME"] = "localhost"
            environ["SERVER_PORT"] = "80"

        if "client" in scope:
            environ["REMOTE_ADDR"] = scope["client"][0]

        # Go through headers and make them into environ entries
        for name, value in self.scope.get("headers", []):
            name = name.decode("latin1")
            if name == "content-length":
                corrected_name = "CONTENT_LENGTH"
            elif name == "content-type":
                corrected_name = "CONTENT_TYPE"
            else:
                corrected_name = "HTTP_%s" % name.upper().replace("-", "_")
            # HTTPbis say only ASCII chars are allowed in headers, but we
            # latin1 just in case
            value = value.decode("latin1")
            if corrected_name in environ:
                value = environ[corrected_name] + "," + value
            environ[corrected_name] = value
        return environ

    def start_response(self, status, response_headers, exc_info=None):
        """WSGI start_response callable."""

        # Don't allow re-calling once response has begun
        if self.response_started:
            raise exc_info[1].with_traceback(exc_info[2])
        # Don't allow re-calling without exc_info
        if hasattr(self, "response_start") and exc_info is None:
            raise ValueError(
                "You cannot call start_response a second time without exc_info"
            )
        # Extract status code
        status_code, _ = status.split(" ", 1)
        status_code = int(status_code)
        # Extract headers
        headers = [
            (name.lower().encode("ascii"), value.encode("ascii"))
            for name, value in response_headers
        ]
        # Build and send response start message.
        self.response_start = {
            "type": "http.response.start",
            "status": status_code,
            "headers": headers,
        }

    @async_
    def run_wsgi_app(self, body):
        """WSGI app greenlet."""
        # Translate the scope and incoming request body into a WSGI environ
        environ = self.build_environ(self.scope, body)
        # Run the WSGI app
        for output in self.wsgi_application(environ, self.start_response):
            # If this is the first response, include the response headers
            if not self.response_started:
                self.response_started = True
                self.sync_send(self.response_start)
            self.sync_send({"type": "http.response.body", "body": output,
                            "more_body": True})
        # Close connection
        if not self.response_started:
            self.response_started = True
            self.sync_send(self.response_start)
        self.sync_send({"type": "http.response.body"})


================================================
FILE: src/aioflask/cli.py
================================================
from functools import wraps
from inspect import iscoroutinefunction
import os
import sys

from flask.cli import *
from flask.cli import AppGroup, ScriptInfo, update_wrapper, \
    SeparatedPathType, pass_script_info, get_debug_flag, NoAppException, \
    prepare_import
from flask.cli import _validate_key
from flask.globals import _app_ctx_stack
from flask.helpers import get_env
from greenletio import await_
from werkzeug.utils import import_string
import click
import uvicorn

try:
    import ssl
except ImportError:  # pragma: no cover
    ssl = None

OriginalAppGroup = AppGroup


def _ensure_sync(func, with_appcontext=False):
    if not iscoroutinefunction(func):
        return func

    def decorated(*args, **kwargs):
        if with_appcontext:
            appctx = _app_ctx_stack.top

            @await_
            async def _coro():
                with appctx:
                    return await func(*args, **kwargs)
        else:
            @await_
            async def _coro():
                return await func(*args, **kwargs)

        return _coro()

    return decorated


def with_appcontext(f):
    """Wraps a callback so that it's guaranteed to be executed with the
    script's application context.  If callbacks are registered directly
    to the ``app.cli`` object then they are wrapped with this function
    by default unless it's disabled.
    """

    @click.pass_context
    def decorator(__ctx, *args, **kwargs):
        with __ctx.ensure_object(ScriptInfo).load_app().app_context():
            return __ctx.invoke(_ensure_sync(f, True), *args, **kwargs)

    return update_wrapper(decorator, f)


class AppGroup(OriginalAppGroup):
    """This works similar to a regular click :class:`~click.Group` but it
    changes the behavior of the :meth:`command` decorator so that it
    automatically wraps the functions in :func:`with_appcontext`.
    Not to be confused with :class:`FlaskGroup`.
    """

    def command(self, *args, **kwargs):
        """This works exactly like the method of the same name on a regular
        :class:`click.Group` but it wraps callbacks in :func:`with_appcontext`
        unless it's disabled by passing ``with_appcontext=False``.
        """
        wrap_for_ctx = kwargs.pop("with_appcontext", True)

        def decorator(f):
            if wrap_for_ctx:
                f = with_appcontext(f)
            return click.Group.command(self, *args, **kwargs)(_ensure_sync(f))

        return decorator


def show_server_banner(env, debug, app_import_path, eager_loading):
    """Show extra startup messages the first time the server is run,
    ignoring the reloader.
    """
    if app_import_path is not None:
        message = f" * Serving Flask app {app_import_path!r}"

        click.echo(message)

    click.echo(f" * Environment: {env}")

    if debug is not None:
        click.echo(f" * Debug mode: {'on' if debug else 'off'}")


class CertParamType(click.ParamType):
    """Click option type for the ``--cert`` option. Allows either an
    existing file, the string ``'adhoc'``, or an import for a
    :class:`~ssl.SSLContext` object.
    """

    name = "path"

    def __init__(self):
        self.path_type = click.Path(exists=True, dir_okay=False,
                                    resolve_path=True)

    def convert(self, value, param, ctx):
        if ssl is None:
            raise click.BadParameter('Using "--cert" requires Python to be '
                                     'compiled with SSL support.',
                                     ctx, param)

        try:
            return self.path_type(value, param, ctx)
        except click.BadParameter:
            value = click.STRING(value, param, ctx).lower()

            if value == "adhoc":
                raise click.BadParameter("Aad-hoc certificates are currently "
                                         "not supported by aioflask.",
                                         ctx, param)

                return value

            obj = import_string(value, silent=True)

            if isinstance(obj, ssl.SSLContext):
                return obj

            raise


@click.command("run", short_help="Run a development server.")
@click.option("--host", "-h", default="127.0.0.1",
              help="The interface to bind to.")
@click.option("--port", "-p", default=5000, help="The port to bind to.")
@click.option(
    "--cert", type=CertParamType(),
    help="Specify a certificate file to use HTTPS."
)
@click.option(
    "--key",
    type=click.Path(exists=True, dir_okay=False, resolve_path=True),
    callback=_validate_key,
    expose_value=False,
    help="The key file to use when specifying a certificate.",
)
@click.option(
    "--reload/--no-reload",
    default=None,
    help="Enable or disable the reloader. By default the reloader "
    "is active if debug is enabled.",
)
@click.option(
    "--debugger/--no-debugger",
    default=None,
    help="Enable or disable the debugger. By default the debugger "
    "is active if debug is enabled.",
)
@click.option(
    "--eager-loading/--lazy-loading",
    default=None,
    help="Enable or disable eager loading. By default eager "
    "loading is enabled if the reloader is disabled.",
)
@click.option(
    "--with-threads/--without-threads",
    default=True,
    help="Enable or disable multithreading.",
)
@click.option(
    "--extra-files",
    default=None,
    type=SeparatedPathType(),
    help=(
        "Extra files that trigger a reload on change. Multiple paths"
        f" are separated by {os.path.pathsep!r}."
    ),
)
@pass_script_info
def run_command(info, host, port, reload, debugger, eager_loading,
                with_threads, cert, extra_files):
    """Run a local development server.
    This server is for development purposes only. It does not provide
    the stability, security, or performance of production WSGI servers.
    The reloader and debugger are enabled by default if
    FLASK_ENV=development or FLASK_DEBUG=1.
    """
    debug = get_debug_flag()

    if reload is None:
        reload = debug

    if debugger is None:
        debugger = debug
    if debugger:
        os.environ['AIOFLASK_USE_DEBUGGER'] = 'true'

    certfile = None
    keyfile = None
    if cert is not None and len(cert) == 2:
        certfile = cert[0]
        keyfile = cert[1]

    show_server_banner(get_env(), debug, info.app_import_path, eager_loading)

    app_import_path = info.app_import_path
    if app_import_path is None:
        for path in ('wsgi', 'app'):
            if os.path.exists(path) or os.path.exists(path + '.py'):
                app_import_path = path
                break
        if app_import_path is None:
            raise NoAppException(
                "Could not locate a Flask application. You did not provide "
                'the "FLASK_APP" environment variable, and a "wsgi.py" or '
                '"app.py" module was not found in the current directory.'
            )
    if app_import_path.endswith('.py'):
        app_import_path = app_import_path[:-3]

    factory = False
    if app_import_path.endswith('()'):
        # TODO: this needs to be expanded to accept arguments for the factory
        # function
        app_import_path = app_import_path[:-2]
        factory = True

    if ':' not in app_import_path:
        app_import_path += ':app'

    import_name, app_name = app_import_path.split(':')
    import_name = prepare_import(import_name)

    uvicorn.run(
        import_name + ':' + app_name,
        factory=factory,
        host=host,
        port=port,
        reload=reload,
        workers=1,
        log_level='debug' if debug else 'info',
        ssl_certfile=certfile,
        ssl_keyfile=keyfile,
    )

    # currently not supported:
    # - eager_loading
    # - with_threads
    # - adhoc certs
    # - extra_files


================================================
FILE: src/aioflask/ctx.py
================================================
import sys
from greenletio import async_
from flask.ctx import *
from flask.ctx import AppContext as OriginalAppContext, \
    RequestContext as OriginalRequestContext, _sentinel, _app_ctx_stack, \
    _request_ctx_stack, appcontext_popped


class AppContext(OriginalAppContext):
    async def apush(self):
        """Binds the app context to the current context."""
        self.push()

    async def apop(self, exc=_sentinel):
        """Pops the app context."""
        try:
            self._refcnt -= 1
            if self._refcnt <= 0:
                if exc is _sentinel:  # pragma: no cover
                    exc = sys.exc_info()[1]

                @async_
                def do_teardown_async():
                    _app_ctx_stack.push(self)
                    self.app.do_teardown_appcontext(exc)
                    _app_ctx_stack.pop()

                await do_teardown_async()
        finally:
            rv = _app_ctx_stack.pop()
        assert rv is self, \
            f"Popped wrong app context.  ({rv!r} instead of {self!r})"
        appcontext_popped.send(self.app)

    async def __aenter__(self):
        await self.apush()
        return self

    async def __aexit__(self, exc_type, exc_value, tb):
        await self.apop(exc_value)


class RequestContext(OriginalRequestContext):
    async def apush(self):
        self.push()

    async def apop(self, exc=_sentinel):
        app_ctx = self._implicit_app_ctx_stack.pop()
        clear_request = False

        try:
            if not self._implicit_app_ctx_stack:
                if hasattr(self, 'preserved'):  # Flask < 2.2
                    self.preserved = False
                    self._preserved_exc = None
                if exc is _sentinel:  # pragma: no cover
                    exc = sys.exc_info()[1]

                @async_
                def do_teardown():
                    _request_ctx_stack.push(self)
                    self.app.do_teardown_request(exc)
                    _request_ctx_stack.pop()

                await do_teardown()

                request_close = getattr(self.request, "close", None)
                if request_close is not None:  # pragma: no branch
                    request_close()
                clear_request = True
        finally:
            rv = _request_ctx_stack.pop()

            # get rid of circular dependencies at the end of the request
            # so that we don't require the GC to be active.
            if clear_request:
                rv.request.environ["werkzeug.request"] = None

            # Get rid of the app as well if necessary.
            if app_ctx is not None:
                await app_ctx.apop(exc)

            assert (
                rv is self
            ), f"Popped wrong request context. ({rv!r} instead of {self!r})"

    async def aauto_pop(self, exc):
        if hasattr(self, 'preserved'):  # Flask < 2.2
            if self.request.environ.get("flask._preserve_context") or (
                exc is not None and self.app.preserve_context_on_exception
            ):  # pragma: no cover
                self.preserved = True
                self._preserved_exc = exc
            else:
                await self.apop(exc)
        else:
            await self.apop(exc)

    async def __aenter__(self):
        await self.apush()
        return self

    async def __aexit__(self, exc_type, exc_value, tb):
        await self.aauto_pop(exc_value)


================================================
FILE: src/aioflask/patch.py
================================================
from functools import wraps
from aioflask import current_app


def patch_decorator(decorator):
    def patched_decorator(f):
        @wraps(f)
        def ensure_sync(*a, **kw):
            return current_app.ensure_sync(f)(*a, **kw)

        return decorator(ensure_sync)
    return patched_decorator


def patch_decorator_with_args(decorator):
    def patched_decorator(*args, **kwargs):
        def inner_patched_decorator(f):
            @wraps(f)
            def ensure_sync(*a, **kw):
                return current_app.ensure_sync(f)(*a, **kw)

            return decorator(*args, **kwargs)(ensure_sync)
        return inner_patched_decorator
    return patched_decorator


def patch_decorator_method(class_, method_name):
    original_decorator = getattr(class_, method_name)

    def patched_decorator_method(self, f):
        @wraps(f)
        def ensure_sync(*a, **kw):
            return current_app.ensure_sync(f)(*a, **kw)

        return original_decorator(self, ensure_sync)
    return patched_decorator_method


def patch_decorator_method_with_args(class_, method_name):
    original_decorator = getattr(class_, method_name)

    def patched_decorator_method(self, *args, **kwargs):
        def inner_patched_decorator_method(f):
            @wraps(f)
            def ensure_sync(*a, **kw):
                return current_app.ensure_sync(f)(*a, **kw)

            return original_decorator(self, *args, **kwargs)(ensure_sync)
        return inner_patched_decorator_method
    return patched_decorator_method


================================================
FILE: src/aioflask/patched/__init__.py
================================================


================================================
FILE: src/aioflask/patched/flask_login/__init__.py
================================================
from functools import wraps
import sys
from werkzeug.local import LocalProxy
from aioflask import current_app, g
from flask import _request_ctx_stack
from aioflask.patch import patch_decorator, patch_decorator_method
import flask_login
from flask_login import login_required, fresh_login_required, \
    LoginManager as OriginalLoginManager

for symbol in flask_login.__all__:
    try:
        globals()[symbol] = getattr(flask_login, symbol)
    except AttributeError:
        pass


def _user_context_processor():
    return {'current_user': _get_user()}


def _load_user():
    # Obtain the current user and preserve it in the g object. Flask-Login
    # saves the user in a custom attribute of the request context, but that
    # doesn't work with aioflask because when a copy of the request context is
    # made, custom attributes are not carried over to the copy.
    current_app.login_manager._load_user()
    g.flask_login_current_user = _request_ctx_stack.top.user


def _get_user():
    # Return the current user. This function is linked to the current_user
    # context local, but unlike the original in Flask-Login, it does not
    # attempt to load the user, it just returns the user that was pre-loaded.
    # This avoids the somewhat tricky complication of triggering database
    # operations that need to be awaited, which would require using something
    # like (await current_user)
    if hasattr(g, 'flask_login_current_user'):
        return g.flask_login_current_user
    return current_app.login_manager.anonymous_user()


class LoginManager(OriginalLoginManager):
    def init_app(self, app, add_context_processor=True):
        super().init_app(app, add_context_processor=False)
        if add_context_processor:
            app.context_processor(_user_context_processor)

        # To prevent the current_user context local from triggering I/O at a
        # random time when it is first referenced (which is a big complication
        # if the I/O is async and needs to be awaited), we force the user to be
        # loaded before each request. This isn't a perfect solution, because
        # a before request handler registered before this one will not see the
        # current user.
        app.before_request(_load_user)

    # the decorators that register callbacks need to be patched to support
    # async views
    user_loader = patch_decorator_method(OriginalLoginManager, 'user_loader')
    header_loader = patch_decorator_method(
        OriginalLoginManager, 'header_loader')
    request_loader = patch_decorator_method(
        OriginalLoginManager, 'request_loader')
    unauthorized_handler = patch_decorator_method(
        OriginalLoginManager, 'unauthorized_handler')
    needs_refresh_handler = patch_decorator_method(
        OriginalLoginManager, 'needs_refresh_handler')


# patch the two login_required decorators so that they accept async views
login_required = patch_decorator(login_required)
fresh_login_required = patch_decorator(fresh_login_required)


# redefine the current_user context local
current_user = LocalProxy(_get_user)

# patch the _get_user() function in the flask_login.utils module so that any
# calls to get current_user in Flask-Login functions are redirected here
setattr(sys.modules['flask_login.utils'], '_get_user', _get_user)


================================================
FILE: src/aioflask/templating.py
================================================
from flask.templating import *
from flask.templating import _app_ctx_stack, before_render_template, \
    template_rendered


async def _render(template, context, app):
    """Renders the template and fires the signal"""

    before_render_template.send(app, template=template, context=context)
    rv = await template.render_async(context)
    template_rendered.send(app, template=template, context=context)
    return rv


async def render_template(template_name_or_list, **context):
    """Renders a template from the template folder with the given
    context.
    :param template_name_or_list: the name of the template to be
                                  rendered, or an iterable with template names
                                  the first one existing will be rendered
    :param context: the variables that should be available in the
                    context of the template.
    """
    ctx = _app_ctx_stack.top
    ctx.app.update_template_context(context)
    return await _render(
        ctx.app.jinja_env.get_or_select_template(template_name_or_list),
        context,
        ctx.app,
    )


async def render_template_string(source, **context):
    """Renders a template from the given template source string
    with the given context. Template variables will be autoescaped.
    :param source: the source code of the template to be
                   rendered
    :param context: the variables that should be available in the
                    context of the template.
    """
    ctx = _app_ctx_stack.top
    ctx.app.update_template_context(context)
    return await _render(ctx.app.jinja_env.from_string(source), context,
                         ctx.app)


================================================
FILE: src/aioflask/testing.py
================================================
from flask.testing import *
from flask.testing import FlaskClient as OriginalFlaskClient, \
    FlaskCliRunner as OriginalFlaskCliRunner
from flask import _request_ctx_stack
from werkzeug.test import run_wsgi_app
from greenletio import async_


class FlaskClient(OriginalFlaskClient):
    def run_wsgi_app(self, environ, buffered=False):
        """Runs the wrapped WSGI app with the given environment.
        :meta private:
        """
        if self.cookie_jar is not None:
            self.cookie_jar.inject_wsgi(environ)

        rv = run_wsgi_app(self.application.wsgi_app, environ,
                          buffered=buffered)

        if self.cookie_jar is not None:
            self.cookie_jar.extract_wsgi(environ, rv[2])

        return rv

    async def get(self, *args, **kwargs):
        return await async_(super().get)(*args, **kwargs)

    async def post(self, *args, **kwargs):
        return await async_(super().post)(*args, **kwargs)

    async def put(self, *args, **kwargs):
        return await async_(super().put)(*args, **kwargs)

    async def patch(self, *args, **kwargs):
        return await async_(super().patch)(*args, **kwargs)

    async def delete(self, *args, **kwargs):
        return await async_(super().delete)(*args, **kwargs)

    async def head(self, *args, **kwargs):
        return await async_(super().head)(*args, **kwargs)

    async def options(self, *args, **kwargs):
        return await async_(super().options)(*args, **kwargs)

    async def trace(self, *args, **kwargs):
        return await async_(super().trace)(*args, **kwargs)

    async def __aenter__(self):
        if self.preserve_context:
            raise RuntimeError("Cannot nest client invocations")
        self.preserve_context = True
        return self

    async def __aexit__(self, exc_type, exc_value, tb):
        self.preserve_context = False

        # Normally the request context is preserved until the next
        # request in the same thread comes. When the client exits we
        # want to clean up earlier. Pop request contexts until the stack
        # is empty or a non-preserved one is found.
        while True:
            top = _request_ctx_stack.top

            if top is not None and top.preserved:
                await top.apop()
            else:
                break


class FlaskCliRunner(OriginalFlaskCliRunner):
    async def invoke(self, *args, **kwargs):
        return await async_(super().invoke)(*args, **kwargs)


================================================
FILE: tests/__init__.py
================================================


================================================
FILE: tests/templates/template.html
================================================
{{ g.x }}{{ session.y }}


================================================
FILE: tests/test_app.py
================================================
import asyncio
import os
import unittest
from unittest import mock
import aioflask
from .utils import async_test


class TestApp(unittest.TestCase):
    @async_test
    async def test_app(self):
        app = aioflask.Flask(__name__)

        @app.route('/async')
        async def async_route():
            await asyncio.sleep(0)
            assert aioflask.current_app._get_current_object() == app
            return 'async'

        @app.route('/sync')
        def sync_route():
            assert aioflask.current_app._get_current_object() == app
            return 'sync'

        client = app.test_client()
        response = await client.get('/async')
        assert response.data == b'async'
        response = await client.get('/sync')
        assert response.data == b'sync'

    @async_test
    async def test_g(self):
        app = aioflask.Flask(__name__)
        app.secret_key = 'secret'

        @app.before_request
        async def async_before_request():
            aioflask.g.asyncvar = 'async'

        @app.before_request
        def sync_before_request():
            aioflask.g.syncvar = 'sync'

        @app.route('/async')
        async def async_route():
            aioflask.session['a'] = 'async'
            return f'{aioflask.g.asyncvar}-{aioflask.g.syncvar}'

        @app.route('/sync')
        async def sync_route():
            aioflask.session['s'] = 'sync'
            return f'{aioflask.g.asyncvar}-{aioflask.g.syncvar}'

        @app.route('/session')
        async def session():
            return f'{aioflask.session.get("a")}-{aioflask.session.get("s")}'

        @app.after_request
        async def after_request(rv):
            rv.data += f'/{aioflask.g.asyncvar}-{aioflask.g.syncvar}'.encode()
            return rv

        client = app.test_client()
        response = await client.get('/session')
        assert response.data == b'None-None/async-sync'
        response = await client.get('/async')
        assert response.data == b'async-sync/async-sync'
        response = await client.get('/session')
        assert response.data == b'async-None/async-sync'
        response = await client.get('/sync')
        assert response.data == b'async-sync/async-sync'
        response = await client.get('/session')
        assert response.data == b'async-sync/async-sync'

    @mock.patch('aioflask.app.uvicorn')
    def test_app_run(self, uvicorn):
        app = aioflask.Flask(__name__)

        app.run()
        uvicorn.run.assert_called_with('tests.test_app:app',
                                       host='127.0.0.1', port=5000,
                                       reload=False, workers=1,
                                       log_level='info', ssl_certfile=None,
                                       ssl_keyfile=None)
        app.run(host='1.2.3.4', port=3000)
        uvicorn.run.assert_called_with('tests.test_app:app',
                                       host='1.2.3.4', port=3000,
                                       reload=False, workers=1,
                                       log_level='info', ssl_certfile=None,
                                       ssl_keyfile=None)
        app.run(debug=True)
        uvicorn.run.assert_called_with('tests.test_app:app',
                                       host='127.0.0.1', port=5000,
                                       reload=True, workers=1,
                                       log_level='debug', ssl_certfile=None,
                                       ssl_keyfile=None)
        app.run(debug=True, use_reloader=False)
        uvicorn.run.assert_called_with('tests.test_app:app',
                                       host='127.0.0.1', port=5000,
                                       reload=False, workers=1,
                                       log_level='debug', ssl_certfile=None,
                                       ssl_keyfile=None)
        if 'FLASK_DEBUG' in os.environ:
            del os.environ['FLASK_DEBUG']
        if 'AIOFLASK_USE_DEBUGGER' in os.environ:
            del os.environ['AIOFLASK_USE_DEBUGGER']


================================================
FILE: tests/test_cli.py
================================================
import os
import unittest
from unittest import mock
import click
from click.testing import CliRunner
import aioflask
import aioflask.cli
from .utils import async_test


class TestCli(unittest.TestCase):
    @async_test
    async def test_command_with_appcontext(self):
        app = aioflask.Flask('testapp')

        @app.cli.command(with_appcontext=True)
        async def testcmd():
            click.echo(aioflask.current_app.name)

        result = await app.test_cli_runner().invoke(testcmd)
        assert result.exit_code == 0
        assert result.output == "testapp\n"

    @async_test
    async def test_command_without_appcontext(self):
        app = aioflask.Flask('testapp')

        @app.cli.command(with_appcontext=False)
        async def testcmd():
            click.echo(aioflask.current_app.name)

        result = await app.test_cli_runner().invoke(testcmd)
        assert result.exit_code == 1
        assert type(result.exception) == RuntimeError

    @async_test
    async def test_with_appcontext(self):
        @click.command()
        @aioflask.cli.with_appcontext
        async def testcmd():
            click.echo(aioflask.current_app.name)

        app = aioflask.Flask('testapp')

        result = await app.test_cli_runner().invoke(testcmd)
        assert result.exit_code == 0
        assert result.output == "testapp\n"

    @mock.patch('aioflask.cli.uvicorn')
    def test_aiorun(self, uvicorn):
        app = aioflask.Flask('testapp')
        obj = aioflask.cli.ScriptInfo(app_import_path='app.py',
                                      create_app=lambda: app)

        result = CliRunner().invoke(aioflask.cli.run_command, obj=obj)
        assert result.exit_code == 0
        uvicorn.run.assert_called_with('app:app', factory=False,
                                       host='127.0.0.1', port=5000,
                                       reload=False, workers=1,
                                       log_level='info', ssl_certfile=None,
                                       ssl_keyfile=None)
        result = CliRunner().invoke(aioflask.cli.run_command,
                                    '--host 1.2.3.4 --port 3000', obj=obj)
        assert result.exit_code == 0
        uvicorn.run.assert_called_with('app:app', factory=False,
                                       host='1.2.3.4', port=3000,
                                       reload=False, workers=1,
                                       log_level='info', ssl_certfile=None,
                                       ssl_keyfile=None)
        os.environ['FLASK_DEBUG'] = 'true'
        result = CliRunner().invoke(aioflask.cli.run_command, obj=obj)
        assert result.exit_code == 0
        uvicorn.run.assert_called_with('app:app', factory=False,
                                       host='127.0.0.1', port=5000,
                                       reload=True, workers=1,
                                       log_level='debug', ssl_certfile=None,
                                       ssl_keyfile=None)
        os.environ['FLASK_DEBUG'] = 'true'
        result = CliRunner().invoke(aioflask.cli.run_command, '--no-reload',
                                    obj=obj)
        assert result.exit_code == 0
        uvicorn.run.assert_called_with('app:app', factory=False,
                                       host='127.0.0.1', port=5000,
                                       reload=False, workers=1,
                                       log_level='debug', ssl_certfile=None,
                                       ssl_keyfile=None)

        if 'FLASK_DEBUG' in os.environ:
            del os.environ['FLASK_DEBUG']
        if 'AIOFLASK_USE_DEBUGGER' in os.environ:
            del os.environ['AIOFLASK_USE_DEBUGGER']

    @mock.patch('aioflask.cli.uvicorn')
    def test_aiorun_with_factory(self, uvicorn):
        app = aioflask.Flask('testapp')
        obj = aioflask.cli.ScriptInfo(app_import_path='app:create_app()',
                                      create_app=lambda: app)

        result = CliRunner().invoke(aioflask.cli.run_command, obj=obj)
        assert result.exit_code == 0
        uvicorn.run.assert_called_with('app:create_app', factory=True,
                                       host='127.0.0.1', port=5000,
                                       reload=False, workers=1,
                                       log_level='info', ssl_certfile=None,
                                       ssl_keyfile=None)


================================================
FILE: tests/test_ctx.py
================================================
import unittest
import pytest
import aioflask
from .utils import async_test


class TestApp(unittest.TestCase):
    @async_test
    async def test_app_context(self):
        app = aioflask.Flask(__name__)
        called_t1 = False
        called_t2 = False

        @app.teardown_appcontext
        async def t1(exc):
            nonlocal called_t1
            called_t1 = True

        @app.teardown_appcontext
        def t2(exc):
            nonlocal called_t2
            called_t2 = True

        async with app.app_context():
            assert aioflask.current_app == app
            async with app.app_context():
                assert aioflask.current_app == app
            assert aioflask.current_app == app

        assert called_t1
        assert called_t2
        with pytest.raises(RuntimeError):
            print(aioflask.current_app)

    @async_test
    async def test_req_context(self):
        app = aioflask.Flask(__name__)
        called_t1 = False
        called_t2 = False

        @app.teardown_appcontext
        async def t1(exc):
            nonlocal called_t1
            called_t1 = True

        @app.teardown_appcontext
        def t2(exc):
            nonlocal called_t2
            called_t2 = True

        async with app.test_request_context('/foo'):
            assert aioflask.current_app == app
            assert aioflask.request.path == '/foo'

        assert called_t1
        assert called_t2

        async with app.app_context():
            async with app.test_request_context('/bar') as reqctx:
                assert aioflask.current_app == app
                assert aioflask.request.path == '/bar'
                async with reqctx:
                    assert aioflask.current_app == app
                    assert aioflask.request.path == '/bar'

        with pytest.raises(RuntimeError):
            print(aioflask.current_app)


================================================
FILE: tests/test_patch.py
================================================
import unittest
import aioflask
import aioflask.patch
from .utils import async_test


class TestPatch(unittest.TestCase):
    @async_test
    async def test_decorator(self):
        def foo(f):
            def decorator(*args, **kwargs):
                return f(*args, **kwargs) + '-decorated'

            return decorator

        foo = aioflask.patch.patch_decorator(foo)

        app = aioflask.Flask(__name__)

        @app.route('/abc/<int:id>')
        @foo
        async def abc(id):
            return str(id)

        client = app.test_client()
        response = await client.get('/abc/123')
        assert response.data == b'123-decorated'

    @async_test
    async def test_decorator_with_args(self):
        def foo(value):
            def inner_foo(f):
                def decorator(*args, **kwargs):
                    return f(*args, **kwargs) + str(value)

                return decorator
            return inner_foo

        foo = aioflask.patch.patch_decorator_with_args(foo)

        app = aioflask.Flask(__name__)

        @app.route('/abc/<int:id>')
        @foo(456)
        async def abc(id):
            return str(id)

        client = app.test_client()
        response = await client.get('/abc/123')
        assert response.data == b'123456'

    @async_test
    async def test_decorator_method(self):
        class Foo:
            def __init__(self, value):
                self.value = value

            def deco(self, f):
                def decorator(*args, **kwargs):
                    return f(*args, **kwargs) + str(self.value)

                return decorator

        Foo.deco = aioflask.patch.patch_decorator_method(Foo, 'deco')

        app = aioflask.Flask(__name__)
        foo = Foo(456)

        @app.route('/abc/<int:id>')
        @foo.deco
        async def abc(id):
            return str(id)

        client = app.test_client()
        response = await client.get('/abc/123')
        assert response.data == b'123456'

    @async_test
    async def test_decorator_method_with_args(self):
        class Foo:
            def __init__(self, value):
                self.value = value

            def deco(self, value2):
                def decorator(f):
                    def inner_decorator(*args, **kwargs):
                        return f(*args, **kwargs) + str(self.value) + \
                            str(value2)

                    return inner_decorator
                return decorator

        Foo.deco = aioflask.patch.patch_decorator_method_with_args(Foo, 'deco')

        app = aioflask.Flask(__name__)
        foo = Foo(456)

        @app.route('/abc/<int:id>')
        @foo.deco(789)
        async def abc(id):
            return str(id)

        client = app.test_client()
        response = await client.get('/abc/123')
        assert response.data == b'123456789'


================================================
FILE: tests/test_templating.py
================================================
import asyncio
import os
import unittest
from unittest import mock
import aioflask
from .utils import async_test


class TestTemplating(unittest.TestCase):
    @async_test
    async def test_template_strng(self):
        app = aioflask.Flask(__name__)
        app.secret_key = 'secret'

        @app.before_request
        def before_request():
            aioflask.g.x = 'foo'
            aioflask.session['y'] = 'bar'

        @app.route('/')
        async def async_route():
            return await aioflask.render_template_string(
                '{{ g.x }}{{ session.y }}')

        client = app.test_client()
        response = await client.get('/')
        assert response.data == b'foobar'

    @async_test
    async def test_template(self):
        app = aioflask.Flask(__name__)
        app.secret_key = 'secret'

        @app.before_request
        def before_request():
            aioflask.g.x = 'foo'
            aioflask.session['y'] = 'bar'

        @app.route('/')
        async def async_route():
            return await aioflask.render_template('template.html')

        client = app.test_client()
        response = await client.get('/')
        assert response.data == b'foobar'


================================================
FILE: tests/utils.py
================================================
import asyncio
from greenletio.core import bridge


def async_test(f):
    def wrapper(*args, **kwargs):
        asyncio.get_event_loop().run_until_complete(f(*args, **kwargs))

    return wrapper


================================================
FILE: tox.ini
================================================
[tox]
envlist=flake8,,py37,py38,py39,py310,pypy3,docs
skip_missing_interpreters=True

[gh-actions]
python =
    3.7: py37
    3.8: py38
    3.9: py39
    3.10: py310
    pypy3: pypy-3

[testenv]
commands=
    pip install -e .
    pytest -p no:logging --cov=src/aioflask --cov-branch examples/aioflaskr/tests
    pytest -p no:logging --cov=src/aioflask --cov-branch --cov-report=term-missing --cov-report=xml --cov-append tests
deps=
    aiosqlite
    greenletio
    alchemical
    flask-login
    pytest
    pytest-asyncio
    pytest-cov

[testenv:flake8]
deps=
    flake8
commands=
    flake8 --ignore=F401,F403 --exclude=".*" src/aioflask tests

[testenv:docs]
changedir=docs
deps=
    sphinx
whitelist_externals=
    make
commands=
    make html
Download .txt
gitextract_sepaq9a1/

├── .github/
│   └── workflows/
│       └── tests.yml
├── .gitignore
├── .readthedocs.yaml
├── CHANGES.md
├── LICENSE
├── README.md
├── examples/
│   ├── AsyncProgressBar/
│   │   ├── README.md
│   │   ├── progress_bar.py
│   │   └── requirements.txt
│   ├── aioflaskr/
│   │   ├── .flaskenv
│   │   ├── LICENSE
│   │   ├── README.md
│   │   ├── flaskr/
│   │   │   ├── __init__.py
│   │   │   ├── auth.py
│   │   │   ├── blog.py
│   │   │   ├── models.py
│   │   │   ├── static/
│   │   │   │   └── style.css
│   │   │   └── templates/
│   │   │       ├── auth/
│   │   │       │   ├── login.html
│   │   │       │   └── register.html
│   │   │       ├── base.html
│   │   │       └── blog/
│   │   │           ├── create.html
│   │   │           ├── index.html
│   │   │           └── update.html
│   │   ├── requirements.txt
│   │   └── tests/
│   │       ├── __init__.py
│   │       ├── conftest.py
│   │       ├── test_auth.py
│   │       ├── test_blog.py
│   │       └── test_init.py
│   ├── g/
│   │   └── app.py
│   ├── hello_world/
│   │   ├── app.py
│   │   └── templates/
│   │       └── index.html
│   ├── login/
│   │   └── app.py
│   ├── quotes-aiohttp/
│   │   ├── README.md
│   │   └── quotes.py
│   └── quotes-requests/
│       ├── README.md
│       ├── quotes.py
│       └── quotes_app.py
├── pyproject.toml
├── setup.cfg
├── setup.py
├── src/
│   └── aioflask/
│       ├── __init__.py
│       ├── app.py
│       ├── asgi.py
│       ├── cli.py
│       ├── ctx.py
│       ├── patch.py
│       ├── patched/
│       │   ├── __init__.py
│       │   └── flask_login/
│       │       └── __init__.py
│       ├── templating.py
│       └── testing.py
├── tests/
│   ├── __init__.py
│   ├── templates/
│   │   └── template.html
│   ├── test_app.py
│   ├── test_cli.py
│   ├── test_ctx.py
│   ├── test_patch.py
│   ├── test_templating.py
│   └── utils.py
└── tox.ini
Download .txt
SYMBOL INDEX (150 symbols across 28 files)

FILE: examples/AsyncProgressBar/progress_bar.py
  function some_work (line 13) | async def some_work():
  function check_status (line 26) | async def check_status():
  function progress (line 55) | async def progress():
  function index (line 111) | async def index():
  function start_work (line 117) | async def start_work():

FILE: examples/aioflaskr/flaskr/__init__.py
  function create_app (line 14) | def create_app(test_config=None):
  function init_db (line 59) | async def init_db():
  function init_db_command (line 66) | async def init_db_command():

FILE: examples/aioflaskr/flaskr/auth.py
  function load_user (line 18) | async def load_user(id):
  function register (line 23) | async def register():
  function login (line 56) | async def login():
  function logout (line 82) | async def logout():

FILE: examples/aioflaskr/flaskr/blog.py
  function index (line 18) | async def index():
  function get_post (line 24) | async def get_post(id, check_author=True):
  function create (line 47) | async def create():
  function update (line 69) | async def update(id):
  function delete (line 94) | async def delete(id):

FILE: examples/aioflaskr/flaskr/models.py
  class User (line 11) | class User(UserMixin, db.Model):
    method password (line 17) | def password(self):
    method password (line 21) | def password(self, value):
    method check_password (line 25) | def check_password(self, value):
  class Post (line 29) | class Post(db.Model):
    method update_url (line 43) | def update_url(self):
    method delete_url (line 47) | def delete_url(self):

FILE: examples/aioflaskr/tests/conftest.py
  function app (line 13) | async def app():
  function client (line 41) | def client(app):
  function runner (line 47) | def runner(app):
  class AuthActions (line 52) | class AuthActions:
    method __init__ (line 53) | def __init__(self, client):
    method login (line 56) | async def login(self, username="test", password="test"):
    method logout (line 61) | async def logout(self):
  function auth (line 66) | def auth(client):

FILE: examples/aioflaskr/tests/test_auth.py
  function test_register (line 8) | async def test_register(client, app):
  function test_user_password (line 23) | def test_user_password(app):
  function test_register_validate_input (line 38) | async def test_register_validate_input(client, username, password, messa...
  function test_login (line 46) | async def test_login(client, auth):
  function test_login_validate_input (line 67) | async def test_login_validate_input(auth, username, password, message):
  function test_logout (line 73) | async def test_logout(client, auth):

FILE: examples/aioflaskr/tests/test_blog.py
  function test_index (line 11) | async def test_index(client, auth):
  function test_login_required (line 26) | async def test_login_required(client, path):
  function test_author_required (line 32) | async def test_author_required(app, client, auth):
  function test_exists_required (line 48) | async def test_exists_required(client, auth, path):
  function test_create (line 54) | async def test_create(client, auth, app):
  function test_update (line 65) | async def test_update(client, auth, app):
  function test_create_update_validate (line 76) | async def test_create_update_validate(client, auth, path):
  function test_delete (line 83) | async def test_delete(client, auth, app):

FILE: examples/aioflaskr/tests/test_init.py
  function test_config (line 5) | def test_config():
  function test_db_url_environ (line 11) | def test_db_url_environ(monkeypatch):
  function test_init_db_command (line 19) | async def test_init_db_command(runner, monkeypatch):

FILE: examples/g/app.py
  function before_request (line 8) | async def before_request():
  function teardown_appcontext (line 13) | async def teardown_appcontext(exc):
  function index (line 18) | async def index():

FILE: examples/hello_world/app.py
  function index (line 7) | async def index():
  function hello (line 12) | async def hello():

FILE: examples/login/app.py
  class User (line 11) | class User(UserMixin):
    method __init__ (line 12) | def __init__(self, user_id):
  function load_user (line 17) | async def load_user(user_id):
  function index (line 23) | async def index():
  function login (line 36) | def login():
  function logout (line 53) | def logout():

FILE: examples/quotes-aiohttp/quotes.py
  function get_quote (line 20) | async def get_quote(session):
  function index (line 26) | async def index():

FILE: examples/quotes-requests/quotes_app.py
  function get_quote (line 26) | def get_quote():
  function index (line 32) | async def index():

FILE: src/aioflask/app.py
  class Flask (line 18) | class Flask(OriginalFlask):
    method __init__ (line 19) | def __init__(self, *args, **kwargs):
    method ensure_sync (line 27) | def ensure_sync(self, func):
    method app_context (line 51) | def app_context(self):
    method request_context (line 54) | def request_context(self, environ):
    method _fix_async (line 57) | def _fix_async(self):  # pragma: no cover
    method asgi_app (line 65) | async def asgi_app(self, scope, receive, send):  # pragma: no cover
    method __call__ (line 70) | async def __call__(self, scope, receive, send=None):  # pragma: no cover
    method run (line 77) | def run(self, host=None, port=None, debug=None, load_dotenv=True,

FILE: src/aioflask/asgi.py
  class wsgi_to_asgi (line 6) | class wsgi_to_asgi:  # pragma: no cover
    method __init__ (line 9) | def __init__(self, wsgi_application):
    method __call__ (line 12) | async def __call__(self, scope, receive, send):
  class WsgiToAsgiInstance (line 21) | class WsgiToAsgiInstance:  # pragma: no cover
    method __init__ (line 24) | def __init__(self, wsgi_application):
    method __call__ (line 28) | async def __call__(self, scope, receive, send):
    method build_environ (line 49) | def build_environ(self, scope, body):
    method start_response (line 93) | def start_response(self, status, response_headers, exc_info=None):
    method run_wsgi_app (line 120) | def run_wsgi_app(self, body):

FILE: src/aioflask/cli.py
  function _ensure_sync (line 26) | def _ensure_sync(func, with_appcontext=False):
  function with_appcontext (line 48) | def with_appcontext(f):
  class AppGroup (line 63) | class AppGroup(OriginalAppGroup):
    method command (line 70) | def command(self, *args, **kwargs):
  function show_server_banner (line 85) | def show_server_banner(env, debug, app_import_path, eager_loading):
  class CertParamType (line 100) | class CertParamType(click.ParamType):
    method __init__ (line 108) | def __init__(self):
    method convert (line 112) | def convert(self, value, param, ctx):
  function run_command (line 186) | def run_command(info, host, port, reload, debugger, eager_loading,

FILE: src/aioflask/ctx.py
  class AppContext (line 9) | class AppContext(OriginalAppContext):
    method apush (line 10) | async def apush(self):
    method apop (line 14) | async def apop(self, exc=_sentinel):
    method __aenter__ (line 35) | async def __aenter__(self):
    method __aexit__ (line 39) | async def __aexit__(self, exc_type, exc_value, tb):
  class RequestContext (line 43) | class RequestContext(OriginalRequestContext):
    method apush (line 44) | async def apush(self):
    method apop (line 47) | async def apop(self, exc=_sentinel):
    method aauto_pop (line 87) | async def aauto_pop(self, exc):
    method __aenter__ (line 99) | async def __aenter__(self):
    method __aexit__ (line 103) | async def __aexit__(self, exc_type, exc_value, tb):

FILE: src/aioflask/patch.py
  function patch_decorator (line 5) | def patch_decorator(decorator):
  function patch_decorator_with_args (line 15) | def patch_decorator_with_args(decorator):
  function patch_decorator_method (line 27) | def patch_decorator_method(class_, method_name):
  function patch_decorator_method_with_args (line 39) | def patch_decorator_method_with_args(class_, method_name):

FILE: src/aioflask/patched/flask_login/__init__.py
  function _user_context_processor (line 18) | def _user_context_processor():
  function _load_user (line 22) | def _load_user():
  function _get_user (line 31) | def _get_user():
  class LoginManager (line 43) | class LoginManager(OriginalLoginManager):
    method init_app (line 44) | def init_app(self, app, add_context_processor=True):

FILE: src/aioflask/templating.py
  function _render (line 6) | async def _render(template, context, app):
  function render_template (line 15) | async def render_template(template_name_or_list, **context):
  function render_template_string (line 33) | async def render_template_string(source, **context):

FILE: src/aioflask/testing.py
  class FlaskClient (line 9) | class FlaskClient(OriginalFlaskClient):
    method run_wsgi_app (line 10) | def run_wsgi_app(self, environ, buffered=False):
    method get (line 25) | async def get(self, *args, **kwargs):
    method post (line 28) | async def post(self, *args, **kwargs):
    method put (line 31) | async def put(self, *args, **kwargs):
    method patch (line 34) | async def patch(self, *args, **kwargs):
    method delete (line 37) | async def delete(self, *args, **kwargs):
    method head (line 40) | async def head(self, *args, **kwargs):
    method options (line 43) | async def options(self, *args, **kwargs):
    method trace (line 46) | async def trace(self, *args, **kwargs):
    method __aenter__ (line 49) | async def __aenter__(self):
    method __aexit__ (line 55) | async def __aexit__(self, exc_type, exc_value, tb):
  class FlaskCliRunner (line 71) | class FlaskCliRunner(OriginalFlaskCliRunner):
    method invoke (line 72) | async def invoke(self, *args, **kwargs):

FILE: tests/test_app.py
  class TestApp (line 9) | class TestApp(unittest.TestCase):
    method test_app (line 11) | async def test_app(self):
    method test_g (line 32) | async def test_g(self):
    method test_app_run (line 76) | def test_app_run(self, uvicorn):

FILE: tests/test_cli.py
  class TestCli (line 11) | class TestCli(unittest.TestCase):
    method test_command_with_appcontext (line 13) | async def test_command_with_appcontext(self):
    method test_command_without_appcontext (line 25) | async def test_command_without_appcontext(self):
    method test_with_appcontext (line 37) | async def test_with_appcontext(self):
    method test_aiorun (line 50) | def test_aiorun(self, uvicorn):
    method test_aiorun_with_factory (line 94) | def test_aiorun_with_factory(self, uvicorn):

FILE: tests/test_ctx.py
  class TestApp (line 7) | class TestApp(unittest.TestCase):
    method test_app_context (line 9) | async def test_app_context(self):
    method test_req_context (line 36) | async def test_req_context(self):

FILE: tests/test_patch.py
  class TestPatch (line 7) | class TestPatch(unittest.TestCase):
    method test_decorator (line 9) | async def test_decorator(self):
    method test_decorator_with_args (line 30) | async def test_decorator_with_args(self):
    method test_decorator_method (line 53) | async def test_decorator_method(self):
    method test_decorator_method_with_args (line 79) | async def test_decorator_method_with_args(self):

FILE: tests/test_templating.py
  class TestTemplating (line 9) | class TestTemplating(unittest.TestCase):
    method test_template_strng (line 11) | async def test_template_strng(self):
    method test_template (line 30) | async def test_template(self):

FILE: tests/utils.py
  function async_test (line 5) | def async_test(f):
Condensed preview — 60 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (99K chars).
[
  {
    "path": ".github/workflows/tests.yml",
    "chars": 1114,
    "preview": "name: build\non:\n  push:\n    branches:\n      - main\n  pull_request:\n    branches:\n      - main\njobs:\n  lint:\n    name: li"
  },
  {
    "path": ".gitignore",
    "chars": 1799,
    "preview": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packagi"
  },
  {
    "path": ".readthedocs.yaml",
    "chars": 198,
    "preview": "version: 2\n\nbuild:\n  os: ubuntu-22.04\n  tools:\n    python: \"3.11\"\n\nsphinx:\n  configuration: docs/conf.py\n\npython:\n  inst"
  },
  {
    "path": "CHANGES.md",
    "chars": 3776,
    "preview": "# aioflask change log\n\n**Release 0.4.0** - 2021-08-18\n\n- Support for app factory functions with uvicorn ([commit](https:"
  },
  {
    "path": "LICENSE",
    "chars": 1072,
    "preview": "MIT License\n\nCopyright (c) 2020 Miguel Grinberg\n\nPermission is hereby granted, free of charge, to any person obtaining a"
  },
  {
    "path": "README.md",
    "chars": 2076,
    "preview": "# aioflask\n\n![Build status](https://github.com/miguelgrinberg/aioflask/workflows/build/badge.svg) [![codecov](https://co"
  },
  {
    "path": "examples/AsyncProgressBar/README.md",
    "chars": 177,
    "preview": "AsyncProgressBar\n================\n\nThis is the *AsyncProgressBar* from Quart ported to Flask. You need to have a\nRedis s"
  },
  {
    "path": "examples/AsyncProgressBar/progress_bar.py",
    "chars": 4245,
    "preview": "import asyncio\nimport random\nimport aioredis\nimport redis\nfrom aioflask import Flask, request, url_for, jsonify\n\napp = F"
  },
  {
    "path": "examples/AsyncProgressBar/requirements.txt",
    "chars": 272,
    "preview": "aioflask\naioredis==1.3.1\nasync-timeout==3.0.1\nclick==7.1.2\nFlask==1.1.2\ngreenlet==0.4.16\ngreenletio\nh11==0.9.0\nhiredis=="
  },
  {
    "path": "examples/aioflaskr/.flaskenv",
    "chars": 30,
    "preview": "FLASK_APP=flaskr:create_app()\n"
  },
  {
    "path": "examples/aioflaskr/LICENSE",
    "chars": 1540,
    "preview": "Copyright 2010 Pallets (original version)\nCopyright 2021 Miguel Grinberg (this version)\n\nRedistribution and use in sourc"
  },
  {
    "path": "examples/aioflaskr/README.md",
    "chars": 994,
    "preview": "aioflaskr\n=========\n\nThis is the \"Flaskr\" application from the tutorial section of the Flask\ndocumentation, adapted to w"
  },
  {
    "path": "examples/aioflaskr/flaskr/__init__.py",
    "chars": 1913,
    "preview": "import os\n\nimport click\nfrom aioflask import Flask\nfrom aioflask.cli import with_appcontext\nfrom alchemical.aioflask imp"
  },
  {
    "path": "examples/aioflaskr/flaskr/auth.py",
    "chars": 2570,
    "preview": "from aioflask import Blueprint\nfrom aioflask import flash\nfrom aioflask import redirect\nfrom aioflask import render_temp"
  },
  {
    "path": "examples/aioflaskr/flaskr/blog.py",
    "chars": 2896,
    "preview": "from aioflask import Blueprint\nfrom aioflask import flash\nfrom aioflask import redirect\nfrom aioflask import render_temp"
  },
  {
    "path": "examples/aioflaskr/flaskr/models.py",
    "chars": 1547,
    "preview": "from werkzeug.security import check_password_hash\nfrom werkzeug.security import generate_password_hash\nfrom aioflask imp"
  },
  {
    "path": "examples/aioflaskr/flaskr/static/style.css",
    "chars": 1696,
    "preview": "html {\n  font-family: sans-serif;\n  background: #eee;\n  padding: 1rem;\n}\n\nbody {\n  max-width: 960px;\n  margin: 0 auto;\n "
  },
  {
    "path": "examples/aioflaskr/flaskr/templates/auth/login.html",
    "chars": 424,
    "preview": "{% extends 'base.html' %}\n\n{% block header %}\n  <h1>{% block title %}Log In{% endblock %}</h1>\n{% endblock %}\n\n{% block "
  },
  {
    "path": "examples/aioflaskr/flaskr/templates/auth/register.html",
    "chars": 428,
    "preview": "{% extends 'base.html' %}\n\n{% block header %}\n  <h1>{% block title %}Register{% endblock %}</h1>\n{% endblock %}\n\n{% bloc"
  },
  {
    "path": "examples/aioflaskr/flaskr/templates/base.html",
    "chars": 778,
    "preview": "<!doctype html>\n<title>{% block title %}{% endblock %} - Flaskr</title>\n<link rel=\"stylesheet\" href=\"{{ url_for('static'"
  },
  {
    "path": "examples/aioflaskr/flaskr/templates/blog/create.html",
    "chars": 447,
    "preview": "{% extends 'base.html' %}\n\n{% block header %}\n  <h1>{% block title %}New Post{% endblock %}</h1>\n{% endblock %}\n\n{% bloc"
  },
  {
    "path": "examples/aioflaskr/flaskr/templates/blog/index.html",
    "chars": 780,
    "preview": "{% extends 'base.html' %}\n\n{% block header %}\n  <h1>{% block title %}Posts{% endblock %}</h1>\n  {% if current_user.is_au"
  },
  {
    "path": "examples/aioflaskr/flaskr/templates/blog/update.html",
    "chars": 668,
    "preview": "{% extends 'base.html' %}\n\n{% block header %}\n  <h1>{% block title %}Edit \"{{ post['title'] }}\"{% endblock %}</h1>\n{% en"
  },
  {
    "path": "examples/aioflaskr/requirements.txt",
    "chars": 328,
    "preview": "aioflask\naiosqlite==0.17.0\nalchemical\nasgiref==3.4.1\nclick==8.0.1\nFlask==2.0.1\nFlask-Login==0.5.0\ngreenlet==1.1.1\ngreenl"
  },
  {
    "path": "examples/aioflaskr/tests/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "examples/aioflaskr/tests/conftest.py",
    "chars": 1644,
    "preview": "from datetime import datetime\n\nimport pytest\n\nfrom flaskr import create_app\nfrom flaskr import db\nfrom flaskr import ini"
  },
  {
    "path": "examples/aioflaskr/tests/test_auth.py",
    "chars": 2447,
    "preview": "import pytest\n\nfrom flaskr import db\nfrom flaskr.models import User\n\n\n@pytest.mark.asyncio\nasync def test_register(clien"
  },
  {
    "path": "examples/aioflaskr/tests/test_blog.py",
    "chars": 2859,
    "preview": "import pytest\nfrom sqlalchemy import func\nfrom sqlalchemy import select\n\nfrom flaskr import db\nfrom flaskr.models import"
  },
  {
    "path": "examples/aioflaskr/tests/test_init.py",
    "chars": 808,
    "preview": "import pytest\nfrom flaskr import create_app\n\n\ndef test_config():\n    \"\"\"Test create_app without passing test config.\"\"\"\n"
  },
  {
    "path": "examples/g/app.py",
    "chars": 401,
    "preview": "from aioflask import Flask, g\nimport aiohttp\n\napp = Flask(__name__)\n\n\n@app.before_request\nasync def before_request():\n  "
  },
  {
    "path": "examples/hello_world/app.py",
    "chars": 248,
    "preview": "from aioflask import Flask, render_template\n\napp = Flask(__name__)\n\n\n@app.route('/')\nasync def index():\n    return await"
  },
  {
    "path": "examples/hello_world/templates/index.html",
    "chars": 143,
    "preview": "<!doctype html>\n<html>\n  <head>\n    <title>Hello (async) world!</title>\n  </head>\n  <body>\n    <h1>Hello (async) world!<"
  },
  {
    "path": "examples/login/app.py",
    "chars": 1191,
    "preview": "from aioflask import Flask, request, redirect\nfrom aioflask.patched.flask_login import LoginManager, login_required, Use"
  },
  {
    "path": "examples/quotes-aiohttp/README.md",
    "chars": 321,
    "preview": "Quotes\n======\n\nReturns 10 famous quotes each time the page is refreshed. Quotes are obtained\nby sending concurrent HTTP "
  },
  {
    "path": "examples/quotes-aiohttp/quotes.py",
    "chars": 737,
    "preview": "import asyncio\nimport aiohttp\nfrom aioflask import Flask, render_template_string\n\napp = Flask(__name__)\ntemplate = '''<!"
  },
  {
    "path": "examples/quotes-requests/README.md",
    "chars": 434,
    "preview": "Quotes\n======\n\nReturns 10 famous quotes each time the page is refreshed. Quotes are obtained\nby sending concurrent HTTP "
  },
  {
    "path": "examples/quotes-requests/quotes.py",
    "chars": 148,
    "preview": "import greenletio\n\n# import the application with blocking functions monkey patched\nwith greenletio.patch_blocking():\n   "
  },
  {
    "path": "examples/quotes-requests/quotes_app.py",
    "chars": 957,
    "preview": "import asyncio\nfrom aioflask import Flask, render_template_string\nfrom greenletio import async_\nimport requests\n\napp = F"
  },
  {
    "path": "pyproject.toml",
    "chars": 104,
    "preview": "[build-system]\nrequires = [\n    \"setuptools>=42\",\n    \"wheel\"\n]\nbuild-backend = \"setuptools.build_meta\"\n"
  },
  {
    "path": "setup.cfg",
    "chars": 882,
    "preview": "[metadata]\nname = aioflask\nversion = 0.4.1.dev0\nauthor = Miguel Grinberg\nauthor_email = miguel.grinberg@gmail.com\ndescri"
  },
  {
    "path": "setup.py",
    "chars": 38,
    "preview": "import setuptools\n\nsetuptools.setup()\n"
  },
  {
    "path": "src/aioflask/__init__.py",
    "chars": 140,
    "preview": "from flask import *\nfrom .app import Flask\nfrom .templating import render_template, render_template_string\nfrom .testing"
  },
  {
    "path": "src/aioflask/app.py",
    "chars": 4801,
    "preview": "import asyncio\nfrom functools import wraps\nfrom inspect import iscoroutinefunction\nimport os\nfrom flask.app import *\nfro"
  },
  {
    "path": "src/aioflask/asgi.py",
    "chars": 5444,
    "preview": "import sys\nfrom tempfile import SpooledTemporaryFile\nfrom greenletio import async_, await_\n\n\nclass wsgi_to_asgi:  # prag"
  },
  {
    "path": "src/aioflask/cli.py",
    "chars": 7820,
    "preview": "from functools import wraps\nfrom inspect import iscoroutinefunction\nimport os\nimport sys\n\nfrom flask.cli import *\nfrom f"
  },
  {
    "path": "src/aioflask/ctx.py",
    "chars": 3431,
    "preview": "import sys\nfrom greenletio import async_\nfrom flask.ctx import *\nfrom flask.ctx import AppContext as OriginalAppContext,"
  },
  {
    "path": "src/aioflask/patch.py",
    "chars": 1525,
    "preview": "from functools import wraps\nfrom aioflask import current_app\n\n\ndef patch_decorator(decorator):\n    def patched_decorator"
  },
  {
    "path": "src/aioflask/patched/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/aioflask/patched/flask_login/__init__.py",
    "chars": 3308,
    "preview": "from functools import wraps\nimport sys\nfrom werkzeug.local import LocalProxy\nfrom aioflask import current_app, g\nfrom fl"
  },
  {
    "path": "src/aioflask/templating.py",
    "chars": 1687,
    "preview": "from flask.templating import *\nfrom flask.templating import _app_ctx_stack, before_render_template, \\\n    template_rende"
  },
  {
    "path": "src/aioflask/testing.py",
    "chars": 2471,
    "preview": "from flask.testing import *\nfrom flask.testing import FlaskClient as OriginalFlaskClient, \\\n    FlaskCliRunner as Origin"
  },
  {
    "path": "tests/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/templates/template.html",
    "chars": 25,
    "preview": "{{ g.x }}{{ session.y }}\n"
  },
  {
    "path": "tests/test_app.py",
    "chars": 4064,
    "preview": "import asyncio\nimport os\nimport unittest\nfrom unittest import mock\nimport aioflask\nfrom .utils import async_test\n\n\nclass"
  },
  {
    "path": "tests/test_cli.py",
    "chars": 4449,
    "preview": "import os\nimport unittest\nfrom unittest import mock\nimport click\nfrom click.testing import CliRunner\nimport aioflask\nimp"
  },
  {
    "path": "tests/test_ctx.py",
    "chars": 1881,
    "preview": "import unittest\nimport pytest\nimport aioflask\nfrom .utils import async_test\n\n\nclass TestApp(unittest.TestCase):\n    @asy"
  },
  {
    "path": "tests/test_patch.py",
    "chars": 2844,
    "preview": "import unittest\nimport aioflask\nimport aioflask.patch\nfrom .utils import async_test\n\n\nclass TestPatch(unittest.TestCase)"
  },
  {
    "path": "tests/test_templating.py",
    "chars": 1202,
    "preview": "import asyncio\nimport os\nimport unittest\nfrom unittest import mock\nimport aioflask\nfrom .utils import async_test\n\n\nclass"
  },
  {
    "path": "tests/utils.py",
    "chars": 197,
    "preview": "import asyncio\nfrom greenletio.core import bridge\n\n\ndef async_test(f):\n    def wrapper(*args, **kwargs):\n        asyncio"
  },
  {
    "path": "tox.ini",
    "chars": 749,
    "preview": "[tox]\nenvlist=flake8,,py37,py38,py39,py310,pypy3,docs\nskip_missing_interpreters=True\n\n[gh-actions]\npython =\n    3.7: py3"
  }
]

About this extraction

This page contains the full source code of the miguelgrinberg/aioflask GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 60 files (89.0 KB), approximately 23.7k tokens, and a symbol index with 150 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!