diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..8e52ba28f --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,32 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/python +{ + "name": "Python VS Code Flask Tutorial", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "mcr.microsoft.com/devcontainers/python:1-3.12-bullseye", + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + "postCreateCommand": "pip3 install --user -r requirements.txt", + + // Configure tool-specific properties. + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python", + "ms-python.black-formatter", + "ms-python.vscode-pylance", + "charliermarsh.ruff", + "ms-python.debugpy" + ] + } + } + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..a39d2b053 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,12 @@ +node_modules +npm-debug.log +Dockerfile* +docker-compose* +.dockerignore +.git +.gitignore +README.md +LICENSE +.vscode +__pycache__ +env diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..f33a02cd1 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for more information: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates +# https://containers.dev/guide/dependabot + +version: 2 +updates: + - package-ecosystem: "devcontainers" + directory: "/" + schedule: + interval: weekly diff --git a/.gitignore b/.gitignore index 894a44cc0..45e7bbe6d 100644 --- a/.gitignore +++ b/.gitignore @@ -102,3 +102,6 @@ venv.bak/ # mypy .mypy_cache/ + +.vscode/ +settings.json diff --git a/.vscode/launch.json b/.vscode/launch.json index d3e8cf2e6..9a2737d78 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,121 +5,21 @@ "version": "0.2.0", "configurations": [ { - "name": "Python: runserver.py", - "type": "python", - "request": "launch", - "program": "${workspaceFolder}/runserver.py", - }, - { - "name": "Python: Current File", - "type": "python", - "request": "launch", - "program": "${file}", - "env": { - "FLASK_ENV": "development" - } - }, - { - "name": "Python: Attach", - "type": "python", - "request": "attach", - "localRoot": "${workspaceFolder}", - "remoteRoot": "${workspaceFolder}", - "port": 3000, - "secret": "my_secret", - "host": "localhost" - }, - { - "name": "Python: Terminal (integrated)", - "type": "python", - "request": "launch", - "program": "${file}", - "console": "integratedTerminal" - }, - { - "name": "Python: Terminal (external)", - "type": "python", - "request": "launch", - "program": "${file}", - "console": "externalTerminal" - }, - { - "name": "Python: Django", - "type": "python", - "request": "launch", - "program": "${workspaceFolder}/manage.py", - "args": [ - "runserver", - "--noreload", - "--nothreading" - ], - "debugOptions": [ - "RedirectOutput", - "Django" - ] - }, - { - "name": "Python: Flask (0.11.x or later)", - "type": "python", + "name": "Python Debugger: Flask", + "type": "debugpy", "request": "launch", "module": "flask", "env": { - "FLASK_APP": "hello_app.webapp" + "FLASK_APP": "hello_app.webapp", + "FLASK_DEBUG": "1" }, "args": [ "run", "--no-debugger", - "--no-reload" // Remove to auto-reload modified pages - ] - }, - { - "name": "Python: Module", - "type": "python", - "request": "launch", - "module": "module.name" - }, - { - "name": "Python: Pyramid", - "type": "python", - "request": "launch", - "args": [ - "${workspaceFolder}/development.ini" - ], - "debugOptions": [ - "RedirectOutput", - "Pyramid" - ] - }, - { - "name": "Python: Watson", - "type": "python", - "request": "launch", - "program": "${workspaceFolder}/console.py", - "args": [ - "dev", - "runserver", - "--noreload=True" - ] - }, - { - "name": "Python: All debug Options", - "type": "python", - "request": "launch", - "pythonPath": "${config:python.pythonPath}", - "program": "${file}", - "module": "module.name", - "env": { - "VAR1": "1", - "VAR2": "2" - }, - "envFile": "${workspaceFolder}/.env", - "args": [ - "arg1", - "arg2" + "--no-reload" ], - "debugOptions": [ - "RedirectOutput" - ] + "jinja": true, + "justMyCode": true } ] } diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..4c775f6b9 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,34 @@ +# Pull a pre-built alpine docker image with nginx and python3 installed +FROM tiangolo/uwsgi-nginx:python3.8-alpine-2020-12-19 + +# Set the port on which the app runs; make both values the same. +# +# IMPORTANT: When deploying to Azure App Service, go to the App Service on the Azure +# portal, navigate to the Applications Settings blade, and create a setting named +# WEBSITES_PORT with a value that matches the port here (the Azure default is 80). +# You can also create a setting through the App Service Extension in VS Code. +ENV LISTEN_PORT=5000 +EXPOSE 5000 + +# Indicate where uwsgi.ini lives +ENV UWSGI_INI uwsgi.ini + +# Tell nginx where static files live. Typically, developers place static files for +# multiple apps in a shared folder, but for the purposes here we can use the one +# app's folder. Note that when multiple apps share a folder, you should create subfolders +# with the same name as the app underneath "static" so there aren't any collisions +# when all those static files are collected together. +ENV STATIC_URL /hello_app/static + +# Set the folder where uwsgi looks for the app +WORKDIR /hello_app + +# Copy the app contents to the image +COPY . /hello_app + +# If you have additional requirements beyond Flask (which is included in the +# base image), generate a requirements.txt file with pip freeze and uncomment +# the next three lines. +#COPY requirements.txt / +#RUN pip install --no-cache-dir -U pip +#RUN pip install --no-cache-dir -r /requirements.txt diff --git a/README.md b/README.md index 46ce7f06d..068a76fd5 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,32 @@ -This sample contains the completed program from the tutorial, [Using Flask in Visual Studio Code](https://code.visualstudio.com/docs/python/tutorial-flask). Intermediate steps are not included. +# Python/Flask Tutorial for Visual Studio Code -Contributions to the sample are welcome. When submitting changes, also consider submitting matching changes to the tutorial, the source file for which is [tutorial-flask.md](https://github.com/Microsoft/vscode-docs/blob/master/docs/python/tutorial-flask.md). +* This sample contains the completed program from the tutorial, make sure to visit the link: [Using Flask in Visual Studio Code](https://code.visualstudio.com/docs/python/tutorial-flask). Intermediate steps are not included. + +* It also contains the *Dockerfile* and *uwsgi.ini* files necessary to build a container with a production server. The resulting image works both locally and when deployed to Azure App Service. See [Deploy Python using Docker containers](https://code.visualstudio.com/docs/python/tutorial-deploy-containers). + +* To run the app locally: + 1. Run the command `cd hello_app`, to change into the folder that contains the Flask app. + 1. Run the command `set FLASK_APP=webapp` (Windows cmd) or `FLASK_APP=webapp` (macOS/Linux) to point to the app module. + 1. Start the Flask server with `flask run`. + +## The startup.py file + +In the root folder, the `startup.py` file is specifically for deploying to Azure App Service on Linux without using a containerized version of the app (that is, deploying the code directly, not as a container). +Because the app code is in its own *module* in the `hello_app` folder (which has an `__init__.py`), trying to start the Gunicorn server within App Service on Linux produces an "Attempted relative import in non-package" error. + +The `startup.py` file, therefore, is a shim to import the app object from the `hello_app` module, which then allows you to use startup:app in the Gunicorn command line (see `startup.txt`). + +## Contributing + +Contributions to the sample are welcome. When submitting changes, also consider submitting matching changes to the tutorial, the source file for which is [tutorial-flask.md](https://github.com/Microsoft/vscode-docs/blob/master/docs/python/tutorial-flask.md). -# Contributing +Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com. -This project welcomes contributions and suggestions. Most contributions require you to agree to a -Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us -the rights to use your contribution. For details, visit https://cla.microsoft.com. +When you submit a pull request, a CLA-bot automatically determines whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. -When you submit a pull request, a CLA-bot will automatically determine whether you need to provide -a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions -provided by the bot. You will only need to do this once across all repos using our CLA. +## Additional details -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). -For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or -contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. +* This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +* For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or +* Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..869fdfe2b --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). + + diff --git a/hello_app/__init__.py b/hello_app/__init__.py index cf1f4140e..eb82cbf69 100644 --- a/hello_app/__init__.py +++ b/hello_app/__init__.py @@ -1,2 +1,2 @@ -from flask import Flask -app = Flask(__name__) +from flask import Flask # Import the Flask class +app = Flask(__name__) # Create an instance of the class for our use diff --git a/hello_app/templates/contact.html b/hello_app/templates/contact.html index 40e85de8c..3321c9454 100644 --- a/hello_app/templates/contact.html +++ b/hello_app/templates/contact.html @@ -3,5 +3,5 @@ Contact us {% endblock %} {% block content %} -

Contact page for the VIsual Studio Code Flask tutorial.

+

Contact page for the Visual Studio Code Flask tutorial.

{% endblock %} diff --git a/hello_app/templates/hello_there.html b/hello_app/templates/hello_there.html index 7c54f6437..de1ef49d1 100644 --- a/hello_app/templates/hello_there.html +++ b/hello_app/templates/hello_there.html @@ -6,6 +6,11 @@ Hello, Flask - Hello there, {{ name }}! It's {{ date.strftime("%A, %d %B, %Y at %X") }}. + {%if name %} + Hello there, {{ name }}! It's {{ date.strftime("%A, %d %B, %Y at %X") }}. + {% else %} + What's your name? Provide it after /hello/ in the URL. + {% endif %} + diff --git a/hello_app/views.py b/hello_app/views.py index 91807bbf9..8f714ea90 100644 --- a/hello_app/views.py +++ b/hello_app/views.py @@ -6,16 +6,17 @@ def home(): return render_template("home.html") -@app.route("/about") +@app.route("/about/") def about(): return render_template("about.html") -@app.route("/contact") +@app.route("/contact/") def contact(): return render_template("contact.html") +@app.route("/hello/") @app.route("/hello/") -def hello_there(name): +def hello_there(name = None): return render_template( "hello_there.html", name=name, @@ -24,4 +25,4 @@ def hello_there(name): @app.route("/api/data") def get_data(): - return app.send_static_file('data.json') + return app.send_static_file("data.json") diff --git a/hello_app/webapp.py b/hello_app/webapp.py index c76b9bcb3..56386def8 100644 --- a/hello_app/webapp.py +++ b/hello_app/webapp.py @@ -1,4 +1,4 @@ -"""Entry point for the application.""" +# Entry point for the application. from . import app # For application discovery by the 'flask' command. from . import views # For import side-effects of setting up routes. diff --git a/requirements.txt b/requirements.txt index 34ffbb7b2..e3e9a71d9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1 @@ -click==6.7 -Flask==1.0.2 -itsdangerous==0.24 -Jinja2==2.10 -MarkupSafe==1.0 -Werkzeug==0.14.1 +Flask diff --git a/startup.py b/startup.py new file mode 100644 index 000000000..9fa523781 --- /dev/null +++ b/startup.py @@ -0,0 +1,12 @@ +""" +In this sample, the Flask app object is contained within the hello_app *module*; +that is, hello_app contains an __init__.py along with relative imports. Because +of this structure, a file like webapp.py cannot be run directly as the startup +file through Gunicorn; the result is "Attempted relative import in non-package". + +The solution is to provide a simple alternate startup file, like this present +startup.py, that just imports the app object. You can then just specify +startup:app in the Gunicorn command. +""" + +from hello_app.webapp import app diff --git a/startup.txt b/startup.txt new file mode 100644 index 000000000..068e6a817 --- /dev/null +++ b/startup.txt @@ -0,0 +1 @@ +gunicorn --bind=0.0.0.0 --workers=4 startup:app diff --git a/test_test1.py b/test_test1.py new file mode 100644 index 000000000..da8dc03d2 --- /dev/null +++ b/test_test1.py @@ -0,0 +1,2 @@ +def test_mock(): + assert True diff --git a/uwsgi.ini b/uwsgi.ini new file mode 100644 index 000000000..0326d2899 --- /dev/null +++ b/uwsgi.ini @@ -0,0 +1,7 @@ +[uwsgi] +module = hello_app.webapp +callable = app +uid = 1000 +master = true +threads = 2 +processes = 4