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 4e88939b9..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": "HelloFlask/app.py" + "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 } ] -} \ No newline at end of file +} 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/HelloFlask/__init__.py b/HelloFlask/__init__.py deleted file mode 100644 index 8d9202853..000000000 --- a/HelloFlask/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from flask import Flask -app = Flask(__name__) \ No newline at end of file diff --git a/HelloFlask/app.py b/HelloFlask/app.py deleted file mode 100644 index 466f21397..000000000 --- a/HelloFlask/app.py +++ /dev/null @@ -1,5 +0,0 @@ -from HelloFlask import app -from HelloFlask import views - -# Time-saver: output a URL to the VS Code terminal so you can easily Ctrl+click to open a browser -# print('http://127.0.0.1:5000/hello/VSCode') \ No newline at end of file diff --git a/HelloFlask/templates/about.html b/HelloFlask/templates/about.html deleted file mode 100644 index 95b01e1c5..000000000 --- a/HelloFlask/templates/about.html +++ /dev/null @@ -1,4 +0,0 @@ -{% extends "layout.html" %} -{% block content %} -

About page for the Flask tutorial.

-{% endblock %} \ No newline at end of file diff --git a/HelloFlask/templates/contact.html b/HelloFlask/templates/contact.html deleted file mode 100644 index d0a9baebd..000000000 --- a/HelloFlask/templates/contact.html +++ /dev/null @@ -1,4 +0,0 @@ -{% extends "layout.html" %} -{% block content %} -

Contact page for the Flask tutorial.

-{% endblock %} \ No newline at end of file diff --git a/HelloFlask/templates/hello_there.html b/HelloFlask/templates/hello_there.html deleted file mode 100644 index 258c06827..000000000 --- a/HelloFlask/templates/hello_there.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - {{ title }} - - - {{ message }} It's {{ date }}. - - \ No newline at end of file diff --git a/HelloFlask/templates/home.html b/HelloFlask/templates/home.html deleted file mode 100644 index c3d51a15d..000000000 --- a/HelloFlask/templates/home.html +++ /dev/null @@ -1,4 +0,0 @@ -{% extends "layout.html" %} -{% block content %} -

Home page for the Flask tutorial.

-{% endblock %} \ No newline at end of file diff --git a/HelloFlask/views.py b/HelloFlask/views.py deleted file mode 100644 index 46d1f1e1d..000000000 --- a/HelloFlask/views.py +++ /dev/null @@ -1,31 +0,0 @@ -from flask import Flask -from flask import render_template -from HelloFlask import app - -@app.route('/') -def home(): - return render_template("home.html", title = "Home") - -@app.route('/about') -def about(): - return render_template("about.html", title = "About us") - -@app.route('/contact') -def contact(): - return render_template("contact.html", title = "Contact us") - -@app.route('/hello/') -def hello_there(name): - from datetime import datetime - now = datetime.now() - - return render_template( - "hello_there.html", - title ='Hello, Flask', - message = "Hello there, " + name + "!", - date = now.strftime("%A, %d %B, %Y at %X") - ) - -@app.route('/api/data') -def get_data(): - return app.send_static_file('data.json') \ No newline at end of file diff --git a/README.md b/README.md index 72f1506a9..068a76fd5 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,32 @@ +# Python/Flask Tutorial for Visual Studio Code -# Contributing +* 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. -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. +* 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). -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. +* 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`. -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. +## 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). + +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. + +## 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. 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 new file mode 100644 index 000000000..eb82cbf69 --- /dev/null +++ b/hello_app/__init__.py @@ -0,0 +1,2 @@ +from flask import Flask # Import the Flask class +app = Flask(__name__) # Create an instance of the class for our use diff --git a/HelloFlask/static/data.json b/hello_app/static/data.json similarity index 100% rename from HelloFlask/static/data.json rename to hello_app/static/data.json diff --git a/HelloFlask/static/site.css b/hello_app/static/site.css similarity index 100% rename from HelloFlask/static/site.css rename to hello_app/static/site.css diff --git a/hello_app/templates/about.html b/hello_app/templates/about.html new file mode 100644 index 000000000..8611b6414 --- /dev/null +++ b/hello_app/templates/about.html @@ -0,0 +1,7 @@ +{% extends "layout.html" %} +{% block title %} +About us +{% endblock %} +{% block content %} +

About page for the Visual Studio Code Flask tutorial.

+{% endblock %} diff --git a/hello_app/templates/contact.html b/hello_app/templates/contact.html new file mode 100644 index 000000000..3321c9454 --- /dev/null +++ b/hello_app/templates/contact.html @@ -0,0 +1,7 @@ +{% extends "layout.html" %} +{% block title %} +Contact us +{% endblock %} +{% block content %} +

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 new file mode 100644 index 000000000..de1ef49d1 --- /dev/null +++ b/hello_app/templates/hello_there.html @@ -0,0 +1,16 @@ + + + + + + Hello, Flask + + + {%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/templates/home.html b/hello_app/templates/home.html new file mode 100644 index 000000000..95609fecd --- /dev/null +++ b/hello_app/templates/home.html @@ -0,0 +1,7 @@ +{% extends "layout.html" %} +{% block title %} +Home +{% endblock %} +{% block content %} +

Home page for the Visual Studio Code Flask tutorial.

+{% endblock %} diff --git a/HelloFlask/templates/layout.html b/hello_app/templates/layout.html similarity index 77% rename from HelloFlask/templates/layout.html rename to hello_app/templates/layout.html index b2de0859a..fd83b92c8 100644 --- a/HelloFlask/templates/layout.html +++ b/hello_app/templates/layout.html @@ -2,8 +2,8 @@ - {{ title }} - + {% block title %}{% endblock %} + @@ -22,4 +22,4 @@ - \ No newline at end of file + diff --git a/hello_app/views.py b/hello_app/views.py new file mode 100644 index 000000000..8f714ea90 --- /dev/null +++ b/hello_app/views.py @@ -0,0 +1,28 @@ +from datetime import datetime +from flask import Flask, render_template +from . import app + +@app.route("/") +def home(): + return render_template("home.html") + +@app.route("/about/") +def about(): + return render_template("about.html") + +@app.route("/contact/") +def contact(): + return render_template("contact.html") + +@app.route("/hello/") +@app.route("/hello/") +def hello_there(name = None): + return render_template( + "hello_there.html", + name=name, + date=datetime.now() + ) + +@app.route("/api/data") +def get_data(): + return app.send_static_file("data.json") diff --git a/hello_app/webapp.py b/hello_app/webapp.py new file mode 100644 index 000000000..56386def8 --- /dev/null +++ b/hello_app/webapp.py @@ -0,0 +1,6 @@ +# 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. + +# Time-saver: output a URL to the VS Code terminal so you can easily Ctrl+click to open a browser +# print('http://127.0.0.1:5000/hello/VSCode') 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