├── .editorconfig ├── .gitignore ├── .readthedocs.yaml ├── 05-tutorial.md ├── 06-templates.md ├── 07-testing-flask-applications.md ├── 08-handling-application-errors.md ├── 09-debugging-application-errors.md ├── 10-logging.md ├── 11-configuration-handling.md ├── 12-signals.md ├── 13-pluggable-views.md ├── 14-the-application-context.md ├── 15-the-request-context.md ├── CHANGES.rst ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.rst ├── LICENSE.rst ├── README.rst ├── docs ├── Makefile ├── _static │ ├── debugger.png │ ├── flask-horizontal.png │ ├── flask-vertical.png │ ├── pycharm-run-config.png │ └── shortcut-icon.png ├── api.rst ├── appcontext.rst ├── async-await.rst ├── blueprints.rst ├── changes.rst ├── cli.rst ├── conf.py ├── config.rst ├── contributing.rst ├── debugging.rst ├── deploying │ ├── apache-httpd.rst │ ├── asgi.rst │ ├── eventlet.rst │ ├── gevent.rst │ ├── gunicorn.rst │ ├── index.rst │ ├── mod_wsgi.rst │ ├── nginx.rst │ ├── proxy_fix.rst │ ├── uwsgi.rst │ └── waitress.rst ├── design.rst ├── errorhandling.rst ├── extensiondev.rst ├── extensions.rst ├── index.rst ├── installation.rst ├── license.rst ├── lifecycle.rst ├── logging.rst ├── make.bat ├── patterns │ ├── appdispatch.rst │ ├── appfactories.rst │ ├── caching.rst │ ├── celery.rst │ ├── deferredcallbacks.rst │ ├── favicon.rst │ ├── fileuploads.rst │ ├── flashing.rst │ ├── index.rst │ ├── javascript.rst │ ├── jquery.rst │ ├── lazyloading.rst │ ├── methodoverrides.rst │ ├── mongoengine.rst │ ├── packages.rst │ ├── requestchecksum.rst │ ├── singlepageapplications.rst │ ├── sqlalchemy.rst │ ├── sqlite3.rst │ ├── streaming.rst │ ├── subclassing.rst │ ├── templateinheritance.rst │ ├── urlprocessors.rst │ ├── viewdecorators.rst │ └── wtforms.rst ├── quickstart.rst ├── reqcontext.rst ├── security.rst ├── server.rst ├── shell.rst ├── signals.rst ├── templating.rst ├── testing.rst ├── tutorial │ ├── blog.rst │ ├── database.rst │ ├── deploy.rst │ ├── factory.rst │ ├── flaskr_edit.png │ ├── flaskr_index.png │ ├── flaskr_login.png │ ├── index.rst │ ├── install.rst │ ├── layout.rst │ ├── next.rst │ ├── static.rst │ ├── templates.rst │ ├── tests.rst │ └── views.rst └── views.rst ├── examples ├── celery │ ├── README.md │ ├── make_celery.py │ ├── pyproject.toml │ ├── requirements.txt │ └── src │ │ └── task_app │ │ ├── __init__.py │ │ ├── tasks.py │ │ ├── templates │ │ └── index.html │ │ └── views.py ├── javascript │ ├── .gitignore │ ├── LICENSE.rst │ ├── README.rst │ ├── js_example │ │ ├── __init__.py │ │ ├── templates │ │ │ ├── base.html │ │ │ ├── fetch.html │ │ │ ├── jquery.html │ │ │ └── xhr.html │ │ └── views.py │ ├── pyproject.toml │ └── tests │ │ ├── conftest.py │ │ └── test_js_example.py └── tutorial │ ├── .gitignore │ ├── LICENSE.rst │ ├── README.rst │ ├── flaskr │ ├── __init__.py │ ├── auth.py │ ├── blog.py │ ├── db.py │ ├── schema.sql │ ├── static │ │ └── style.css │ └── templates │ │ ├── auth │ │ ├── login.html │ │ └── register.html │ │ ├── base.html │ │ └── blog │ │ ├── create.html │ │ ├── index.html │ │ └── update.html │ ├── pyproject.toml │ └── tests │ ├── conftest.py │ ├── data.sql │ ├── test_auth.py │ ├── test_blog.py │ ├── test_db.py │ └── test_factory.py ├── pyproject.toml ├── requirements ├── docs.in └── docs.txt ├── src └── flask │ ├── __init__.py │ ├── __main__.py │ ├── app.py │ ├── blueprints.py │ ├── cli.py │ ├── config.py │ ├── ctx.py │ ├── debughelpers.py │ ├── globals.py │ ├── helpers.py │ ├── json │ ├── __init__.py │ ├── provider.py │ └── tag.py │ ├── logging.py │ ├── py.typed │ ├── sansio │ ├── README.md │ ├── app.py │ ├── blueprints.py │ └── scaffold.py │ ├── sessions.py │ ├── signals.py │ ├── templating.py │ ├── testing.py │ ├── typing.py │ ├── views.py │ └── wrappers.py ├── static_files ├── debugger.png ├── flask-logo.png ├── flaskr_edit.png ├── flaskr_index.png ├── flaskr_login.png ├── no.png ├── pycharm-runconfig.png └── yes.png ├── tests ├── conftest.py ├── static │ ├── config.json │ ├── config.toml │ └── index.html ├── templates │ ├── _macro.html │ ├── context_template.html │ ├── escaping_template.html │ ├── mail.txt │ ├── nested │ │ └── nested.txt │ ├── non_escaping_template.txt │ ├── simple_template.html │ ├── template_filter.html │ └── template_test.html ├── test_appctx.py ├── test_apps │ ├── blueprintapp │ │ ├── __init__.py │ │ └── apps │ │ │ ├── __init__.py │ │ │ ├── admin │ │ │ ├── __init__.py │ │ │ ├── static │ │ │ │ ├── css │ │ │ │ │ └── test.css │ │ │ │ └── test.txt │ │ │ └── templates │ │ │ │ └── admin │ │ │ │ └── index.html │ │ │ └── frontend │ │ │ ├── __init__.py │ │ │ └── templates │ │ │ └── frontend │ │ │ └── index.html │ ├── cliapp │ │ ├── __init__.py │ │ ├── app.py │ │ ├── factory.py │ │ ├── importerrorapp.py │ │ ├── inner1 │ │ │ ├── __init__.py │ │ │ └── inner2 │ │ │ │ ├── __init__.py │ │ │ │ └── flask.py │ │ ├── message.txt │ │ └── multiapp.py │ ├── helloworld │ │ ├── hello.py │ │ └── wsgi.py │ └── subdomaintestmodule │ │ ├── __init__.py │ │ └── static │ │ └── hello.txt ├── test_async.py ├── test_basic.py ├── test_blueprints.py ├── test_cli.py ├── test_config.py ├── test_converters.py ├── test_helpers.py ├── test_instance_config.py ├── test_json.py ├── test_json_tag.py ├── test_logging.py ├── test_regression.py ├── test_reqctx.py ├── test_session_interface.py ├── test_signals.py ├── test_subclassing.py ├── test_templating.py ├── test_testing.py ├── test_user_error_handler.py ├── test_views.py └── typing │ ├── typing_app_decorators.py │ ├── typing_error_handler.py │ └── typing_route.py └── tox.ini /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | end_of_line = lf 9 | charset = utf-8 10 | max_line_length = 88 11 | 12 | [*.{yml,yaml,json,js,css,html}] 13 | indent_size = 2 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .env 3 | .flaskenv 4 | *.pyc 5 | *.pyo 6 | env/ 7 | venv/ 8 | .venv/ 9 | env* 10 | dist/ 11 | build/ 12 | *.egg 13 | *.egg-info/ 14 | .tox/ 15 | .cache/ 16 | .pytest_cache/ 17 | .idea/ 18 | docs/_build/ 19 | .vscode 20 | 21 | # Coverage reports 22 | htmlcov/ 23 | .coverage 24 | .coverage.* 25 | *,cover 26 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | build: 3 | os: ubuntu-20.04 4 | tools: 5 | python: "3.10" 6 | python: 7 | install: 8 | - requirements: requirements/docs.txt 9 | - method: pip 10 | path: . 11 | sphinx: 12 | builder: dirhtml 13 | fail_on_warning: true 14 | -------------------------------------------------------------------------------- /09-debugging-application-errors.md: -------------------------------------------------------------------------------- 1 | # Depurando Erros de Aplicação 2 | 3 | 4 | ## Em Produção 5 | 6 | **Não execute o servidor de desenvolvimento, ou ative o depurador embutido, em um ambiente de produção.** O depurador permite executar código Python de maneira arbitrária a partir do browser. É protegido por um fixador, mas que não deve ser confiado por questões de segurança. 7 | 8 | Use uma ferramenta registador de erro, tal como Sentry, como descrito em [Ferramentas de Registo de Erros](), ou ative o registo e notificações como descrito em [Registando (Logging)](). 9 | 10 | Se você tiver acesso ao servidor, você poderia adicionar algum código capaz de iniciar um depurador externo se `request.remote_addr` corresponder ao seu IP. Algumas depuradores de IDEs também tem um modo remoto assim é possível interagir localmente com os pontos de paradas do servidor. Apenas ative temporariamente o depurador. 11 | 12 | 13 | ## O Depurador Embutido 14 | 15 | O servidor de desenvolvimento embutido do Werzeug oferece um depurador que exibe rastreador interativo dentro do browser sempre que um erro não manipulado ocorrer durante uma requisição. Este depurador deve ser apenas usado durante o desenvolvimento. 16 | 17 | ![imagem do rastreador no browser exibindo erro de tipagem](./static_files/debugger.png) 18 | 19 | --- 20 | 21 | 22 | ### Aviso: 23 | 24 | O depurador permite executar arbitrariamente código Python a partir do browser. É protegido por um fixador, mas continua a representar a maioria dos riscos de segurança. Não execute o servidor de desenvolvimento ou depurador em ambiente de produção. 25 | 26 | --- 27 | 28 | Para ativar o depurador, execute o servidor de desenvolvimento com a variável de ambiente `FLASK_ENV` definida para `development`. Isto poem o Flask em modo de depuração, que muda a maneira de como ele manipula certos erros, e ativa o depurador junto com o recarregador. 29 | 30 | Bash 31 | ```sh 32 | $ export FLASK_ENV=development 33 | $ flask run 34 | ``` 35 | 36 | CMD 37 | ```cmd 38 | > set FLASK_ENV=development 39 | > flask run 40 | ``` 41 | 42 | Powershell 43 | ```ps 44 | > $env:FLASK_ENV = "development" 45 | > flask run 46 | ``` 47 | 48 | `FLASK_ENV` apenas pode ser definido como uma variável de ambiente. Sempre que estiver executando a partir do código Python, passar `debug=True` ativa o modo de depuração, que é na maioria dos casos equivalente. O modo de depuração pode ser também controlado separadamente do `FLASK_ENV` com a variável de ambiente `FLASK_DEBUG`. 49 | 50 | ```py 51 | app.run(debug=True) 52 | ``` 53 | 54 | O [Servidor de Desenvolvimento]() e a [Interface de Linha de Comando]() possuem mais informação sobre como executar o depurador, modo de depuração, e modo de desenvolvimento. Informações mais detalhadas sobre o depurador pode ser achada na documentação do [Werkzeug](https://werkzeug.palletsprojects.com/debug/). 55 | 56 | 57 | ## Depuradores Externos 58 | 59 | Depuradores externos, tais como aqueles oferecidos pelas IDEs, podem oferecer uma experiência de depuração mais rica do que os depuradores embutidos. Eles também podem ser usados para caminhar pelo código durante uma requisição antes de um erro ser levantado, ou se nenhum erro for de todo acionado. Alguns até têm um modo remoto assim você pode depurar o código enquanto estiver ainda executando, isso a partir de uma outra máquina. 60 | 61 | Sempre que estiver usando um depurador externo, a aplicação deve estar no modo de depuração, mas pode ser útil desativar o depurador e recarregador embutido, em caso de interferência. 62 | 63 | Quando estiver executando a partir da linha de comando: 64 | 65 | Bash 66 | ```sh 67 | $ export FLASK_ENV=development 68 | $ flask run --no-debugger --no-reload 69 | ``` 70 | 71 | CMD 72 | ```cmd 73 | > set FLASK_ENV=development 74 | > flask run --no-debugger --no-reload 75 | ``` 76 | 77 | Powershell 78 | ```ps 79 | > $env:FLASK_ENV = "development" 80 | > flask run --no-debugger --no-reload 81 | ``` 82 | 83 | Quando estiver executando a partir do código Python: 84 | 85 | ```py 86 | app.run(debug=True, use_debugger=False, use_reloader=False) 87 | ``` 88 | 89 | Desativação destes não é obrigatória, um depurador externo continuará a funcionar com os seguintes avisos. Se o depurador embutido não estiver desativado, ele capturará exceções não manipuladas antes do depurador externo poder faze-lo. Se o recarregador não estiver desativado, ele pode causar um recarregamento inesperado se o código mudar durante a depuração. -------------------------------------------------------------------------------- /15-the-request-context.md: -------------------------------------------------------------------------------- 1 | # O Contexto de Requisição 2 | 3 | 4 | O contexto de requisição mantêm o rastreio de dados no nível da requisição durante uma requisição. No lugar da passagem do objeto de requisição para cada função que executa durante uma requisição, as delegações [**`request`**](#) e [**`session`**](#) são acessadas. 5 | 6 | Isto é semelhante ao [O Contexto da Aplicação](14-the-application-context.md), o qual mantêm o rastreio de dados no nível de aplicação independente de uma requisição. Um contexto de aplicação correspondente é empurrado quando um contexto de requisição é empurrado. -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at report@palletsprojects.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /LICENSE.rst: -------------------------------------------------------------------------------- 1 | Copyright 2010 Pallets 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 21 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 24 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 26 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Flask 2 | ===== 3 | 4 | Flask é uma leve abstração de aplicação de Web de `WSGI`_. Ela está desenhada para tornar o começo rápido e fácil, com a habilidade de escalar para aplicações complexas. Ela começou como um simples capa em torno da `Werkzeug`_ e `Jinja`_ e tornou-se uma das mais populares abstrações de aplicações de Web da Python. 5 | 6 | Flask oferece sugestões, mas não impõe quaisquer dependências ou disposição de projeto. Está sobre os programadores escolher as ferramentas e bibliotecas que quiserem usar. Existem muitas extensões fornecidas pela comunidade que facilitam a adição de nova funcionalidade. 7 | 8 | .. _WSGI: https://wsgi.readthedocs.io/ 9 | .. _Werkzeug: https://werkzeug.palletsprojects.com/ 10 | .. _Jinja: https://jinja.palletsprojects.com/ 11 | 12 | 13 | Instalação 14 | ---------- 15 | 16 | Instalar e atualizar usando `pip`_: 17 | 18 | .. code-block:: text 19 | 20 | $ pip install -U Flask 21 | 22 | .. _pip: https://pip.pypa.io/en/stable/getting-started/ 23 | 24 | 25 | Um Exemplo Simples 26 | ---------------- 27 | 28 | .. code-block:: python 29 | 30 | # guardar isto como `app.py` 31 | from flask import Flask 32 | 33 | app = Flask(__name__) 34 | 35 | @app.route("/") 36 | def hello(): 37 | return "Hello, World!" 38 | 39 | .. code-block:: text 40 | 41 | $ flask run 42 | * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) 43 | 44 | 45 | Contribuir 46 | ------------ 47 | 48 | Para orientação sobre configurar um ambiente de desenvolvimento e como fazer uma contribuição à Flask, consulte as `contributing guidelines`_. 49 | 50 | .. _contributing guidelines: https://github.com/pallets/flask/blob/main/CONTRIBUTING.rst 51 | 52 | 53 | Doar 54 | ------ 55 | 56 | A organização Pallets desenvolve e suporta a Flask e as bibliotecas que ela usa. No sentido aumentar a comunidade de colaboradores e utilizadores, e permitir os responsáveis de dedicar mais tempo aos projetos, `please donate today`_. 57 | 58 | .. _please donate today: https://palletsprojects.com/donate 59 | 60 | 61 | Ligações 62 | ----- 63 | 64 | - Documentação: https://flask.palletsprojects.com/ 65 | - Mudanças: https://flask.palletsprojects.com/changes/ 66 | - Lançamentos da PyPI: https://pypi.org/project/Flask/ 67 | - Código-fonte: https://github.com/pallets/flask/ 68 | - Monitor de Problema: https://github.com/pallets/flask/issues/ 69 | - Conversas: https://discord.gg/pallets 70 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/_static/debugger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nazarepiedady/flask-docs-pt/c5b543b639a661befebe8aa1afda4327642e5b8f/docs/_static/debugger.png -------------------------------------------------------------------------------- /docs/_static/flask-horizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nazarepiedady/flask-docs-pt/c5b543b639a661befebe8aa1afda4327642e5b8f/docs/_static/flask-horizontal.png -------------------------------------------------------------------------------- /docs/_static/flask-vertical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nazarepiedady/flask-docs-pt/c5b543b639a661befebe8aa1afda4327642e5b8f/docs/_static/flask-vertical.png -------------------------------------------------------------------------------- /docs/_static/pycharm-run-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nazarepiedady/flask-docs-pt/c5b543b639a661befebe8aa1afda4327642e5b8f/docs/_static/pycharm-run-config.png -------------------------------------------------------------------------------- /docs/_static/shortcut-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nazarepiedady/flask-docs-pt/c5b543b639a661befebe8aa1afda4327642e5b8f/docs/_static/shortcut-icon.png -------------------------------------------------------------------------------- /docs/changes.rst: -------------------------------------------------------------------------------- 1 | Changes 2 | ======= 3 | 4 | .. include:: ../CHANGES.rst 5 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | import packaging.version 2 | from pallets_sphinx_themes import get_version 3 | from pallets_sphinx_themes import ProjectLink 4 | 5 | # Project -------------------------------------------------------------- 6 | 7 | project = "Flask" 8 | copyright = "2010 Pallets" 9 | author = "Pallets" 10 | release, version = get_version("Flask") 11 | language = 'pt' 12 | 13 | # General -------------------------------------------------------------- 14 | 15 | master_doc = "index" 16 | extensions = [ 17 | "sphinx.ext.autodoc", 18 | "sphinx.ext.intersphinx", 19 | "sphinxcontrib.log_cabinet", 20 | "pallets_sphinx_themes", 21 | "sphinx_issues", 22 | "sphinx_tabs.tabs", 23 | ] 24 | autodoc_typehints = "description" 25 | intersphinx_mapping = { 26 | "python": ("https://docs.python.org/3/", None), 27 | "werkzeug": ("https://werkzeug.palletsprojects.com/", None), 28 | "click": ("https://click.palletsprojects.com/", None), 29 | "jinja": ("https://jinja.palletsprojects.com/", None), 30 | "itsdangerous": ("https://itsdangerous.palletsprojects.com/", None), 31 | "sqlalchemy": ("https://docs.sqlalchemy.org/", None), 32 | "wtforms": ("https://wtforms.readthedocs.io/", None), 33 | "blinker": ("https://blinker.readthedocs.io/", None), 34 | } 35 | issues_github_path = "pallets/flask" 36 | 37 | # HTML ----------------------------------------------------------------- 38 | 39 | html_theme = "flask" 40 | html_theme_options = {"index_sidebar_logo": False} 41 | html_context = { 42 | "project_links": [ 43 | ProjectLink("Donate", "https://palletsprojects.com/donate"), 44 | ProjectLink("PyPI Releases", "https://pypi.org/project/Flask/"), 45 | ProjectLink("Source Code", "https://github.com/pallets/flask/"), 46 | ProjectLink("Issue Tracker", "https://github.com/pallets/flask/issues/"), 47 | ProjectLink("Chat", "https://discord.gg/pallets"), 48 | ] 49 | } 50 | html_sidebars = { 51 | "index": ["project.html", "localtoc.html", "searchbox.html", "ethicalads.html"], 52 | "**": ["localtoc.html", "relations.html", "searchbox.html", "ethicalads.html"], 53 | } 54 | singlehtml_sidebars = {"index": ["project.html", "localtoc.html", "ethicalads.html"]} 55 | html_static_path = ["_static"] 56 | html_favicon = "_static/shortcut-icon.png" 57 | html_logo = "_static/flask-vertical.png" 58 | html_title = f"Documentação da Flask ({version})" 59 | html_show_sourcelink = False 60 | 61 | # LaTeX ---------------------------------------------------------------- 62 | 63 | latex_documents = [(master_doc, f"Flask-{version}.tex", html_title, author, "manual")] 64 | 65 | # Local Extensions ----------------------------------------------------- 66 | 67 | 68 | def github_link(name, rawtext, text, lineno, inliner, options=None, content=None): 69 | app = inliner.document.settings.env.app 70 | release = app.config.release 71 | base_url = "https://github.com/pallets/flask/tree/" 72 | 73 | if text.endswith(">"): 74 | words, text = text[:-1].rsplit("<", 1) 75 | words = words.strip() 76 | else: 77 | words = None 78 | 79 | if packaging.version.parse(release).is_devrelease: 80 | url = f"{base_url}main/{text}" 81 | else: 82 | url = f"{base_url}{release}/{text}" 83 | 84 | if words is None: 85 | words = url 86 | 87 | from docutils.nodes import reference 88 | from docutils.parsers.rst.roles import set_classes 89 | 90 | options = options or {} 91 | set_classes(options) 92 | node = reference(rawtext, words, refuri=url, **options) 93 | return [node], [] 94 | 95 | 96 | def setup(app): 97 | app.add_role("gh", github_link) 98 | -------------------------------------------------------------------------------- /docs/contributing.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../CONTRIBUTING.rst 2 | -------------------------------------------------------------------------------- /docs/debugging.rst: -------------------------------------------------------------------------------- 1 | Debugging Application Errors 2 | ============================ 3 | 4 | 5 | In Production 6 | ------------- 7 | 8 | **Do not run the development server, or enable the built-in debugger, in 9 | a production environment.** The debugger allows executing arbitrary 10 | Python code from the browser. It's protected by a pin, but that should 11 | not be relied on for security. 12 | 13 | Use an error logging tool, such as Sentry, as described in 14 | :ref:`error-logging-tools`, or enable logging and notifications as 15 | described in :doc:`/logging`. 16 | 17 | If you have access to the server, you could add some code to start an 18 | external debugger if ``request.remote_addr`` matches your IP. Some IDE 19 | debuggers also have a remote mode so breakpoints on the server can be 20 | interacted with locally. Only enable a debugger temporarily. 21 | 22 | 23 | The Built-In Debugger 24 | --------------------- 25 | 26 | The built-in Werkzeug development server provides a debugger which shows 27 | an interactive traceback in the browser when an unhandled error occurs 28 | during a request. This debugger should only be used during development. 29 | 30 | .. image:: _static/debugger.png 31 | :align: center 32 | :class: screenshot 33 | :alt: screenshot of debugger in action 34 | 35 | .. warning:: 36 | 37 | The debugger allows executing arbitrary Python code from the 38 | browser. It is protected by a pin, but still represents a major 39 | security risk. Do not run the development server or debugger in a 40 | production environment. 41 | 42 | The debugger is enabled by default when the development server is run in debug mode. 43 | 44 | .. code-block:: text 45 | 46 | $ flask --app hello run --debug 47 | 48 | When running from Python code, passing ``debug=True`` enables debug mode, which is 49 | mostly equivalent. 50 | 51 | .. code-block:: python 52 | 53 | app.run(debug=True) 54 | 55 | :doc:`/server` and :doc:`/cli` have more information about running the debugger and 56 | debug mode. More information about the debugger can be found in the `Werkzeug 57 | documentation `__. 58 | 59 | 60 | External Debuggers 61 | ------------------ 62 | 63 | External debuggers, such as those provided by IDEs, can offer a more 64 | powerful debugging experience than the built-in debugger. They can also 65 | be used to step through code during a request before an error is raised, 66 | or if no error is raised. Some even have a remote mode so you can debug 67 | code running on another machine. 68 | 69 | When using an external debugger, the app should still be in debug mode, otherwise Flask 70 | turns unhandled errors into generic 500 error pages. However, the built-in debugger and 71 | reloader should be disabled so they don't interfere with the external debugger. 72 | 73 | .. code-block:: text 74 | 75 | $ flask --app hello run --debug --no-debugger --no-reload 76 | 77 | When running from Python: 78 | 79 | .. code-block:: python 80 | 81 | app.run(debug=True, use_debugger=False, use_reloader=False) 82 | 83 | Disabling these isn't required, an external debugger will continue to work with the 84 | following caveats. 85 | 86 | - If the built-in debugger is not disabled, it will catch unhandled exceptions before 87 | the external debugger can. 88 | - If the reloader is not disabled, it could cause an unexpected reload if code changes 89 | during a breakpoint. 90 | - The development server will still catch unhandled exceptions if the built-in 91 | debugger is disabled, otherwise it would crash on any error. If you want that (and 92 | usually you don't) pass ``passthrough_errors=True`` to ``app.run``. 93 | 94 | .. code-block:: python 95 | 96 | app.run( 97 | debug=True, passthrough_errors=True, 98 | use_debugger=False, use_reloader=False 99 | ) 100 | -------------------------------------------------------------------------------- /docs/deploying/apache-httpd.rst: -------------------------------------------------------------------------------- 1 | Apache httpd 2 | ============ 3 | 4 | `Apache httpd`_ is a fast, production level HTTP server. When serving 5 | your application with one of the WSGI servers listed in :doc:`index`, it 6 | is often good or necessary to put a dedicated HTTP server in front of 7 | it. This "reverse proxy" can handle incoming requests, TLS, and other 8 | security and performance concerns better than the WSGI server. 9 | 10 | httpd can be installed using your system package manager, or a pre-built 11 | executable for Windows. Installing and running httpd itself is outside 12 | the scope of this doc. This page outlines the basics of configuring 13 | httpd to proxy your application. Be sure to read its documentation to 14 | understand what features are available. 15 | 16 | .. _Apache httpd: https://httpd.apache.org/ 17 | 18 | 19 | Domain Name 20 | ----------- 21 | 22 | Acquiring and configuring a domain name is outside the scope of this 23 | doc. In general, you will buy a domain name from a registrar, pay for 24 | server space with a hosting provider, and then point your registrar 25 | at the hosting provider's name servers. 26 | 27 | To simulate this, you can also edit your ``hosts`` file, located at 28 | ``/etc/hosts`` on Linux. Add a line that associates a name with the 29 | local IP. 30 | 31 | Modern Linux systems may be configured to treat any domain name that 32 | ends with ``.localhost`` like this without adding it to the ``hosts`` 33 | file. 34 | 35 | .. code-block:: python 36 | :caption: ``/etc/hosts`` 37 | 38 | 127.0.0.1 hello.localhost 39 | 40 | 41 | Configuration 42 | ------------- 43 | 44 | The httpd configuration is located at ``/etc/httpd/conf/httpd.conf`` on 45 | Linux. It may be different depending on your operating system. Check the 46 | docs and look for ``httpd.conf``. 47 | 48 | Remove or comment out any existing ``DocumentRoot`` directive. Add the 49 | config lines below. We'll assume the WSGI server is listening locally at 50 | ``http://127.0.0.1:8000``. 51 | 52 | .. code-block:: apache 53 | :caption: ``/etc/httpd/conf/httpd.conf`` 54 | 55 | LoadModule proxy_module modules/mod_proxy.so 56 | LoadModule proxy_http_module modules/mod_proxy_http.so 57 | ProxyPass / http://127.0.0.1:8000/ 58 | RequestHeader set X-Forwarded-Proto http 59 | RequestHeader set X-Forwarded-Prefix / 60 | 61 | The ``LoadModule`` lines might already exist. If so, make sure they are 62 | uncommented instead of adding them manually. 63 | 64 | Then :doc:`proxy_fix` so that your application uses the ``X-Forwarded`` 65 | headers. ``X-Forwarded-For`` and ``X-Forwarded-Host`` are automatically 66 | set by ``ProxyPass``. 67 | -------------------------------------------------------------------------------- /docs/deploying/asgi.rst: -------------------------------------------------------------------------------- 1 | ASGI 2 | ==== 3 | 4 | If you'd like to use an ASGI server you will need to utilise WSGI to 5 | ASGI middleware. The asgiref 6 | `WsgiToAsgi `_ 7 | adapter is recommended as it integrates with the event loop used for 8 | Flask's :ref:`async_await` support. You can use the adapter by 9 | wrapping the Flask app, 10 | 11 | .. code-block:: python 12 | 13 | from asgiref.wsgi import WsgiToAsgi 14 | from flask import Flask 15 | 16 | app = Flask(__name__) 17 | 18 | ... 19 | 20 | asgi_app = WsgiToAsgi(app) 21 | 22 | and then serving the ``asgi_app`` with the ASGI server, e.g. using 23 | `Hypercorn `_, 24 | 25 | .. sourcecode:: text 26 | 27 | $ hypercorn module:asgi_app 28 | -------------------------------------------------------------------------------- /docs/deploying/eventlet.rst: -------------------------------------------------------------------------------- 1 | eventlet 2 | ======== 3 | 4 | Prefer using :doc:`gunicorn` with eventlet workers rather than using 5 | `eventlet`_ directly. Gunicorn provides a much more configurable and 6 | production-tested server. 7 | 8 | `eventlet`_ allows writing asynchronous, coroutine-based code that looks 9 | like standard synchronous Python. It uses `greenlet`_ to enable task 10 | switching without writing ``async/await`` or using ``asyncio``. 11 | 12 | :doc:`gevent` is another library that does the same thing. Certain 13 | dependencies you have, or other considerations, may affect which of the 14 | two you choose to use. 15 | 16 | eventlet provides a WSGI server that can handle many connections at once 17 | instead of one per worker process. You must actually use eventlet in 18 | your own code to see any benefit to using the server. 19 | 20 | .. _eventlet: https://eventlet.net/ 21 | .. _greenlet: https://greenlet.readthedocs.io/en/latest/ 22 | 23 | 24 | Installing 25 | ---------- 26 | 27 | When using eventlet, greenlet>=1.0 is required, otherwise context locals 28 | such as ``request`` will not work as expected. When using PyPy, 29 | PyPy>=7.3.7 is required. 30 | 31 | Create a virtualenv, install your application, then install 32 | ``eventlet``. 33 | 34 | .. code-block:: text 35 | 36 | $ cd hello-app 37 | $ python -m venv .venv 38 | $ . .venv/bin/activate 39 | $ pip install . # install your application 40 | $ pip install eventlet 41 | 42 | 43 | Running 44 | ------- 45 | 46 | To use eventlet to serve your application, write a script that imports 47 | its ``wsgi.server``, as well as your app or app factory. 48 | 49 | .. code-block:: python 50 | :caption: ``wsgi.py`` 51 | 52 | import eventlet 53 | from eventlet import wsgi 54 | from hello import create_app 55 | 56 | app = create_app() 57 | wsgi.server(eventlet.listen(("127.0.0.1", 8000)), app) 58 | 59 | .. code-block:: text 60 | 61 | $ python wsgi.py 62 | (x) wsgi starting up on http://127.0.0.1:8000 63 | 64 | 65 | Binding Externally 66 | ------------------ 67 | 68 | eventlet should not be run as root because it would cause your 69 | application code to run as root, which is not secure. However, this 70 | means it will not be possible to bind to port 80 or 443. Instead, a 71 | reverse proxy such as :doc:`nginx` or :doc:`apache-httpd` should be used 72 | in front of eventlet. 73 | 74 | You can bind to all external IPs on a non-privileged port by using 75 | ``0.0.0.0`` in the server arguments shown in the previous section. 76 | Don't do this when using a reverse proxy setup, otherwise it will be 77 | possible to bypass the proxy. 78 | 79 | ``0.0.0.0`` is not a valid address to navigate to, you'd use a specific 80 | IP address in your browser. 81 | -------------------------------------------------------------------------------- /docs/deploying/gevent.rst: -------------------------------------------------------------------------------- 1 | gevent 2 | ====== 3 | 4 | Prefer using :doc:`gunicorn` or :doc:`uwsgi` with gevent workers rather 5 | than using `gevent`_ directly. Gunicorn and uWSGI provide much more 6 | configurable and production-tested servers. 7 | 8 | `gevent`_ allows writing asynchronous, coroutine-based code that looks 9 | like standard synchronous Python. It uses `greenlet`_ to enable task 10 | switching without writing ``async/await`` or using ``asyncio``. 11 | 12 | :doc:`eventlet` is another library that does the same thing. Certain 13 | dependencies you have, or other considerations, may affect which of the 14 | two you choose to use. 15 | 16 | gevent provides a WSGI server that can handle many connections at once 17 | instead of one per worker process. You must actually use gevent in your 18 | own code to see any benefit to using the server. 19 | 20 | .. _gevent: https://www.gevent.org/ 21 | .. _greenlet: https://greenlet.readthedocs.io/en/latest/ 22 | 23 | 24 | Installing 25 | ---------- 26 | 27 | When using gevent, greenlet>=1.0 is required, otherwise context locals 28 | such as ``request`` will not work as expected. When using PyPy, 29 | PyPy>=7.3.7 is required. 30 | 31 | Create a virtualenv, install your application, then install ``gevent``. 32 | 33 | .. code-block:: text 34 | 35 | $ cd hello-app 36 | $ python -m venv .venv 37 | $ . .venv/bin/activate 38 | $ pip install . # install your application 39 | $ pip install gevent 40 | 41 | 42 | Running 43 | ------- 44 | 45 | To use gevent to serve your application, write a script that imports its 46 | ``WSGIServer``, as well as your app or app factory. 47 | 48 | .. code-block:: python 49 | :caption: ``wsgi.py`` 50 | 51 | from gevent.pywsgi import WSGIServer 52 | from hello import create_app 53 | 54 | app = create_app() 55 | http_server = WSGIServer(("127.0.0.1", 8000), app) 56 | http_server.serve_forever() 57 | 58 | .. code-block:: text 59 | 60 | $ python wsgi.py 61 | 62 | No output is shown when the server starts. 63 | 64 | 65 | Binding Externally 66 | ------------------ 67 | 68 | gevent should not be run as root because it would cause your 69 | application code to run as root, which is not secure. However, this 70 | means it will not be possible to bind to port 80 or 443. Instead, a 71 | reverse proxy such as :doc:`nginx` or :doc:`apache-httpd` should be used 72 | in front of gevent. 73 | 74 | You can bind to all external IPs on a non-privileged port by using 75 | ``0.0.0.0`` in the server arguments shown in the previous section. Don't 76 | do this when using a reverse proxy setup, otherwise it will be possible 77 | to bypass the proxy. 78 | 79 | ``0.0.0.0`` is not a valid address to navigate to, you'd use a specific 80 | IP address in your browser. 81 | -------------------------------------------------------------------------------- /docs/deploying/index.rst: -------------------------------------------------------------------------------- 1 | Deploying to Production 2 | ======================= 3 | 4 | After developing your application, you'll want to make it available 5 | publicly to other users. When you're developing locally, you're probably 6 | using the built-in development server, debugger, and reloader. These 7 | should not be used in production. Instead, you should use a dedicated 8 | WSGI server or hosting platform, some of which will be described here. 9 | 10 | "Production" means "not development", which applies whether you're 11 | serving your application publicly to millions of users or privately / 12 | locally to a single user. **Do not use the development server when 13 | deploying to production. It is intended for use only during local 14 | development. It is not designed to be particularly secure, stable, or 15 | efficient.** 16 | 17 | Self-Hosted Options 18 | ------------------- 19 | 20 | Flask is a WSGI *application*. A WSGI *server* is used to run the 21 | application, converting incoming HTTP requests to the standard WSGI 22 | environ, and converting outgoing WSGI responses to HTTP responses. 23 | 24 | The primary goal of these docs is to familiarize you with the concepts 25 | involved in running a WSGI application using a production WSGI server 26 | and HTTP server. There are many WSGI servers and HTTP servers, with many 27 | configuration possibilities. The pages below discuss the most common 28 | servers, and show the basics of running each one. The next section 29 | discusses platforms that can manage this for you. 30 | 31 | .. toctree:: 32 | :maxdepth: 1 33 | 34 | gunicorn 35 | waitress 36 | mod_wsgi 37 | uwsgi 38 | gevent 39 | eventlet 40 | asgi 41 | 42 | WSGI servers have HTTP servers built-in. However, a dedicated HTTP 43 | server may be safer, more efficient, or more capable. Putting an HTTP 44 | server in front of the WSGI server is called a "reverse proxy." 45 | 46 | .. toctree:: 47 | :maxdepth: 1 48 | 49 | proxy_fix 50 | nginx 51 | apache-httpd 52 | 53 | This list is not exhaustive, and you should evaluate these and other 54 | servers based on your application's needs. Different servers will have 55 | different capabilities, configuration, and support. 56 | 57 | 58 | Hosting Platforms 59 | ----------------- 60 | 61 | There are many services available for hosting web applications without 62 | needing to maintain your own server, networking, domain, etc. Some 63 | services may have a free tier up to a certain time or bandwidth. Many of 64 | these services use one of the WSGI servers described above, or a similar 65 | interface. The links below are for some of the most common platforms, 66 | which have instructions for Flask, WSGI, or Python. 67 | 68 | - `PythonAnywhere `_ 69 | - `Google App Engine `_ 70 | - `Google Cloud Run `_ 71 | - `AWS Elastic Beanstalk `_ 72 | - `Microsoft Azure `_ 73 | 74 | This list is not exhaustive, and you should evaluate these and other 75 | services based on your application's needs. Different services will have 76 | different capabilities, configuration, pricing, and support. 77 | 78 | You'll probably need to :doc:`proxy_fix` when using most hosting 79 | platforms. 80 | -------------------------------------------------------------------------------- /docs/deploying/mod_wsgi.rst: -------------------------------------------------------------------------------- 1 | mod_wsgi 2 | ======== 3 | 4 | `mod_wsgi`_ is a WSGI server integrated with the `Apache httpd`_ server. 5 | The modern `mod_wsgi-express`_ command makes it easy to configure and 6 | start the server without needing to write Apache httpd configuration. 7 | 8 | * Tightly integrated with Apache httpd. 9 | * Supports Windows directly. 10 | * Requires a compiler and the Apache development headers to install. 11 | * Does not require a reverse proxy setup. 12 | 13 | This page outlines the basics of running mod_wsgi-express, not the more 14 | complex installation and configuration with httpd. Be sure to read the 15 | `mod_wsgi-express`_, `mod_wsgi`_, and `Apache httpd`_ documentation to 16 | understand what features are available. 17 | 18 | .. _mod_wsgi-express: https://pypi.org/project/mod-wsgi/ 19 | .. _mod_wsgi: https://modwsgi.readthedocs.io/ 20 | .. _Apache httpd: https://httpd.apache.org/ 21 | 22 | 23 | Installing 24 | ---------- 25 | 26 | Installing mod_wsgi requires a compiler and the Apache server and 27 | development headers installed. You will get an error if they are not. 28 | How to install them depends on the OS and package manager that you use. 29 | 30 | Create a virtualenv, install your application, then install 31 | ``mod_wsgi``. 32 | 33 | .. code-block:: text 34 | 35 | $ cd hello-app 36 | $ python -m venv .venv 37 | $ . .venv/bin/activate 38 | $ pip install . # install your application 39 | $ pip install mod_wsgi 40 | 41 | 42 | Running 43 | ------- 44 | 45 | The only argument to ``mod_wsgi-express`` specifies a script containing 46 | your Flask application, which must be called ``application``. You can 47 | write a small script to import your app with this name, or to create it 48 | if using the app factory pattern. 49 | 50 | .. code-block:: python 51 | :caption: ``wsgi.py`` 52 | 53 | from hello import app 54 | 55 | application = app 56 | 57 | .. code-block:: python 58 | :caption: ``wsgi.py`` 59 | 60 | from hello import create_app 61 | 62 | application = create_app() 63 | 64 | Now run the ``mod_wsgi-express start-server`` command. 65 | 66 | .. code-block:: text 67 | 68 | $ mod_wsgi-express start-server wsgi.py --processes 4 69 | 70 | The ``--processes`` option specifies the number of worker processes to 71 | run; a starting value could be ``CPU * 2``. 72 | 73 | Logs for each request aren't show in the terminal. If an error occurs, 74 | its information is written to the error log file shown when starting the 75 | server. 76 | 77 | 78 | Binding Externally 79 | ------------------ 80 | 81 | Unlike the other WSGI servers in these docs, mod_wsgi can be run as 82 | root to bind to privileged ports like 80 and 443. However, it must be 83 | configured to drop permissions to a different user and group for the 84 | worker processes. 85 | 86 | For example, if you created a ``hello`` user and group, you should 87 | install your virtualenv and application as that user, then tell 88 | mod_wsgi to drop to that user after starting. 89 | 90 | .. code-block:: text 91 | 92 | $ sudo /home/hello/.venv/bin/mod_wsgi-express start-server \ 93 | /home/hello/wsgi.py \ 94 | --user hello --group hello --port 80 --processes 4 95 | -------------------------------------------------------------------------------- /docs/deploying/nginx.rst: -------------------------------------------------------------------------------- 1 | nginx 2 | ===== 3 | 4 | `nginx`_ is a fast, production level HTTP server. When serving your 5 | application with one of the WSGI servers listed in :doc:`index`, it is 6 | often good or necessary to put a dedicated HTTP server in front of it. 7 | This "reverse proxy" can handle incoming requests, TLS, and other 8 | security and performance concerns better than the WSGI server. 9 | 10 | Nginx can be installed using your system package manager, or a pre-built 11 | executable for Windows. Installing and running Nginx itself is outside 12 | the scope of this doc. This page outlines the basics of configuring 13 | Nginx to proxy your application. Be sure to read its documentation to 14 | understand what features are available. 15 | 16 | .. _nginx: https://nginx.org/ 17 | 18 | 19 | Domain Name 20 | ----------- 21 | 22 | Acquiring and configuring a domain name is outside the scope of this 23 | doc. In general, you will buy a domain name from a registrar, pay for 24 | server space with a hosting provider, and then point your registrar 25 | at the hosting provider's name servers. 26 | 27 | To simulate this, you can also edit your ``hosts`` file, located at 28 | ``/etc/hosts`` on Linux. Add a line that associates a name with the 29 | local IP. 30 | 31 | Modern Linux systems may be configured to treat any domain name that 32 | ends with ``.localhost`` like this without adding it to the ``hosts`` 33 | file. 34 | 35 | .. code-block:: python 36 | :caption: ``/etc/hosts`` 37 | 38 | 127.0.0.1 hello.localhost 39 | 40 | 41 | Configuration 42 | ------------- 43 | 44 | The nginx configuration is located at ``/etc/nginx/nginx.conf`` on 45 | Linux. It may be different depending on your operating system. Check the 46 | docs and look for ``nginx.conf``. 47 | 48 | Remove or comment out any existing ``server`` section. Add a ``server`` 49 | section and use the ``proxy_pass`` directive to point to the address the 50 | WSGI server is listening on. We'll assume the WSGI server is listening 51 | locally at ``http://127.0.0.1:8000``. 52 | 53 | .. code-block:: nginx 54 | :caption: ``/etc/nginx.conf`` 55 | 56 | server { 57 | listen 80; 58 | server_name _; 59 | 60 | location / { 61 | proxy_pass http://127.0.0.1:8000/; 62 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 63 | proxy_set_header X-Forwarded-Proto $scheme; 64 | proxy_set_header X-Forwarded-Host $host; 65 | proxy_set_header X-Forwarded-Prefix /; 66 | } 67 | } 68 | 69 | Then :doc:`proxy_fix` so that your application uses these headers. 70 | -------------------------------------------------------------------------------- /docs/deploying/proxy_fix.rst: -------------------------------------------------------------------------------- 1 | Tell Flask it is Behind a Proxy 2 | =============================== 3 | 4 | When using a reverse proxy, or many Python hosting platforms, the proxy 5 | will intercept and forward all external requests to the local WSGI 6 | server. 7 | 8 | From the WSGI server and Flask application's perspectives, requests are 9 | now coming from the HTTP server to the local address, rather than from 10 | the remote address to the external server address. 11 | 12 | HTTP servers should set ``X-Forwarded-`` headers to pass on the real 13 | values to the application. The application can then be told to trust and 14 | use those values by wrapping it with the 15 | :doc:`werkzeug:middleware/proxy_fix` middleware provided by Werkzeug. 16 | 17 | This middleware should only be used if the application is actually 18 | behind a proxy, and should be configured with the number of proxies that 19 | are chained in front of it. Not all proxies set all the headers. Since 20 | incoming headers can be faked, you must set how many proxies are setting 21 | each header so the middleware knows what to trust. 22 | 23 | .. code-block:: python 24 | 25 | from werkzeug.middleware.proxy_fix import ProxyFix 26 | 27 | app.wsgi_app = ProxyFix( 28 | app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1 29 | ) 30 | 31 | Remember, only apply this middleware if you are behind a proxy, and set 32 | the correct number of proxies that set each header. It can be a security 33 | issue if you get this configuration wrong. 34 | -------------------------------------------------------------------------------- /docs/deploying/waitress.rst: -------------------------------------------------------------------------------- 1 | Waitress 2 | ======== 3 | 4 | `Waitress`_ is a pure Python WSGI server. 5 | 6 | * It is easy to configure. 7 | * It supports Windows directly. 8 | * It is easy to install as it does not require additional dependencies 9 | or compilation. 10 | * It does not support streaming requests, full request data is always 11 | buffered. 12 | * It uses a single process with multiple thread workers. 13 | 14 | This page outlines the basics of running Waitress. Be sure to read its 15 | documentation and ``waitress-serve --help`` to understand what features 16 | are available. 17 | 18 | .. _Waitress: https://docs.pylonsproject.org/projects/waitress/ 19 | 20 | 21 | Installing 22 | ---------- 23 | 24 | Create a virtualenv, install your application, then install 25 | ``waitress``. 26 | 27 | .. code-block:: text 28 | 29 | $ cd hello-app 30 | $ python -m venv .venv 31 | $ . .venv/bin/activate 32 | $ pip install . # install your application 33 | $ pip install waitress 34 | 35 | 36 | Running 37 | ------- 38 | 39 | The only required argument to ``waitress-serve`` tells it how to load 40 | your Flask application. The syntax is ``{module}:{app}``. ``module`` is 41 | the dotted import name to the module with your application. ``app`` is 42 | the variable with the application. If you're using the app factory 43 | pattern, use ``--call {module}:{factory}`` instead. 44 | 45 | .. code-block:: text 46 | 47 | # equivalent to 'from hello import app' 48 | $ waitress-serve --host 127.0.0.1 hello:app 49 | 50 | # equivalent to 'from hello import create_app; create_app()' 51 | $ waitress-serve --host 127.0.0.1 --call hello:create_app 52 | 53 | Serving on http://127.0.0.1:8080 54 | 55 | The ``--host`` option binds the server to local ``127.0.0.1`` only. 56 | 57 | Logs for each request aren't shown, only errors are shown. Logging can 58 | be configured through the Python interface instead of the command line. 59 | 60 | 61 | Binding Externally 62 | ------------------ 63 | 64 | Waitress should not be run as root because it would cause your 65 | application code to run as root, which is not secure. However, this 66 | means it will not be possible to bind to port 80 or 443. Instead, a 67 | reverse proxy such as :doc:`nginx` or :doc:`apache-httpd` should be used 68 | in front of Waitress. 69 | 70 | You can bind to all external IPs on a non-privileged port by not 71 | specifying the ``--host`` option. Don't do this when using a revers 72 | proxy setup, otherwise it will be possible to bypass the proxy. 73 | 74 | ``0.0.0.0`` is not a valid address to navigate to, you'd use a specific 75 | IP address in your browser. 76 | -------------------------------------------------------------------------------- /docs/extensions.rst: -------------------------------------------------------------------------------- 1 | Extensions 2 | ========== 3 | 4 | Extensions are extra packages that add functionality to a Flask 5 | application. For example, an extension might add support for sending 6 | email or connecting to a database. Some extensions add entire new 7 | frameworks to help build certain types of applications, like a REST API. 8 | 9 | 10 | Finding Extensions 11 | ------------------ 12 | 13 | Flask extensions are usually named "Flask-Foo" or "Foo-Flask". You can 14 | search PyPI for packages tagged with `Framework :: Flask `_. 15 | 16 | 17 | Using Extensions 18 | ---------------- 19 | 20 | Consult each extension's documentation for installation, configuration, 21 | and usage instructions. Generally, extensions pull their own 22 | configuration from :attr:`app.config ` and are 23 | passed an application instance during initialization. For example, 24 | an extension called "Flask-Foo" might be used like this:: 25 | 26 | from flask_foo import Foo 27 | 28 | foo = Foo() 29 | 30 | app = Flask(__name__) 31 | app.config.update( 32 | FOO_BAR='baz', 33 | FOO_SPAM='eggs', 34 | ) 35 | 36 | foo.init_app(app) 37 | 38 | 39 | Building Extensions 40 | ------------------- 41 | 42 | While `PyPI `_ contains many Flask extensions, you may not find 43 | an extension that fits your need. If this is the case, you can create 44 | your own, and publish it for others to use as well. Read 45 | :doc:`extensiondev` to develop your own Flask extension. 46 | 47 | 48 | .. _pypi: https://pypi.org/search/?c=Framework+%3A%3A+Flask 49 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. rst-class:: hide-header 2 | 3 | Bem-vindo à Flask 4 | ================= 5 | 6 | .. image:: _static/flask-horizontal.png 7 | :align: center 8 | 9 | Bem-vindo à documentação da Flask. Começa com a :doc:`installation` e depois tenha uma perspetiva geral com a :doc:`quickstart`. Também existe uma :doc:`tutorial/index` mais detalhada que mostra como criar uma pequena mas completa aplicação com a Flask. Os padrões comuns são descritos na seção :doc:`patterns/index`. O resto da documentação descreve cada componente da Flask em detalhes, com uma referência completa na seção :doc:`api`. 10 | 11 | A Flask depende do conjunto de ferramentas de WSGI `Werkzeug`_, o motor de modelos de marcação `Jinja`_, e o conjunto de ferramentas de interface de linha de comando `Click`_. Certifica-te de consultar as suas documentações assim como a da Flask quando procurares por informação. 12 | 13 | .. _Werkzeug: https://werkzeug.palletsprojects.com 14 | .. _Jinja: https://jinja.palletsprojects.com 15 | .. _Click: https://click.palletsprojects.com 16 | 17 | 18 | Guia do Utilizador 19 | ------------------ 20 | 21 | A Flask fornece configuração e convenções, com padrões sensíveis, para começar. Esta seção da documentação explica as diferentes partes da abstração Flask e como podem ser usadas, personalizadas, e estendidas. Além da própria Flask, procure por extensões mantidas pela comunidade para adicionar ainda mais funcionalidade. 22 | 23 | .. toctree:: 24 | :maxdepth: 2 25 | 26 | installation 27 | quickstart 28 | tutorial/index 29 | templating 30 | testing 31 | errorhandling 32 | debugging 33 | logging 34 | config 35 | signals 36 | views 37 | lifecycle 38 | appcontext 39 | reqcontext 40 | blueprints 41 | extensions 42 | cli 43 | server 44 | shell 45 | patterns/index 46 | security 47 | deploying/index 48 | async-await 49 | 50 | 51 | Referência da API 52 | ----------------- 53 | 54 | Se estiveres a procurar por informação sobre uma função, classe ou método específico, esta parte da documentação é para ti. 55 | 56 | .. toctree:: 57 | :maxdepth: 2 58 | 59 | api 60 | 61 | 62 | Notas Adicionais 63 | ---------------- 64 | 65 | .. toctree:: 66 | :maxdepth: 2 67 | 68 | design 69 | extensiondev 70 | contributing 71 | license 72 | changes 73 | -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | Instalação 2 | ============ 3 | 4 | 5 | Versão da Python 6 | ------------------ 7 | 8 | Nós recomendamos usar a versão mais recente da Python. A Flask suporta a Python 3.8 e as mais recentes. 9 | 10 | 11 | Dependências 12 | -------------- 13 | 14 | Estas distribuições serão instaladas automaticamente quando instalares a Flask. 15 | 16 | * `Werkzeug`_ implementa a WSGI, a interface da Python padrão entre as aplicações e servidores. 17 | * `Jinja`_ é uma linguagem de modelo de marcação que desenha as páginas que a tua aplicação serve. 18 | * `MarkupSafe`_ vem com a Jinja. Ela escapa entradas duvidosas quando geramos os modelos de marcação para evitar ataques de injeção. 19 | * `ItsDangerous`_ assina com segurança o dado para garantir sua integridade. Isto é usado para proteger o cookie da sessão da Flask. 20 | * `Click`_ é uma abstração para escrita de aplicações de linha de comando. Ela fornece o comando ``flask`` e permite adicionar comandos de gestão personalizados. 21 | * `Blinker`_ fornece suporte para os :doc:`signals`. 22 | 23 | .. _Werkzeug: https://palletsprojects.com/p/werkzeug/ 24 | .. _Jinja: https://palletsprojects.com/p/jinja/ 25 | .. _MarkupSafe: https://palletsprojects.com/p/markupsafe/ 26 | .. _ItsDangerous: https://palletsprojects.com/p/itsdangerous/ 27 | .. _Click: https://palletsprojects.com/p/click/ 28 | .. _Blinker: https://blinker.readthedocs.io/ 29 | 30 | 31 | Dependências Opcionais 32 | ~~~~~~~~~~~~~~~~~~~~~~~~ 33 | 34 | Estas distribuições não serão instaladas automaticamente. A Flask detetará e as usará se as instalarmos. 35 | 36 | * `python-dotenv`_ ativa suporte para a :ref:`dotenv` quando executamos os comandos da ``flask``. 37 | * `Watchdog`_ fornece um recarregador mais rápido e mais eficiente para o servidor de desenvolvimento. 38 | 39 | .. _python-dotenv: https://github.com/theskumar/python-dotenv#readme 40 | .. _watchdog: https://pythonhosted.org/watchdog/ 41 | 42 | 43 | greenlet 44 | ~~~~~~~~~~~ 45 | 46 | Nós podemos escolher usar ``gevent`` ou ``eventlet`` com a nossa aplicação. Neste caso, ``greenlet>=1.0`` é obrigatório. Quando usamos PyPY, ``PyPy>=7.3.7`` é obrigatório. 47 | 48 | Estas não sãos as versões mínimas suportadas, apenas indicam as primeiras versões que adicionaram funcionalidades necessárias. Nós deveríamos usar as versões mais recentes de cada. 49 | 50 | 51 | Ambientes Virtuais 52 | -------------------- 53 | 54 | Nós devemos sempre usar um ambiente virtual para gerir as dependências para o nosso projeto, tanto em desenvolvimento como em produção. 55 | 56 | Qual problema um ambiente virtual soluciona? Quanto mais projetos de Python temos, mais provavelmente é que precisaremos de trabalhar com diferentes versões de bibliotecas de Python, ou mesmo a própria Python. As versões mais novas de bibliotecas para um projeto pode quebrar a compatibilidade em um outro projeto. 57 | 58 | Os ambientes virtuais são grupos independentes de bibliotecas da Python, um para cada projeto. Os pacotes instalados para um projeto não afetarão outros projetos ou os pacotes do sistema operativo. 59 | 60 | A Python vem empacotada com o módulo :mod:`venv` para criar ambientes virtuais: 61 | 62 | 63 | .. _install-create-env: 64 | 65 | Criar um Ambiente 66 | ~~~~~~~~~~~~~~~~~~~~~ 67 | 68 | Criar uma pasta de projeto e um pasta :file:`.venv` dentro dela: 69 | 70 | .. tabs:: 71 | 72 | .. group-tab:: macOS/Linux 73 | 74 | .. code-block:: text 75 | 76 | $ mkdir myproject 77 | $ cd myproject 78 | $ python3 -m venv .venv 79 | 80 | .. group-tab:: Windows 81 | 82 | .. code-block:: text 83 | 84 | > mkdir myproject 85 | > cd myproject 86 | > py -3 -m venv .venv 87 | 88 | 89 | .. _install-activate-env: 90 | 91 | Ativar o ambiente 92 | ~~~~~~~~~~~~~~~~~~~~~~~~ 93 | 94 | Antes de trabalharmos no nosso projeto, vamos ativar o ambiente correspondente: 95 | 96 | .. tabs:: 97 | 98 | .. group-tab:: macOS/Linux 99 | 100 | .. code-block:: text 101 | 102 | $ . .venv/bin/activate 103 | 104 | .. group-tab:: Windows 105 | 106 | .. code-block:: text 107 | 108 | > .venv\Scripts\activate 109 | 110 | O nosso pronto da shell mudará para mostrar o nome do ambiente ativado. 111 | 112 | 113 | Instalação da Flask 114 | ---------------------- 115 | 116 | Dentro do ambiente ativado, usamos o seguinte comando para instalar a Flask: 117 | 118 | .. code-block:: sh 119 | 120 | $ pip install Flask 121 | 122 | A Flask agora está instalada. Consulte a :doc:`/quickstart` ou siga para a :doc:`Perspetiva geral da documentação `. 123 | -------------------------------------------------------------------------------- /docs/license.rst: -------------------------------------------------------------------------------- 1 | BSD-3-Clause License 2 | ==================== 3 | 4 | .. include:: ../LICENSE.rst 5 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/patterns/appfactories.rst: -------------------------------------------------------------------------------- 1 | Application Factories 2 | ===================== 3 | 4 | If you are already using packages and blueprints for your application 5 | (:doc:`/blueprints`) there are a couple of really nice ways to further improve 6 | the experience. A common pattern is creating the application object when 7 | the blueprint is imported. But if you move the creation of this object 8 | into a function, you can then create multiple instances of this app later. 9 | 10 | So why would you want to do this? 11 | 12 | 1. Testing. You can have instances of the application with different 13 | settings to test every case. 14 | 2. Multiple instances. Imagine you want to run different versions of the 15 | same application. Of course you could have multiple instances with 16 | different configs set up in your webserver, but if you use factories, 17 | you can have multiple instances of the same application running in the 18 | same application process which can be handy. 19 | 20 | So how would you then actually implement that? 21 | 22 | Basic Factories 23 | --------------- 24 | 25 | The idea is to set up the application in a function. Like this:: 26 | 27 | def create_app(config_filename): 28 | app = Flask(__name__) 29 | app.config.from_pyfile(config_filename) 30 | 31 | from yourapplication.model import db 32 | db.init_app(app) 33 | 34 | from yourapplication.views.admin import admin 35 | from yourapplication.views.frontend import frontend 36 | app.register_blueprint(admin) 37 | app.register_blueprint(frontend) 38 | 39 | return app 40 | 41 | The downside is that you cannot use the application object in the blueprints 42 | at import time. You can however use it from within a request. How do you 43 | get access to the application with the config? Use 44 | :data:`~flask.current_app`:: 45 | 46 | from flask import current_app, Blueprint, render_template 47 | admin = Blueprint('admin', __name__, url_prefix='/admin') 48 | 49 | @admin.route('/') 50 | def index(): 51 | return render_template(current_app.config['INDEX_TEMPLATE']) 52 | 53 | Here we look up the name of a template in the config. 54 | 55 | Factories & Extensions 56 | ---------------------- 57 | 58 | It's preferable to create your extensions and app factories so that the 59 | extension object does not initially get bound to the application. 60 | 61 | Using `Flask-SQLAlchemy `_, 62 | as an example, you should not do something along those lines:: 63 | 64 | def create_app(config_filename): 65 | app = Flask(__name__) 66 | app.config.from_pyfile(config_filename) 67 | 68 | db = SQLAlchemy(app) 69 | 70 | But, rather, in model.py (or equivalent):: 71 | 72 | db = SQLAlchemy() 73 | 74 | and in your application.py (or equivalent):: 75 | 76 | def create_app(config_filename): 77 | app = Flask(__name__) 78 | app.config.from_pyfile(config_filename) 79 | 80 | from yourapplication.model import db 81 | db.init_app(app) 82 | 83 | Using this design pattern, no application-specific state is stored on the 84 | extension object, so one extension object can be used for multiple apps. 85 | For more information about the design of extensions refer to :doc:`/extensiondev`. 86 | 87 | Using Applications 88 | ------------------ 89 | 90 | To run such an application, you can use the :command:`flask` command: 91 | 92 | .. code-block:: text 93 | 94 | $ flask --app hello run 95 | 96 | Flask will automatically detect the factory if it is named 97 | ``create_app`` or ``make_app`` in ``hello``. You can also pass arguments 98 | to the factory like this: 99 | 100 | .. code-block:: text 101 | 102 | $ flask --app hello:create_app(local_auth=True) run 103 | 104 | Then the ``create_app`` factory in ``myapp`` is called with the keyword 105 | argument ``local_auth=True``. See :doc:`/cli` for more detail. 106 | 107 | Factory Improvements 108 | -------------------- 109 | 110 | The factory function above is not very clever, but you can improve it. 111 | The following changes are straightforward to implement: 112 | 113 | 1. Make it possible to pass in configuration values for unit tests so that 114 | you don't have to create config files on the filesystem. 115 | 2. Call a function from a blueprint when the application is setting up so 116 | that you have a place to modify attributes of the application (like 117 | hooking in before/after request handlers etc.) 118 | 3. Add in WSGI middlewares when the application is being created if necessary. 119 | -------------------------------------------------------------------------------- /docs/patterns/caching.rst: -------------------------------------------------------------------------------- 1 | Caching 2 | ======= 3 | 4 | When your application runs slow, throw some caches in. Well, at least 5 | it's the easiest way to speed up things. What does a cache do? Say you 6 | have a function that takes some time to complete but the results would 7 | still be good enough if they were 5 minutes old. So then the idea is that 8 | you actually put the result of that calculation into a cache for some 9 | time. 10 | 11 | Flask itself does not provide caching for you, but `Flask-Caching`_, an 12 | extension for Flask does. Flask-Caching supports various backends, and it is 13 | even possible to develop your own caching backend. 14 | 15 | 16 | .. _Flask-Caching: https://flask-caching.readthedocs.io/en/latest/ 17 | -------------------------------------------------------------------------------- /docs/patterns/deferredcallbacks.rst: -------------------------------------------------------------------------------- 1 | Deferred Request Callbacks 2 | ========================== 3 | 4 | One of the design principles of Flask is that response objects are created and 5 | passed down a chain of potential callbacks that can modify them or replace 6 | them. When the request handling starts, there is no response object yet. It is 7 | created as necessary either by a view function or by some other component in 8 | the system. 9 | 10 | What happens if you want to modify the response at a point where the response 11 | does not exist yet? A common example for that would be a 12 | :meth:`~flask.Flask.before_request` callback that wants to set a cookie on the 13 | response object. 14 | 15 | One way is to avoid the situation. Very often that is possible. For instance 16 | you can try to move that logic into a :meth:`~flask.Flask.after_request` 17 | callback instead. However, sometimes moving code there makes it 18 | more complicated or awkward to reason about. 19 | 20 | As an alternative, you can use :func:`~flask.after_this_request` to register 21 | callbacks that will execute after only the current request. This way you can 22 | defer code execution from anywhere in the application, based on the current 23 | request. 24 | 25 | At any time during a request, we can register a function to be called at the 26 | end of the request. For example you can remember the current language of the 27 | user in a cookie in a :meth:`~flask.Flask.before_request` callback:: 28 | 29 | from flask import request, after_this_request 30 | 31 | @app.before_request 32 | def detect_user_language(): 33 | language = request.cookies.get('user_lang') 34 | 35 | if language is None: 36 | language = guess_language_from_request() 37 | 38 | # when the response exists, set a cookie with the language 39 | @after_this_request 40 | def remember_language(response): 41 | response.set_cookie('user_lang', language) 42 | return response 43 | 44 | g.language = language 45 | -------------------------------------------------------------------------------- /docs/patterns/favicon.rst: -------------------------------------------------------------------------------- 1 | Adding a favicon 2 | ================ 3 | 4 | A "favicon" is an icon used by browsers for tabs and bookmarks. This helps 5 | to distinguish your website and to give it a unique brand. 6 | 7 | A common question is how to add a favicon to a Flask application. First, of 8 | course, you need an icon. It should be 16 × 16 pixels and in the ICO file 9 | format. This is not a requirement but a de-facto standard supported by all 10 | relevant browsers. Put the icon in your static directory as 11 | :file:`favicon.ico`. 12 | 13 | Now, to get browsers to find your icon, the correct way is to add a link 14 | tag in your HTML. So, for example: 15 | 16 | .. sourcecode:: html+jinja 17 | 18 | 19 | 20 | That's all you need for most browsers, however some really old ones do not 21 | support this standard. The old de-facto standard is to serve this file, 22 | with this name, at the website root. If your application is not mounted at 23 | the root path of the domain you either need to configure the web server to 24 | serve the icon at the root or if you can't do that you're out of luck. If 25 | however your application is the root you can simply route a redirect:: 26 | 27 | app.add_url_rule('/favicon.ico', 28 | redirect_to=url_for('static', filename='favicon.ico')) 29 | 30 | If you want to save the extra redirect request you can also write a view 31 | using :func:`~flask.send_from_directory`:: 32 | 33 | import os 34 | from flask import send_from_directory 35 | 36 | @app.route('/favicon.ico') 37 | def favicon(): 38 | return send_from_directory(os.path.join(app.root_path, 'static'), 39 | 'favicon.ico', mimetype='image/vnd.microsoft.icon') 40 | 41 | We can leave out the explicit mimetype and it will be guessed, but we may 42 | as well specify it to avoid the extra guessing, as it will always be the 43 | same. 44 | 45 | The above will serve the icon via your application and if possible it's 46 | better to configure your dedicated web server to serve it; refer to the 47 | web server's documentation. 48 | 49 | See also 50 | -------- 51 | 52 | * The `Favicon `_ article on 53 | Wikipedia 54 | -------------------------------------------------------------------------------- /docs/patterns/index.rst: -------------------------------------------------------------------------------- 1 | Patterns for Flask 2 | ================== 3 | 4 | Certain features and interactions are common enough that you will find 5 | them in most web applications. For example, many applications use a 6 | relational database and user authentication. They will open a database 7 | connection at the beginning of the request and get the information for 8 | the logged in user. At the end of the request, the database connection 9 | is closed. 10 | 11 | These types of patterns may be a bit outside the scope of Flask itself, 12 | but Flask makes it easy to implement them. Some common patterns are 13 | collected in the following pages. 14 | 15 | .. toctree:: 16 | :maxdepth: 2 17 | 18 | packages 19 | appfactories 20 | appdispatch 21 | urlprocessors 22 | sqlite3 23 | sqlalchemy 24 | fileuploads 25 | caching 26 | viewdecorators 27 | wtforms 28 | templateinheritance 29 | flashing 30 | javascript 31 | lazyloading 32 | mongoengine 33 | favicon 34 | streaming 35 | deferredcallbacks 36 | methodoverrides 37 | requestchecksum 38 | celery 39 | subclassing 40 | singlepageapplications 41 | -------------------------------------------------------------------------------- /docs/patterns/jquery.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | AJAX with jQuery 4 | ================ 5 | 6 | Obsolete, see :doc:`/patterns/javascript` instead. 7 | -------------------------------------------------------------------------------- /docs/patterns/lazyloading.rst: -------------------------------------------------------------------------------- 1 | Lazily Loading Views 2 | ==================== 3 | 4 | Flask is usually used with the decorators. Decorators are simple and you 5 | have the URL right next to the function that is called for that specific 6 | URL. However there is a downside to this approach: it means all your code 7 | that uses decorators has to be imported upfront or Flask will never 8 | actually find your function. 9 | 10 | This can be a problem if your application has to import quick. It might 11 | have to do that on systems like Google's App Engine or other systems. So 12 | if you suddenly notice that your application outgrows this approach you 13 | can fall back to a centralized URL mapping. 14 | 15 | The system that enables having a central URL map is the 16 | :meth:`~flask.Flask.add_url_rule` function. Instead of using decorators, 17 | you have a file that sets up the application with all URLs. 18 | 19 | Converting to Centralized URL Map 20 | --------------------------------- 21 | 22 | Imagine the current application looks somewhat like this:: 23 | 24 | from flask import Flask 25 | app = Flask(__name__) 26 | 27 | @app.route('/') 28 | def index(): 29 | pass 30 | 31 | @app.route('/user/') 32 | def user(username): 33 | pass 34 | 35 | Then, with the centralized approach you would have one file with the views 36 | (:file:`views.py`) but without any decorator:: 37 | 38 | def index(): 39 | pass 40 | 41 | def user(username): 42 | pass 43 | 44 | And then a file that sets up an application which maps the functions to 45 | URLs:: 46 | 47 | from flask import Flask 48 | from yourapplication import views 49 | app = Flask(__name__) 50 | app.add_url_rule('/', view_func=views.index) 51 | app.add_url_rule('/user/', view_func=views.user) 52 | 53 | Loading Late 54 | ------------ 55 | 56 | So far we only split up the views and the routing, but the module is still 57 | loaded upfront. The trick is to actually load the view function as needed. 58 | This can be accomplished with a helper class that behaves just like a 59 | function but internally imports the real function on first use:: 60 | 61 | from werkzeug.utils import import_string, cached_property 62 | 63 | class LazyView(object): 64 | 65 | def __init__(self, import_name): 66 | self.__module__, self.__name__ = import_name.rsplit('.', 1) 67 | self.import_name = import_name 68 | 69 | @cached_property 70 | def view(self): 71 | return import_string(self.import_name) 72 | 73 | def __call__(self, *args, **kwargs): 74 | return self.view(*args, **kwargs) 75 | 76 | What's important here is is that `__module__` and `__name__` are properly 77 | set. This is used by Flask internally to figure out how to name the 78 | URL rules in case you don't provide a name for the rule yourself. 79 | 80 | Then you can define your central place to combine the views like this:: 81 | 82 | from flask import Flask 83 | from yourapplication.helpers import LazyView 84 | app = Flask(__name__) 85 | app.add_url_rule('/', 86 | view_func=LazyView('yourapplication.views.index')) 87 | app.add_url_rule('/user/', 88 | view_func=LazyView('yourapplication.views.user')) 89 | 90 | You can further optimize this in terms of amount of keystrokes needed to 91 | write this by having a function that calls into 92 | :meth:`~flask.Flask.add_url_rule` by prefixing a string with the project 93 | name and a dot, and by wrapping `view_func` in a `LazyView` as needed. :: 94 | 95 | def url(import_name, url_rules=[], **options): 96 | view = LazyView(f"yourapplication.{import_name}") 97 | for url_rule in url_rules: 98 | app.add_url_rule(url_rule, view_func=view, **options) 99 | 100 | # add a single route to the index view 101 | url('views.index', ['/']) 102 | 103 | # add two routes to a single function endpoint 104 | url_rules = ['/user/','/user/'] 105 | url('views.user', url_rules) 106 | 107 | One thing to keep in mind is that before and after request handlers have 108 | to be in a file that is imported upfront to work properly on the first 109 | request. The same goes for any kind of remaining decorator. 110 | -------------------------------------------------------------------------------- /docs/patterns/methodoverrides.rst: -------------------------------------------------------------------------------- 1 | Adding HTTP Method Overrides 2 | ============================ 3 | 4 | Some HTTP proxies do not support arbitrary HTTP methods or newer HTTP 5 | methods (such as PATCH). In that case it's possible to "proxy" HTTP 6 | methods through another HTTP method in total violation of the protocol. 7 | 8 | The way this works is by letting the client do an HTTP POST request and 9 | set the ``X-HTTP-Method-Override`` header. Then the method is replaced 10 | with the header value before being passed to Flask. 11 | 12 | This can be accomplished with an HTTP middleware:: 13 | 14 | class HTTPMethodOverrideMiddleware(object): 15 | allowed_methods = frozenset([ 16 | 'GET', 17 | 'HEAD', 18 | 'POST', 19 | 'DELETE', 20 | 'PUT', 21 | 'PATCH', 22 | 'OPTIONS' 23 | ]) 24 | bodyless_methods = frozenset(['GET', 'HEAD', 'OPTIONS', 'DELETE']) 25 | 26 | def __init__(self, app): 27 | self.app = app 28 | 29 | def __call__(self, environ, start_response): 30 | method = environ.get('HTTP_X_HTTP_METHOD_OVERRIDE', '').upper() 31 | if method in self.allowed_methods: 32 | environ['REQUEST_METHOD'] = method 33 | if method in self.bodyless_methods: 34 | environ['CONTENT_LENGTH'] = '0' 35 | return self.app(environ, start_response) 36 | 37 | To use this with Flask, wrap the app object with the middleware:: 38 | 39 | from flask import Flask 40 | 41 | app = Flask(__name__) 42 | app.wsgi_app = HTTPMethodOverrideMiddleware(app.wsgi_app) 43 | -------------------------------------------------------------------------------- /docs/patterns/mongoengine.rst: -------------------------------------------------------------------------------- 1 | MongoDB with MongoEngine 2 | ======================== 3 | 4 | Using a document database like MongoDB is a common alternative to 5 | relational SQL databases. This pattern shows how to use 6 | `MongoEngine`_, a document mapper library, to integrate with MongoDB. 7 | 8 | A running MongoDB server and `Flask-MongoEngine`_ are required. :: 9 | 10 | pip install flask-mongoengine 11 | 12 | .. _MongoEngine: http://mongoengine.org 13 | .. _Flask-MongoEngine: https://flask-mongoengine.readthedocs.io 14 | 15 | 16 | Configuration 17 | ------------- 18 | 19 | Basic setup can be done by defining ``MONGODB_SETTINGS`` on 20 | ``app.config`` and creating a ``MongoEngine`` instance. :: 21 | 22 | from flask import Flask 23 | from flask_mongoengine import MongoEngine 24 | 25 | app = Flask(__name__) 26 | app.config['MONGODB_SETTINGS'] = { 27 | "db": "myapp", 28 | } 29 | db = MongoEngine(app) 30 | 31 | 32 | Mapping Documents 33 | ----------------- 34 | 35 | To declare a model that represents a Mongo document, create a class that 36 | inherits from ``Document`` and declare each of the fields. :: 37 | 38 | import mongoengine as me 39 | 40 | class Movie(me.Document): 41 | title = me.StringField(required=True) 42 | year = me.IntField() 43 | rated = me.StringField() 44 | director = me.StringField() 45 | actors = me.ListField() 46 | 47 | If the document has nested fields, use ``EmbeddedDocument`` to 48 | defined the fields of the embedded document and 49 | ``EmbeddedDocumentField`` to declare it on the parent document. :: 50 | 51 | class Imdb(me.EmbeddedDocument): 52 | imdb_id = me.StringField() 53 | rating = me.DecimalField() 54 | votes = me.IntField() 55 | 56 | class Movie(me.Document): 57 | ... 58 | imdb = me.EmbeddedDocumentField(Imdb) 59 | 60 | 61 | Creating Data 62 | ------------- 63 | 64 | Instantiate your document class with keyword arguments for the fields. 65 | You can also assign values to the field attributes after instantiation. 66 | Then call ``doc.save()``. :: 67 | 68 | bttf = Movie(title="Back To The Future", year=1985) 69 | bttf.actors = [ 70 | "Michael J. Fox", 71 | "Christopher Lloyd" 72 | ] 73 | bttf.imdb = Imdb(imdb_id="tt0088763", rating=8.5) 74 | bttf.save() 75 | 76 | 77 | Queries 78 | ------- 79 | 80 | Use the class ``objects`` attribute to make queries. A keyword argument 81 | looks for an equal value on the field. :: 82 | 83 | bttf = Movies.objects(title="Back To The Future").get_or_404() 84 | 85 | Query operators may be used by concatenating them with the field name 86 | using a double-underscore. ``objects``, and queries returned by 87 | calling it, are iterable. :: 88 | 89 | some_theron_movie = Movie.objects(actors__in=["Charlize Theron"]).first() 90 | 91 | for recents in Movie.objects(year__gte=2017): 92 | print(recents.title) 93 | 94 | 95 | Documentation 96 | ------------- 97 | 98 | There are many more ways to define and query documents with MongoEngine. 99 | For more information, check out the `official documentation 100 | `_. 101 | 102 | Flask-MongoEngine adds helpful utilities on top of MongoEngine. Check 103 | out their `documentation `_ as well. 104 | -------------------------------------------------------------------------------- /docs/patterns/requestchecksum.rst: -------------------------------------------------------------------------------- 1 | Request Content Checksums 2 | ========================= 3 | 4 | Various pieces of code can consume the request data and preprocess it. 5 | For instance JSON data ends up on the request object already read and 6 | processed, form data ends up there as well but goes through a different 7 | code path. This seems inconvenient when you want to calculate the 8 | checksum of the incoming request data. This is necessary sometimes for 9 | some APIs. 10 | 11 | Fortunately this is however very simple to change by wrapping the input 12 | stream. 13 | 14 | The following example calculates the SHA1 checksum of the incoming data as 15 | it gets read and stores it in the WSGI environment:: 16 | 17 | import hashlib 18 | 19 | class ChecksumCalcStream(object): 20 | 21 | def __init__(self, stream): 22 | self._stream = stream 23 | self._hash = hashlib.sha1() 24 | 25 | def read(self, bytes): 26 | rv = self._stream.read(bytes) 27 | self._hash.update(rv) 28 | return rv 29 | 30 | def readline(self, size_hint): 31 | rv = self._stream.readline(size_hint) 32 | self._hash.update(rv) 33 | return rv 34 | 35 | def generate_checksum(request): 36 | env = request.environ 37 | stream = ChecksumCalcStream(env['wsgi.input']) 38 | env['wsgi.input'] = stream 39 | return stream._hash 40 | 41 | To use this, all you need to do is to hook the calculating stream in 42 | before the request starts consuming data. (Eg: be careful accessing 43 | ``request.form`` or anything of that nature. ``before_request_handlers`` 44 | for instance should be careful not to access it). 45 | 46 | Example usage:: 47 | 48 | @app.route('/special-api', methods=['POST']) 49 | def special_api(): 50 | hash = generate_checksum(request) 51 | # Accessing this parses the input stream 52 | files = request.files 53 | # At this point the hash is fully constructed. 54 | checksum = hash.hexdigest() 55 | return f"Hash was: {checksum}" 56 | -------------------------------------------------------------------------------- /docs/patterns/singlepageapplications.rst: -------------------------------------------------------------------------------- 1 | Single-Page Applications 2 | ======================== 3 | 4 | Flask can be used to serve Single-Page Applications (SPA) by placing static 5 | files produced by your frontend framework in a subfolder inside of your 6 | project. You will also need to create a catch-all endpoint that routes all 7 | requests to your SPA. 8 | 9 | The following example demonstrates how to serve an SPA along with an API:: 10 | 11 | from flask import Flask, jsonify 12 | 13 | app = Flask(__name__, static_folder='app', static_url_path="/app") 14 | 15 | 16 | @app.route("/heartbeat") 17 | def heartbeat(): 18 | return jsonify({"status": "healthy"}) 19 | 20 | 21 | @app.route('/', defaults={'path': ''}) 22 | @app.route('/') 23 | def catch_all(path): 24 | return app.send_static_file("index.html") 25 | -------------------------------------------------------------------------------- /docs/patterns/streaming.rst: -------------------------------------------------------------------------------- 1 | Streaming Contents 2 | ================== 3 | 4 | Sometimes you want to send an enormous amount of data to the client, much 5 | more than you want to keep in memory. When you are generating the data on 6 | the fly though, how do you send that back to the client without the 7 | roundtrip to the filesystem? 8 | 9 | The answer is by using generators and direct responses. 10 | 11 | Basic Usage 12 | ----------- 13 | 14 | This is a basic view function that generates a lot of CSV data on the fly. 15 | The trick is to have an inner function that uses a generator to generate 16 | data and to then invoke that function and pass it to a response object:: 17 | 18 | @app.route('/large.csv') 19 | def generate_large_csv(): 20 | def generate(): 21 | for row in iter_all_rows(): 22 | yield f"{','.join(row)}\n" 23 | return generate(), {"Content-Type": "text/csv"} 24 | 25 | Each ``yield`` expression is directly sent to the browser. Note though 26 | that some WSGI middlewares might break streaming, so be careful there in 27 | debug environments with profilers and other things you might have enabled. 28 | 29 | Streaming from Templates 30 | ------------------------ 31 | 32 | The Jinja2 template engine supports rendering a template piece by 33 | piece, returning an iterator of strings. Flask provides the 34 | :func:`~flask.stream_template` and :func:`~flask.stream_template_string` 35 | functions to make this easier to use. 36 | 37 | .. code-block:: python 38 | 39 | from flask import stream_template 40 | 41 | @app.get("/timeline") 42 | def timeline(): 43 | return stream_template("timeline.html") 44 | 45 | The parts yielded by the render stream tend to match statement blocks in 46 | the template. 47 | 48 | 49 | Streaming with Context 50 | ---------------------- 51 | 52 | The :data:`~flask.request` will not be active while the generator is 53 | running, because the view has already returned at that point. If you try 54 | to access ``request``, you'll get a ``RuntimeError``. 55 | 56 | If your generator function relies on data in ``request``, use the 57 | :func:`~flask.stream_with_context` wrapper. This will keep the request 58 | context active during the generator. 59 | 60 | .. code-block:: python 61 | 62 | from flask import stream_with_context, request 63 | from markupsafe import escape 64 | 65 | @app.route('/stream') 66 | def streamed_response(): 67 | def generate(): 68 | yield '

Hello ' 69 | yield escape(request.args['name']) 70 | yield '!

' 71 | return stream_with_context(generate()) 72 | 73 | It can also be used as a decorator. 74 | 75 | .. code-block:: python 76 | 77 | @stream_with_context 78 | def generate(): 79 | ... 80 | 81 | return generate() 82 | 83 | The :func:`~flask.stream_template` and 84 | :func:`~flask.stream_template_string` functions automatically 85 | use :func:`~flask.stream_with_context` if a request is active. 86 | -------------------------------------------------------------------------------- /docs/patterns/subclassing.rst: -------------------------------------------------------------------------------- 1 | Subclassing Flask 2 | ================= 3 | 4 | The :class:`~flask.Flask` class is designed for subclassing. 5 | 6 | For example, you may want to override how request parameters are handled to preserve their order:: 7 | 8 | from flask import Flask, Request 9 | from werkzeug.datastructures import ImmutableOrderedMultiDict 10 | class MyRequest(Request): 11 | """Request subclass to override request parameter storage""" 12 | parameter_storage_class = ImmutableOrderedMultiDict 13 | class MyFlask(Flask): 14 | """Flask subclass using the custom request class""" 15 | request_class = MyRequest 16 | 17 | This is the recommended approach for overriding or augmenting Flask's internal functionality. 18 | -------------------------------------------------------------------------------- /docs/patterns/templateinheritance.rst: -------------------------------------------------------------------------------- 1 | Template Inheritance 2 | ==================== 3 | 4 | The most powerful part of Jinja is template inheritance. Template inheritance 5 | allows you to build a base "skeleton" template that contains all the common 6 | elements of your site and defines **blocks** that child templates can override. 7 | 8 | Sounds complicated but is very basic. It's easiest to understand it by starting 9 | with an example. 10 | 11 | 12 | Base Template 13 | ------------- 14 | 15 | This template, which we'll call :file:`layout.html`, defines a simple HTML skeleton 16 | document that you might use for a simple two-column page. It's the job of 17 | "child" templates to fill the empty blocks with content: 18 | 19 | .. sourcecode:: html+jinja 20 | 21 | 22 | 23 | 24 | {% block head %} 25 | 26 | {% block title %}{% endblock %} - My Webpage 27 | {% endblock %} 28 | 29 | 30 |
{% block content %}{% endblock %}
31 | 36 | 37 | 38 | 39 | In this example, the ``{% block %}`` tags define four blocks that child templates 40 | can fill in. All the `block` tag does is tell the template engine that a 41 | child template may override those portions of the template. 42 | 43 | Child Template 44 | -------------- 45 | 46 | A child template might look like this: 47 | 48 | .. sourcecode:: html+jinja 49 | 50 | {% extends "layout.html" %} 51 | {% block title %}Index{% endblock %} 52 | {% block head %} 53 | {{ super() }} 54 | 57 | {% endblock %} 58 | {% block content %} 59 |

Index

60 |

61 | Welcome on my awesome homepage. 62 | {% endblock %} 63 | 64 | The ``{% extends %}`` tag is the key here. It tells the template engine that 65 | this template "extends" another template. When the template system evaluates 66 | this template, first it locates the parent. The extends tag must be the 67 | first tag in the template. To render the contents of a block defined in 68 | the parent template, use ``{{ super() }}``. 69 | -------------------------------------------------------------------------------- /docs/server.rst: -------------------------------------------------------------------------------- 1 | .. currentmodule:: flask 2 | 3 | Development Server 4 | ================== 5 | 6 | Flask provides a ``run`` command to run the application with a development server. In 7 | debug mode, this server provides an interactive debugger and will reload when code is 8 | changed. 9 | 10 | .. warning:: 11 | 12 | Do not use the development server when deploying to production. It 13 | is intended for use only during local development. It is not 14 | designed to be particularly efficient, stable, or secure. 15 | 16 | See :doc:`/deploying/index` for deployment options. 17 | 18 | Command Line 19 | ------------ 20 | 21 | The ``flask run`` CLI command is the recommended way to run the development server. Use 22 | the ``--app`` option to point to your application, and the ``--debug`` option to enable 23 | debug mode. 24 | 25 | .. code-block:: text 26 | 27 | $ flask --app hello run --debug 28 | 29 | This enables debug mode, including the interactive debugger and reloader, and then 30 | starts the server on http://localhost:5000/. Use ``flask run --help`` to see the 31 | available options, and :doc:`/cli` for detailed instructions about configuring and using 32 | the CLI. 33 | 34 | 35 | .. _address-already-in-use: 36 | 37 | Address already in use 38 | ~~~~~~~~~~~~~~~~~~~~~~ 39 | 40 | If another program is already using port 5000, you'll see an ``OSError`` 41 | when the server tries to start. It may have one of the following 42 | messages: 43 | 44 | - ``OSError: [Errno 98] Address already in use`` 45 | - ``OSError: [WinError 10013] An attempt was made to access a socket 46 | in a way forbidden by its access permissions`` 47 | 48 | Either identify and stop the other program, or use 49 | ``flask run --port 5001`` to pick a different port. 50 | 51 | You can use ``netstat`` or ``lsof`` to identify what process id is using 52 | a port, then use other operating system tools stop that process. The 53 | following example shows that process id 6847 is using port 5000. 54 | 55 | .. tabs:: 56 | 57 | .. tab:: ``netstat`` (Linux) 58 | 59 | .. code-block:: text 60 | 61 | $ netstat -nlp | grep 5000 62 | tcp 0 0 127.0.0.1:5000 0.0.0.0:* LISTEN 6847/python 63 | 64 | .. tab:: ``lsof`` (macOS / Linux) 65 | 66 | .. code-block:: text 67 | 68 | $ lsof -P -i :5000 69 | Python 6847 IPv4 TCP localhost:5000 (LISTEN) 70 | 71 | .. tab:: ``netstat`` (Windows) 72 | 73 | .. code-block:: text 74 | 75 | > netstat -ano | findstr 5000 76 | TCP 127.0.0.1:5000 0.0.0.0:0 LISTENING 6847 77 | 78 | macOS Monterey and later automatically starts a service that uses port 79 | 5000. To disable the service, go to System Preferences, Sharing, and 80 | disable "AirPlay Receiver". 81 | 82 | 83 | Deferred Errors on Reload 84 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 85 | 86 | When using the ``flask run`` command with the reloader, the server will 87 | continue to run even if you introduce syntax errors or other 88 | initialization errors into the code. Accessing the site will show the 89 | interactive debugger for the error, rather than crashing the server. 90 | 91 | If a syntax error is already present when calling ``flask run``, it will 92 | fail immediately and show the traceback rather than waiting until the 93 | site is accessed. This is intended to make errors more visible initially 94 | while still allowing the server to handle errors on reload. 95 | 96 | 97 | In Code 98 | ------- 99 | 100 | The development server can also be started from Python with the :meth:`Flask.run` 101 | method. This method takes arguments similar to the CLI options to control the server. 102 | The main difference from the CLI command is that the server will crash if there are 103 | errors when reloading. ``debug=True`` can be passed to enable debug mode. 104 | 105 | Place the call in a main block, otherwise it will interfere when trying to import and 106 | run the application with a production server later. 107 | 108 | .. code-block:: python 109 | 110 | if __name__ == "__main__": 111 | app.run(debug=True) 112 | 113 | .. code-block:: text 114 | 115 | $ python hello.py 116 | -------------------------------------------------------------------------------- /docs/shell.rst: -------------------------------------------------------------------------------- 1 | Working with the Shell 2 | ====================== 3 | 4 | .. versionadded:: 0.3 5 | 6 | One of the reasons everybody loves Python is the interactive shell. It 7 | basically allows you to execute Python commands in real time and 8 | immediately get results back. Flask itself does not come with an 9 | interactive shell, because it does not require any specific setup upfront, 10 | just import your application and start playing around. 11 | 12 | There are however some handy helpers to make playing around in the shell a 13 | more pleasant experience. The main issue with interactive console 14 | sessions is that you're not triggering a request like a browser does which 15 | means that :data:`~flask.g`, :data:`~flask.request` and others are not 16 | available. But the code you want to test might depend on them, so what 17 | can you do? 18 | 19 | This is where some helper functions come in handy. Keep in mind however 20 | that these functions are not only there for interactive shell usage, but 21 | also for unit testing and other situations that require a faked request 22 | context. 23 | 24 | Generally it's recommended that you read :doc:`reqcontext` first. 25 | 26 | Command Line Interface 27 | ---------------------- 28 | 29 | Starting with Flask 0.11 the recommended way to work with the shell is the 30 | ``flask shell`` command which does a lot of this automatically for you. 31 | For instance the shell is automatically initialized with a loaded 32 | application context. 33 | 34 | For more information see :doc:`/cli`. 35 | 36 | Creating a Request Context 37 | -------------------------- 38 | 39 | The easiest way to create a proper request context from the shell is by 40 | using the :attr:`~flask.Flask.test_request_context` method which creates 41 | us a :class:`~flask.ctx.RequestContext`: 42 | 43 | >>> ctx = app.test_request_context() 44 | 45 | Normally you would use the ``with`` statement to make this request object 46 | active, but in the shell it's easier to use the 47 | :meth:`~flask.ctx.RequestContext.push` and 48 | :meth:`~flask.ctx.RequestContext.pop` methods by hand: 49 | 50 | >>> ctx.push() 51 | 52 | From that point onwards you can work with the request object until you 53 | call `pop`: 54 | 55 | >>> ctx.pop() 56 | 57 | Firing Before/After Request 58 | --------------------------- 59 | 60 | By just creating a request context, you still don't have run the code that 61 | is normally run before a request. This might result in your database 62 | being unavailable if you are connecting to the database in a 63 | before-request callback or the current user not being stored on the 64 | :data:`~flask.g` object etc. 65 | 66 | This however can easily be done yourself. Just call 67 | :meth:`~flask.Flask.preprocess_request`: 68 | 69 | >>> ctx = app.test_request_context() 70 | >>> ctx.push() 71 | >>> app.preprocess_request() 72 | 73 | Keep in mind that the :meth:`~flask.Flask.preprocess_request` function 74 | might return a response object, in that case just ignore it. 75 | 76 | To shutdown a request, you need to trick a bit before the after request 77 | functions (triggered by :meth:`~flask.Flask.process_response`) operate on 78 | a response object: 79 | 80 | >>> app.process_response(app.response_class()) 81 | 82 | >>> ctx.pop() 83 | 84 | The functions registered as :meth:`~flask.Flask.teardown_request` are 85 | automatically called when the context is popped. So this is the perfect 86 | place to automatically tear down resources that were needed by the request 87 | context (such as database connections). 88 | 89 | 90 | Further Improving the Shell Experience 91 | -------------------------------------- 92 | 93 | If you like the idea of experimenting in a shell, create yourself a module 94 | with stuff you want to star import into your interactive session. There 95 | you could also define some more helper methods for common things such as 96 | initializing the database, dropping tables etc. 97 | 98 | Just put them into a module (like `shelltools`) and import from there: 99 | 100 | >>> from shelltools import * 101 | -------------------------------------------------------------------------------- /docs/tutorial/deploy.rst: -------------------------------------------------------------------------------- 1 | Deploy to Production 2 | ==================== 3 | 4 | This part of the tutorial assumes you have a server that you want to 5 | deploy your application to. It gives an overview of how to create the 6 | distribution file and install it, but won't go into specifics about 7 | what server or software to use. You can set up a new environment on your 8 | development computer to try out the instructions below, but probably 9 | shouldn't use it for hosting a real public application. See 10 | :doc:`/deploying/index` for a list of many different ways to host your 11 | application. 12 | 13 | 14 | Build and Install 15 | ----------------- 16 | 17 | When you want to deploy your application elsewhere, you build a *wheel* 18 | (``.whl``) file. Install and use the ``build`` tool to do this. 19 | 20 | .. code-block:: none 21 | 22 | $ pip install build 23 | $ python -m build --wheel 24 | 25 | You can find the file in ``dist/flaskr-1.0.0-py3-none-any.whl``. The 26 | file name is in the format of {project name}-{version}-{python tag} 27 | -{abi tag}-{platform tag}. 28 | 29 | Copy this file to another machine, 30 | :ref:`set up a new virtualenv `, then install the 31 | file with ``pip``. 32 | 33 | .. code-block:: none 34 | 35 | $ pip install flaskr-1.0.0-py3-none-any.whl 36 | 37 | Pip will install your project along with its dependencies. 38 | 39 | Since this is a different machine, you need to run ``init-db`` again to 40 | create the database in the instance folder. 41 | 42 | .. code-block:: text 43 | 44 | $ flask --app flaskr init-db 45 | 46 | When Flask detects that it's installed (not in editable mode), it uses 47 | a different directory for the instance folder. You can find it at 48 | ``.venv/var/flaskr-instance`` instead. 49 | 50 | 51 | Configure the Secret Key 52 | ------------------------ 53 | 54 | In the beginning of the tutorial that you gave a default value for 55 | :data:`SECRET_KEY`. This should be changed to some random bytes in 56 | production. Otherwise, attackers could use the public ``'dev'`` key to 57 | modify the session cookie, or anything else that uses the secret key. 58 | 59 | You can use the following command to output a random secret key: 60 | 61 | .. code-block:: none 62 | 63 | $ python -c 'import secrets; print(secrets.token_hex())' 64 | 65 | '192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf' 66 | 67 | Create the ``config.py`` file in the instance folder, which the factory 68 | will read from if it exists. Copy the generated value into it. 69 | 70 | .. code-block:: python 71 | :caption: ``.venv/var/flaskr-instance/config.py`` 72 | 73 | SECRET_KEY = '192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf' 74 | 75 | You can also set any other necessary configuration here, although 76 | ``SECRET_KEY`` is the only one needed for Flaskr. 77 | 78 | 79 | Run with a Production Server 80 | ---------------------------- 81 | 82 | When running publicly rather than in development, you should not use the 83 | built-in development server (``flask run``). The development server is 84 | provided by Werkzeug for convenience, but is not designed to be 85 | particularly efficient, stable, or secure. 86 | 87 | Instead, use a production WSGI server. For example, to use `Waitress`_, 88 | first install it in the virtual environment: 89 | 90 | .. code-block:: none 91 | 92 | $ pip install waitress 93 | 94 | You need to tell Waitress about your application, but it doesn't use 95 | ``--app`` like ``flask run`` does. You need to tell it to import and 96 | call the application factory to get an application object. 97 | 98 | .. code-block:: none 99 | 100 | $ waitress-serve --call 'flaskr:create_app' 101 | 102 | Serving on http://0.0.0.0:8080 103 | 104 | See :doc:`/deploying/index` for a list of many different ways to host 105 | your application. Waitress is just an example, chosen for the tutorial 106 | because it supports both Windows and Linux. There are many more WSGI 107 | servers and deployment options that you may choose for your project. 108 | 109 | .. _Waitress: https://docs.pylonsproject.org/projects/waitress/en/stable/ 110 | 111 | Continue to :doc:`next`. 112 | -------------------------------------------------------------------------------- /docs/tutorial/flaskr_edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nazarepiedady/flask-docs-pt/c5b543b639a661befebe8aa1afda4327642e5b8f/docs/tutorial/flaskr_edit.png -------------------------------------------------------------------------------- /docs/tutorial/flaskr_index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nazarepiedady/flask-docs-pt/c5b543b639a661befebe8aa1afda4327642e5b8f/docs/tutorial/flaskr_index.png -------------------------------------------------------------------------------- /docs/tutorial/flaskr_login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nazarepiedady/flask-docs-pt/c5b543b639a661befebe8aa1afda4327642e5b8f/docs/tutorial/flaskr_login.png -------------------------------------------------------------------------------- /docs/tutorial/index.rst: -------------------------------------------------------------------------------- 1 | Tutorial 2 | ======== 3 | 4 | .. toctree:: 5 | :caption: Contents: 6 | :maxdepth: 1 7 | 8 | layout 9 | factory 10 | database 11 | views 12 | templates 13 | static 14 | blog 15 | install 16 | tests 17 | deploy 18 | next 19 | 20 | This tutorial will walk you through creating a basic blog application 21 | called Flaskr. Users will be able to register, log in, create posts, 22 | and edit or delete their own posts. You will be able to package and 23 | install the application on other computers. 24 | 25 | .. image:: flaskr_index.png 26 | :align: center 27 | :class: screenshot 28 | :alt: screenshot of index page 29 | 30 | It's assumed that you're already familiar with Python. The `official 31 | tutorial`_ in the Python docs is a great way to learn or review first. 32 | 33 | .. _official tutorial: https://docs.python.org/3/tutorial/ 34 | 35 | While it's designed to give a good starting point, the tutorial doesn't 36 | cover all of Flask's features. Check out the :doc:`/quickstart` for an 37 | overview of what Flask can do, then dive into the docs to find out more. 38 | The tutorial only uses what's provided by Flask and Python. In another 39 | project, you might decide to use :doc:`/extensions` or other libraries 40 | to make some tasks simpler. 41 | 42 | .. image:: flaskr_login.png 43 | :align: center 44 | :class: screenshot 45 | :alt: screenshot of login page 46 | 47 | Flask is flexible. It doesn't require you to use any particular project 48 | or code layout. However, when first starting, it's helpful to use a more 49 | structured approach. This means that the tutorial will require a bit of 50 | boilerplate up front, but it's done to avoid many common pitfalls that 51 | new developers encounter, and it creates a project that's easy to expand 52 | on. Once you become more comfortable with Flask, you can step out of 53 | this structure and take full advantage of Flask's flexibility. 54 | 55 | .. image:: flaskr_edit.png 56 | :align: center 57 | :class: screenshot 58 | :alt: screenshot of edit page 59 | 60 | :gh:`The tutorial project is available as an example in the Flask 61 | repository `, if you want to compare your project 62 | with the final product as you follow the tutorial. 63 | 64 | Continue to :doc:`layout`. 65 | -------------------------------------------------------------------------------- /docs/tutorial/install.rst: -------------------------------------------------------------------------------- 1 | Make the Project Installable 2 | ============================ 3 | 4 | Making your project installable means that you can build a *wheel* file and install that 5 | in another environment, just like you installed Flask in your project's environment. 6 | This makes deploying your project the same as installing any other library, so you're 7 | using all the standard Python tools to manage everything. 8 | 9 | Installing also comes with other benefits that might not be obvious from 10 | the tutorial or as a new Python user, including: 11 | 12 | * Currently, Python and Flask understand how to use the ``flaskr`` 13 | package only because you're running from your project's directory. 14 | Installing means you can import it no matter where you run from. 15 | 16 | * You can manage your project's dependencies just like other packages 17 | do, so ``pip install yourproject.whl`` installs them. 18 | 19 | * Test tools can isolate your test environment from your development 20 | environment. 21 | 22 | .. note:: 23 | This is being introduced late in the tutorial, but in your future 24 | projects you should always start with this. 25 | 26 | 27 | Describe the Project 28 | -------------------- 29 | 30 | The ``pyproject.toml`` file describes your project and how to build it. 31 | 32 | .. code-block:: toml 33 | :caption: ``pyproject.toml`` 34 | 35 | [project] 36 | name = "flaskr" 37 | version = "1.0.0" 38 | description = "The basic blog app built in the Flask tutorial." 39 | dependencies = [ 40 | "flask", 41 | ] 42 | 43 | [build-system] 44 | requires = ["flit_core<4"] 45 | build-backend = "flit_core.buildapi" 46 | 47 | See the official `Packaging tutorial `_ for more 48 | explanation of the files and options used. 49 | 50 | .. _packaging tutorial: https://packaging.python.org/tutorials/packaging-projects/ 51 | 52 | 53 | Install the Project 54 | ------------------- 55 | 56 | Use ``pip`` to install your project in the virtual environment. 57 | 58 | .. code-block:: none 59 | 60 | $ pip install -e . 61 | 62 | This tells pip to find ``pyproject.toml`` in the current directory and install the 63 | project in *editable* or *development* mode. Editable mode means that as you make 64 | changes to your local code, you'll only need to re-install if you change the metadata 65 | about the project, such as its dependencies. 66 | 67 | You can observe that the project is now installed with ``pip list``. 68 | 69 | .. code-block:: none 70 | 71 | $ pip list 72 | 73 | Package Version Location 74 | -------------- --------- ---------------------------------- 75 | click 6.7 76 | Flask 1.0 77 | flaskr 1.0.0 /home/user/Projects/flask-tutorial 78 | itsdangerous 0.24 79 | Jinja2 2.10 80 | MarkupSafe 1.0 81 | pip 9.0.3 82 | Werkzeug 0.14.1 83 | 84 | Nothing changes from how you've been running your project so far. 85 | ``--app`` is still set to ``flaskr`` and ``flask run`` still runs 86 | the application, but you can call it from anywhere, not just the 87 | ``flask-tutorial`` directory. 88 | 89 | Continue to :doc:`tests`. 90 | -------------------------------------------------------------------------------- /docs/tutorial/layout.rst: -------------------------------------------------------------------------------- 1 | Project Layout 2 | ============== 3 | 4 | Create a project directory and enter it: 5 | 6 | .. code-block:: none 7 | 8 | $ mkdir flask-tutorial 9 | $ cd flask-tutorial 10 | 11 | Then follow the :doc:`installation instructions ` to set 12 | up a Python virtual environment and install Flask for your project. 13 | 14 | The tutorial will assume you're working from the ``flask-tutorial`` 15 | directory from now on. The file names at the top of each code block are 16 | relative to this directory. 17 | 18 | ---- 19 | 20 | A Flask application can be as simple as a single file. 21 | 22 | .. code-block:: python 23 | :caption: ``hello.py`` 24 | 25 | from flask import Flask 26 | 27 | app = Flask(__name__) 28 | 29 | 30 | @app.route('/') 31 | def hello(): 32 | return 'Hello, World!' 33 | 34 | However, as a project gets bigger, it becomes overwhelming to keep all 35 | the code in one file. Python projects use *packages* to organize code 36 | into multiple modules that can be imported where needed, and the 37 | tutorial will do this as well. 38 | 39 | The project directory will contain: 40 | 41 | * ``flaskr/``, a Python package containing your application code and 42 | files. 43 | * ``tests/``, a directory containing test modules. 44 | * ``.venv/``, a Python virtual environment where Flask and other 45 | dependencies are installed. 46 | * Installation files telling Python how to install your project. 47 | * Version control config, such as `git`_. You should make a habit of 48 | using some type of version control for all your projects, no matter 49 | the size. 50 | * Any other project files you might add in the future. 51 | 52 | .. _git: https://git-scm.com/ 53 | 54 | By the end, your project layout will look like this: 55 | 56 | .. code-block:: none 57 | 58 | /home/user/Projects/flask-tutorial 59 | ├── flaskr/ 60 | │ ├── __init__.py 61 | │ ├── db.py 62 | │ ├── schema.sql 63 | │ ├── auth.py 64 | │ ├── blog.py 65 | │ ├── templates/ 66 | │ │ ├── base.html 67 | │ │ ├── auth/ 68 | │ │ │ ├── login.html 69 | │ │ │ └── register.html 70 | │ │ └── blog/ 71 | │ │ ├── create.html 72 | │ │ ├── index.html 73 | │ │ └── update.html 74 | │ └── static/ 75 | │ └── style.css 76 | ├── tests/ 77 | │ ├── conftest.py 78 | │ ├── data.sql 79 | │ ├── test_factory.py 80 | │ ├── test_db.py 81 | │ ├── test_auth.py 82 | │ └── test_blog.py 83 | ├── .venv/ 84 | ├── pyproject.toml 85 | └── MANIFEST.in 86 | 87 | If you're using version control, the following files that are generated 88 | while running your project should be ignored. There may be other files 89 | based on the editor you use. In general, ignore files that you didn't 90 | write. For example, with git: 91 | 92 | .. code-block:: none 93 | :caption: ``.gitignore`` 94 | 95 | .venv/ 96 | 97 | *.pyc 98 | __pycache__/ 99 | 100 | instance/ 101 | 102 | .pytest_cache/ 103 | .coverage 104 | htmlcov/ 105 | 106 | dist/ 107 | build/ 108 | *.egg-info/ 109 | 110 | Continue to :doc:`factory`. 111 | -------------------------------------------------------------------------------- /docs/tutorial/next.rst: -------------------------------------------------------------------------------- 1 | Keep Developing! 2 | ================ 3 | 4 | You've learned about quite a few Flask and Python concepts throughout 5 | the tutorial. Go back and review the tutorial and compare your code with 6 | the steps you took to get there. Compare your project to the 7 | :gh:`example project `, which might look a bit 8 | different due to the step-by-step nature of the tutorial. 9 | 10 | There's a lot more to Flask than what you've seen so far. Even so, 11 | you're now equipped to start developing your own web applications. Check 12 | out the :doc:`/quickstart` for an overview of what Flask can do, then 13 | dive into the docs to keep learning. Flask uses `Jinja`_, `Click`_, 14 | `Werkzeug`_, and `ItsDangerous`_ behind the scenes, and they all have 15 | their own documentation too. You'll also be interested in 16 | :doc:`/extensions` which make tasks like working with the database or 17 | validating form data easier and more powerful. 18 | 19 | If you want to keep developing your Flaskr project, here are some ideas 20 | for what to try next: 21 | 22 | * A detail view to show a single post. Click a post's title to go to 23 | its page. 24 | * Like / unlike a post. 25 | * Comments. 26 | * Tags. Clicking a tag shows all the posts with that tag. 27 | * A search box that filters the index page by name. 28 | * Paged display. Only show 5 posts per page. 29 | * Upload an image to go along with a post. 30 | * Format posts using Markdown. 31 | * An RSS feed of new posts. 32 | 33 | Have fun and make awesome applications! 34 | 35 | .. _Jinja: https://palletsprojects.com/p/jinja/ 36 | .. _Click: https://palletsprojects.com/p/click/ 37 | .. _Werkzeug: https://palletsprojects.com/p/werkzeug/ 38 | .. _ItsDangerous: https://palletsprojects.com/p/itsdangerous/ 39 | -------------------------------------------------------------------------------- /docs/tutorial/static.rst: -------------------------------------------------------------------------------- 1 | Static Files 2 | ============ 3 | 4 | The authentication views and templates work, but they look very plain 5 | right now. Some `CSS`_ can be added to add style to the HTML layout you 6 | constructed. The style won't change, so it's a *static* file rather than 7 | a template. 8 | 9 | Flask automatically adds a ``static`` view that takes a path relative 10 | to the ``flaskr/static`` directory and serves it. The ``base.html`` 11 | template already has a link to the ``style.css`` file: 12 | 13 | .. code-block:: html+jinja 14 | 15 | {{ url_for('static', filename='style.css') }} 16 | 17 | Besides CSS, other types of static files might be files with JavaScript 18 | functions, or a logo image. They are all placed under the 19 | ``flaskr/static`` directory and referenced with 20 | ``url_for('static', filename='...')``. 21 | 22 | This tutorial isn't focused on how to write CSS, so you can just copy 23 | the following into the ``flaskr/static/style.css`` file: 24 | 25 | .. code-block:: css 26 | :caption: ``flaskr/static/style.css`` 27 | 28 | html { font-family: sans-serif; background: #eee; padding: 1rem; } 29 | body { max-width: 960px; margin: 0 auto; background: white; } 30 | h1 { font-family: serif; color: #377ba8; margin: 1rem 0; } 31 | a { color: #377ba8; } 32 | hr { border: none; border-top: 1px solid lightgray; } 33 | nav { background: lightgray; display: flex; align-items: center; padding: 0 0.5rem; } 34 | nav h1 { flex: auto; margin: 0; } 35 | nav h1 a { text-decoration: none; padding: 0.25rem 0.5rem; } 36 | nav ul { display: flex; list-style: none; margin: 0; padding: 0; } 37 | nav ul li a, nav ul li span, header .action { display: block; padding: 0.5rem; } 38 | .content { padding: 0 1rem 1rem; } 39 | .content > header { border-bottom: 1px solid lightgray; display: flex; align-items: flex-end; } 40 | .content > header h1 { flex: auto; margin: 1rem 0 0.25rem 0; } 41 | .flash { margin: 1em 0; padding: 1em; background: #cae6f6; border: 1px solid #377ba8; } 42 | .post > header { display: flex; align-items: flex-end; font-size: 0.85em; } 43 | .post > header > div:first-of-type { flex: auto; } 44 | .post > header h1 { font-size: 1.5em; margin-bottom: 0; } 45 | .post .about { color: slategray; font-style: italic; } 46 | .post .body { white-space: pre-line; } 47 | .content:last-child { margin-bottom: 0; } 48 | .content form { margin: 1em 0; display: flex; flex-direction: column; } 49 | .content label { font-weight: bold; margin-bottom: 0.5em; } 50 | .content input, .content textarea { margin-bottom: 1em; } 51 | .content textarea { min-height: 12em; resize: vertical; } 52 | input.danger { color: #cc2f2e; } 53 | input[type=submit] { align-self: start; min-width: 10em; } 54 | 55 | You can find a less compact version of ``style.css`` in the 56 | :gh:`example code `. 57 | 58 | Go to http://127.0.0.1:5000/auth/login and the page should look like the 59 | screenshot below. 60 | 61 | .. image:: flaskr_login.png 62 | :align: center 63 | :class: screenshot 64 | :alt: screenshot of login page 65 | 66 | You can read more about CSS from `Mozilla's documentation `_. If 67 | you change a static file, refresh the browser page. If the change 68 | doesn't show up, try clearing your browser's cache. 69 | 70 | .. _CSS: https://developer.mozilla.org/docs/Web/CSS 71 | 72 | Continue to :doc:`blog`. 73 | -------------------------------------------------------------------------------- /examples/celery/README.md: -------------------------------------------------------------------------------- 1 | Background Tasks with Celery 2 | ============================ 3 | 4 | This example shows how to configure Celery with Flask, how to set up an API for 5 | submitting tasks and polling results, and how to use that API with JavaScript. See 6 | [Flask's documentation about Celery](https://flask.palletsprojects.com/patterns/celery/). 7 | 8 | From this directory, create a virtualenv and install the application into it. Then run a 9 | Celery worker. 10 | 11 | ```shell 12 | $ python3 -m venv .venv 13 | $ . ./.venv/bin/activate 14 | $ pip install -r requirements.txt && pip install -e . 15 | $ celery -A make_celery worker --loglevel INFO 16 | ``` 17 | 18 | In a separate terminal, activate the virtualenv and run the Flask development server. 19 | 20 | ```shell 21 | $ . ./.venv/bin/activate 22 | $ flask -A task_app run --debug 23 | ``` 24 | 25 | Go to http://localhost:5000/ and use the forms to submit tasks. You can see the polling 26 | requests in the browser dev tools and the Flask logs. You can see the tasks submitting 27 | and completing in the Celery logs. 28 | -------------------------------------------------------------------------------- /examples/celery/make_celery.py: -------------------------------------------------------------------------------- 1 | from task_app import create_app 2 | 3 | flask_app = create_app() 4 | celery_app = flask_app.extensions["celery"] 5 | -------------------------------------------------------------------------------- /examples/celery/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "flask-example-celery" 3 | version = "1.0.0" 4 | description = "Example Flask application with Celery background tasks." 5 | readme = "README.md" 6 | requires-python = ">=3.8" 7 | dependencies = ["flask>=2.2.2", "celery[redis]>=5.2.7"] 8 | 9 | [build-system] 10 | requires = ["flit_core<4"] 11 | build-backend = "flit_core.buildapi" 12 | 13 | [tool.flit.module] 14 | name = "task_app" 15 | -------------------------------------------------------------------------------- /examples/celery/requirements.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile with Python 3.11 3 | # by the following command: 4 | # 5 | # pip-compile --resolver=backtracking pyproject.toml 6 | # 7 | amqp==5.1.1 8 | # via kombu 9 | async-timeout==4.0.2 10 | # via redis 11 | billiard==3.6.4.0 12 | # via celery 13 | blinker==1.6.2 14 | # via flask 15 | celery[redis]==5.2.7 16 | # via flask-example-celery (pyproject.toml) 17 | click==8.1.3 18 | # via 19 | # celery 20 | # click-didyoumean 21 | # click-plugins 22 | # click-repl 23 | # flask 24 | click-didyoumean==0.3.0 25 | # via celery 26 | click-plugins==1.1.1 27 | # via celery 28 | click-repl==0.2.0 29 | # via celery 30 | flask==2.3.2 31 | # via flask-example-celery (pyproject.toml) 32 | itsdangerous==2.1.2 33 | # via flask 34 | jinja2==3.1.2 35 | # via flask 36 | kombu==5.2.4 37 | # via celery 38 | markupsafe==2.1.2 39 | # via 40 | # jinja2 41 | # werkzeug 42 | prompt-toolkit==3.0.38 43 | # via click-repl 44 | pytz==2023.3 45 | # via celery 46 | redis==4.5.4 47 | # via celery 48 | six==1.16.0 49 | # via click-repl 50 | vine==5.0.0 51 | # via 52 | # amqp 53 | # celery 54 | # kombu 55 | wcwidth==0.2.6 56 | # via prompt-toolkit 57 | werkzeug==2.3.3 58 | # via flask 59 | -------------------------------------------------------------------------------- /examples/celery/src/task_app/__init__.py: -------------------------------------------------------------------------------- 1 | from celery import Celery 2 | from celery import Task 3 | from flask import Flask 4 | from flask import render_template 5 | 6 | 7 | def create_app() -> Flask: 8 | app = Flask(__name__) 9 | app.config.from_mapping( 10 | CELERY=dict( 11 | broker_url="redis://localhost", 12 | result_backend="redis://localhost", 13 | task_ignore_result=True, 14 | ), 15 | ) 16 | app.config.from_prefixed_env() 17 | celery_init_app(app) 18 | 19 | @app.route("/") 20 | def index() -> str: 21 | return render_template("index.html") 22 | 23 | from . import views 24 | 25 | app.register_blueprint(views.bp) 26 | return app 27 | 28 | 29 | def celery_init_app(app: Flask) -> Celery: 30 | class FlaskTask(Task): 31 | def __call__(self, *args: object, **kwargs: object) -> object: 32 | with app.app_context(): 33 | return self.run(*args, **kwargs) 34 | 35 | celery_app = Celery(app.name, task_cls=FlaskTask) 36 | celery_app.config_from_object(app.config["CELERY"]) 37 | celery_app.set_default() 38 | app.extensions["celery"] = celery_app 39 | return celery_app 40 | -------------------------------------------------------------------------------- /examples/celery/src/task_app/tasks.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | from celery import shared_task 4 | from celery import Task 5 | 6 | 7 | @shared_task(ignore_result=False) 8 | def add(a: int, b: int) -> int: 9 | return a + b 10 | 11 | 12 | @shared_task() 13 | def block() -> None: 14 | time.sleep(5) 15 | 16 | 17 | @shared_task(bind=True, ignore_result=False) 18 | def process(self: Task, total: int) -> object: 19 | for i in range(total): 20 | self.update_state(state="PROGRESS", meta={"current": i + 1, "total": total}) 21 | time.sleep(1) 22 | 23 | return {"current": total, "total": total} 24 | -------------------------------------------------------------------------------- /examples/celery/src/task_app/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Celery Example 6 | 7 | 8 |

Celery Example

9 | Execute background tasks with Celery. Submits tasks and shows results using JavaScript. 10 | 11 |
12 |

Add

13 |

Start a task to add two numbers, then poll for the result. 14 |

15 |
16 |
17 | 18 |
19 |

Result:

20 | 21 |
22 |

Block

23 |

Start a task that takes 5 seconds. However, the response will return immediately. 24 |

25 | 26 |
27 |

28 | 29 |
30 |

Process

31 |

Start a task that counts, waiting one second each time, showing progress. 32 |

33 |
34 | 35 |
36 |

37 | 38 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /examples/celery/src/task_app/views.py: -------------------------------------------------------------------------------- 1 | from celery.result import AsyncResult 2 | from flask import Blueprint 3 | from flask import request 4 | 5 | from . import tasks 6 | 7 | bp = Blueprint("tasks", __name__, url_prefix="/tasks") 8 | 9 | 10 | @bp.get("/result/") 11 | def result(id: str) -> dict[str, object]: 12 | result = AsyncResult(id) 13 | ready = result.ready() 14 | return { 15 | "ready": ready, 16 | "successful": result.successful() if ready else None, 17 | "value": result.get() if ready else result.result, 18 | } 19 | 20 | 21 | @bp.post("/add") 22 | def add() -> dict[str, object]: 23 | a = request.form.get("a", type=int) 24 | b = request.form.get("b", type=int) 25 | result = tasks.add.delay(a, b) 26 | return {"result_id": result.id} 27 | 28 | 29 | @bp.post("/block") 30 | def block() -> dict[str, object]: 31 | result = tasks.block.delay() 32 | return {"result_id": result.id} 33 | 34 | 35 | @bp.post("/process") 36 | def process() -> dict[str, object]: 37 | result = tasks.process.delay(total=request.form.get("total", type=int)) 38 | return {"result_id": result.id} 39 | -------------------------------------------------------------------------------- /examples/javascript/.gitignore: -------------------------------------------------------------------------------- 1 | .venv/ 2 | *.pyc 3 | __pycache__/ 4 | instance/ 5 | .cache/ 6 | .pytest_cache/ 7 | .coverage 8 | htmlcov/ 9 | dist/ 10 | build/ 11 | *.egg-info/ 12 | .idea/ 13 | *.swp 14 | *~ 15 | -------------------------------------------------------------------------------- /examples/javascript/LICENSE.rst: -------------------------------------------------------------------------------- 1 | Copyright 2010 Pallets 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 21 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 24 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 26 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /examples/javascript/README.rst: -------------------------------------------------------------------------------- 1 | JavaScript Ajax Example 2 | ======================= 3 | 4 | Demonstrates how to post form data and process a JSON response using 5 | JavaScript. This allows making requests without navigating away from the 6 | page. Demonstrates using |fetch|_, |XMLHttpRequest|_, and 7 | |jQuery.ajax|_. See the `Flask docs`_ about JavaScript and Ajax. 8 | 9 | .. |fetch| replace:: ``fetch`` 10 | .. _fetch: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch 11 | 12 | .. |XMLHttpRequest| replace:: ``XMLHttpRequest`` 13 | .. _XMLHttpRequest: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest 14 | 15 | .. |jQuery.ajax| replace:: ``jQuery.ajax`` 16 | .. _jQuery.ajax: https://api.jquery.com/jQuery.ajax/ 17 | 18 | .. _Flask docs: https://flask.palletsprojects.com/patterns/jquery/ 19 | 20 | 21 | Install 22 | ------- 23 | 24 | .. code-block:: text 25 | 26 | $ python3 -m venv .venv 27 | $ . .venv/bin/activate 28 | $ pip install -e . 29 | 30 | 31 | Run 32 | --- 33 | 34 | .. code-block:: text 35 | 36 | $ flask --app js_example run 37 | 38 | Open http://127.0.0.1:5000 in a browser. 39 | 40 | 41 | Test 42 | ---- 43 | 44 | .. code-block:: text 45 | 46 | $ pip install -e '.[test]' 47 | $ coverage run -m pytest 48 | $ coverage report 49 | -------------------------------------------------------------------------------- /examples/javascript/js_example/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | 3 | app = Flask(__name__) 4 | 5 | from js_example import views # noqa: E402, F401 6 | -------------------------------------------------------------------------------- /examples/javascript/js_example/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | JavaScript Example 3 | 4 | 5 | 14 | 23 |
24 |

{% block intro %}{% endblock %}

25 |
26 |
27 | 28 | + 29 | 30 | 31 |
32 | = 33 | {% block script %}{% endblock %} 34 | -------------------------------------------------------------------------------- /examples/javascript/js_example/templates/fetch.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block intro %} 4 | fetch 5 | is the modern plain JavaScript way to make requests. It's 6 | supported in all modern browsers. 7 | {% endblock %} 8 | 9 | {% block script %} 10 | 33 | {% endblock %} 34 | -------------------------------------------------------------------------------- /examples/javascript/js_example/templates/jquery.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block intro %} 4 | jQuery is a popular library that 5 | adds cross browser APIs for common tasks. However, it requires loading 6 | an extra library. 7 | {% endblock %} 8 | 9 | {% block script %} 10 | 11 | 27 | {% endblock %} 28 | -------------------------------------------------------------------------------- /examples/javascript/js_example/templates/xhr.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block intro %} 4 | XMLHttpRequest 5 | is the original JavaScript way to make requests. It's natively supported 6 | by all browsers, but has been superseded by 7 | fetch. 8 | {% endblock %} 9 | 10 | {% block script %} 11 | 29 | {% endblock %} 30 | -------------------------------------------------------------------------------- /examples/javascript/js_example/views.py: -------------------------------------------------------------------------------- 1 | from flask import jsonify 2 | from flask import render_template 3 | from flask import request 4 | 5 | from js_example import app 6 | 7 | 8 | @app.route("/", defaults={"js": "fetch"}) 9 | @app.route("/") 10 | def index(js): 11 | return render_template(f"{js}.html", js=js) 12 | 13 | 14 | @app.route("/add", methods=["POST"]) 15 | def add(): 16 | a = request.form.get("a", 0, type=float) 17 | b = request.form.get("b", 0, type=float) 18 | return jsonify(result=a + b) 19 | -------------------------------------------------------------------------------- /examples/javascript/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "js_example" 3 | version = "1.1.0" 4 | description = "Demonstrates making AJAX requests to Flask." 5 | readme = "README.rst" 6 | license = {file = "LICENSE.rst"} 7 | maintainers = [{name = "Pallets", email = "contact@palletsprojects.com"}] 8 | dependencies = ["flask"] 9 | 10 | [project.urls] 11 | Documentation = "https://flask.palletsprojects.com/patterns/jquery/" 12 | 13 | [project.optional-dependencies] 14 | test = ["pytest"] 15 | 16 | [build-system] 17 | requires = ["flit_core<4"] 18 | build-backend = "flit_core.buildapi" 19 | 20 | [tool.flit.module] 21 | name = "js_example" 22 | 23 | [tool.pytest.ini_options] 24 | testpaths = ["tests"] 25 | filterwarnings = ["error"] 26 | 27 | [tool.coverage.run] 28 | branch = true 29 | source = ["js_example", "tests"] 30 | -------------------------------------------------------------------------------- /examples/javascript/tests/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from js_example import app 4 | 5 | 6 | @pytest.fixture(name="app") 7 | def fixture_app(): 8 | app.testing = True 9 | yield app 10 | app.testing = False 11 | 12 | 13 | @pytest.fixture 14 | def client(app): 15 | return app.test_client() 16 | -------------------------------------------------------------------------------- /examples/javascript/tests/test_js_example.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from flask import template_rendered 3 | 4 | 5 | @pytest.mark.parametrize( 6 | ("path", "template_name"), 7 | ( 8 | ("/", "xhr.html"), 9 | ("/plain", "xhr.html"), 10 | ("/fetch", "fetch.html"), 11 | ("/jquery", "jquery.html"), 12 | ), 13 | ) 14 | def test_index(app, client, path, template_name): 15 | def check(sender, template, context): 16 | assert template.name == template_name 17 | 18 | with template_rendered.connected_to(check, app): 19 | client.get(path) 20 | 21 | 22 | @pytest.mark.parametrize( 23 | ("a", "b", "result"), ((2, 3, 5), (2.5, 3, 5.5), (2, None, 2), (2, "b", 2)) 24 | ) 25 | def test_add(client, a, b, result): 26 | response = client.post("/add", data={"a": a, "b": b}) 27 | assert response.get_json()["result"] == result 28 | -------------------------------------------------------------------------------- /examples/tutorial/.gitignore: -------------------------------------------------------------------------------- 1 | .venv/ 2 | *.pyc 3 | __pycache__/ 4 | instance/ 5 | .cache/ 6 | .pytest_cache/ 7 | .coverage 8 | htmlcov/ 9 | dist/ 10 | build/ 11 | *.egg-info/ 12 | .idea/ 13 | *.swp 14 | *~ 15 | -------------------------------------------------------------------------------- /examples/tutorial/LICENSE.rst: -------------------------------------------------------------------------------- 1 | Copyright 2010 Pallets 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 21 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 24 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 26 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /examples/tutorial/README.rst: -------------------------------------------------------------------------------- 1 | Flaskr 2 | ====== 3 | 4 | The basic blog app built in the Flask `tutorial`_. 5 | 6 | .. _tutorial: https://flask.palletsprojects.com/tutorial/ 7 | 8 | 9 | Install 10 | ------- 11 | 12 | **Be sure to use the same version of the code as the version of the docs 13 | you're reading.** You probably want the latest tagged version, but the 14 | default Git version is the main branch. :: 15 | 16 | # clone the repository 17 | $ git clone https://github.com/pallets/flask 18 | $ cd flask 19 | # checkout the correct version 20 | $ git tag # shows the tagged versions 21 | $ git checkout latest-tag-found-above 22 | $ cd examples/tutorial 23 | 24 | Create a virtualenv and activate it:: 25 | 26 | $ python3 -m venv .venv 27 | $ . .venv/bin/activate 28 | 29 | Or on Windows cmd:: 30 | 31 | $ py -3 -m venv .venv 32 | $ .venv\Scripts\activate.bat 33 | 34 | Install Flaskr:: 35 | 36 | $ pip install -e . 37 | 38 | Or if you are using the main branch, install Flask from source before 39 | installing Flaskr:: 40 | 41 | $ pip install -e ../.. 42 | $ pip install -e . 43 | 44 | 45 | Run 46 | --- 47 | 48 | .. code-block:: text 49 | 50 | $ flask --app flaskr init-db 51 | $ flask --app flaskr run --debug 52 | 53 | Open http://127.0.0.1:5000 in a browser. 54 | 55 | 56 | Test 57 | ---- 58 | 59 | :: 60 | 61 | $ pip install '.[test]' 62 | $ pytest 63 | 64 | Run with coverage report:: 65 | 66 | $ coverage run -m pytest 67 | $ coverage report 68 | $ coverage html # open htmlcov/index.html in a browser 69 | -------------------------------------------------------------------------------- /examples/tutorial/flaskr/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from flask import Flask 4 | 5 | 6 | def create_app(test_config=None): 7 | """Create and configure an instance of the Flask application.""" 8 | app = Flask(__name__, instance_relative_config=True) 9 | app.config.from_mapping( 10 | # a default secret that should be overridden by instance config 11 | SECRET_KEY="dev", 12 | # store the database in the instance folder 13 | DATABASE=os.path.join(app.instance_path, "flaskr.sqlite"), 14 | ) 15 | 16 | if test_config is None: 17 | # load the instance config, if it exists, when not testing 18 | app.config.from_pyfile("config.py", silent=True) 19 | else: 20 | # load the test config if passed in 21 | app.config.update(test_config) 22 | 23 | # ensure the instance folder exists 24 | try: 25 | os.makedirs(app.instance_path) 26 | except OSError: 27 | pass 28 | 29 | @app.route("/hello") 30 | def hello(): 31 | return "Hello, World!" 32 | 33 | # register the database commands 34 | from flaskr import db 35 | 36 | db.init_app(app) 37 | 38 | # apply the blueprints to the app 39 | from flaskr import auth, blog 40 | 41 | app.register_blueprint(auth.bp) 42 | app.register_blueprint(blog.bp) 43 | 44 | # make url_for('index') == url_for('blog.index') 45 | # in another app, you might define a separate main index here with 46 | # app.route, while giving the blog blueprint a url_prefix, but for 47 | # the tutorial the blog will be the main index 48 | app.add_url_rule("/", endpoint="index") 49 | 50 | return app 51 | -------------------------------------------------------------------------------- /examples/tutorial/flaskr/auth.py: -------------------------------------------------------------------------------- 1 | import functools 2 | 3 | from flask import Blueprint 4 | from flask import flash 5 | from flask import g 6 | from flask import redirect 7 | from flask import render_template 8 | from flask import request 9 | from flask import session 10 | from flask import url_for 11 | from werkzeug.security import check_password_hash 12 | from werkzeug.security import generate_password_hash 13 | 14 | from flaskr.db import get_db 15 | 16 | bp = Blueprint("auth", __name__, url_prefix="/auth") 17 | 18 | 19 | def login_required(view): 20 | """View decorator that redirects anonymous users to the login page.""" 21 | 22 | @functools.wraps(view) 23 | def wrapped_view(**kwargs): 24 | if g.user is None: 25 | return redirect(url_for("auth.login")) 26 | 27 | return view(**kwargs) 28 | 29 | return wrapped_view 30 | 31 | 32 | @bp.before_app_request 33 | def load_logged_in_user(): 34 | """If a user id is stored in the session, load the user object from 35 | the database into ``g.user``.""" 36 | user_id = session.get("user_id") 37 | 38 | if user_id is None: 39 | g.user = None 40 | else: 41 | g.user = ( 42 | get_db().execute("SELECT * FROM user WHERE id = ?", (user_id,)).fetchone() 43 | ) 44 | 45 | 46 | @bp.route("/register", methods=("GET", "POST")) 47 | def register(): 48 | """Register a new user. 49 | 50 | Validates that the username is not already taken. Hashes the 51 | password for security. 52 | """ 53 | if request.method == "POST": 54 | username = request.form["username"] 55 | password = request.form["password"] 56 | db = get_db() 57 | error = None 58 | 59 | if not username: 60 | error = "Username is required." 61 | elif not password: 62 | error = "Password is required." 63 | 64 | if error is None: 65 | try: 66 | db.execute( 67 | "INSERT INTO user (username, password) VALUES (?, ?)", 68 | (username, generate_password_hash(password)), 69 | ) 70 | db.commit() 71 | except db.IntegrityError: 72 | # The username was already taken, which caused the 73 | # commit to fail. Show a validation error. 74 | error = f"User {username} is already registered." 75 | else: 76 | # Success, go to the login page. 77 | return redirect(url_for("auth.login")) 78 | 79 | flash(error) 80 | 81 | return render_template("auth/register.html") 82 | 83 | 84 | @bp.route("/login", methods=("GET", "POST")) 85 | def login(): 86 | """Log in a registered user by adding the user id to the session.""" 87 | if request.method == "POST": 88 | username = request.form["username"] 89 | password = request.form["password"] 90 | db = get_db() 91 | error = None 92 | user = db.execute( 93 | "SELECT * FROM user WHERE username = ?", (username,) 94 | ).fetchone() 95 | 96 | if user is None: 97 | error = "Incorrect username." 98 | elif not check_password_hash(user["password"], password): 99 | error = "Incorrect password." 100 | 101 | if error is None: 102 | # store the user id in a new session and return to the index 103 | session.clear() 104 | session["user_id"] = user["id"] 105 | return redirect(url_for("index")) 106 | 107 | flash(error) 108 | 109 | return render_template("auth/login.html") 110 | 111 | 112 | @bp.route("/logout") 113 | def logout(): 114 | """Clear the current session, including the stored user id.""" 115 | session.clear() 116 | return redirect(url_for("index")) 117 | -------------------------------------------------------------------------------- /examples/tutorial/flaskr/blog.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | from flask import flash 3 | from flask import g 4 | from flask import redirect 5 | from flask import render_template 6 | from flask import request 7 | from flask import url_for 8 | from werkzeug.exceptions import abort 9 | 10 | from flaskr.auth import login_required 11 | from flaskr.db import get_db 12 | 13 | bp = Blueprint("blog", __name__) 14 | 15 | 16 | @bp.route("/") 17 | def index(): 18 | """Show all the posts, most recent first.""" 19 | db = get_db() 20 | posts = db.execute( 21 | "SELECT p.id, title, body, created, author_id, username" 22 | " FROM post p JOIN user u ON p.author_id = u.id" 23 | " ORDER BY created DESC" 24 | ).fetchall() 25 | return render_template("blog/index.html", posts=posts) 26 | 27 | 28 | def get_post(id, check_author=True): 29 | """Get a post and its author by id. 30 | 31 | Checks that the id exists and optionally that the current user is 32 | the author. 33 | 34 | :param id: id of post to get 35 | :param check_author: require the current user to be the author 36 | :return: the post with author information 37 | :raise 404: if a post with the given id doesn't exist 38 | :raise 403: if the current user isn't the author 39 | """ 40 | post = ( 41 | get_db() 42 | .execute( 43 | "SELECT p.id, title, body, created, author_id, username" 44 | " FROM post p JOIN user u ON p.author_id = u.id" 45 | " WHERE p.id = ?", 46 | (id,), 47 | ) 48 | .fetchone() 49 | ) 50 | 51 | if post is None: 52 | abort(404, f"Post id {id} doesn't exist.") 53 | 54 | if check_author and post["author_id"] != g.user["id"]: 55 | abort(403) 56 | 57 | return post 58 | 59 | 60 | @bp.route("/create", methods=("GET", "POST")) 61 | @login_required 62 | def create(): 63 | """Create a new post for the current user.""" 64 | if request.method == "POST": 65 | title = request.form["title"] 66 | body = request.form["body"] 67 | error = None 68 | 69 | if not title: 70 | error = "Title is required." 71 | 72 | if error is not None: 73 | flash(error) 74 | else: 75 | db = get_db() 76 | db.execute( 77 | "INSERT INTO post (title, body, author_id) VALUES (?, ?, ?)", 78 | (title, body, g.user["id"]), 79 | ) 80 | db.commit() 81 | return redirect(url_for("blog.index")) 82 | 83 | return render_template("blog/create.html") 84 | 85 | 86 | @bp.route("//update", methods=("GET", "POST")) 87 | @login_required 88 | def update(id): 89 | """Update a post if the current user is the author.""" 90 | post = get_post(id) 91 | 92 | if request.method == "POST": 93 | title = request.form["title"] 94 | body = request.form["body"] 95 | error = None 96 | 97 | if not title: 98 | error = "Title is required." 99 | 100 | if error is not None: 101 | flash(error) 102 | else: 103 | db = get_db() 104 | db.execute( 105 | "UPDATE post SET title = ?, body = ? WHERE id = ?", (title, body, id) 106 | ) 107 | db.commit() 108 | return redirect(url_for("blog.index")) 109 | 110 | return render_template("blog/update.html", post=post) 111 | 112 | 113 | @bp.route("//delete", methods=("POST",)) 114 | @login_required 115 | def delete(id): 116 | """Delete a post. 117 | 118 | Ensures that the post exists and that the logged in user is the 119 | author of the post. 120 | """ 121 | get_post(id) 122 | db = get_db() 123 | db.execute("DELETE FROM post WHERE id = ?", (id,)) 124 | db.commit() 125 | return redirect(url_for("blog.index")) 126 | -------------------------------------------------------------------------------- /examples/tutorial/flaskr/db.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | import click 4 | from flask import current_app 5 | from flask import g 6 | 7 | 8 | def get_db(): 9 | """Connect to the application's configured database. The connection 10 | is unique for each request and will be reused if this is called 11 | again. 12 | """ 13 | if "db" not in g: 14 | g.db = sqlite3.connect( 15 | current_app.config["DATABASE"], detect_types=sqlite3.PARSE_DECLTYPES 16 | ) 17 | g.db.row_factory = sqlite3.Row 18 | 19 | return g.db 20 | 21 | 22 | def close_db(e=None): 23 | """If this request connected to the database, close the 24 | connection. 25 | """ 26 | db = g.pop("db", None) 27 | 28 | if db is not None: 29 | db.close() 30 | 31 | 32 | def init_db(): 33 | """Clear existing data and create new tables.""" 34 | db = get_db() 35 | 36 | with current_app.open_resource("schema.sql") as f: 37 | db.executescript(f.read().decode("utf8")) 38 | 39 | 40 | @click.command("init-db") 41 | def init_db_command(): 42 | """Clear existing data and create new tables.""" 43 | init_db() 44 | click.echo("Initialized the database.") 45 | 46 | 47 | def init_app(app): 48 | """Register database functions with the Flask app. This is called by 49 | the application factory. 50 | """ 51 | app.teardown_appcontext(close_db) 52 | app.cli.add_command(init_db_command) 53 | -------------------------------------------------------------------------------- /examples/tutorial/flaskr/schema.sql: -------------------------------------------------------------------------------- 1 | -- Initialize the database. 2 | -- Drop any existing data and create empty tables. 3 | 4 | DROP TABLE IF EXISTS user; 5 | DROP TABLE IF EXISTS post; 6 | 7 | CREATE TABLE user ( 8 | id INTEGER PRIMARY KEY AUTOINCREMENT, 9 | username TEXT UNIQUE NOT NULL, 10 | password TEXT NOT NULL 11 | ); 12 | 13 | CREATE TABLE post ( 14 | id INTEGER PRIMARY KEY AUTOINCREMENT, 15 | author_id INTEGER NOT NULL, 16 | created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 17 | title TEXT NOT NULL, 18 | body TEXT NOT NULL, 19 | FOREIGN KEY (author_id) REFERENCES user (id) 20 | ); 21 | -------------------------------------------------------------------------------- /examples/tutorial/flaskr/static/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-family: sans-serif; 3 | background: #eee; 4 | padding: 1rem; 5 | } 6 | 7 | body { 8 | max-width: 960px; 9 | margin: 0 auto; 10 | background: white; 11 | } 12 | 13 | h1, h2, h3, h4, h5, h6 { 14 | font-family: serif; 15 | color: #377ba8; 16 | margin: 1rem 0; 17 | } 18 | 19 | a { 20 | color: #377ba8; 21 | } 22 | 23 | hr { 24 | border: none; 25 | border-top: 1px solid lightgray; 26 | } 27 | 28 | nav { 29 | background: lightgray; 30 | display: flex; 31 | align-items: center; 32 | padding: 0 0.5rem; 33 | } 34 | 35 | nav h1 { 36 | flex: auto; 37 | margin: 0; 38 | } 39 | 40 | nav h1 a { 41 | text-decoration: none; 42 | padding: 0.25rem 0.5rem; 43 | } 44 | 45 | nav ul { 46 | display: flex; 47 | list-style: none; 48 | margin: 0; 49 | padding: 0; 50 | } 51 | 52 | nav ul li a, nav ul li span, header .action { 53 | display: block; 54 | padding: 0.5rem; 55 | } 56 | 57 | .content { 58 | padding: 0 1rem 1rem; 59 | } 60 | 61 | .content > header { 62 | border-bottom: 1px solid lightgray; 63 | display: flex; 64 | align-items: flex-end; 65 | } 66 | 67 | .content > header h1 { 68 | flex: auto; 69 | margin: 1rem 0 0.25rem 0; 70 | } 71 | 72 | .flash { 73 | margin: 1em 0; 74 | padding: 1em; 75 | background: #cae6f6; 76 | border: 1px solid #377ba8; 77 | } 78 | 79 | .post > header { 80 | display: flex; 81 | align-items: flex-end; 82 | font-size: 0.85em; 83 | } 84 | 85 | .post > header > div:first-of-type { 86 | flex: auto; 87 | } 88 | 89 | .post > header h1 { 90 | font-size: 1.5em; 91 | margin-bottom: 0; 92 | } 93 | 94 | .post .about { 95 | color: slategray; 96 | font-style: italic; 97 | } 98 | 99 | .post .body { 100 | white-space: pre-line; 101 | } 102 | 103 | .content:last-child { 104 | margin-bottom: 0; 105 | } 106 | 107 | .content form { 108 | margin: 1em 0; 109 | display: flex; 110 | flex-direction: column; 111 | } 112 | 113 | .content label { 114 | font-weight: bold; 115 | margin-bottom: 0.5em; 116 | } 117 | 118 | .content input, .content textarea { 119 | margin-bottom: 1em; 120 | } 121 | 122 | .content textarea { 123 | min-height: 12em; 124 | resize: vertical; 125 | } 126 | 127 | input.danger { 128 | color: #cc2f2e; 129 | } 130 | 131 | input[type=submit] { 132 | align-self: start; 133 | min-width: 10em; 134 | } 135 | -------------------------------------------------------------------------------- /examples/tutorial/flaskr/templates/auth/login.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block header %} 4 |

{% block title %}Log In{% endblock %}

5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 | 10 | 11 | 12 | 13 | 14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /examples/tutorial/flaskr/templates/auth/register.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block header %} 4 |

{% block title %}Register{% endblock %}

5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 | 10 | 11 | 12 | 13 | 14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /examples/tutorial/flaskr/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | {% block title %}{% endblock %} - Flaskr 3 | 4 | 16 |
17 |
18 | {% block header %}{% endblock %} 19 |
20 | {% for message in get_flashed_messages() %} 21 |
{{ message }}
22 | {% endfor %} 23 | {% block content %}{% endblock %} 24 |
25 | -------------------------------------------------------------------------------- /examples/tutorial/flaskr/templates/blog/create.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block header %} 4 |

{% block title %}New Post{% endblock %}

5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 | 10 | 11 | 12 | 13 | 14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /examples/tutorial/flaskr/templates/blog/index.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block header %} 4 |

{% block title %}Posts{% endblock %}

5 | {% if g.user %} 6 | New 7 | {% endif %} 8 | {% endblock %} 9 | 10 | {% block content %} 11 | {% for post in posts %} 12 |
13 |
14 |
15 |

{{ post['title'] }}

16 |
by {{ post['username'] }} on {{ post['created'].strftime('%Y-%m-%d') }}
17 |
18 | {% if g.user['id'] == post['author_id'] %} 19 | Edit 20 | {% endif %} 21 |
22 |

{{ post['body'] }}

23 |
24 | {% if not loop.last %} 25 |
26 | {% endif %} 27 | {% endfor %} 28 | {% endblock %} 29 | -------------------------------------------------------------------------------- /examples/tutorial/flaskr/templates/blog/update.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block header %} 4 |

{% block title %}Edit "{{ post['title'] }}"{% endblock %}

5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 | 18 |
19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /examples/tutorial/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "flaskr" 3 | version = "1.0.0" 4 | description = "The basic blog app built in the Flask tutorial." 5 | readme = "README.rst" 6 | license = {text = "BSD-3-Clause"} 7 | maintainers = [{name = "Pallets", email = "contact@palletsprojects.com"}] 8 | dependencies = [ 9 | "flask", 10 | ] 11 | 12 | [project.urls] 13 | Documentation = "https://flask.palletsprojects.com/tutorial/" 14 | 15 | [project.optional-dependencies] 16 | test = ["pytest"] 17 | 18 | [build-system] 19 | requires = ["flit_core<4"] 20 | build-backend = "flit_core.buildapi" 21 | 22 | [tool.flit.module] 23 | name = "flaskr" 24 | 25 | [tool.flit.sdist] 26 | include = [ 27 | "tests/", 28 | ] 29 | 30 | [tool.pytest.ini_options] 31 | testpaths = ["tests"] 32 | filterwarnings = ["error"] 33 | 34 | [tool.coverage.run] 35 | branch = true 36 | source = ["flaskr", "tests"] 37 | -------------------------------------------------------------------------------- /examples/tutorial/tests/conftest.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tempfile 3 | 4 | import pytest 5 | 6 | from flaskr import create_app 7 | from flaskr.db import get_db 8 | from flaskr.db import init_db 9 | 10 | # read in SQL for populating test data 11 | with open(os.path.join(os.path.dirname(__file__), "data.sql"), "rb") as f: 12 | _data_sql = f.read().decode("utf8") 13 | 14 | 15 | @pytest.fixture 16 | def app(): 17 | """Create and configure a new app instance for each test.""" 18 | # create a temporary file to isolate the database for each test 19 | db_fd, db_path = tempfile.mkstemp() 20 | # create the app with common test config 21 | app = create_app({"TESTING": True, "DATABASE": db_path}) 22 | 23 | # create the database and load test data 24 | with app.app_context(): 25 | init_db() 26 | get_db().executescript(_data_sql) 27 | 28 | yield app 29 | 30 | # close and remove the temporary database 31 | os.close(db_fd) 32 | os.unlink(db_path) 33 | 34 | 35 | @pytest.fixture 36 | def client(app): 37 | """A test client for the app.""" 38 | return app.test_client() 39 | 40 | 41 | @pytest.fixture 42 | def runner(app): 43 | """A test runner for the app's Click commands.""" 44 | return app.test_cli_runner() 45 | 46 | 47 | class AuthActions: 48 | def __init__(self, client): 49 | self._client = client 50 | 51 | def login(self, username="test", password="test"): 52 | return self._client.post( 53 | "/auth/login", data={"username": username, "password": password} 54 | ) 55 | 56 | def logout(self): 57 | return self._client.get("/auth/logout") 58 | 59 | 60 | @pytest.fixture 61 | def auth(client): 62 | return AuthActions(client) 63 | -------------------------------------------------------------------------------- /examples/tutorial/tests/data.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO user (username, password) 2 | VALUES 3 | ('test', 'pbkdf2:sha256:50000$TCI4GzcX$0de171a4f4dac32e3364c7ddc7c14f3e2fa61f2d17574483f7ffbb431b4acb2f'), 4 | ('other', 'pbkdf2:sha256:50000$kJPKsz6N$d2d4784f1b030a9761f5ccaeeaca413f27f2ecb76d6168407af962ddce849f79'); 5 | 6 | INSERT INTO post (title, body, author_id, created) 7 | VALUES 8 | ('test title', 'test' || x'0a' || 'body', 1, '2018-01-01 00:00:00'); 9 | -------------------------------------------------------------------------------- /examples/tutorial/tests/test_auth.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from flask import g 3 | from flask import session 4 | 5 | from flaskr.db import get_db 6 | 7 | 8 | def test_register(client, app): 9 | # test that viewing the page renders without template errors 10 | assert client.get("/auth/register").status_code == 200 11 | 12 | # test that successful registration redirects to the login page 13 | response = client.post("/auth/register", data={"username": "a", "password": "a"}) 14 | assert response.headers["Location"] == "/auth/login" 15 | 16 | # test that the user was inserted into the database 17 | with app.app_context(): 18 | assert ( 19 | get_db().execute("SELECT * FROM user WHERE username = 'a'").fetchone() 20 | is not None 21 | ) 22 | 23 | 24 | @pytest.mark.parametrize( 25 | ("username", "password", "message"), 26 | ( 27 | ("", "", b"Username is required."), 28 | ("a", "", b"Password is required."), 29 | ("test", "test", b"already registered"), 30 | ), 31 | ) 32 | def test_register_validate_input(client, username, password, message): 33 | response = client.post( 34 | "/auth/register", data={"username": username, "password": password} 35 | ) 36 | assert message in response.data 37 | 38 | 39 | def test_login(client, auth): 40 | # test that viewing the page renders without template errors 41 | assert client.get("/auth/login").status_code == 200 42 | 43 | # test that successful login redirects to the index page 44 | response = auth.login() 45 | assert response.headers["Location"] == "/" 46 | 47 | # login request set the user_id in the session 48 | # check that the user is loaded from the session 49 | with client: 50 | client.get("/") 51 | assert session["user_id"] == 1 52 | assert g.user["username"] == "test" 53 | 54 | 55 | @pytest.mark.parametrize( 56 | ("username", "password", "message"), 57 | (("a", "test", b"Incorrect username."), ("test", "a", b"Incorrect password.")), 58 | ) 59 | def test_login_validate_input(auth, username, password, message): 60 | response = auth.login(username, password) 61 | assert message in response.data 62 | 63 | 64 | def test_logout(client, auth): 65 | auth.login() 66 | 67 | with client: 68 | auth.logout() 69 | assert "user_id" not in session 70 | -------------------------------------------------------------------------------- /examples/tutorial/tests/test_blog.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from flaskr.db import get_db 4 | 5 | 6 | def test_index(client, auth): 7 | response = client.get("/") 8 | assert b"Log In" in response.data 9 | assert b"Register" in response.data 10 | 11 | auth.login() 12 | response = client.get("/") 13 | assert b"test title" in response.data 14 | assert b"by test on 2018-01-01" in response.data 15 | assert b"test\nbody" in response.data 16 | assert b'href="/1/update"' in response.data 17 | 18 | 19 | @pytest.mark.parametrize("path", ("/create", "/1/update", "/1/delete")) 20 | def test_login_required(client, path): 21 | response = client.post(path) 22 | assert response.headers["Location"] == "/auth/login" 23 | 24 | 25 | def test_author_required(app, client, auth): 26 | # change the post author to another user 27 | with app.app_context(): 28 | db = get_db() 29 | db.execute("UPDATE post SET author_id = 2 WHERE id = 1") 30 | db.commit() 31 | 32 | auth.login() 33 | # current user can't modify other user's post 34 | assert client.post("/1/update").status_code == 403 35 | assert client.post("/1/delete").status_code == 403 36 | # current user doesn't see edit link 37 | assert b'href="/1/update"' not in client.get("/").data 38 | 39 | 40 | @pytest.mark.parametrize("path", ("/2/update", "/2/delete")) 41 | def test_exists_required(client, auth, path): 42 | auth.login() 43 | assert client.post(path).status_code == 404 44 | 45 | 46 | def test_create(client, auth, app): 47 | auth.login() 48 | assert client.get("/create").status_code == 200 49 | client.post("/create", data={"title": "created", "body": ""}) 50 | 51 | with app.app_context(): 52 | db = get_db() 53 | count = db.execute("SELECT COUNT(id) FROM post").fetchone()[0] 54 | assert count == 2 55 | 56 | 57 | def test_update(client, auth, app): 58 | auth.login() 59 | assert client.get("/1/update").status_code == 200 60 | client.post("/1/update", data={"title": "updated", "body": ""}) 61 | 62 | with app.app_context(): 63 | db = get_db() 64 | post = db.execute("SELECT * FROM post WHERE id = 1").fetchone() 65 | assert post["title"] == "updated" 66 | 67 | 68 | @pytest.mark.parametrize("path", ("/create", "/1/update")) 69 | def test_create_update_validate(client, auth, path): 70 | auth.login() 71 | response = client.post(path, data={"title": "", "body": ""}) 72 | assert b"Title is required." in response.data 73 | 74 | 75 | def test_delete(client, auth, app): 76 | auth.login() 77 | response = client.post("/1/delete") 78 | assert response.headers["Location"] == "/" 79 | 80 | with app.app_context(): 81 | db = get_db() 82 | post = db.execute("SELECT * FROM post WHERE id = 1").fetchone() 83 | assert post is None 84 | -------------------------------------------------------------------------------- /examples/tutorial/tests/test_db.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | import pytest 4 | 5 | from flaskr.db import get_db 6 | 7 | 8 | def test_get_close_db(app): 9 | with app.app_context(): 10 | db = get_db() 11 | assert db is get_db() 12 | 13 | with pytest.raises(sqlite3.ProgrammingError) as e: 14 | db.execute("SELECT 1") 15 | 16 | assert "closed" in str(e.value) 17 | 18 | 19 | def test_init_db_command(runner, monkeypatch): 20 | class Recorder: 21 | called = False 22 | 23 | def fake_init_db(): 24 | Recorder.called = True 25 | 26 | monkeypatch.setattr("flaskr.db.init_db", fake_init_db) 27 | result = runner.invoke(args=["init-db"]) 28 | assert "Initialized" in result.output 29 | assert Recorder.called 30 | -------------------------------------------------------------------------------- /examples/tutorial/tests/test_factory.py: -------------------------------------------------------------------------------- 1 | from flaskr import create_app 2 | 3 | 4 | def test_config(): 5 | """Test create_app without passing test config.""" 6 | assert not create_app().testing 7 | assert create_app({"TESTING": True}).testing 8 | 9 | 10 | def test_hello(client): 11 | response = client.get("/hello") 12 | assert response.data == b"Hello, World!" 13 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "Flask" 3 | description = "Uma abstração simples para construção de aplicações de Web complexas." 4 | readme = "README.rst" 5 | license = {file = "LICENSE.rst"} 6 | maintainers = [{name = "Pallets", email = "contact@palletsprojects.com"}] 7 | classifiers = [ 8 | "Development Status :: 5 - Production/Stable", 9 | "Environment :: Web Environment", 10 | "Framework :: Flask", 11 | "Intended Audience :: Developers", 12 | "License :: OSI Approved :: BSD License", 13 | "Operating System :: OS Independent", 14 | "Programming Language :: Python", 15 | "Topic :: Internet :: WWW/HTTP :: Dynamic Content", 16 | "Topic :: Internet :: WWW/HTTP :: WSGI", 17 | "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", 18 | "Topic :: Software Development :: Libraries :: Application Frameworks", 19 | ] 20 | requires-python = ">=3.8" 21 | dependencies = [ 22 | "Werkzeug>=2.3.7", 23 | "Jinja2>=3.1.2", 24 | "itsdangerous>=2.1.2", 25 | "click>=8.1.3", 26 | "blinker>=1.6.2", 27 | "importlib-metadata>=3.6.0; python_version < '3.10'", 28 | ] 29 | dynamic = ["version"] 30 | 31 | [project.urls] 32 | Donate = "https://palletsprojects.com/donate" 33 | Documentation = "https://flask.palletsprojects.com/" 34 | Changes = "https://flask.palletsprojects.com/changes/" 35 | "Source Code" = "https://github.com/pallets/flask/" 36 | "Issue Tracker" = "https://github.com/pallets/flask/issues/" 37 | Chat = "https://discord.gg/pallets" 38 | 39 | [project.optional-dependencies] 40 | async = ["asgiref>=3.2"] 41 | dotenv = ["python-dotenv"] 42 | 43 | [project.scripts] 44 | flask = "flask.cli:main" 45 | 46 | [build-system] 47 | requires = ["flit_core<4"] 48 | build-backend = "flit_core.buildapi" 49 | 50 | [tool.flit.module] 51 | name = "flask" 52 | 53 | [tool.flit.sdist] 54 | include = [ 55 | "docs/", 56 | "examples/", 57 | "requirements/", 58 | "tests/", 59 | "CHANGES.rst", 60 | "CONTRIBUTING.rst", 61 | "tox.ini", 62 | ] 63 | exclude = [ 64 | "docs/_build/", 65 | ] 66 | 67 | [tool.pytest.ini_options] 68 | testpaths = ["tests"] 69 | filterwarnings = [ 70 | "error", 71 | ] 72 | 73 | [tool.coverage.run] 74 | branch = true 75 | source = ["flask", "tests"] 76 | 77 | [tool.coverage.paths] 78 | source = ["src", "*/site-packages"] 79 | 80 | [tool.mypy] 81 | python_version = "3.8" 82 | files = ["src/flask"] 83 | show_error_codes = true 84 | pretty = true 85 | #strict = true 86 | allow_redefinition = true 87 | disallow_subclassing_any = true 88 | #disallow_untyped_calls = true 89 | #disallow_untyped_defs = true 90 | #disallow_incomplete_defs = true 91 | no_implicit_optional = true 92 | local_partial_types = true 93 | #no_implicit_reexport = true 94 | strict_equality = true 95 | warn_redundant_casts = true 96 | warn_unused_configs = true 97 | warn_unused_ignores = true 98 | #warn_return_any = true 99 | #warn_unreachable = true 100 | 101 | [[tool.mypy.overrides]] 102 | module = [ 103 | "asgiref.*", 104 | "dotenv.*", 105 | "cryptography.*", 106 | "importlib_metadata", 107 | ] 108 | ignore_missing_imports = true 109 | -------------------------------------------------------------------------------- /requirements/docs.in: -------------------------------------------------------------------------------- 1 | Pallets-Sphinx-Themes 2 | Sphinx 3 | sphinx-issues 4 | sphinxcontrib-log-cabinet 5 | sphinx-tabs 6 | -------------------------------------------------------------------------------- /requirements/docs.txt: -------------------------------------------------------------------------------- 1 | wheel 2 | alabaster==0.7.13 3 | Babel==2.12.1 4 | blinker==1.6.2 5 | certifi==2023.7.22 6 | charset-normalizer==3.2.0 7 | click==8.1.7 8 | docutils==0.18.1 9 | Flask==2.3.3 10 | idna==3.4 11 | imagesize==1.4.1 12 | itsdangerous==2.1.2 13 | Jinja2==3.1.2 14 | MarkupSafe==2.1.3 15 | packaging==23.1 16 | Pallets-Sphinx-Themes==2.1.1 17 | Pygments==2.16.1 18 | requests==2.31.0 19 | snowballstemmer==2.2.0 20 | Sphinx==7.1.2 21 | sphinx-issues==3.0.1 22 | sphinx-tabs==3.4.1 23 | sphinxcontrib-applehelp==1.0.7 24 | sphinxcontrib-devhelp==1.0.5 25 | sphinxcontrib-htmlhelp==2.0.4 26 | sphinxcontrib-jsmath==1.0.1 27 | sphinxcontrib-log-cabinet==1.0.1 28 | sphinxcontrib-qthelp==1.0.6 29 | sphinxcontrib-serializinghtml==1.1.8 30 | urllib3==2.0.4 31 | Werkzeug==2.3.7 32 | -------------------------------------------------------------------------------- /src/flask/__init__.py: -------------------------------------------------------------------------------- 1 | from . import json as json 2 | from .app import Flask as Flask 3 | from .blueprints import Blueprint as Blueprint 4 | from .config import Config as Config 5 | from .ctx import after_this_request as after_this_request 6 | from .ctx import copy_current_request_context as copy_current_request_context 7 | from .ctx import has_app_context as has_app_context 8 | from .ctx import has_request_context as has_request_context 9 | from .globals import current_app as current_app 10 | from .globals import g as g 11 | from .globals import request as request 12 | from .globals import session as session 13 | from .helpers import abort as abort 14 | from .helpers import flash as flash 15 | from .helpers import get_flashed_messages as get_flashed_messages 16 | from .helpers import get_template_attribute as get_template_attribute 17 | from .helpers import make_response as make_response 18 | from .helpers import redirect as redirect 19 | from .helpers import send_file as send_file 20 | from .helpers import send_from_directory as send_from_directory 21 | from .helpers import stream_with_context as stream_with_context 22 | from .helpers import url_for as url_for 23 | from .json import jsonify as jsonify 24 | from .signals import appcontext_popped as appcontext_popped 25 | from .signals import appcontext_pushed as appcontext_pushed 26 | from .signals import appcontext_tearing_down as appcontext_tearing_down 27 | from .signals import before_render_template as before_render_template 28 | from .signals import got_request_exception as got_request_exception 29 | from .signals import message_flashed as message_flashed 30 | from .signals import request_finished as request_finished 31 | from .signals import request_started as request_started 32 | from .signals import request_tearing_down as request_tearing_down 33 | from .signals import template_rendered as template_rendered 34 | from .templating import render_template as render_template 35 | from .templating import render_template_string as render_template_string 36 | from .templating import stream_template as stream_template 37 | from .templating import stream_template_string as stream_template_string 38 | from .wrappers import Request as Request 39 | from .wrappers import Response as Response 40 | 41 | __version__ = "3.0.0.dev" 42 | -------------------------------------------------------------------------------- /src/flask/__main__.py: -------------------------------------------------------------------------------- 1 | from .cli import main 2 | 3 | main() 4 | -------------------------------------------------------------------------------- /src/flask/blueprints.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import os 4 | import typing as t 5 | from datetime import timedelta 6 | 7 | from .globals import current_app 8 | from .helpers import send_from_directory 9 | from .sansio.blueprints import Blueprint as SansioBlueprint 10 | from .sansio.blueprints import BlueprintSetupState as BlueprintSetupState # noqa 11 | 12 | if t.TYPE_CHECKING: # pragma: no cover 13 | from .wrappers import Response 14 | 15 | 16 | class Blueprint(SansioBlueprint): 17 | def get_send_file_max_age(self, filename: str | None) -> int | None: 18 | """Used by :func:`send_file` to determine the ``max_age`` cache 19 | value for a given file path if it wasn't passed. 20 | 21 | By default, this returns :data:`SEND_FILE_MAX_AGE_DEFAULT` from 22 | the configuration of :data:`~flask.current_app`. This defaults 23 | to ``None``, which tells the browser to use conditional requests 24 | instead of a timed cache, which is usually preferable. 25 | 26 | Note this is a duplicate of the same method in the Flask 27 | class. 28 | 29 | .. versionchanged:: 2.0 30 | The default configuration is ``None`` instead of 12 hours. 31 | 32 | .. versionadded:: 0.9 33 | """ 34 | value = current_app.config["SEND_FILE_MAX_AGE_DEFAULT"] 35 | 36 | if value is None: 37 | return None 38 | 39 | if isinstance(value, timedelta): 40 | return int(value.total_seconds()) 41 | 42 | return value 43 | 44 | def send_static_file(self, filename: str) -> Response: 45 | """The view function used to serve files from 46 | :attr:`static_folder`. A route is automatically registered for 47 | this view at :attr:`static_url_path` if :attr:`static_folder` is 48 | set. 49 | 50 | Note this is a duplicate of the same method in the Flask 51 | class. 52 | 53 | .. versionadded:: 0.5 54 | 55 | """ 56 | if not self.has_static_folder: 57 | raise RuntimeError("'static_folder' must be set to serve static_files.") 58 | 59 | # send_file only knows to call get_send_file_max_age on the app, 60 | # call it here so it works for blueprints too. 61 | max_age = self.get_send_file_max_age(filename) 62 | return send_from_directory( 63 | t.cast(str, self.static_folder), filename, max_age=max_age 64 | ) 65 | 66 | def open_resource(self, resource: str, mode: str = "rb") -> t.IO[t.AnyStr]: 67 | """Open a resource file relative to :attr:`root_path` for 68 | reading. 69 | 70 | For example, if the file ``schema.sql`` is next to the file 71 | ``app.py`` where the ``Flask`` app is defined, it can be opened 72 | with: 73 | 74 | .. code-block:: python 75 | 76 | with app.open_resource("schema.sql") as f: 77 | conn.executescript(f.read()) 78 | 79 | :param resource: Path to the resource relative to 80 | :attr:`root_path`. 81 | :param mode: Open the file in this mode. Only reading is 82 | supported, valid values are "r" (or "rt") and "rb". 83 | 84 | Note this is a duplicate of the same method in the Flask 85 | class. 86 | 87 | """ 88 | if mode not in {"r", "rt", "rb"}: 89 | raise ValueError("Resources can only be opened for reading.") 90 | 91 | return open(os.path.join(self.root_path, resource), mode) 92 | -------------------------------------------------------------------------------- /src/flask/globals.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import typing as t 4 | from contextvars import ContextVar 5 | 6 | from werkzeug.local import LocalProxy 7 | 8 | if t.TYPE_CHECKING: # pragma: no cover 9 | from .app import Flask 10 | from .ctx import _AppCtxGlobals 11 | from .ctx import AppContext 12 | from .ctx import RequestContext 13 | from .sessions import SessionMixin 14 | from .wrappers import Request 15 | 16 | 17 | _no_app_msg = """\ 18 | Working outside of application context. 19 | 20 | This typically means that you attempted to use functionality that needed 21 | the current application. To solve this, set up an application context 22 | with app.app_context(). See the documentation for more information.\ 23 | """ 24 | _cv_app: ContextVar[AppContext] = ContextVar("flask.app_ctx") 25 | app_ctx: AppContext = LocalProxy( # type: ignore[assignment] 26 | _cv_app, unbound_message=_no_app_msg 27 | ) 28 | current_app: Flask = LocalProxy( # type: ignore[assignment] 29 | _cv_app, "app", unbound_message=_no_app_msg 30 | ) 31 | g: _AppCtxGlobals = LocalProxy( # type: ignore[assignment] 32 | _cv_app, "g", unbound_message=_no_app_msg 33 | ) 34 | 35 | _no_req_msg = """\ 36 | Working outside of request context. 37 | 38 | This typically means that you attempted to use functionality that needed 39 | an active HTTP request. Consult the documentation on testing for 40 | information about how to avoid this problem.\ 41 | """ 42 | _cv_request: ContextVar[RequestContext] = ContextVar("flask.request_ctx") 43 | request_ctx: RequestContext = LocalProxy( # type: ignore[assignment] 44 | _cv_request, unbound_message=_no_req_msg 45 | ) 46 | request: Request = LocalProxy( # type: ignore[assignment] 47 | _cv_request, "request", unbound_message=_no_req_msg 48 | ) 49 | session: SessionMixin = LocalProxy( # type: ignore[assignment] 50 | _cv_request, "session", unbound_message=_no_req_msg 51 | ) 52 | -------------------------------------------------------------------------------- /src/flask/logging.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import logging 4 | import sys 5 | import typing as t 6 | 7 | from werkzeug.local import LocalProxy 8 | 9 | from .globals import request 10 | 11 | if t.TYPE_CHECKING: # pragma: no cover 12 | from .sansio.app import App 13 | 14 | 15 | @LocalProxy 16 | def wsgi_errors_stream() -> t.TextIO: 17 | """Find the most appropriate error stream for the application. If a request 18 | is active, log to ``wsgi.errors``, otherwise use ``sys.stderr``. 19 | 20 | If you configure your own :class:`logging.StreamHandler`, you may want to 21 | use this for the stream. If you are using file or dict configuration and 22 | can't import this directly, you can refer to it as 23 | ``ext://flask.logging.wsgi_errors_stream``. 24 | """ 25 | return request.environ["wsgi.errors"] if request else sys.stderr 26 | 27 | 28 | def has_level_handler(logger: logging.Logger) -> bool: 29 | """Check if there is a handler in the logging chain that will handle the 30 | given logger's :meth:`effective level <~logging.Logger.getEffectiveLevel>`. 31 | """ 32 | level = logger.getEffectiveLevel() 33 | current = logger 34 | 35 | while current: 36 | if any(handler.level <= level for handler in current.handlers): 37 | return True 38 | 39 | if not current.propagate: 40 | break 41 | 42 | current = current.parent # type: ignore 43 | 44 | return False 45 | 46 | 47 | #: Log messages to :func:`~flask.logging.wsgi_errors_stream` with the format 48 | #: ``[%(asctime)s] %(levelname)s in %(module)s: %(message)s``. 49 | default_handler = logging.StreamHandler(wsgi_errors_stream) # type: ignore 50 | default_handler.setFormatter( 51 | logging.Formatter("[%(asctime)s] %(levelname)s in %(module)s: %(message)s") 52 | ) 53 | 54 | 55 | def create_logger(app: App) -> logging.Logger: 56 | """Get the Flask app's logger and configure it if needed. 57 | 58 | The logger name will be the same as 59 | :attr:`app.import_name `. 60 | 61 | When :attr:`~flask.Flask.debug` is enabled, set the logger level to 62 | :data:`logging.DEBUG` if it is not set. 63 | 64 | If there is no handler for the logger's effective level, add a 65 | :class:`~logging.StreamHandler` for 66 | :func:`~flask.logging.wsgi_errors_stream` with a basic format. 67 | """ 68 | logger = logging.getLogger(app.name) 69 | 70 | if app.debug and not logger.level: 71 | logger.setLevel(logging.DEBUG) 72 | 73 | if not has_level_handler(logger): 74 | logger.addHandler(default_handler) 75 | 76 | return logger 77 | -------------------------------------------------------------------------------- /src/flask/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nazarepiedady/flask-docs-pt/c5b543b639a661befebe8aa1afda4327642e5b8f/src/flask/py.typed -------------------------------------------------------------------------------- /src/flask/sansio/README.md: -------------------------------------------------------------------------------- 1 | # Sansio 2 | 3 | This folder contains code that can be used by alternative Flask 4 | implementations, for example Quart. The code therefore cannot do any 5 | IO, nor be part of a likely IO path. Finally this code cannot use the 6 | Flask globals. 7 | -------------------------------------------------------------------------------- /src/flask/signals.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from blinker import Namespace 4 | 5 | # This namespace is only for signals provided by Flask itself. 6 | _signals = Namespace() 7 | 8 | template_rendered = _signals.signal("template-rendered") 9 | before_render_template = _signals.signal("before-render-template") 10 | request_started = _signals.signal("request-started") 11 | request_finished = _signals.signal("request-finished") 12 | request_tearing_down = _signals.signal("request-tearing-down") 13 | got_request_exception = _signals.signal("got-request-exception") 14 | appcontext_tearing_down = _signals.signal("appcontext-tearing-down") 15 | appcontext_pushed = _signals.signal("appcontext-pushed") 16 | appcontext_popped = _signals.signal("appcontext-popped") 17 | message_flashed = _signals.signal("message-flashed") 18 | -------------------------------------------------------------------------------- /src/flask/typing.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import typing as t 4 | 5 | if t.TYPE_CHECKING: # pragma: no cover 6 | from _typeshed.wsgi import WSGIApplication # noqa: F401 7 | from werkzeug.datastructures import Headers # noqa: F401 8 | from werkzeug.sansio.response import Response # noqa: F401 9 | 10 | # The possible types that are directly convertible or are a Response object. 11 | ResponseValue = t.Union[ 12 | "Response", 13 | str, 14 | bytes, 15 | t.List[t.Any], 16 | # Only dict is actually accepted, but Mapping allows for TypedDict. 17 | t.Mapping[str, t.Any], 18 | t.Iterator[str], 19 | t.Iterator[bytes], 20 | ] 21 | 22 | # the possible types for an individual HTTP header 23 | # This should be a Union, but mypy doesn't pass unless it's a TypeVar. 24 | HeaderValue = t.Union[str, t.List[str], t.Tuple[str, ...]] 25 | 26 | # the possible types for HTTP headers 27 | HeadersValue = t.Union[ 28 | "Headers", 29 | t.Mapping[str, HeaderValue], 30 | t.Sequence[t.Tuple[str, HeaderValue]], 31 | ] 32 | 33 | # The possible types returned by a route function. 34 | ResponseReturnValue = t.Union[ 35 | ResponseValue, 36 | t.Tuple[ResponseValue, HeadersValue], 37 | t.Tuple[ResponseValue, int], 38 | t.Tuple[ResponseValue, int, HeadersValue], 39 | "WSGIApplication", 40 | ] 41 | 42 | # Allow any subclass of werkzeug.Response, such as the one from Flask, 43 | # as a callback argument. Using werkzeug.Response directly makes a 44 | # callback annotated with flask.Response fail type checking. 45 | ResponseClass = t.TypeVar("ResponseClass", bound="Response") 46 | 47 | AppOrBlueprintKey = t.Optional[str] # The App key is None, whereas blueprints are named 48 | AfterRequestCallable = t.Union[ 49 | t.Callable[[ResponseClass], ResponseClass], 50 | t.Callable[[ResponseClass], t.Awaitable[ResponseClass]], 51 | ] 52 | BeforeFirstRequestCallable = t.Union[ 53 | t.Callable[[], None], t.Callable[[], t.Awaitable[None]] 54 | ] 55 | BeforeRequestCallable = t.Union[ 56 | t.Callable[[], t.Optional[ResponseReturnValue]], 57 | t.Callable[[], t.Awaitable[t.Optional[ResponseReturnValue]]], 58 | ] 59 | ShellContextProcessorCallable = t.Callable[[], t.Dict[str, t.Any]] 60 | TeardownCallable = t.Union[ 61 | t.Callable[[t.Optional[BaseException]], None], 62 | t.Callable[[t.Optional[BaseException]], t.Awaitable[None]], 63 | ] 64 | TemplateContextProcessorCallable = t.Union[ 65 | t.Callable[[], t.Dict[str, t.Any]], 66 | t.Callable[[], t.Awaitable[t.Dict[str, t.Any]]], 67 | ] 68 | TemplateFilterCallable = t.Callable[..., t.Any] 69 | TemplateGlobalCallable = t.Callable[..., t.Any] 70 | TemplateTestCallable = t.Callable[..., bool] 71 | URLDefaultCallable = t.Callable[[str, dict], None] 72 | URLValuePreprocessorCallable = t.Callable[[t.Optional[str], t.Optional[dict]], None] 73 | 74 | # This should take Exception, but that either breaks typing the argument 75 | # with a specific exception, or decorating multiple times with different 76 | # exceptions (and using a union type on the argument). 77 | # https://github.com/pallets/flask/issues/4095 78 | # https://github.com/pallets/flask/issues/4295 79 | # https://github.com/pallets/flask/issues/4297 80 | ErrorHandlerCallable = t.Union[ 81 | t.Callable[[t.Any], ResponseReturnValue], 82 | t.Callable[[t.Any], t.Awaitable[ResponseReturnValue]], 83 | ] 84 | 85 | RouteCallable = t.Union[ 86 | t.Callable[..., ResponseReturnValue], 87 | t.Callable[..., t.Awaitable[ResponseReturnValue]], 88 | ] 89 | -------------------------------------------------------------------------------- /static_files/debugger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nazarepiedady/flask-docs-pt/c5b543b639a661befebe8aa1afda4327642e5b8f/static_files/debugger.png -------------------------------------------------------------------------------- /static_files/flask-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nazarepiedady/flask-docs-pt/c5b543b639a661befebe8aa1afda4327642e5b8f/static_files/flask-logo.png -------------------------------------------------------------------------------- /static_files/flaskr_edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nazarepiedady/flask-docs-pt/c5b543b639a661befebe8aa1afda4327642e5b8f/static_files/flaskr_edit.png -------------------------------------------------------------------------------- /static_files/flaskr_index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nazarepiedady/flask-docs-pt/c5b543b639a661befebe8aa1afda4327642e5b8f/static_files/flaskr_index.png -------------------------------------------------------------------------------- /static_files/flaskr_login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nazarepiedady/flask-docs-pt/c5b543b639a661befebe8aa1afda4327642e5b8f/static_files/flaskr_login.png -------------------------------------------------------------------------------- /static_files/no.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nazarepiedady/flask-docs-pt/c5b543b639a661befebe8aa1afda4327642e5b8f/static_files/no.png -------------------------------------------------------------------------------- /static_files/pycharm-runconfig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nazarepiedady/flask-docs-pt/c5b543b639a661befebe8aa1afda4327642e5b8f/static_files/pycharm-runconfig.png -------------------------------------------------------------------------------- /static_files/yes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nazarepiedady/flask-docs-pt/c5b543b639a661befebe8aa1afda4327642e5b8f/static_files/yes.png -------------------------------------------------------------------------------- /tests/static/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "TEST_KEY": "foo", 3 | "SECRET_KEY": "config" 4 | } 5 | -------------------------------------------------------------------------------- /tests/static/config.toml: -------------------------------------------------------------------------------- 1 | TEST_KEY="foo" 2 | SECRET_KEY="config" 3 | -------------------------------------------------------------------------------- /tests/static/index.html: -------------------------------------------------------------------------------- 1 |

Hello World!

2 | -------------------------------------------------------------------------------- /tests/templates/_macro.html: -------------------------------------------------------------------------------- 1 | {% macro hello(name) %}Hello {{ name }}!{% endmacro %} 2 | -------------------------------------------------------------------------------- /tests/templates/context_template.html: -------------------------------------------------------------------------------- 1 |

{{ value }}|{{ injected_value }} 2 | -------------------------------------------------------------------------------- /tests/templates/escaping_template.html: -------------------------------------------------------------------------------- 1 | {{ text }} 2 | {{ html }} 3 | {% autoescape false %}{{ text }} 4 | {{ html }}{% endautoescape %} 5 | {% autoescape true %}{{ text }} 6 | {{ html }}{% endautoescape %} 7 | -------------------------------------------------------------------------------- /tests/templates/mail.txt: -------------------------------------------------------------------------------- 1 | {{ foo}} Mail 2 | -------------------------------------------------------------------------------- /tests/templates/nested/nested.txt: -------------------------------------------------------------------------------- 1 | I'm nested 2 | -------------------------------------------------------------------------------- /tests/templates/non_escaping_template.txt: -------------------------------------------------------------------------------- 1 | {{ text }} 2 | {{ html }} 3 | {% autoescape false %}{{ text }} 4 | {{ html }}{% endautoescape %} 5 | {% autoescape true %}{{ text }} 6 | {{ html }}{% endautoescape %} 7 | {{ text }} 8 | {{ html }} 9 | -------------------------------------------------------------------------------- /tests/templates/simple_template.html: -------------------------------------------------------------------------------- 1 |

{{ whiskey }}

2 | -------------------------------------------------------------------------------- /tests/templates/template_filter.html: -------------------------------------------------------------------------------- 1 | {{ value|super_reverse }} 2 | -------------------------------------------------------------------------------- /tests/templates/template_test.html: -------------------------------------------------------------------------------- 1 | {% if value is boolean %} 2 | Success! 3 | {% endif %} 4 | -------------------------------------------------------------------------------- /tests/test_apps/blueprintapp/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | 3 | app = Flask(__name__) 4 | app.config["DEBUG"] = True 5 | from blueprintapp.apps.admin import admin # noqa: E402 6 | from blueprintapp.apps.frontend import frontend # noqa: E402 7 | 8 | app.register_blueprint(admin) 9 | app.register_blueprint(frontend) 10 | -------------------------------------------------------------------------------- /tests/test_apps/blueprintapp/apps/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nazarepiedady/flask-docs-pt/c5b543b639a661befebe8aa1afda4327642e5b8f/tests/test_apps/blueprintapp/apps/__init__.py -------------------------------------------------------------------------------- /tests/test_apps/blueprintapp/apps/admin/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | from flask import render_template 3 | 4 | admin = Blueprint( 5 | "admin", 6 | __name__, 7 | url_prefix="/admin", 8 | template_folder="templates", 9 | static_folder="static", 10 | ) 11 | 12 | 13 | @admin.route("/") 14 | def index(): 15 | return render_template("admin/index.html") 16 | 17 | 18 | @admin.route("/index2") 19 | def index2(): 20 | return render_template("./admin/index.html") 21 | -------------------------------------------------------------------------------- /tests/test_apps/blueprintapp/apps/admin/static/css/test.css: -------------------------------------------------------------------------------- 1 | /* nested file */ 2 | -------------------------------------------------------------------------------- /tests/test_apps/blueprintapp/apps/admin/static/test.txt: -------------------------------------------------------------------------------- 1 | Admin File 2 | -------------------------------------------------------------------------------- /tests/test_apps/blueprintapp/apps/admin/templates/admin/index.html: -------------------------------------------------------------------------------- 1 | Hello from the Admin 2 | -------------------------------------------------------------------------------- /tests/test_apps/blueprintapp/apps/frontend/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | from flask import render_template 3 | 4 | frontend = Blueprint("frontend", __name__, template_folder="templates") 5 | 6 | 7 | @frontend.route("/") 8 | def index(): 9 | return render_template("frontend/index.html") 10 | 11 | 12 | @frontend.route("/missing") 13 | def missing_template(): 14 | return render_template("missing_template.html") 15 | -------------------------------------------------------------------------------- /tests/test_apps/blueprintapp/apps/frontend/templates/frontend/index.html: -------------------------------------------------------------------------------- 1 | Hello from the Frontend 2 | -------------------------------------------------------------------------------- /tests/test_apps/cliapp/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nazarepiedady/flask-docs-pt/c5b543b639a661befebe8aa1afda4327642e5b8f/tests/test_apps/cliapp/__init__.py -------------------------------------------------------------------------------- /tests/test_apps/cliapp/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | 3 | testapp = Flask("testapp") 4 | -------------------------------------------------------------------------------- /tests/test_apps/cliapp/factory.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | 3 | 4 | def create_app(): 5 | return Flask("app") 6 | 7 | 8 | def create_app2(foo, bar): 9 | return Flask("_".join(["app2", foo, bar])) 10 | 11 | 12 | def no_app(): 13 | pass 14 | -------------------------------------------------------------------------------- /tests/test_apps/cliapp/importerrorapp.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | 3 | raise ImportError() 4 | 5 | testapp = Flask("testapp") 6 | -------------------------------------------------------------------------------- /tests/test_apps/cliapp/inner1/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | 3 | application = Flask(__name__) 4 | -------------------------------------------------------------------------------- /tests/test_apps/cliapp/inner1/inner2/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nazarepiedady/flask-docs-pt/c5b543b639a661befebe8aa1afda4327642e5b8f/tests/test_apps/cliapp/inner1/inner2/__init__.py -------------------------------------------------------------------------------- /tests/test_apps/cliapp/inner1/inner2/flask.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | 3 | app = Flask(__name__) 4 | -------------------------------------------------------------------------------- /tests/test_apps/cliapp/message.txt: -------------------------------------------------------------------------------- 1 | So long, and thanks for all the fish. 2 | -------------------------------------------------------------------------------- /tests/test_apps/cliapp/multiapp.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | 3 | app1 = Flask("app1") 4 | app2 = Flask("app2") 5 | -------------------------------------------------------------------------------- /tests/test_apps/helloworld/hello.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | 3 | app = Flask(__name__) 4 | 5 | 6 | @app.route("/") 7 | def hello(): 8 | return "Hello World!" 9 | -------------------------------------------------------------------------------- /tests/test_apps/helloworld/wsgi.py: -------------------------------------------------------------------------------- 1 | from hello import app # noqa: F401 2 | -------------------------------------------------------------------------------- /tests/test_apps/subdomaintestmodule/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Module 2 | 3 | 4 | mod = Module(__name__, "foo", subdomain="foo") 5 | -------------------------------------------------------------------------------- /tests/test_apps/subdomaintestmodule/static/hello.txt: -------------------------------------------------------------------------------- 1 | Hello Subdomain 2 | -------------------------------------------------------------------------------- /tests/test_async.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | import pytest 4 | 5 | from flask import Blueprint 6 | from flask import Flask 7 | from flask import request 8 | from flask.views import MethodView 9 | from flask.views import View 10 | 11 | pytest.importorskip("asgiref") 12 | 13 | 14 | class AppError(Exception): 15 | pass 16 | 17 | 18 | class BlueprintError(Exception): 19 | pass 20 | 21 | 22 | class AsyncView(View): 23 | methods = ["GET", "POST"] 24 | 25 | async def dispatch_request(self): 26 | await asyncio.sleep(0) 27 | return request.method 28 | 29 | 30 | class AsyncMethodView(MethodView): 31 | async def get(self): 32 | await asyncio.sleep(0) 33 | return "GET" 34 | 35 | async def post(self): 36 | await asyncio.sleep(0) 37 | return "POST" 38 | 39 | 40 | @pytest.fixture(name="async_app") 41 | def _async_app(): 42 | app = Flask(__name__) 43 | 44 | @app.route("/", methods=["GET", "POST"]) 45 | @app.route("/home", methods=["GET", "POST"]) 46 | async def index(): 47 | await asyncio.sleep(0) 48 | return request.method 49 | 50 | @app.errorhandler(AppError) 51 | async def handle(_): 52 | return "", 412 53 | 54 | @app.route("/error") 55 | async def error(): 56 | raise AppError() 57 | 58 | blueprint = Blueprint("bp", __name__) 59 | 60 | @blueprint.route("/", methods=["GET", "POST"]) 61 | async def bp_index(): 62 | await asyncio.sleep(0) 63 | return request.method 64 | 65 | @blueprint.errorhandler(BlueprintError) 66 | async def bp_handle(_): 67 | return "", 412 68 | 69 | @blueprint.route("/error") 70 | async def bp_error(): 71 | raise BlueprintError() 72 | 73 | app.register_blueprint(blueprint, url_prefix="/bp") 74 | 75 | app.add_url_rule("/view", view_func=AsyncView.as_view("view")) 76 | app.add_url_rule("/methodview", view_func=AsyncMethodView.as_view("methodview")) 77 | 78 | return app 79 | 80 | 81 | @pytest.mark.parametrize("path", ["/", "/home", "/bp/", "/view", "/methodview"]) 82 | def test_async_route(path, async_app): 83 | test_client = async_app.test_client() 84 | response = test_client.get(path) 85 | assert b"GET" in response.get_data() 86 | response = test_client.post(path) 87 | assert b"POST" in response.get_data() 88 | 89 | 90 | @pytest.mark.parametrize("path", ["/error", "/bp/error"]) 91 | def test_async_error_handler(path, async_app): 92 | test_client = async_app.test_client() 93 | response = test_client.get(path) 94 | assert response.status_code == 412 95 | 96 | 97 | def test_async_before_after_request(): 98 | app_before_called = False 99 | app_after_called = False 100 | bp_before_called = False 101 | bp_after_called = False 102 | 103 | app = Flask(__name__) 104 | 105 | @app.route("/") 106 | def index(): 107 | return "" 108 | 109 | @app.before_request 110 | async def before(): 111 | nonlocal app_before_called 112 | app_before_called = True 113 | 114 | @app.after_request 115 | async def after(response): 116 | nonlocal app_after_called 117 | app_after_called = True 118 | return response 119 | 120 | blueprint = Blueprint("bp", __name__) 121 | 122 | @blueprint.route("/") 123 | def bp_index(): 124 | return "" 125 | 126 | @blueprint.before_request 127 | async def bp_before(): 128 | nonlocal bp_before_called 129 | bp_before_called = True 130 | 131 | @blueprint.after_request 132 | async def bp_after(response): 133 | nonlocal bp_after_called 134 | bp_after_called = True 135 | return response 136 | 137 | app.register_blueprint(blueprint, url_prefix="/bp") 138 | 139 | test_client = app.test_client() 140 | test_client.get("/") 141 | assert app_before_called 142 | assert app_after_called 143 | test_client.get("/bp/") 144 | assert bp_before_called 145 | assert bp_after_called 146 | -------------------------------------------------------------------------------- /tests/test_converters.py: -------------------------------------------------------------------------------- 1 | from werkzeug.routing import BaseConverter 2 | 3 | from flask import request 4 | from flask import session 5 | from flask import url_for 6 | 7 | 8 | def test_custom_converters(app, client): 9 | class ListConverter(BaseConverter): 10 | def to_python(self, value): 11 | return value.split(",") 12 | 13 | def to_url(self, value): 14 | base_to_url = super().to_url 15 | return ",".join(base_to_url(x) for x in value) 16 | 17 | app.url_map.converters["list"] = ListConverter 18 | 19 | @app.route("/") 20 | def index(args): 21 | return "|".join(args) 22 | 23 | assert client.get("/1,2,3").data == b"1|2|3" 24 | 25 | with app.test_request_context(): 26 | assert url_for("index", args=[4, 5, 6]) == "/4,5,6" 27 | 28 | 29 | def test_context_available(app, client): 30 | class ContextConverter(BaseConverter): 31 | def to_python(self, value): 32 | assert request is not None 33 | assert session is not None 34 | return value 35 | 36 | app.url_map.converters["ctx"] = ContextConverter 37 | 38 | @app.get("/") 39 | def index(name): 40 | return name 41 | 42 | assert client.get("/admin").data == b"admin" 43 | -------------------------------------------------------------------------------- /tests/test_instance_config.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import pytest 4 | 5 | import flask 6 | 7 | 8 | def test_explicit_instance_paths(modules_tmp_path): 9 | with pytest.raises(ValueError, match=".*must be absolute"): 10 | flask.Flask(__name__, instance_path="instance") 11 | 12 | app = flask.Flask(__name__, instance_path=os.fspath(modules_tmp_path)) 13 | assert app.instance_path == os.fspath(modules_tmp_path) 14 | 15 | 16 | def test_uninstalled_module_paths(modules_tmp_path, purge_module): 17 | (modules_tmp_path / "config_module_app.py").write_text( 18 | "import os\n" 19 | "import flask\n" 20 | "here = os.path.abspath(os.path.dirname(__file__))\n" 21 | "app = flask.Flask(__name__)\n" 22 | ) 23 | purge_module("config_module_app") 24 | 25 | from config_module_app import app 26 | 27 | assert app.instance_path == os.fspath(modules_tmp_path / "instance") 28 | 29 | 30 | def test_uninstalled_package_paths(modules_tmp_path, purge_module): 31 | app = modules_tmp_path / "config_package_app" 32 | app.mkdir() 33 | (app / "__init__.py").write_text( 34 | "import os\n" 35 | "import flask\n" 36 | "here = os.path.abspath(os.path.dirname(__file__))\n" 37 | "app = flask.Flask(__name__)\n" 38 | ) 39 | purge_module("config_package_app") 40 | 41 | from config_package_app import app 42 | 43 | assert app.instance_path == os.fspath(modules_tmp_path / "instance") 44 | 45 | 46 | def test_uninstalled_namespace_paths(tmp_path, monkeypatch, purge_module): 47 | def create_namespace(package): 48 | project = tmp_path / f"project-{package}" 49 | monkeypatch.syspath_prepend(os.fspath(project)) 50 | ns = project / "namespace" / package 51 | ns.mkdir(parents=True) 52 | (ns / "__init__.py").write_text("import flask\napp = flask.Flask(__name__)\n") 53 | return project 54 | 55 | _ = create_namespace("package1") 56 | project2 = create_namespace("package2") 57 | purge_module("namespace.package2") 58 | purge_module("namespace") 59 | 60 | from namespace.package2 import app 61 | 62 | assert app.instance_path == os.fspath(project2 / "instance") 63 | 64 | 65 | def test_installed_module_paths( 66 | modules_tmp_path, modules_tmp_path_prefix, purge_module, site_packages, limit_loader 67 | ): 68 | (site_packages / "site_app.py").write_text( 69 | "import flask\napp = flask.Flask(__name__)\n" 70 | ) 71 | purge_module("site_app") 72 | 73 | from site_app import app 74 | 75 | assert app.instance_path == os.fspath( 76 | modules_tmp_path / "var" / "site_app-instance" 77 | ) 78 | 79 | 80 | def test_installed_package_paths( 81 | limit_loader, modules_tmp_path, modules_tmp_path_prefix, purge_module, monkeypatch 82 | ): 83 | installed_path = modules_tmp_path / "path" 84 | installed_path.mkdir() 85 | monkeypatch.syspath_prepend(installed_path) 86 | 87 | app = installed_path / "installed_package" 88 | app.mkdir() 89 | (app / "__init__.py").write_text("import flask\napp = flask.Flask(__name__)\n") 90 | purge_module("installed_package") 91 | 92 | from installed_package import app 93 | 94 | assert app.instance_path == os.fspath( 95 | modules_tmp_path / "var" / "installed_package-instance" 96 | ) 97 | 98 | 99 | def test_prefix_package_paths( 100 | limit_loader, modules_tmp_path, modules_tmp_path_prefix, purge_module, site_packages 101 | ): 102 | app = site_packages / "site_package" 103 | app.mkdir() 104 | (app / "__init__.py").write_text("import flask\napp = flask.Flask(__name__)\n") 105 | purge_module("site_package") 106 | 107 | import site_package 108 | 109 | assert site_package.app.instance_path == os.fspath( 110 | modules_tmp_path / "var" / "site_package-instance" 111 | ) 112 | -------------------------------------------------------------------------------- /tests/test_json_tag.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from datetime import timezone 3 | from uuid import uuid4 4 | 5 | import pytest 6 | from markupsafe import Markup 7 | 8 | from flask.json.tag import JSONTag 9 | from flask.json.tag import TaggedJSONSerializer 10 | 11 | 12 | @pytest.mark.parametrize( 13 | "data", 14 | ( 15 | {" t": (1, 2, 3)}, 16 | {" t__": b"a"}, 17 | {" di": " di"}, 18 | {"x": (1, 2, 3), "y": 4}, 19 | (1, 2, 3), 20 | [(1, 2, 3)], 21 | b"\xff", 22 | Markup(""), 23 | uuid4(), 24 | datetime.now(tz=timezone.utc).replace(microsecond=0), 25 | ), 26 | ) 27 | def test_dump_load_unchanged(data): 28 | s = TaggedJSONSerializer() 29 | assert s.loads(s.dumps(data)) == data 30 | 31 | 32 | def test_duplicate_tag(): 33 | class TagDict(JSONTag): 34 | key = " d" 35 | 36 | s = TaggedJSONSerializer() 37 | pytest.raises(KeyError, s.register, TagDict) 38 | s.register(TagDict, force=True, index=0) 39 | assert isinstance(s.tags[" d"], TagDict) 40 | assert isinstance(s.order[0], TagDict) 41 | 42 | 43 | def test_custom_tag(): 44 | class Foo: # noqa: B903, for Python2 compatibility 45 | def __init__(self, data): 46 | self.data = data 47 | 48 | class TagFoo(JSONTag): 49 | __slots__ = () 50 | key = " f" 51 | 52 | def check(self, value): 53 | return isinstance(value, Foo) 54 | 55 | def to_json(self, value): 56 | return self.serializer.tag(value.data) 57 | 58 | def to_python(self, value): 59 | return Foo(value) 60 | 61 | s = TaggedJSONSerializer() 62 | s.register(TagFoo) 63 | assert s.loads(s.dumps(Foo("bar"))).data == "bar" 64 | 65 | 66 | def test_tag_interface(): 67 | t = JSONTag(None) 68 | pytest.raises(NotImplementedError, t.check, None) 69 | pytest.raises(NotImplementedError, t.to_json, None) 70 | pytest.raises(NotImplementedError, t.to_python, None) 71 | 72 | 73 | def test_tag_order(): 74 | class Tag1(JSONTag): 75 | key = " 1" 76 | 77 | class Tag2(JSONTag): 78 | key = " 2" 79 | 80 | s = TaggedJSONSerializer() 81 | 82 | s.register(Tag1, index=-1) 83 | assert isinstance(s.order[-2], Tag1) 84 | 85 | s.register(Tag2, index=None) 86 | assert isinstance(s.order[-1], Tag2) 87 | -------------------------------------------------------------------------------- /tests/test_logging.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import sys 3 | from io import StringIO 4 | 5 | import pytest 6 | 7 | from flask.logging import default_handler 8 | from flask.logging import has_level_handler 9 | from flask.logging import wsgi_errors_stream 10 | 11 | 12 | @pytest.fixture(autouse=True) 13 | def reset_logging(pytestconfig): 14 | root_handlers = logging.root.handlers[:] 15 | logging.root.handlers = [] 16 | root_level = logging.root.level 17 | 18 | logger = logging.getLogger("flask_test") 19 | logger.handlers = [] 20 | logger.setLevel(logging.NOTSET) 21 | 22 | logging_plugin = pytestconfig.pluginmanager.unregister(name="logging-plugin") 23 | 24 | yield 25 | 26 | logging.root.handlers[:] = root_handlers 27 | logging.root.setLevel(root_level) 28 | 29 | logger.handlers = [] 30 | logger.setLevel(logging.NOTSET) 31 | 32 | if logging_plugin: 33 | pytestconfig.pluginmanager.register(logging_plugin, "logging-plugin") 34 | 35 | 36 | def test_logger(app): 37 | assert app.logger.name == "flask_test" 38 | assert app.logger.level == logging.NOTSET 39 | assert app.logger.handlers == [default_handler] 40 | 41 | 42 | def test_logger_debug(app): 43 | app.debug = True 44 | assert app.logger.level == logging.DEBUG 45 | assert app.logger.handlers == [default_handler] 46 | 47 | 48 | def test_existing_handler(app): 49 | logging.root.addHandler(logging.StreamHandler()) 50 | assert app.logger.level == logging.NOTSET 51 | assert not app.logger.handlers 52 | 53 | 54 | def test_wsgi_errors_stream(app, client): 55 | @app.route("/") 56 | def index(): 57 | app.logger.error("test") 58 | return "" 59 | 60 | stream = StringIO() 61 | client.get("/", errors_stream=stream) 62 | assert "ERROR in test_logging: test" in stream.getvalue() 63 | 64 | assert wsgi_errors_stream._get_current_object() is sys.stderr 65 | 66 | with app.test_request_context(errors_stream=stream): 67 | assert wsgi_errors_stream._get_current_object() is stream 68 | 69 | 70 | def test_has_level_handler(): 71 | logger = logging.getLogger("flask.app") 72 | assert not has_level_handler(logger) 73 | 74 | handler = logging.StreamHandler() 75 | logging.root.addHandler(handler) 76 | assert has_level_handler(logger) 77 | 78 | logger.propagate = False 79 | assert not has_level_handler(logger) 80 | logger.propagate = True 81 | 82 | handler.setLevel(logging.ERROR) 83 | assert not has_level_handler(logger) 84 | 85 | 86 | def test_log_view_exception(app, client): 87 | @app.route("/") 88 | def index(): 89 | raise Exception("test") 90 | 91 | app.testing = False 92 | stream = StringIO() 93 | rv = client.get("/", errors_stream=stream) 94 | assert rv.status_code == 500 95 | assert rv.data 96 | err = stream.getvalue() 97 | assert "Exception on / [GET]" in err 98 | assert "Exception: test" in err 99 | -------------------------------------------------------------------------------- /tests/test_regression.py: -------------------------------------------------------------------------------- 1 | import flask 2 | 3 | 4 | def test_aborting(app): 5 | class Foo(Exception): 6 | whatever = 42 7 | 8 | @app.errorhandler(Foo) 9 | def handle_foo(e): 10 | return str(e.whatever) 11 | 12 | @app.route("/") 13 | def index(): 14 | raise flask.abort(flask.redirect(flask.url_for("test"))) 15 | 16 | @app.route("/test") 17 | def test(): 18 | raise Foo() 19 | 20 | with app.test_client() as c: 21 | rv = c.get("/") 22 | location_parts = rv.headers["Location"].rpartition("/") 23 | 24 | if location_parts[0]: 25 | # For older Werkzeug that used absolute redirects. 26 | assert location_parts[0] == "http://localhost" 27 | 28 | assert location_parts[2] == "test" 29 | rv = c.get("/test") 30 | assert rv.data == b"42" 31 | -------------------------------------------------------------------------------- /tests/test_session_interface.py: -------------------------------------------------------------------------------- 1 | import flask 2 | from flask.globals import request_ctx 3 | from flask.sessions import SessionInterface 4 | 5 | 6 | def test_open_session_with_endpoint(): 7 | """If request.endpoint (or other URL matching behavior) is needed 8 | while loading the session, RequestContext.match_request() can be 9 | called manually. 10 | """ 11 | 12 | class MySessionInterface(SessionInterface): 13 | def save_session(self, app, session, response): 14 | pass 15 | 16 | def open_session(self, app, request): 17 | request_ctx.match_request() 18 | assert request.endpoint is not None 19 | 20 | app = flask.Flask(__name__) 21 | app.session_interface = MySessionInterface() 22 | 23 | @app.get("/") 24 | def index(): 25 | return "Hello, World!" 26 | 27 | response = app.test_client().get("/") 28 | assert response.status_code == 200 29 | -------------------------------------------------------------------------------- /tests/test_subclassing.py: -------------------------------------------------------------------------------- 1 | from io import StringIO 2 | 3 | import flask 4 | 5 | 6 | def test_suppressed_exception_logging(): 7 | class SuppressedFlask(flask.Flask): 8 | def log_exception(self, exc_info): 9 | pass 10 | 11 | out = StringIO() 12 | app = SuppressedFlask(__name__) 13 | 14 | @app.route("/") 15 | def index(): 16 | raise Exception("test") 17 | 18 | rv = app.test_client().get("/", errors_stream=out) 19 | assert rv.status_code == 500 20 | assert b"Internal Server Error" in rv.data 21 | assert not out.getvalue() 22 | -------------------------------------------------------------------------------- /tests/typing/typing_app_decorators.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import typing as t 4 | 5 | from flask import Flask 6 | from flask import Response 7 | 8 | app = Flask(__name__) 9 | 10 | 11 | @app.after_request 12 | def after_sync(response: Response) -> Response: 13 | return Response() 14 | 15 | 16 | @app.after_request 17 | async def after_async(response: Response) -> Response: 18 | return Response() 19 | 20 | 21 | @app.before_request 22 | def before_sync() -> None: 23 | ... 24 | 25 | 26 | @app.before_request 27 | async def before_async() -> None: 28 | ... 29 | 30 | 31 | @app.teardown_appcontext 32 | def teardown_sync(exc: t.Optional[BaseException]) -> None: 33 | ... 34 | 35 | 36 | @app.teardown_appcontext 37 | async def teardown_async(exc: t.Optional[BaseException]) -> None: 38 | ... 39 | -------------------------------------------------------------------------------- /tests/typing/typing_error_handler.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from http import HTTPStatus 4 | 5 | from werkzeug.exceptions import BadRequest 6 | from werkzeug.exceptions import NotFound 7 | 8 | from flask import Flask 9 | 10 | app = Flask(__name__) 11 | 12 | 13 | @app.errorhandler(400) 14 | @app.errorhandler(HTTPStatus.BAD_REQUEST) 15 | @app.errorhandler(BadRequest) 16 | def handle_400(e: BadRequest) -> str: 17 | return "" 18 | 19 | 20 | @app.errorhandler(ValueError) 21 | def handle_custom(e: ValueError) -> str: 22 | return "" 23 | 24 | 25 | @app.errorhandler(ValueError) 26 | def handle_accept_base(e: Exception) -> str: 27 | return "" 28 | 29 | 30 | @app.errorhandler(BadRequest) 31 | @app.errorhandler(404) 32 | def handle_multiple(e: BadRequest | NotFound) -> str: 33 | return "" 34 | -------------------------------------------------------------------------------- /tests/typing/typing_route.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import typing as t 4 | from http import HTTPStatus 5 | 6 | from flask import Flask 7 | from flask import jsonify 8 | from flask import stream_template 9 | from flask.templating import render_template 10 | from flask.views import View 11 | from flask.wrappers import Response 12 | 13 | app = Flask(__name__) 14 | 15 | 16 | @app.route("/str") 17 | def hello_str() -> str: 18 | return "

Hello, World!

" 19 | 20 | 21 | @app.route("/bytes") 22 | def hello_bytes() -> bytes: 23 | return b"

Hello, World!

" 24 | 25 | 26 | @app.route("/json") 27 | def hello_json() -> Response: 28 | return jsonify("Hello, World!") 29 | 30 | 31 | @app.route("/json/dict") 32 | def hello_json_dict() -> t.Dict[str, t.Any]: 33 | return {"response": "Hello, World!"} 34 | 35 | 36 | @app.route("/json/dict") 37 | def hello_json_list() -> t.List[t.Any]: 38 | return [{"message": "Hello"}, {"message": "World"}] 39 | 40 | 41 | class StatusJSON(t.TypedDict): 42 | status: str 43 | 44 | 45 | @app.route("/typed-dict") 46 | def typed_dict() -> StatusJSON: 47 | return {"status": "ok"} 48 | 49 | 50 | @app.route("/generator") 51 | def hello_generator() -> t.Generator[str, None, None]: 52 | def show() -> t.Generator[str, None, None]: 53 | for x in range(100): 54 | yield f"data:{x}\n\n" 55 | 56 | return show() 57 | 58 | 59 | @app.route("/generator-expression") 60 | def hello_generator_expression() -> t.Iterator[bytes]: 61 | return (f"data:{x}\n\n".encode() for x in range(100)) 62 | 63 | 64 | @app.route("/iterator") 65 | def hello_iterator() -> t.Iterator[str]: 66 | return iter([f"data:{x}\n\n" for x in range(100)]) 67 | 68 | 69 | @app.route("/status") 70 | @app.route("/status/") 71 | def tuple_status(code: int = 200) -> tuple[str, int]: 72 | return "hello", code 73 | 74 | 75 | @app.route("/status-enum") 76 | def tuple_status_enum() -> tuple[str, int]: 77 | return "hello", HTTPStatus.OK 78 | 79 | 80 | @app.route("/headers") 81 | def tuple_headers() -> tuple[str, dict[str, str]]: 82 | return "Hello, World!", {"Content-Type": "text/plain"} 83 | 84 | 85 | @app.route("/template") 86 | @app.route("/template/") 87 | def return_template(name: str | None = None) -> str: 88 | return render_template("index.html", name=name) 89 | 90 | 91 | @app.route("/template") 92 | def return_template_stream() -> t.Iterator[str]: 93 | return stream_template("index.html", name="Hello") 94 | 95 | 96 | @app.route("/async") 97 | async def async_route() -> str: 98 | return "Hello" 99 | 100 | 101 | class RenderTemplateView(View): 102 | def __init__(self: RenderTemplateView, template_name: str) -> None: 103 | self.template_name = template_name 104 | 105 | def dispatch_request(self: RenderTemplateView) -> str: 106 | return render_template(self.template_name) 107 | 108 | 109 | app.add_url_rule( 110 | "/about", 111 | view_func=RenderTemplateView.as_view("about_page", template_name="about.html"), 112 | ) 113 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = 3 | py3{12,11,10,9,8} 4 | pypy310 5 | py311-min 6 | py38-dev 7 | style 8 | typing 9 | docs 10 | skip_missing_interpreters = true 11 | 12 | [testenv] 13 | package = wheel 14 | wheel_build_env = .pkg 15 | envtmpdir = {toxworkdir}/tmp/{envname} 16 | constrain_package_deps = true 17 | use_frozen_constraints = true 18 | deps = 19 | -r requirements/tests.txt 20 | min: -r requirements/tests-pallets-min.txt 21 | dev: https://github.com/pallets/werkzeug/archive/refs/heads/main.tar.gz 22 | dev: https://github.com/pallets/jinja/archive/refs/heads/main.tar.gz 23 | dev: https://github.com/pallets/markupsafe/archive/refs/heads/main.tar.gz 24 | dev: https://github.com/pallets/itsdangerous/archive/refs/heads/main.tar.gz 25 | dev: https://github.com/pallets/click/archive/refs/heads/main.tar.gz 26 | # examples/tutorial[test] 27 | # examples/javascript[test] 28 | # commands = pytest -v --tb=short --basetemp={envtmpdir} {posargs:tests examples} 29 | commands = pytest -v --tb=short --basetemp={envtmpdir} {posargs:tests} 30 | 31 | [testenv:style] 32 | deps = pre-commit 33 | skip_install = true 34 | commands = pre-commit run --all-files 35 | 36 | [testenv:typing] 37 | deps = -r requirements/typing.txt 38 | commands = mypy 39 | 40 | [testenv:docs] 41 | deps = -r requirements/docs.txt 42 | commands = sphinx-build -W -b html -d {envtmpdir}/doctrees docs {envtmpdir}/html 43 | --------------------------------------------------------------------------------