From 99edc2c82541336d8e31efe457710c00cc9ecc65 Mon Sep 17 00:00:00 2001 From: Luciana Abud Date: Fri, 13 Oct 2023 20:20:17 -0700 Subject: [PATCH 01/34] Add initial draft of FastAPI tutorial --- .../create_environment_interpreters_list.png | 3 + ...create_environment_select_requirements.png | 3 + .../fastapi-tutorial/debugger_breakpoint.png | 3 + .../devcontainer_indicator.png | 3 + .../devcontainers_python3.png | 3 + .../devcontainers_redis_server_feature.png | 3 + .../fastapi_add_redis_quickfix.png | 3 + .../fastapi_breakpoint_hit.png | 3 + .../fastapi_debug_config_option.png | 3 + .../fastapi_debug_terminal.png | 3 + .../fastapi_debugger_variables.png | 3 + .../fastapi_evaluate_debug_console.png | 3 + .../fastapi_execute_button.png | 3 + .../fastapi_first_swagger_page.png | 3 + .../fastapi_tryitout_button.png | 3 + ...and_variable_return_type_hint_settings.png | 3 + .../fastapi-tutorial/helloworld_browser.png | 3 + .../fastapi-tutorial/pylance_inlay_hints.png | 3 + .../pylance_redis_typehint.png | 3 + .../type_checking_mode_setting.png | 3 + docs/python/python-on-azure.md | 2 +- docs/python/python-web.md | 2 +- docs/python/settings-reference.md | 2 +- docs/python/tutorial-create-containers.md | 2 +- docs/python/tutorial-fastapi.md | 596 ++++++++++++++++++ docs/python/tutorial-flask.md | 2 +- 26 files changed, 661 insertions(+), 5 deletions(-) create mode 100644 docs/python/images/fastapi-tutorial/create_environment_interpreters_list.png create mode 100644 docs/python/images/fastapi-tutorial/create_environment_select_requirements.png create mode 100644 docs/python/images/fastapi-tutorial/debugger_breakpoint.png create mode 100644 docs/python/images/fastapi-tutorial/devcontainer_indicator.png create mode 100644 docs/python/images/fastapi-tutorial/devcontainers_python3.png create mode 100644 docs/python/images/fastapi-tutorial/devcontainers_redis_server_feature.png create mode 100644 docs/python/images/fastapi-tutorial/fastapi_add_redis_quickfix.png create mode 100644 docs/python/images/fastapi-tutorial/fastapi_breakpoint_hit.png create mode 100644 docs/python/images/fastapi-tutorial/fastapi_debug_config_option.png create mode 100644 docs/python/images/fastapi-tutorial/fastapi_debug_terminal.png create mode 100644 docs/python/images/fastapi-tutorial/fastapi_debugger_variables.png create mode 100644 docs/python/images/fastapi-tutorial/fastapi_evaluate_debug_console.png create mode 100644 docs/python/images/fastapi-tutorial/fastapi_execute_button.png create mode 100644 docs/python/images/fastapi-tutorial/fastapi_first_swagger_page.png create mode 100644 docs/python/images/fastapi-tutorial/fastapi_tryitout_button.png create mode 100644 docs/python/images/fastapi-tutorial/function_and_variable_return_type_hint_settings.png create mode 100644 docs/python/images/fastapi-tutorial/helloworld_browser.png create mode 100644 docs/python/images/fastapi-tutorial/pylance_inlay_hints.png create mode 100644 docs/python/images/fastapi-tutorial/pylance_redis_typehint.png create mode 100644 docs/python/images/fastapi-tutorial/type_checking_mode_setting.png create mode 100644 docs/python/tutorial-fastapi.md diff --git a/docs/python/images/fastapi-tutorial/create_environment_interpreters_list.png b/docs/python/images/fastapi-tutorial/create_environment_interpreters_list.png new file mode 100644 index 0000000000..9a05d20484 --- /dev/null +++ b/docs/python/images/fastapi-tutorial/create_environment_interpreters_list.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:08b85c291938d11dbc09b8c249ca5f2414ed34c32cd1a64ccd2345c4259e1ec2 +size 63969 diff --git a/docs/python/images/fastapi-tutorial/create_environment_select_requirements.png b/docs/python/images/fastapi-tutorial/create_environment_select_requirements.png new file mode 100644 index 0000000000..5e270afddb --- /dev/null +++ b/docs/python/images/fastapi-tutorial/create_environment_select_requirements.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:196b256b81ffff5a73ba9c22e6ed33746b5baa2c83500044dd4a7b652242e9be +size 11790 diff --git a/docs/python/images/fastapi-tutorial/debugger_breakpoint.png b/docs/python/images/fastapi-tutorial/debugger_breakpoint.png new file mode 100644 index 0000000000..821d247b9f --- /dev/null +++ b/docs/python/images/fastapi-tutorial/debugger_breakpoint.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9f11695535da4a0e17e09b45695ee2b0e1d232db7501210504e4ba7c877357bb +size 28795 diff --git a/docs/python/images/fastapi-tutorial/devcontainer_indicator.png b/docs/python/images/fastapi-tutorial/devcontainer_indicator.png new file mode 100644 index 0000000000..3fa5dd6641 --- /dev/null +++ b/docs/python/images/fastapi-tutorial/devcontainer_indicator.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:13d6528a58e710fe498edf98150523fa5c972b67cc9ba96d5eedf25f4707fcd2 +size 1969 diff --git a/docs/python/images/fastapi-tutorial/devcontainers_python3.png b/docs/python/images/fastapi-tutorial/devcontainers_python3.png new file mode 100644 index 0000000000..cd53484e3b --- /dev/null +++ b/docs/python/images/fastapi-tutorial/devcontainers_python3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6d2909aa6c070591b7601ed8c497fcf347d687aa7665bda9a3b6a0850c4d8b5c +size 19469 diff --git a/docs/python/images/fastapi-tutorial/devcontainers_redis_server_feature.png b/docs/python/images/fastapi-tutorial/devcontainers_redis_server_feature.png new file mode 100644 index 0000000000..e94e982af7 --- /dev/null +++ b/docs/python/images/fastapi-tutorial/devcontainers_redis_server_feature.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:93e483c3be8c8656be0a487c9cd99538717c0743956a637737ad92d513fb39b8 +size 63800 diff --git a/docs/python/images/fastapi-tutorial/fastapi_add_redis_quickfix.png b/docs/python/images/fastapi-tutorial/fastapi_add_redis_quickfix.png new file mode 100644 index 0000000000..7258e6ecd3 --- /dev/null +++ b/docs/python/images/fastapi-tutorial/fastapi_add_redis_quickfix.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c2bfec85cf05586b7650310783dc60ac50414923d87c83d7c237353d4f68b93a +size 24834 diff --git a/docs/python/images/fastapi-tutorial/fastapi_breakpoint_hit.png b/docs/python/images/fastapi-tutorial/fastapi_breakpoint_hit.png new file mode 100644 index 0000000000..967fe6c967 --- /dev/null +++ b/docs/python/images/fastapi-tutorial/fastapi_breakpoint_hit.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:960a3442358bda66696d5180eb1d6c5b46b851b34ec51b1619304eda4be2bc28 +size 32906 diff --git a/docs/python/images/fastapi-tutorial/fastapi_debug_config_option.png b/docs/python/images/fastapi-tutorial/fastapi_debug_config_option.png new file mode 100644 index 0000000000..3ec9b7d629 --- /dev/null +++ b/docs/python/images/fastapi-tutorial/fastapi_debug_config_option.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bd410b1ce6d9a80d30a161de3ec148ba171c04c2d7746ce2bce6f5045ca941f0 +size 89173 diff --git a/docs/python/images/fastapi-tutorial/fastapi_debug_terminal.png b/docs/python/images/fastapi-tutorial/fastapi_debug_terminal.png new file mode 100644 index 0000000000..c69a6ce9d9 --- /dev/null +++ b/docs/python/images/fastapi-tutorial/fastapi_debug_terminal.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7a78119695dc51f92a6a8b88b67d36ae0c57ccef730609d388adc2775768fe82 +size 217635 diff --git a/docs/python/images/fastapi-tutorial/fastapi_debugger_variables.png b/docs/python/images/fastapi-tutorial/fastapi_debugger_variables.png new file mode 100644 index 0000000000..ff17513824 --- /dev/null +++ b/docs/python/images/fastapi-tutorial/fastapi_debugger_variables.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ee9a8ec1590c96589805e9555ecb370343fefa84943d888d7b6df277dddab8c3 +size 39029 diff --git a/docs/python/images/fastapi-tutorial/fastapi_evaluate_debug_console.png b/docs/python/images/fastapi-tutorial/fastapi_evaluate_debug_console.png new file mode 100644 index 0000000000..f0b63c7f2a --- /dev/null +++ b/docs/python/images/fastapi-tutorial/fastapi_evaluate_debug_console.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:678cf1ef75ea061c8ee41d627f9cfad0cc8409a74a5e8202a225919c5e229547 +size 218380 diff --git a/docs/python/images/fastapi-tutorial/fastapi_execute_button.png b/docs/python/images/fastapi-tutorial/fastapi_execute_button.png new file mode 100644 index 0000000000..88f98a6d99 --- /dev/null +++ b/docs/python/images/fastapi-tutorial/fastapi_execute_button.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ebd6086c58198d00f131c67a9b936e7b363ea520beeb6bf55f54b870c35f63fe +size 17389 diff --git a/docs/python/images/fastapi-tutorial/fastapi_first_swagger_page.png b/docs/python/images/fastapi-tutorial/fastapi_first_swagger_page.png new file mode 100644 index 0000000000..1765df0426 --- /dev/null +++ b/docs/python/images/fastapi-tutorial/fastapi_first_swagger_page.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c0a36093050aec8c7c450fa9146c28abb2645af91974d5720e7035ddfe26b75a +size 18501 diff --git a/docs/python/images/fastapi-tutorial/fastapi_tryitout_button.png b/docs/python/images/fastapi-tutorial/fastapi_tryitout_button.png new file mode 100644 index 0000000000..8632afcf0f --- /dev/null +++ b/docs/python/images/fastapi-tutorial/fastapi_tryitout_button.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cc1c58d18a5fe84f999e0f99bbf56d1e86523f4188ae31f04bfa55a62288edf3 +size 15419 diff --git a/docs/python/images/fastapi-tutorial/function_and_variable_return_type_hint_settings.png b/docs/python/images/fastapi-tutorial/function_and_variable_return_type_hint_settings.png new file mode 100644 index 0000000000..f0346f686d --- /dev/null +++ b/docs/python/images/fastapi-tutorial/function_and_variable_return_type_hint_settings.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9601216cc1b14b32ad4ff4b5571529c022e396f142fa8bdf6890e892e9ac7784 +size 39279 diff --git a/docs/python/images/fastapi-tutorial/helloworld_browser.png b/docs/python/images/fastapi-tutorial/helloworld_browser.png new file mode 100644 index 0000000000..8f26b9a4ca --- /dev/null +++ b/docs/python/images/fastapi-tutorial/helloworld_browser.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8644a576287caa5574ffb7f8bed7b0f2bfd0220808a13d0dd797f1f497035537 +size 22309 diff --git a/docs/python/images/fastapi-tutorial/pylance_inlay_hints.png b/docs/python/images/fastapi-tutorial/pylance_inlay_hints.png new file mode 100644 index 0000000000..98835ceb76 --- /dev/null +++ b/docs/python/images/fastapi-tutorial/pylance_inlay_hints.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e6c86a35905344280cc5e2b8c3d01ec56e56ecf1b648e7e0e9a2b5c1c6473936 +size 132177 diff --git a/docs/python/images/fastapi-tutorial/pylance_redis_typehint.png b/docs/python/images/fastapi-tutorial/pylance_redis_typehint.png new file mode 100644 index 0000000000..2531e54229 --- /dev/null +++ b/docs/python/images/fastapi-tutorial/pylance_redis_typehint.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7af157b21f7e3d8ce5cac6b837c50611d81a1786da5e3761f35dcc1bb07496fe +size 13253 diff --git a/docs/python/images/fastapi-tutorial/type_checking_mode_setting.png b/docs/python/images/fastapi-tutorial/type_checking_mode_setting.png new file mode 100644 index 0000000000..2265c2fe83 --- /dev/null +++ b/docs/python/images/fastapi-tutorial/type_checking_mode_setting.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1d29bc166be1c978481192d7ee811f9739cea1bebdefbadd072674cc97d31b90 +size 32421 diff --git a/docs/python/python-on-azure.md b/docs/python/python-on-azure.md index 403bdccb09..bae1f1b17e 100644 --- a/docs/python/python-on-azure.md +++ b/docs/python/python-on-azure.md @@ -1,5 +1,5 @@ --- -Order: 12 +Order: 13 Area: python TOCTitle: Deploy Python Apps ContentId: 12bf713e-5f20-46ac-81bb-8e05565aba3a diff --git a/docs/python/python-web.md b/docs/python/python-web.md index bb3117c7a1..552d00ece8 100644 --- a/docs/python/python-web.md +++ b/docs/python/python-web.md @@ -1,5 +1,5 @@ --- -Order: 13 +Order: 14 Area: python TOCTitle: Python in the Web ContentId: 366e4bbf-fa87-4813-9dfc-6c831b20a4d2 diff --git a/docs/python/settings-reference.md b/docs/python/settings-reference.md index a6aff8ded1..52935b693b 100644 --- a/docs/python/settings-reference.md +++ b/docs/python/settings-reference.md @@ -1,5 +1,5 @@ --- -Order: 14 +Order: 15 Area: python TOCTitle: Settings Reference ContentId: d256dc5c-95e9-4c02-a82f-947bf34a3517 diff --git a/docs/python/tutorial-create-containers.md b/docs/python/tutorial-create-containers.md index 7abe394dd6..96bdaffdff 100644 --- a/docs/python/tutorial-create-containers.md +++ b/docs/python/tutorial-create-containers.md @@ -1,5 +1,5 @@ --- -Order: 11 +Order: 12 Area: python TOCTitle: Create containers ContentId: 4e45a3f6-b72d-4647-82a5-22f7ee593d47 diff --git a/docs/python/tutorial-fastapi.md b/docs/python/tutorial-fastapi.md new file mode 100644 index 0000000000..a18dcaa8ae --- /dev/null +++ b/docs/python/tutorial-fastapi.md @@ -0,0 +1,596 @@ +--- +Order: 10 +Area: python +TOCTitle: FastAPI Tutorial +ContentId: 0d32bced91aa5c2ee5696fc7995370ae +PageTitle: Python and FastAPI Tutorial in Visual Studio Code +DateApproved: +MetaDescription: Python FastAPI tutorial showing IntelliSense and debugging support in Visual Studio Code, the best Python IDE. +--- +# FastAPI Tutorial in Visual Studio Code + +FastAPI is a modern high-performant web framework for building APIs with Python. It is designed to make it easy to build APIs quickly and efficiently while providing features like automatic validation, serialization, and documentation of your API, making it a popular choice for building web services and microservices. + +In this FastAPI tutorial, we will create a groceries list app using FastAPI. You will be able to understand how to work with FastAPI in the VS Code terminal, editor and debugger. This tutorial does not explore various details about FastAPI itself. For that you can refer to the [FastAPI documentation link at the end of this tutorial](#next-steps)l. + +The completed code project from this FastAPI tutorial can be found on GitHub: + +If you have any problems, you can search for answers or ask a question on the [Python extension Discussions Q&A](https://github.com/microsoft/vscode-python/discussions/categories/q-a). + +## Set up the project +There are different ways you can set up your project for this tutorial. We will cover how you can set it up in [GitHub Codespaces](TODO: add link to section below) and in [VS Code on your local machine](TODO: ADD Link to section below). + +## GitHub Codespaces +You can set up this project to develop in [GitHub Codespaces](https://github.com/features/codespaces), where you can code, debug, and run your app remotely in a Codespace. A Codespace allows you to access a fully-configured development environment hosted in the cloud, eliminating the need for local setup. This environment includes your project's dependencies, tools, and extensions, ensuring a consistent and reproducible development experience. It streamlines collaboration by providing real-time editing, integrated version control, and easy access to debugging and testing tools, all while maintaining the security and reliability of your project. + +To set it up for this tutorial, navigate to the [FastAPI template repository]. This template contains all the necessary configurations and dependencies to quickly get started with FastAPI development in a GitHub Codespace. Next, select Code -> Codespaces -> Create Codespace on
to create and open a Codespace for your project. + +Once you're done, you can continue with the [Start Coding](#start-coding) section below. + +## Locally in VS Code +To successfully complete this tutorial in [VS Code](https://code.visualstudio.com/), you need to first setup your Python development environment. Specifically, this tutorial requires: +- Python 3 (check the [installation guide](python-tutorial.md#install-a-python-interpreter) if you don’t have it installed) +- [Python extension for VS Code](https://marketplace.visualstudio.com/items?itemName=ms-python.python) (For additional details on installing extensions, see [Extension Marketplace](https://code.visualstudio.com/docs/editor/extension-marketplace)) + +In this section, we will create a folder to be open as a workspace in VS Code, set up a Python virtual environment, install the project’s dependencies and write the first lines of our FastAPI app. + +1. On your file system, create a project folder for this tutorial, such as `groceries-plugin``. +2. Open this new folder in VS Code (**File** > **Open Folder…** or `kb(workbench.action.files.openFolder)`). + + +Now, let’s create a requirements.txt file listing the dependencies we wish to install for our application. The requirements.txt file is a common practice in Python development, used to specify the libraries and their versions that your project relies on. This file helps ensure that anyone working on the project can recreate a similar development environment, making it a convenient component for maintaining consistency across different development environments. + +We will want to install FastAPI for creating our app, uvicorn to work as the server, and redis and type-redis for handling data storage and interacting with a Redis database. + +3. Create a new file in VS Code (**File** > **New File** or `kb(workbench.action.files.newUntitledFile)`). +4. Add the following content to it: + ``` + fastapi + redis + types-redis + uvicorn + ``` +5. Save the file (`kb(workbench.action.files.save)`) and name it requirements.txt +6. Create a virtual environment by opening the Command Palette (`kb(workbench.action.showCommands)`) and running the **Python: Create Environment** command. +7. When asked by the environment type, select **Venv**: +![Drop down with "Venv" or "Conda" as options for environments that can be created with the Python: Create Environment command.](images/environments/create_environment_dropdown.png) + +8. Then select the latest version of Python available on your machine: +![List of available global envionrmetns that can be use to create a virtual environment.](images/fastapi-tutorial/create_environment_interpreters_list.png) + +9. Select the requirements.txt file from the drop-down list, so our dependencies are automatically installed, and then select "OK": +![Check box selected to install dependencies from requirements.txt file](images/fastapi-tutorial/create_environment_select_requirements.png) + +The virtual environment will be created, our dependencies will be automatically installed and the environment will be selected for your workspace to be used by the Python extension. + +## Start Coding + +Let’s create our application! +1. Create a new Python file by clicking on **File** > **New File…** (`kb(workbench.action.files.newFile)`), and then select "Python File". +2. Save it as `main.py` (`kb(workbench.action.files.saveAs)`) in the `groceries-plugin` folder. +3. Add the following code to the file: + ```python + from fastapi import FastAPI + + app = FastAPI() + + @app.get("/") + def root(): + return {"message": "Hello World"} + ``` +4. Run the code by starting up the debugger (`kb(workbench.action.debug.start)`) +5. From the drop-down menu, select the **FastAPI** configuration option from the drop-down list: +![Drop down with debugger configuration options, with FastAPI being highlighted.](images/fastapi-tutorial/fastapi_debug_config_option.png) + + This will invoke uvicorn to start the application server through the debugger, which will allow us to step through the code to inspect its behaviour. You will see something like the following in the terminal: + + ![Uvicorn server running message displayed in the terminal, with an URL to access the app.](images/fastapi-tutorial/fastapi_debug_terminal.png) + +6. `Ctrl`` + click the http://127.0.0.1:8000/ URL in the terminal to open your default browser to that address: +![Hello World message displayed in the browser.](images/fastapi-tutorial/helloworld_browser.png) +7. Stop the debugger by clicking on the stop icon in the debug toolbar, or through `kb(workbench.action.debug.stop)`. + +## Create a model for grocery list items +Now that we have our FastAPI app working, let’s define our grocery list items by creating a Pydantic model. +Pydantic is a data validation and parsing library that integrates seamlessly with FastAPI. It allows you to define data models using Python classes with [type hints](https://docs.python.org/3/library/typing.html) for automatic validation and parsing of incoming data (which are called payloads) in API requests. + +Pylance, the default language server for Python in VS Code, supports a few type hinting features that can be helpful for working with Pydantic models and FastAPI. Let's enable a few of them: +1. Open the Settings UI page (`kb(workbench.action.openSettings)`) +2. Search for "python type checking mode" and set it to "basic" to enable basic type checking. This will enable Pylance to show diagnostics and warnings to catch simple type-related errors. Alternetivaly, you can set it to "strict" to enable more advanced [type checking rules](https://microsoft.github.io/pyright/#/configuration?id=diagnostic-rule-defaults). + ![Python Analysis Type Checking Mode options (off, basic and strict) in settings UI page.](images/fastapi-tutorial/type_checking_mode_setting.png) +3. Next, search for "Python inlay type hints", and enable inlay hints for Variable Types and Function Return Types: + ![Two Python Analysis Type Hints settings being enabled in the settings UI page: for Function Return Types and for Variable Types.](images/fastapi-tutorial/function_and_variable_return_type_hint_settings.png) + +Now let's create a Pydantic model for grocery list items. +1. Create a new Python file (`kb(workbench.action.files.newFile)` and select "Python File") +2. Add the following lines to the file, and then save it in the `groceries-plugin` folder as `models.py` (`kb(workbench.action.files.saveAs)`): + ``` + from typing import Optional + from pydantic import BaseModel + + class ItemPayload(BaseModel): + item_id: Optional[int] + item_name: str + quantity: int + ``` + +## Create Routes +Now we need a place to store the grocery list items. For simplicity, let’s start with an empty dictionary. +1. First, let's import all the packages we will need for our sample. Open the main.py file and replace the first import line with the following ones: + ``` + from fastapi import FastAPI, HTTPException + + from models import ItemPayload + ``` +2. Now add the following line right below app = FastAPI(): + ``` + grocery_list: Dict[int, ItemPayload] = {} + ``` + + This creates a new empty dictionary that will receive keys of type int (which will be each item id) and values of the ItemPayload type. + +We’ll now define routes that will allow us to add and retrieve individual items, as well as list all items in the grocery list. + +3. Add the following route at the end of the `main.py` file: + ```python + # Route to add a item + @app.post("/items/{item_name}/{quantity}") + def add_item(item_name: str, quantity: int): + if quantity <= 0: + raise HTTPException(status_code=400, detail="Quantity must be greater than 0.") + # if item already exists, we'll just add the quantity. + # get all item names + items_names = [item.item_name for item in grocery_list.values()] + if item_name in items_names: + # get index of item.item_name in item_names, which is the item_id + item_id = items_names.index(item_name) + grocery_list[item_id].quantity += quantity + # otherwise, create a new item + else: + # generate an id for the item based on the highest ID in the grocery_list + item_id = max(grocery_list.keys()) + 1 if grocery_list else 0 + grocery_list[item_id] = ItemPayload(item_id=item_id, item_name=item_name, quantity=quantity) + + return {"item": grocery_list[item_id]} + + ``` + + You might notice Pylance adds inlay hints with the function return type, as well as the types for `item_names` and `item_id`. You can double click on each suggestion to insert them into the code: + ![Inlay function return and variable type hints being displayed by Pylance throughout the sample code.](images/fastapi-tutorial/pylance_inlay_hints.png) + +Now let's see if this route is working as expected. The fastest way to do so is to leverage both VS Code's debugger as well as FastAPI's `/docs` endpoint, which provides information about all the available API routes and allows you to interact with the API to explore their parameters and responses. This documentation is generated dynamically based on the metadata and type hints defined in your FastAPI application. + +4. Add a breakpoint next to the if item.quantity <= 0 statement, by clicking on the left margin of the line number (or `kb(editor.debug.action.toggleBreakpoint)`). This will make the debugger stop prior to the execution of that line, allowing us to inspect the code line by line. + ![Breakpoint set next to the first line in the add_item function.](images/fastapi-tutorial/debugger_breakpoint.png) + +5. Start the debugger (`kb(workbench.action.debug.start)`), and then navigate to http://127.0.0.1:8000/docs in the browser. + + You'll see a Swagger-like interface with the two endpoints available in your app: `/items` and root (`/`). + ![Swagger UI displaying two endpoints: /items and /](images/fastapi-tutorial/fastapi_first_swagger_page.png) + +6. Click on the down arrow next to the `/items` route to expand it, and click on the "Try it out" button that appears on the right side. + ![Try it out button displayed next to the /items route in the Swagger UI.](images/fastapi-tutorial/fastapi_tryitout_button.png) + +7. Add a grocery list item by passing a string to the "item" field, and a number to "quantity". For example, you could provide `{"item_id": 0, "item" : "apple", "quantity": 2 }`. + +8. Click on the "Execute" button. + ![Execute button displayed below the /items route.](images/fastapi-tutorial/fastapi_execute_button.png) + +9. Open VS Code again and notice the debugger has stopped at the breakpoint we set earlier. + ![Debugger stopped at the breakpoint set in the add_item function.](images/fastapi-tutorial/fastapi_breakpoint_hit.png) + +On the left side, you will see all local and global variables defined at this point in the Variables window, under the **Run and Debug** view: +![Variables window displayed in the Run and Debug view, with the item and grocery_list variables highlighted.](images/fastapi-tutorial/fastapi_debugger_variables.png) + +Now let’s leverage VS Code's Debug Console to do some exploration. + +10. Select the `item.quantity <= 0` statement, right-click on the editor and select **Evaluate in Debug Console**: + ![Evaluate in Debug Console option displayed in the context menu when right-clicking on a line of code.](images/fastapi-tutorial/fastapi_evaluate_debug_console.png) + + This will open the Debug Console and run the selected expression. As expected, the expression will be evaluated to `False`. + +The Debug Console can be a powerful tool to quickly test expressions and better understand the state of your code at the time of the breakpoint. You can also use it to run arbitrary code, such as calling functions or printing variables. Learn more about Python debugging in VS Code in the [Python tutorial](python-tutorial.md#configure-and-run-the-debugger). + + +Finally, let's add the remaining routes for our application so we can list all and specific items, as well as remove them from our grocery list. + +11. Replace the content in `main.py` with the code below: + ```python + from typing import Dict + from fastapi import FastAPI, HTTPException, Request + import redis + + from models import ItemPayload + + app = FastAPI() + + redis_client = redis.StrictRedis(host='0.0.0.0', port=6379, db=0, decode_responses=True) + + @app.get("/") + def home(request: Request) -> dict[str, str]: + url = str(request.base_url) + return {"message": f"Add /docs to the end of the URL to access the Swagger UI."} + + # Route to add an item + @app.post("/items") + def add_item(item: ItemPayload) -> dict[str, ItemPayload]: + if item.quantity <= 0: + raise HTTPException(status_code=400, detail="Quantity must be greater than 0.") + + # Check if item already exists + item_id_str: str | None = redis_client.hget("item_name_to_id", "item_id") + + if item_id_str is not None: + item_id = int(item_id_str) + redis_client.hincrby(f"item_id:{item_id}", "quantity", item.quantity) + else: + # Generate an id for the item + item_id: int = redis_client.incr("item_id") + redis_client.hset( + f"item_id:{item_id}", + mapping={ + "item_id": item_id, + "item_name": item.item_name, + "quantity": item.quantity, + }, + ) + # Create a set so we can search by name too + redis_client.hset("item_name_to_id", item.item_name, item_id) + + return {"item": item} + + + # Route to list a specific item by id but using Redis + @app.get("/items/{item_id}") + def list_item(item_id: int): + if not redis_client.hexists(f"item_id:{item_id}", "item_id"): + raise HTTPException(status_code=404, detail="Item not found.") + else: + return {"item": redis_client.hgetall(f"item_id:{item_id}")} + + + @app.get("/items") + def list_items() -> dict[str, list[ItemPayload]]: + items: list[ItemPayload] = [] + stored_items: dict[str, str] = redis_client.hgetall("item_name_to_id") + + for name, id_str in stored_items.items(): + item_id: int = int(id_str) + + item_name_str: str | None = redis_client.hget( + f"item_id:{item_id}", "item_name" + ) + if item_name_str is not None: + item_name: str = item_name_str + else: + continue # skip this item if it has no name + + item_quantity_str: str | None = redis_client.hget( + f"item_id:{item_id}", "quantity" + ) + if item_quantity_str is not None: + item_quantity: int = int(item_quantity_str) + else: + item_quantity = 0 + + items.append( + ItemPayload(item_id=item_id, item_name=item_name, quantity=item_quantity) + ) + + return {"items": items} + + + # Route to delete a specific item by id but using Redis + @app.delete("/items/{item_id}") + def delete_item(item_id: int) -> dict[str, str]: + if not redis_client.hexists(f"item_id:{item_id}", "item_id"): + raise HTTPException(status_code=404, detail="Item not found.") + else: + item_name: str | None = redis_client.hget(f"item_id:{item_id}", "item_name") + redis_client.hdel("item_name_to_id", f"{item_name}") + redis_client.hdel(f"item_id:{item_id}", "item_id") + return {"result": "Item deleted."} + + + # Route to remove some quantity of a specific item by id but using Redis + @app.delete("/items/{item_id}/{quantity}") + def remove_quantity(item_id: int, quantity: int): + if not redis_client.hexists(f"item_id:{item_id}", "item_id"): + raise HTTPException(status_code=404, detail="Item not found.") + + item_quantity: str | None = redis_client.hget(f"item_id:{item_id}", "quantity") + + # if quantity to be removed is higher or equal to item's quantity, delete the item + if item_quantity is None: + existing_quantity: int = 0 + else: + existing_quantity: int = int(item_quantity) + if existing_quantity <= quantity: + item_name: str | None = redis_client.hget(f"item_id:{item_id}", "item_name") + redis_client.hdel("item_name_to_id", f"{item_name}") + redis_client.hdel(f"item_id:{item_id}", "item_id") + return {"result": "Item deleted."} + else: + redis_client.hincrby(f"item_id:{item_id}", "quantity", -quantity) + return {"result": f"{quantity} items removed."} + ``` + +12. Save the file (`kb(workbench.action.files.save)`). The application should automatically reload. + +You can now open the `/docs` page again and test the new routes, leveraging the debugger and the debug console to better understand the code execution. + +## Set up the data storage +So far we are storing our data in a dictionary, which is not ideal because all ouf our data will be lost when the application is restarted. + +To persist our data, we will use [Redis](https://redis.io/), which is an open source in-memory data structure store. Due to its speed and versatility, Redis is commonly used as a data storage system in a wide range of applications, including web applications, real-time analytics systems, caching layers, this tutorial, and more. + +If you are already working on **GitHub Codespaces** with our existing template, you can skip directly to the ["Replace the database"](#replace-the-database) section. + +If you are on Windows, you can work with Redis by setting up either a [Docker container](https://www.docker.com/products/docker-desktop/), a [GitHub Codespace](https://github.com/features/codespaces) or through [WSL](https://learn.microsoft.com/en-us/windows/wsl/) (Windows Subsystem for Linux). In this tutorial we will cover the set up using Docker containers, but you can refer to the [section above](#github-codespaces) for instructions on how to set up a GitHub Codespace. + +Otherwise, if you are on a Linux or a macOS machine, you can just install Redis by following the [instructions on their website](https://redis.io/docs/getting-started/), and then skip to the ["Replace the database"](#replace-the-database) section. + +### Setting up a Docker Container on Windows +The Dev Container extension for VS Code offers a simple way to configure a Linux-based workspace in your Windows machine, putting your entire project and its dependencies as well as tools you need for your project into a tidy container. +For the steps below, make sure you have the following requirements installed on your machine: + +#### Requirements +- [Docker for Windows](https://www.docker.com/) +- [Dev Containers extension for VS Code](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) + +#### Create the Dev Container configuration +1. Open the Command Palette and run the “Dev Container: Add Dev Container Configuration Files…" +2. Select "Python 3" + ![Python 3 option selected in the Dev Container configuration files list.](images/fastapi-tutorial/devcontainers_python3.png) +3. Select the default version. + +We can optionally install features to be included in the container. For this tutorial, we will install [Redis Server](https://github.com/itsmechlark/features/tree/main/src/redis-server), which is a community contributed feature that installs and adds the proper dev container set up for Redis. + +4. Select "Redis Server" as an additional feature to be installed, and then press "OK". + ![Redis Server option selected in the Dev Container configuration files list.](images/fastapi-tutorial/devcontainers_redis_server_feature.png) + +This will create a `.devcontainer` folder in your workspace, with a `devcontainer.json` file. + +5. Select the "Reopen in Container" button from the notification that will show up on the bottom right corner once the dev container configuration is added to your workspace, under the `.devcontainer` folder. + + You can learn more about dev containers configuration in the [documentation](https://code.visualstudio.com/docs/devcontainers/containers#_create-a-devcontainerjson-file). + +Once the container is set up, you will notice an indicator on the bottom left corner of VS Code: +![Dev Container indicator displayed on the bottom left corner of VS Code.](images/fastapi-tutorial/devcontainer_indicator.png) + +You should be now ready to move on to the next section, where we will replace our data storage. + +## Replace the database +1. In the `main.py` file, replace the `grocery_list = {}` in the beginning of the file with the lines below: + ```python + redis_client = redis.StrictRedis(host='0.0.0.0', port=6379, db=0, decode_responses=True) + ``` +Pylance will display an error message because redis hasn’t been imported yet. + +2. Click on "redis" in the editor, and select the light bulb the shows up (or `kb(editor.action.quickFix)`). Then select "Add 'import redis'". + ![Light bulb displayed next to the redis variable, with the option to add the import statement.](images/fastapi-tutorial/fastapi_add_redis_quickfix.png) + +You can also set up Pylance to automatically add imports by looking for the "Auto Import Completions" setting in the Settings UI page (`kb(workbench.action.openSettings)`) and enabling it. + +We now have a Redis client object that connects to a Redis server running on the local host (`host="0.0.0.0"`) and listening on port 6379 (`port=6379`). The `db` parameter specifies the Redis database to use. Redis supports multiple databases, and in this code we’re setting to use database 0, which is the default database. We're also passing `decode_responses=True` for the responses to be decoded as strings (instead of bytes). + +This client can now be used to perform operations such as getting, setting, and deleting objects in the Redis database. + +Let's do some more replacements in our first route `add_item`. Instead of looking into all the keys from our dictionary to find the item name that has been provided, we can fetch that information directly from a redis hash. Let’s assume we will create a hash called `item_name_to_id`, with item names mapped into their IDs once they are added in our app. We can then get the id of the item name we’re receiving by invoking the `hget` method from redis. + +3. Delete line 17 (TODO: verify line number): + ```python + items_names = list(grocery_list.values()) + ``` + And replace it with: + ```python + item_id = redis_client.hget("item_name_to_id", "item_id") + ``` + +Note that Pylance raises a problem with this change. This is because the `hget` method returns either `str`, or `None` (if the item doesn’t exist). However, the lines below our code that we haven’t replaced yet expect `item_id` to be of type `int`. Let’s address it by renaming the `item_id` symbol. + +4. Rename `item_id` with `item_id_str` by selecting the symbol and running the **Rename Symbol** command (`kb(editor.action.rename)`). + + This will rename all occurrences of the symbol in the file, including the one in the `return` statement. + +5. Pylance should show a variable type hint next to `item_id_str`. Double click to accept it: + ![Variable type hint displayed next to the item_id_str variable.](images/fastapi-tutorial/pylance_redis_typehint.png) + +6. If the item doesn't exist, then `item_id_str` will be None. So now we can delete line 18 (TODO: verify line): + ```python + if item.item_name in items_names: + ``` + And replace it with: + ```python + if item_id_str is not None: + ``` +Now that we have the `item_id` in a string, we just need to convert it to an `int` and add the quantity provided to the item. Because the redis hash we have so far only maps item names to their IDs, we now need to to map item IDs to their names and quantity. One way to do that is to create hashes for each item, in the format `"item_id:{item_id}"`, and provide it with "name" and "quantity" fields. + +7. Replace lines 19 and 20 with the following, to convert the `item_id` to an `int`, and then incrementing the quantity of our item by calling the `hincrby` method from redis: + ```python + item_id = int(item_id_bytes.decode()) + redis_client.hincrby(f"item_id:{item_id}", "quantity", item.quantity) + ``` + +We now only need to replace the code for when the item does not exist, i.e. `item_id_str` is None. In this case, we will generate a new `item_id`, create a new redis hash for our item, and then add the provided item names and quantity. +To generate a new `item_id`, let’s use the `incr` method from redis, passing a new hash "item_ids". When this method is run for the first time, it will create the item_ids hash with a unique number, and then each time it's run it will generate an incremental number and store it in this hash. + +8. Delete line 21 (TODO: verify line): + ```python + item_id: int = len(grocery_list) + ``` + And add the following: + ```python + item_id: int = redis_client.incr("item_ids") + ``` + +Now we will add our item to our redis hash, using the `hset` method and by providing a mapping of fields or keys (`item_id`, `quantity` and `name`), and the values (the item’s newly created id, and its provided name and quantity). + +9. Delete line 23 (TODO: verify line): + ```python + grocery_list[item_id] = item + ``` + And replace it with: + ```python + redis_client.hset( + f"item_id:{item_id}", + mapping={ + "item_id": item_id, + "item_name": item.item_name, + "quantity": item.quantity, + }, + ``` + +Now we only need to map our newly created id to the item name by setting the hash we referenced in the beginning, `item_name_to_id`. + +10. Add this line to the end of the route, right before the return code: + ```python + redis_client.hset("item_name_to_id", item.item_name, item_id) + ``` + +11. If you would like, you can try to do a similar replacement for the other routes. Otherwise, you can just replace the entire content of your file with the lines below: + ```python + @app.get("/") + def home(request: Request) -> dict[str, str]: + url = str(request.base_url) + return {"message": f"Add /docs to the end of the URL to access the Swagger UI."} + + # Route to add an item + @app.post("/items") + def add_item(item: ItemPayload) -> dict[str, ItemPayload]: + if item.quantity <= 0: + raise HTTPException(status_code=400, detail="Quantity must be greater than 0.") + + # Check if item already exists + item_id_str: str | None = redis_client.hget("item_name_to_id", "item_id") + + if item_id_str is not None: + item_id = int(item_id_str) + redis_client.hincrby(f"item_id:{item_id}", "quantity", item.quantity) + else: + # Generate an id for the item + item_id: int = redis_client.incr("item_id") + redis_client.hset( + f"item_id:{item_id}", + mapping={ + "item_id": item_id, + "item_name": item.item_name, + "quantity": item.quantity, + }, + ) + # Create a set so we can search by name too + redis_client.hset("item_name_to_id", item.item_name, item_id) + + return {"item": item} + + + # Route to list a specific item by id but using Redis + @app.get("/items/{item_id}") + def list_item(item_id: int): + if not redis_client.hexists(f"item_id:{item_id}", "item_id"): + raise HTTPException(status_code=404, detail="Item not found.") + else: + return {"item": redis_client.hgetall(f"item_id:{item_id}")} + + + @app.get("/items") + def list_items() -> dict[str, list[ItemPayload]]: + items: list[ItemPayload] = [] + stored_items: dict[str, str] = redis_client.hgetall("item_name_to_id") + + for name, id_str in stored_items.items(): + item_id: int = int(id_str) + + item_name_str: str | None = redis_client.hget( + f"item_id:{item_id}", "item_name" + ) + if item_name_str is not None: + item_name: str = item_name_str + else: + continue # skip this item if it has no name + + item_quantity_str: str | None = redis_client.hget( + f"item_id:{item_id}", "quantity" + ) + if item_quantity_str is not None: + item_quantity: int = int(item_quantity_str) + else: + item_quantity = 0 + + items.append( + ItemPayload(item_id=item_id, item_name=item_name, quantity=item_quantity) + ) + + return {"items": items} + + + # Route to delete a specific item by id but using Redis + @app.delete("/items/{item_id}") + def delete_item(item_id: int) -> dict[str, str]: + if not redis_client.hexists(f"item_id:{item_id}", "item_id"): + raise HTTPException(status_code=404, detail="Item not found.") + else: + item_name: str | None = redis_client.hget(f"item_id:{item_id}", "item_name") + redis_client.hdel("item_name_to_id", f"{item_name}") + redis_client.hdel(f"item_id:{item_id}", "item_id") + return {"result": "Item deleted."} + + + # Route to remove some quantity of a specific item by id but using Redis + @app.delete("/items/{item_id}/{quantity}") + def remove_quantity(item_id: int, quantity: int): + if not redis_client.hexists(f"item_id:{item_id}", "item_id"): + raise HTTPException(status_code=404, detail="Item not found.") + + item_quantity: str | None = redis_client.hget(f"item_id:{item_id}", "quantity") + + # if quantity to be removed is higher or equal to item's quantity, delete the item + if item_quantity is None: + existing_quantity: int = 0 + else: + existing_quantity: int = int(item_quantity) + if existing_quantity <= quantity: + item_name: str | None = redis_client.hget(f"item_id:{item_id}", "item_name") + redis_client.hdel("item_name_to_id", f"{item_name}") + redis_client.hdel(f"item_id:{item_id}", "item_id") + return {"result": "Item deleted."} + else: + redis_client.hincrby(f"item_id:{item_id}", "quantity", -quantity) + return {"result": f"{quantity} items removed."} + ``` + +12. Re-run the debugger to test this application by interacting with the `/docs` route. + +## Optional: Set up database deletion +Because our data will be now be persisted by redis, you may want to create a script that will allow you to erase all testing data. For that you can create a new file called `flushdb.py` with the following content: +```python +import redis + +redis_client = redis.StrictRedis(host='0.0.0.0', port=6379, db=0, decode_responses=True) +redis_client.flushdb() + +``` +Then when you want to reset your database, you can open the flushdb.py file in VS Code and click on the run button on the top-right corner of the editor or run the **Python: Run Python File in Terminal** command. + +Note that this should be done with caution because it will delete all the keys in the current database, which could lead to data loss if done in production. + +## Optional: Create a ChatGPT plugin using GitHub Codespaces +With GitHub Codespaces, we can host our application for testing purposes to use with [ChagGPT Plugins](https://platform.openai.com/docs/plugins/introduction), which are tools that enable [ChatGPT](https://chat.openai.com/) to interact with existing APIs to enhance ChatGPT’s abilities, allowing it to perform a wide range of actions. ChatGPT Plugins are not currently publicly available, but you can join their [waitlist](https://openai.com/waitlist/plugins) to get access. Once you do, you can follow along the livestream recording below to create your own grocery list plugin for ChatGPT: + + +Note that all personal GitHub.com accounts have a monthly quota of free use of GitHub Codespaces included in the Free or Pro plan. For more information, see "[About billing for GitHub Codespaces](https://docs.github.com/en/billing/managing-billing-for-github-codespaces/about-billing-for-github-codespaces)". + +## Next Steps + +The completed code project from this tutorial can be found on GitHub: [python-sample-vscode-fastapi-tutorial](TODO: Add link). + +Learn more about FastAPI on the [official documentation](https://fastapi.tiangolo.com/). + +To try your app on a production website, check out the tutorial [Deploy Python apps to Azure App Service using Docker Containers](https://learn.microsoft.com/azure/developer/python/tutorial-deploy-containers-01). + +You may also want to review the following articles in the VS Code docs that are relevant to Python: + +- [Editing Python code](/docs/python/editing.md) +- [Managing Python environments](/docs/python/environments.md) +- [Debugging Python](/docs/python/debugging.md) +- [Testing](/docs/python/testing.md) \ No newline at end of file diff --git a/docs/python/tutorial-flask.md b/docs/python/tutorial-flask.md index 5a2d74b1a2..ddec31bc8f 100644 --- a/docs/python/tutorial-flask.md +++ b/docs/python/tutorial-flask.md @@ -1,5 +1,5 @@ --- -Order: 10 +Order: 11 Area: python TOCTitle: Flask Tutorial ContentId: 593d2dd6-20f0-4ad3-8ecd-067cc47ee217 From ce457bd51515b7f67fd1626ba423f5dfe37fc54a Mon Sep 17 00:00:00 2001 From: Greg Van Liew Date: Mon, 16 Oct 2023 09:54:33 -0700 Subject: [PATCH 02/34] Rename "AI Tools in VS Code" to "GitHub Copilot" (#6751) --- blogs/2023/03/30/vscode-copilot.md | 4 ++-- build/sitemap.xml | 2 +- docs/csharp/cs-dev-kit-faq.md | 2 +- .../{artificial-intelligence.md => github-copilot.md} | 6 +++--- docs/editor/intellisense.md | 4 ++-- docs/languages/cpp.md | 2 +- docs/languages/csharp.md | 2 +- docs/languages/java.md | 2 +- docs/languages/javascript.md | 2 +- docs/languages/overview.md | 4 ++-- docs/languages/python.md | 2 +- docs/python/editing.md | 2 +- docs/sourcecontrol/github.md | 2 +- release-notes/v1_75.md | 2 +- release-notes/v1_77.md | 2 +- 15 files changed, 20 insertions(+), 20 deletions(-) rename docs/editor/{artificial-intelligence.md => github-copilot.md} (98%) diff --git a/blogs/2023/03/30/vscode-copilot.md b/blogs/2023/03/30/vscode-copilot.md index 938df1823b..f4c1f6dc68 100644 --- a/blogs/2023/03/30/vscode-copilot.md +++ b/blogs/2023/03/30/vscode-copilot.md @@ -13,7 +13,7 @@ March 30, 2023 by Chris Dias, [@chrisdias](https://twitter.com/chrisdias) **AI did not write this blog post, but it will make your development experiences incredible.** -> **Note**: If you like to learn about the latest GitHub Copilot experience in Visual Studio Code, go to the [AI Tools in VS Code](https://code.visualstudio.com/docs/editor/artificial-intelligence) topic, where you'll find details on the Copilot editor integration and Copilot Chat features such as inline Chat, the dedicated Chat view, and Quick Chat. +> **Note**: If you like to learn about the latest GitHub Copilot experience in Visual Studio Code, go to the [GitHub Copilot in VS Code](https://code.visualstudio.com/docs/editor/github-copilot) topic, where you'll find details on the Copilot editor integration and Copilot Chat features such as inline Chat, the dedicated Chat view, and Quick Chat. There is a lot of buzz, excitement, and some concerns around Artificial Intelligence today. Advancements are happening almost daily, it's hard to keep up. But once you give it a try, you quickly realize what more than a million Copilot users see daily, that this technology does not disappoint, especially with Large Language Models (LLMs) like OpenAI's GPT-3.5/4. @@ -109,7 +109,7 @@ To access the chat experiences (in-editor, Chat view, Quick Chat), you'll need t * A "Chat" icon will appear in the Activity Bar, click on it to open the Chat view. Go ahead, ask Copilot to "write a program to calculate the airspeed velocity of an unladen swallow". * To try out Quick Chat, you can run **Chat: Open Quick Chat** or use the `kb(workbench.action.quickchat.toggle)` keyboard shortcut. -You can learn more about the GitHub Copilot and Copilot Chat extensions in the [AI Tools in VS Code](https://code.visualstudio.com/docs/editor/artificial-intelligence) topic. +You can learn more about the GitHub Copilot and Copilot Chat extensions in the [GitHub Copilot in VS Code](https://code.visualstudio.com/docs/editor/github-copilot) topic. ## Responsible AI diff --git a/build/sitemap.xml b/build/sitemap.xml index 29d128480b..d396fc1dcc 100644 --- a/build/sitemap.xml +++ b/build/sitemap.xml @@ -226,7 +226,7 @@ 0.8 - https://code.visualstudio.com/docs/editor/artificial-intelligence + https://code.visualstudio.com/docs/editor/github-copilot weekly 0.8 diff --git a/docs/csharp/cs-dev-kit-faq.md b/docs/csharp/cs-dev-kit-faq.md index 89bc8c744e..dbeaafdbfa 100644 --- a/docs/csharp/cs-dev-kit-faq.md +++ b/docs/csharp/cs-dev-kit-faq.md @@ -257,4 +257,4 @@ To resolve this issue, clear out old folders from within the `obj` folder or cle ### I am not getting whole line completions -Whole line completions are disabled when the [GitHub Copilot](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot) extension is enabled to allow you to take advantage of the more advanced [AI completion](/docs/editor/artificial-intelligence.md) capabilities. You can verify that Copilot is enabled by checking if the Copilot logo is present in the lower right corner of VS Code. +Whole line completions are disabled when the [GitHub Copilot](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot) extension is enabled to allow you to take advantage of the more advanced [AI completion](/docs/editor/github-copilot.md) capabilities. You can verify that Copilot is enabled by checking if the Copilot logo is present in the lower right corner of VS Code. diff --git a/docs/editor/artificial-intelligence.md b/docs/editor/github-copilot.md similarity index 98% rename from docs/editor/artificial-intelligence.md rename to docs/editor/github-copilot.md index 324a49f976..3c79908744 100644 --- a/docs/editor/artificial-intelligence.md +++ b/docs/editor/github-copilot.md @@ -1,13 +1,13 @@ --- Order: 7 Area: editor -TOCTitle: AI Tools +TOCTitle: GitHub Copilot ContentId: 0aefcb70-7884-487f-953e-46c3e07f7cbe PageTitle: Use GitHub Copilot to enhance your coding with AI DateApproved: 10/4/2023 MetaDescription: Enhance your coding with AI-powered suggestions from GitHub Copilot in Visual Studio Code. --- -# AI Tools in VS Code +# GitHub Copilot in VS Code The [GitHub Copilot](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot) extension is an AI pair programmer tool that helps you write code faster and smarter. You can use the Copilot extension in VS Code to generate code, learn from the code it generates, and even configure your editor. @@ -15,7 +15,7 @@ The [GitHub Copilot](https://marketplace.visualstudio.com/items?itemName=GitHub. ## Prerequisites -You'll use the [GitHub Copilot](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot) extension to power your AI suggestions in VS Code. +You'll use the [GitHub Copilot](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot) extension to power your artificial intelligence (AI) suggestions in VS Code. > Install the GitHub Copilot extension diff --git a/docs/editor/intellisense.md b/docs/editor/intellisense.md index ea8b593bfc..0a325dac60 100644 --- a/docs/editor/intellisense.md +++ b/docs/editor/intellisense.md @@ -195,7 +195,7 @@ In VS Code, you can enhance your coding with artificial intelligence (AI), such [![GitHub Copilot extension in the VS Code Marketplace](images/intellisense/copilot-extension.png)](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot) -You can learn more about how to get started with Copilot in the [Copilot documentation](/docs/editor/artificial-intelligence.md). +You can learn more about how to get started with Copilot in the [Copilot documentation](/docs/editor/github-copilot.md). ## Troubleshooting @@ -213,7 +213,7 @@ IntelliSense is just one of VS Code's powerful features. Read on to learn more: * [Node.js](/docs/nodejs/nodejs-tutorial.md) - See an example of IntelliSense in action in the Node.js walkthrough. * [Debugging](/docs/editor/debugging.md) - Learn how to set up debugging for your application. * [Creating Language extensions](/api/language-extensions/programmatic-language-features.md) - Learn how to create extensions that add IntelliSense for new programming languages. -* [Artificial Intelligence](/docs/editor/artificial-intelligence.md) - Learn how to use AI with GitHub Copilot to enhance your coding. +* [GitHub Copilot in VS Code](/docs/editor/github-copilot.md) - Learn how to use AI with GitHub Copilot to enhance your coding. ## Common questions diff --git a/docs/languages/cpp.md b/docs/languages/cpp.md index 36307d1c23..4826b390ec 100644 --- a/docs/languages/cpp.md +++ b/docs/languages/cpp.md @@ -206,7 +206,7 @@ To install support for Remote Development: GitHub Copilot provides suggestions for numerous languages and a wide variety of frameworks, and it works especially well for Python, JavaScript, TypeScript, Ruby, Go, C# and C++. -You can learn more about how to get started with Copilot in the [Copilot documentation](/docs/editor/artificial-intelligence.md). +You can learn more about how to get started with Copilot in the [Copilot documentation](/docs/editor/github-copilot.md). ## Feedback diff --git a/docs/languages/csharp.md b/docs/languages/csharp.md index 6cdd785105..8b1ab9bf03 100644 --- a/docs/languages/csharp.md +++ b/docs/languages/csharp.md @@ -76,7 +76,7 @@ IntelliSense just works: hit `kb(editor.action.triggerSuggest)` at any time to g GitHub Copilot provides suggestions for numerous languages and a wide variety of frameworks, and it works especially well for Python, JavaScript, TypeScript, Ruby, Go, C# and C++. -You can learn more about how to get started with Copilot in the [Copilot documentation](/docs/editor/artificial-intelligence.md). +You can learn more about how to get started with Copilot in the [Copilot documentation](/docs/editor/github-copilot.md). ## Snippets for C# diff --git a/docs/languages/java.md b/docs/languages/java.md index 49f72345d3..ba00aa33e9 100644 --- a/docs/languages/java.md +++ b/docs/languages/java.md @@ -161,7 +161,7 @@ One of the key advantages of VS Code is speed. When you open your Java source fi GitHub Copilot provides suggestions for numerous languages and a wide variety of frameworks, and it works especially well for Python, JavaScript, TypeScript, Ruby, Go, C# and C++. -You can learn more about how to get started with Copilot in the [Copilot documentation](/docs/editor/artificial-intelligence.md). +You can learn more about how to get started with Copilot in the [Copilot documentation](/docs/editor/github-copilot.md). ## Code snippets diff --git a/docs/languages/javascript.md b/docs/languages/javascript.md index 103826e2d6..2d52410df2 100644 --- a/docs/languages/javascript.md +++ b/docs/languages/javascript.md @@ -255,7 +255,7 @@ Set `"javascript.suggestionActions.enabled"` to `false` to disable suggestions. GitHub Copilot provides suggestions for numerous languages and a wide variety of frameworks, and it works especially well for Python, JavaScript, TypeScript, Ruby, Go, C# and C++. -You can learn more about how to get started with Copilot in the [Copilot documentation](/docs/editor/artificial-intelligence.md). +You can learn more about how to get started with Copilot in the [Copilot documentation](/docs/editor/github-copilot.md). Once you have the Copilot extension installed and enabled, you can test it our for your JavaScript projects. diff --git a/docs/languages/overview.md b/docs/languages/overview.md index 4ef20c0e6f..ad8b3ddab6 100644 --- a/docs/languages/overview.md +++ b/docs/languages/overview.md @@ -33,7 +33,7 @@ Click on any linked item to get an overview of how to use VS Code in the context The richness of support varies across the different languages and their extensions: * Syntax highlighting and bracket matching -* Smart completions (IntelliSense, Artificial Intelligence with [GitHub Copilot](/docs/editor/artificial-intelligence.md)) +* Smart completions (IntelliSense, Artificial Intelligence with [GitHub Copilot](/docs/editor/github-copilot.md)) * Linting and corrections * Code navigation (Go to Definition, Find All References) * Debugging @@ -47,7 +47,7 @@ In VS Code, you can enhance your coding with artificial intelligence (AI), such [![GitHub Copilot extension in the VS Code Marketplace](images/overview/copilot-extension.png)](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot) -You can learn more about how to get started with Copilot in the [Copilot documentation](/docs/editor/artificial-intelligence.md). +You can learn more about how to get started with Copilot in the [Copilot documentation](/docs/editor/github-copilot.md). ## Change the language for the selected file diff --git a/docs/languages/python.md b/docs/languages/python.md index cd326978e5..b54c245d10 100644 --- a/docs/languages/python.md +++ b/docs/languages/python.md @@ -63,7 +63,7 @@ IntelliSense quickly shows methods, class members, and documentation as you type GitHub Copilot provides suggestions for languages beyond Python and a wide variety of frameworks, including JavaScript, TypeScript, Ruby, Go, C# and C++. -You can learn more about how to get started with Copilot in the [Copilot documentation](/docs/editor/artificial-intelligence.md). +You can learn more about how to get started with Copilot in the [Copilot documentation](/docs/editor/github-copilot.md). ## Linting diff --git a/docs/python/editing.md b/docs/python/editing.md index 8c141dc596..9374f9734c 100644 --- a/docs/python/editing.md +++ b/docs/python/editing.md @@ -70,7 +70,7 @@ You can also customize the general behavior of autocomplete and IntelliSense, ev GitHub Copilot provides suggestions for numerous languages and a wide variety of frameworks, and it works especially well for Python, JavaScript, TypeScript, Ruby, Go, C# and C++. -You can learn more about how to get started with Copilot in the [Copilot documentation](/docs/editor/artificial-intelligence.md). +You can learn more about how to get started with Copilot in the [Copilot documentation](/docs/editor/github-copilot.md). ## Navigation diff --git a/docs/sourcecontrol/github.md b/docs/sourcecontrol/github.md index 5f713ba80b..5ac93d2ee7 100644 --- a/docs/sourcecontrol/github.md +++ b/docs/sourcecontrol/github.md @@ -253,4 +253,4 @@ In VS Code, you can enhance your coding with artificial intelligence (AI), such [![GitHub Copilot extension in the VS Code Marketplace](images/github/copilot-extension.png)](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot) -You can learn more about how to get started with Copilot in the [Copilot documentation](/docs/editor/artificial-intelligence.md). \ No newline at end of file +You can learn more about how to get started with Copilot in the [Copilot documentation](/docs/editor/github-copilot.md). \ No newline at end of file diff --git a/release-notes/v1_75.md b/release-notes/v1_75.md index bbbc0d2cc2..861579baff 100644 --- a/release-notes/v1_75.md +++ b/release-notes/v1_75.md @@ -589,7 +589,7 @@ GitHub Copilot is now generally available for businesses, with features like lic To get started, you can sign up for a free trial on the [GitHub Copilot website](https://copilot.github.com). -We've also added a new [AI Tools in VS Code](https://code.visualstudio.com/docs/editor/artificial-intelligence) topic to the VS Code documentation that will help you get started with Copilot. +We've also added a new [GitHub Copilot in VS Code](https://code.visualstudio.com/docs/editor/github-copilot) topic to the VS Code documentation that will help you get started with Copilot. ## Remote Development diff --git a/release-notes/v1_77.md b/release-notes/v1_77.md index 7034a9cd4b..f0b3d53d86 100644 --- a/release-notes/v1_77.md +++ b/release-notes/v1_77.md @@ -239,7 +239,7 @@ You can ask Copilot to look for bugs, explain tricky code, create tests, and eve ![Copilot chat example asking how to change VS Code colors](images/1_77/slash-commands-example.png) -You can learn more about the VS Code team's experience and future with Copilot in the [VS Code and GitHub Copilot](https://code.visualstudio.com/blogs/2023/03/30/vscode-copilot) blog post. You can also read the [AI Tools in VS Code](https://code.visualstudio.com/docs/editor/artificial-intelligence) article for more details about using Copilot in VS Code. +You can learn more about the VS Code team's experience and future with Copilot in the [VS Code and GitHub Copilot](https://code.visualstudio.com/blogs/2023/03/30/vscode-copilot) blog post. You can also read the [GitHub Copilot in VS Code](https://code.visualstudio.com/docs/editor/github-copilot) article for more details about using Copilot in VS Code. ## Preview features From 59ed230bba5f358d0039698b6884456b75ee4969 Mon Sep 17 00:00:00 2001 From: Luiz Date: Tue, 17 Oct 2023 10:38:50 -0500 Subject: [PATCH 03/34] chore: fix webview doc typo (#6752) --- api/extension-guides/webview.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/extension-guides/webview.md b/api/extension-guides/webview.md index dbf1ac62c1..3c5bac300a 100644 --- a/api/extension-guides/webview.md +++ b/api/extension-guides/webview.md @@ -434,7 +434,7 @@ In addition, the **Developer: Reload Webview** command reloads all active webvie Webviews run in isolated contexts that cannot directly access local resources. This is done for security reasons. This means that in order to load images, stylesheets, and other resources from your extension, or to load any content from the user's current workspace, you must use the `Webview.asWebviewUri` function to convert a local `file:` URI into a special URI that VS Code can use to load a subset of local resources. -Imagine that we want to start bundling the cat gifs into our extension rather pulling them from Giphy. To do this, we first create a URI to the file on disk and then pass these URIs through the `asWebviewUri` function: +Imagine that we want to start bundling the cat gifs into our extension rather than pulling them from Giphy. To do this, we first create a URI to the file on disk and then pass these URIs through the `asWebviewUri` function: ```ts import * as vscode from 'vscode'; From 52f7033e59741415275666cdec773586f8551bb8 Mon Sep 17 00:00:00 2001 From: Alex Leung Date: Tue, 17 Oct 2023 19:39:59 -0700 Subject: [PATCH 04/34] Use generic name for program path example (#6753) --- docs/python/debugging.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/python/debugging.md b/docs/python/debugging.md index 02ee141f7e..bafcf80a39 100644 --- a/docs/python/debugging.md +++ b/docs/python/debugging.md @@ -346,13 +346,13 @@ Specifies the mode in which to start debugging: Provides the fully qualified path to the python program's entry module (startup file). The value `${file}`, often used in default configurations, uses the currently active file in the editor. By specifying a specific startup file, you can always be sure of launching your program with the same entry point regardless of which files are open. For example: ```json -"program": "/Users/Me/Projects/PokemonGo-Bot/pokemongo_bot/event_handlers/__init__.py", +"program": "/Users/Me/Projects/MyProject/src/event_handlers/__init__.py", ``` -You can also rely on a relative path from the workspace root. For example, if the root is `/Users/Me/Projects/PokemonGo-Bot` then you can use the following example: +You can also rely on a relative path from the workspace root. For example, if the root is `/Users/Me/Projects/MyProject` then you can use the following example: ```json -"program": "${workspaceFolder}/pokemongo_bot/event_handlers/__init__.py", +"program": "${workspaceFolder}/src/event_handlers/__init__.py", ``` ### `module` From ac4861d265d1922e7a74b01b15ab2c64e7af5755 Mon Sep 17 00:00:00 2001 From: Courtney Webster <60238438+cwebster-99@users.noreply.github.com> Date: Tue, 17 Oct 2023 23:35:12 -0500 Subject: [PATCH 05/34] Updating Python formatters (#6742) * Adding formatter table * adding changes * Update docs/python/formatting.md Co-authored-by: Karthik Nadig * Update docs/python/formatting.md * Update docs/python/formatting.md Co-authored-by: Luciana Abud <45497113+luabud@users.noreply.github.com> * Adding import sorting extensions table * Adding setup instructions for import sorting * typo * fixing more typos * On save sentence * adding workspace settings --------- Co-authored-by: Karthik Nadig Co-authored-by: Luciana Abud <45497113+luabud@users.noreply.github.com> --- docs/python/formatting.md | 41 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/docs/python/formatting.md b/docs/python/formatting.md index ab8fd84e48..dbc9fdc16a 100644 --- a/docs/python/formatting.md +++ b/docs/python/formatting.md @@ -14,7 +14,32 @@ Formatting makes source code easier to read by human beings. By enforcing partic [Linting](/docs/python/linting.md) helps to prevent errors by analyzing code for common syntactical, stylistic, and functional errors and unconventional programming practices. Although there is a little overlap between formatting and linting, the two capabilities are complementary. -The Python extension supports source code formatting through formatter extensions, such as [autopep8](https://marketplace.visualstudio.com/items?itemName=ms-python.autopep8) and [Black Formatter](https://marketplace.visualstudio.com/items?itemName=ms-python.black-formatter). +## Choose a formatter + +Install the formatting tool of your choice from the VS Code [Marketplace](https://marketplace.visualstudio.com/vscode). + +Microsoft publishes the following formatting extensions: + +| Formatter | Extension | +| ------ | ------------------------------------------------------------------------------- | +| autopep8 | [https://marketplace.visualstudio.com/items?itemName=ms-python.autopep8](https://marketplace.visualstudio.com/items?itemName=ms-python.autopep8) | +| Black formatter | [https://marketplace.visualstudio.com/items?itemName=ms-python.black-formatter](https://marketplace.visualstudio.com/items?itemName=ms-python.black-formatter) | + +Formatter extensions offered by the community: + +| Formatter | Extension | +| ------ | ---------------------------------------------------------------------- | +| Ruff | [https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff](https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff) | +| yapf | [https://marketplace.visualstudio.com/items?itemName=eeyore.yapf](https://marketplace.visualstudio.com/items?itemName=eeyore.yapf) | + +Furthermore, below are formatter extensions that support import sorting: + +| Formatter | Extension | +| ------ | ---------------------------------------------------------------------- | +| Ruff | [https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff](https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff) | +| isort | [https://marketplace.visualstudio.com/items?itemName=ms-python.isort](https://marketplace.visualstudio.com/items?itemName=ms-python.isort) | + +> **Note**: If you don't find your preferred formatter in the table above or in the Marketplace, you can add support for it via an extension. You can use the [Python Extension Template](/api/advanced-topics/python-extension-template.md) to integrate new Python tools into VS Code. ## Set a default formatter @@ -36,6 +61,20 @@ For example, to set Black Formatter as the default formatter, add the following } ``` +In order to set a formatter extension as an import sorter, you can set your preference under `"editor.codeActionsOnSave"` in your User `settings.json` file or your Workspace `settings.json` file, under a `[python]` scope. You can open these `settings.json` files using the **Preferences: Open User Settings (JSON)** and **Preferences: Open Workspace Settings (JSON)** commands respectively. This will enable import sorting on save for all Python files. + +For example, to set Ruff as your preferred import sorter, you can add the following setting to your User `settings.json` or your Workspace `settings.json` file: + +```json +{ + "[python]": { + "editor.codeActionsOnSave": { + "source.organizeImports.ruff": true + } + } +} +``` + ## Format your code You can format your code by right-clicking on the editor and selecting **Format Document**, or by using the `kb(editor.action.formatDocument)` keyboard shortcut. From 36c07a6e5550df984ac8019320d65debda4087e4 Mon Sep 17 00:00:00 2001 From: Luciana Abud <45497113+luabud@users.noreply.github.com> Date: Wed, 18 Oct 2023 16:13:49 -0700 Subject: [PATCH 06/34] Fix item query bug --- docs/python/tutorial-fastapi.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/python/tutorial-fastapi.md b/docs/python/tutorial-fastapi.md index a18dcaa8ae..ee7c490fbe 100644 --- a/docs/python/tutorial-fastapi.md +++ b/docs/python/tutorial-fastapi.md @@ -218,7 +218,7 @@ Finally, let's add the remaining routes for our application so we can list all a raise HTTPException(status_code=400, detail="Quantity must be greater than 0.") # Check if item already exists - item_id_str: str | None = redis_client.hget("item_name_to_id", "item_id") + item_id_str: str | None = redis_client.hget("item_name_to_id", item.item_name) if item_id_str is not None: item_id = int(item_id_str) @@ -384,7 +384,7 @@ Let's do some more replacements in our first route `add_item`. Instead of lookin ``` And replace it with: ```python - item_id = redis_client.hget("item_name_to_id", "item_id") + item_id = redis_client.hget("item_name_to_id", item.item_name) ``` Note that Pylance raises a problem with this change. This is because the `hget` method returns either `str`, or `None` (if the item doesn’t exist). However, the lines below our code that we haven’t replaced yet expect `item_id` to be of type `int`. Let’s address it by renaming the `item_id` symbol. @@ -462,7 +462,7 @@ Now we only need to map our newly created id to the item name by setting the has raise HTTPException(status_code=400, detail="Quantity must be greater than 0.") # Check if item already exists - item_id_str: str | None = redis_client.hget("item_name_to_id", "item_id") + item_id_str: str | None = redis_client.hget("item_name_to_id", item.item_name) if item_id_str is not None: item_id = int(item_id_str) From 99f6791b3a61a983c9b3b4d13ffe49b2b029604d Mon Sep 17 00:00:00 2001 From: Luciana Abud <45497113+luabud@users.noreply.github.com> Date: Wed, 18 Oct 2023 17:34:13 -0700 Subject: [PATCH 07/34] Apply suggestions from code review Co-authored-by: Courtney Webster <60238438+cwebster-99@users.noreply.github.com> --- docs/python/tutorial-fastapi.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/python/tutorial-fastapi.md b/docs/python/tutorial-fastapi.md index ee7c490fbe..4d01149364 100644 --- a/docs/python/tutorial-fastapi.md +++ b/docs/python/tutorial-fastapi.md @@ -9,9 +9,9 @@ MetaDescription: Python FastAPI tutorial showing IntelliSense and debugging supp --- # FastAPI Tutorial in Visual Studio Code -FastAPI is a modern high-performant web framework for building APIs with Python. It is designed to make it easy to build APIs quickly and efficiently while providing features like automatic validation, serialization, and documentation of your API, making it a popular choice for building web services and microservices. +[FastAPI](https://fastapi.tiangolo.com/) is a modern high-performant web framework for building APIs with Python. It is designed to make it easy to build APIs quickly and efficiently while providing features like automatic validation, serialization, and documentation of your API, making it a popular choice for building web services and microservices. -In this FastAPI tutorial, we will create a groceries list app using FastAPI. You will be able to understand how to work with FastAPI in the VS Code terminal, editor and debugger. This tutorial does not explore various details about FastAPI itself. For that you can refer to the [FastAPI documentation link at the end of this tutorial](#next-steps)l. +In this FastAPI tutorial, we will create a grocery list app using FastAPI. By the end of the tutorial, you will understand how to work with FastAPI in the VS Code terminal, editor, and debugger. This tutorial does not explore various details about FastAPI itself. For that, you can refer to the [FastAPI documentation link at the end of this tutorial](#next-steps)l. The completed code project from this FastAPI tutorial can be found on GitHub: @@ -23,7 +23,7 @@ There are different ways you can set up your project for this tutorial. We will ## GitHub Codespaces You can set up this project to develop in [GitHub Codespaces](https://github.com/features/codespaces), where you can code, debug, and run your app remotely in a Codespace. A Codespace allows you to access a fully-configured development environment hosted in the cloud, eliminating the need for local setup. This environment includes your project's dependencies, tools, and extensions, ensuring a consistent and reproducible development experience. It streamlines collaboration by providing real-time editing, integrated version control, and easy access to debugging and testing tools, all while maintaining the security and reliability of your project. -To set it up for this tutorial, navigate to the [FastAPI template repository]. This template contains all the necessary configurations and dependencies to quickly get started with FastAPI development in a GitHub Codespace. Next, select Code -> Codespaces -> Create Codespace on
to create and open a Codespace for your project. +To set it up for this tutorial, navigate to the [FastAPI template repository]. This template contains all the necessary configurations and dependencies to quickly get started with FastAPI development in a GitHub Codespace. Next, select **Code** -> **Codespaces** -> **Create Codespace on
** to create and open a Codespace for your project. Once you're done, you can continue with the [Start Coding](#start-coding) section below. @@ -56,7 +56,7 @@ We will want to install FastAPI for creating our app, uvicorn to work as the ser ![Drop down with "Venv" or "Conda" as options for environments that can be created with the Python: Create Environment command.](images/environments/create_environment_dropdown.png) 8. Then select the latest version of Python available on your machine: -![List of available global envionrmetns that can be use to create a virtual environment.](images/fastapi-tutorial/create_environment_interpreters_list.png) +![List of available global environments that can be used to create a virtual environment.](images/fastapi-tutorial/create_environment_interpreters_list.png) 9. Select the requirements.txt file from the drop-down list, so our dependencies are automatically installed, and then select "OK": ![Check box selected to install dependencies from requirements.txt file](images/fastapi-tutorial/create_environment_select_requirements.png) @@ -68,7 +68,7 @@ The virtual environment will be created, our dependencies will be automatically Let’s create our application! 1. Create a new Python file by clicking on **File** > **New File…** (`kb(workbench.action.files.newFile)`), and then select "Python File". 2. Save it as `main.py` (`kb(workbench.action.files.saveAs)`) in the `groceries-plugin` folder. -3. Add the following code to the file: +3. Add the following code to `main.py` and save the file: ```python from fastapi import FastAPI @@ -122,7 +122,7 @@ Now we need a place to store the grocery list items. For simplicity, let’s sta from models import ItemPayload ``` -2. Now add the following line right below app = FastAPI(): +2. Now add the following line right below `app = FastAPI()`: ``` grocery_list: Dict[int, ItemPayload] = {} ``` @@ -192,7 +192,7 @@ Now let’s leverage VS Code's Debug Console to do some exploration. The Debug Console can be a powerful tool to quickly test expressions and better understand the state of your code at the time of the breakpoint. You can also use it to run arbitrary code, such as calling functions or printing variables. Learn more about Python debugging in VS Code in the [Python tutorial](python-tutorial.md#configure-and-run-the-debugger). -Finally, let's add the remaining routes for our application so we can list all and specific items, as well as remove them from our grocery list. +Finally, let's add the remaining routes for our application so we can list all items or specific items, as well as remove them from our grocery list. 11. Replace the content in `main.py` with the code below: ```python @@ -358,7 +358,7 @@ This will create a `.devcontainer` folder in your workspace, with a `devcontaine Once the container is set up, you will notice an indicator on the bottom left corner of VS Code: ![Dev Container indicator displayed on the bottom left corner of VS Code.](images/fastapi-tutorial/devcontainer_indicator.png) -You should be now ready to move on to the next section, where we will replace our data storage. +You should now be ready to move on to the next section, where we will replace our data storage. ## Replace the database 1. In the `main.py` file, replace the `grocery_list = {}` in the beginning of the file with the lines below: From 2f398c7dddccc50b78935931caff530f525f3d8a Mon Sep 17 00:00:00 2001 From: Luciana Abud <45497113+luabud@users.noreply.github.com> Date: Wed, 18 Oct 2023 18:08:07 -0700 Subject: [PATCH 08/34] Fix typo --- docs/python/tutorial-fastapi.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/python/tutorial-fastapi.md b/docs/python/tutorial-fastapi.md index 4d01149364..cb8cf8dfd9 100644 --- a/docs/python/tutorial-fastapi.md +++ b/docs/python/tutorial-fastapi.md @@ -11,7 +11,7 @@ MetaDescription: Python FastAPI tutorial showing IntelliSense and debugging supp [FastAPI](https://fastapi.tiangolo.com/) is a modern high-performant web framework for building APIs with Python. It is designed to make it easy to build APIs quickly and efficiently while providing features like automatic validation, serialization, and documentation of your API, making it a popular choice for building web services and microservices. -In this FastAPI tutorial, we will create a grocery list app using FastAPI. By the end of the tutorial, you will understand how to work with FastAPI in the VS Code terminal, editor, and debugger. This tutorial does not explore various details about FastAPI itself. For that, you can refer to the [FastAPI documentation link at the end of this tutorial](#next-steps)l. +In this FastAPI tutorial, we will create a grocery list app using FastAPI. By the end of the tutorial, you will understand how to work with FastAPI in the VS Code terminal, editor, and debugger. This tutorial does not explore various details about FastAPI itself. For that, you can refer to the [FastAPI documentation link at the end of this tutorial](#next-steps). The completed code project from this FastAPI tutorial can be found on GitHub: @@ -593,4 +593,4 @@ You may also want to review the following articles in the VS Code docs that are - [Editing Python code](/docs/python/editing.md) - [Managing Python environments](/docs/python/environments.md) - [Debugging Python](/docs/python/debugging.md) -- [Testing](/docs/python/testing.md) \ No newline at end of file +- [Testing](/docs/python/testing.md) From 2d927eb67183b0590686c944082c1c312571a9e9 Mon Sep 17 00:00:00 2001 From: Luciana Abud <45497113+luabud@users.noreply.github.com> Date: Wed, 18 Oct 2023 18:09:10 -0700 Subject: [PATCH 09/34] Change code sample to use delete --- docs/python/tutorial-fastapi.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/python/tutorial-fastapi.md b/docs/python/tutorial-fastapi.md index cb8cf8dfd9..8f45a97363 100644 --- a/docs/python/tutorial-fastapi.md +++ b/docs/python/tutorial-fastapi.md @@ -288,7 +288,7 @@ Finally, let's add the remaining routes for our application so we can list all i else: item_name: str | None = redis_client.hget(f"item_id:{item_id}", "item_name") redis_client.hdel("item_name_to_id", f"{item_name}") - redis_client.hdel(f"item_id:{item_id}", "item_id") + redis_client.delete(f"item_id:{item_id}") return {"result": "Item deleted."} @@ -308,7 +308,7 @@ Finally, let's add the remaining routes for our application so we can list all i if existing_quantity <= quantity: item_name: str | None = redis_client.hget(f"item_id:{item_id}", "item_name") redis_client.hdel("item_name_to_id", f"{item_name}") - redis_client.hdel(f"item_id:{item_id}", "item_id") + redis_client.delete(f"item_id:{item_id}") return {"result": "Item deleted."} else: redis_client.hincrby(f"item_id:{item_id}", "quantity", -quantity) From 5c0f72de97985cca214d4fc94127e934276541d1 Mon Sep 17 00:00:00 2001 From: Luciana Abud <45497113+luabud@users.noreply.github.com> Date: Wed, 18 Oct 2023 18:09:43 -0700 Subject: [PATCH 10/34] Fix headers --- docs/python/tutorial-fastapi.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/python/tutorial-fastapi.md b/docs/python/tutorial-fastapi.md index 8f45a97363..bcd8a7f454 100644 --- a/docs/python/tutorial-fastapi.md +++ b/docs/python/tutorial-fastapi.md @@ -20,14 +20,14 @@ If you have any problems, you can search for answers or ask a question on the [P ## Set up the project There are different ways you can set up your project for this tutorial. We will cover how you can set it up in [GitHub Codespaces](TODO: add link to section below) and in [VS Code on your local machine](TODO: ADD Link to section below). -## GitHub Codespaces +### GitHub Codespaces You can set up this project to develop in [GitHub Codespaces](https://github.com/features/codespaces), where you can code, debug, and run your app remotely in a Codespace. A Codespace allows you to access a fully-configured development environment hosted in the cloud, eliminating the need for local setup. This environment includes your project's dependencies, tools, and extensions, ensuring a consistent and reproducible development experience. It streamlines collaboration by providing real-time editing, integrated version control, and easy access to debugging and testing tools, all while maintaining the security and reliability of your project. To set it up for this tutorial, navigate to the [FastAPI template repository]. This template contains all the necessary configurations and dependencies to quickly get started with FastAPI development in a GitHub Codespace. Next, select **Code** -> **Codespaces** -> **Create Codespace on
** to create and open a Codespace for your project. Once you're done, you can continue with the [Start Coding](#start-coding) section below. -## Locally in VS Code +### Locally in VS Code To successfully complete this tutorial in [VS Code](https://code.visualstudio.com/), you need to first setup your Python development environment. Specifically, this tutorial requires: - Python 3 (check the [installation guide](python-tutorial.md#install-a-python-interpreter) if you don’t have it installed) - [Python extension for VS Code](https://marketplace.visualstudio.com/items?itemName=ms-python.python) (For additional details on installing extensions, see [Extension Marketplace](https://code.visualstudio.com/docs/editor/extension-marketplace)) From e76ab58a936ced022929709e76cff72ca7bbe881 Mon Sep 17 00:00:00 2001 From: Luciana Abud Date: Wed, 18 Oct 2023 18:14:40 -0700 Subject: [PATCH 11/34] Fix sample image --- .../python/images/fastapi-tutorial/pylance_redis_typehint.png | 4 ++-- docs/python/tutorial-fastapi.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/python/images/fastapi-tutorial/pylance_redis_typehint.png b/docs/python/images/fastapi-tutorial/pylance_redis_typehint.png index 2531e54229..21b620550b 100644 --- a/docs/python/images/fastapi-tutorial/pylance_redis_typehint.png +++ b/docs/python/images/fastapi-tutorial/pylance_redis_typehint.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7af157b21f7e3d8ce5cac6b837c50611d81a1786da5e3761f35dcc1bb07496fe -size 13253 +oid sha256:227ba1fdccd3de7bbc2d65378a2951f8cd696cbfe30b15e62c0c40fbec09f3b6 +size 14016 diff --git a/docs/python/tutorial-fastapi.md b/docs/python/tutorial-fastapi.md index bcd8a7f454..342998c907 100644 --- a/docs/python/tutorial-fastapi.md +++ b/docs/python/tutorial-fastapi.md @@ -23,7 +23,7 @@ There are different ways you can set up your project for this tutorial. We will ### GitHub Codespaces You can set up this project to develop in [GitHub Codespaces](https://github.com/features/codespaces), where you can code, debug, and run your app remotely in a Codespace. A Codespace allows you to access a fully-configured development environment hosted in the cloud, eliminating the need for local setup. This environment includes your project's dependencies, tools, and extensions, ensuring a consistent and reproducible development experience. It streamlines collaboration by providing real-time editing, integrated version control, and easy access to debugging and testing tools, all while maintaining the security and reliability of your project. -To set it up for this tutorial, navigate to the [FastAPI template repository]. This template contains all the necessary configurations and dependencies to quickly get started with FastAPI development in a GitHub Codespace. Next, select **Code** -> **Codespaces** -> **Create Codespace on
** to create and open a Codespace for your project. +To set it up for this tutorial, navigate to the [FastAPI template repository]. This template contains all the necessary configurations and dependencies to quickly get started with FastAPI development in a GitHub Codespace. Next, select **Code** > **Codespaces** > **Create Codespace on \** to create and open a Codespace for your project. Once you're done, you can continue with the [Start Coding](#start-coding) section below. From 28250910e580823577d777fa3699192c74a4aef2 Mon Sep 17 00:00:00 2001 From: Luciana Abud <45497113+luabud@users.noreply.github.com> Date: Fri, 20 Oct 2023 17:48:20 -0700 Subject: [PATCH 12/34] Fix dictionary sample --- docs/python/tutorial-fastapi.md | 150 +++++++++++--------------------- 1 file changed, 50 insertions(+), 100 deletions(-) diff --git a/docs/python/tutorial-fastapi.md b/docs/python/tutorial-fastapi.md index 342998c907..b5733a879f 100644 --- a/docs/python/tutorial-fastapi.md +++ b/docs/python/tutorial-fastapi.md @@ -196,123 +196,73 @@ Finally, let's add the remaining routes for our application so we can list all i 11. Replace the content in `main.py` with the code below: ```python - from typing import Dict - from fastapi import FastAPI, HTTPException, Request - import redis - + from fastapi import FastAPI, HTTPException + from models import ItemPayload - + app = FastAPI() - - redis_client = redis.StrictRedis(host='0.0.0.0', port=6379, db=0, decode_responses=True) - - @app.get("/") - def home(request: Request) -> dict[str, str]: - url = str(request.base_url) - return {"message": f"Add /docs to the end of the URL to access the Swagger UI."} - + + grocery_list: dict[int, ItemPayload] = {} + + # Route to add an item - @app.post("/items") - def add_item(item: ItemPayload) -> dict[str, ItemPayload]: - if item.quantity <= 0: + @app.post("/items/{item_name}/{quantity}") + def add_item(item_name: str, quantity: int) -> dict[str, ItemPayload]: + if quantity <= 0: raise HTTPException(status_code=400, detail="Quantity must be greater than 0.") - - # Check if item already exists - item_id_str: str | None = redis_client.hget("item_name_to_id", item.item_name) - - if item_id_str is not None: - item_id = int(item_id_str) - redis_client.hincrby(f"item_id:{item_id}", "quantity", item.quantity) + # if item already exists, we'll just add the quantity. + # get all item names + items_ids = {item.item_name: item.item_id if item.item_id is not None else 0 for item in grocery_list.values()} + if item_name in items_ids.keys(): + # get index of item.item_name in item_names, which is the item_id + item_id: int = items_ids[item_name] + grocery_list[item_id].quantity += quantity + # otherwise, create a new item else: - # Generate an id for the item - item_id: int = redis_client.incr("item_id") - redis_client.hset( - f"item_id:{item_id}", - mapping={ - "item_id": item_id, - "item_name": item.item_name, - "quantity": item.quantity, - }, + # generate an id for the item based on the highest ID in the grocery_list + item_id: int = max(grocery_list.keys()) + 1 if grocery_list else 0 + grocery_list[item_id] = ItemPayload( + item_id=item_id, item_name=item_name, quantity=quantity ) - # Create a set so we can search by name too - redis_client.hset("item_name_to_id", item.item_name, item_id) - - return {"item": item} - - - # Route to list a specific item by id but using Redis + + return {"item": grocery_list[item_id]} + + + # Route to list a specific item by id @app.get("/items/{item_id}") - def list_item(item_id: int): - if not redis_client.hexists(f"item_id:{item_id}", "item_id"): + def list_item(item_id: int) -> dict[str, ItemPayload]: + if item_id not in grocery_list: raise HTTPException(status_code=404, detail="Item not found.") - else: - return {"item": redis_client.hgetall(f"item_id:{item_id}")} - - + return {"item": grocery_list[item_id]} + + + # Route to list all items @app.get("/items") - def list_items() -> dict[str, list[ItemPayload]]: - items: list[ItemPayload] = [] - stored_items: dict[str, str] = redis_client.hgetall("item_name_to_id") - - for name, id_str in stored_items.items(): - item_id: int = int(id_str) - - item_name_str: str | None = redis_client.hget( - f"item_id:{item_id}", "item_name" - ) - if item_name_str is not None: - item_name: str = item_name_str - else: - continue # skip this item if it has no name - - item_quantity_str: str | None = redis_client.hget( - f"item_id:{item_id}", "quantity" - ) - if item_quantity_str is not None: - item_quantity: int = int(item_quantity_str) - else: - item_quantity = 0 - - items.append( - ItemPayload(item_id=item_id, item_name=item_name, quantity=item_quantity) - ) - - return {"items": items} - - - # Route to delete a specific item by id but using Redis + def list_items() -> dict[str, dict[int, ItemPayload]]: + return {"items": grocery_list} + + + # Route to delete a specific item by id @app.delete("/items/{item_id}") def delete_item(item_id: int) -> dict[str, str]: - if not redis_client.hexists(f"item_id:{item_id}", "item_id"): + if item_id not in grocery_list: raise HTTPException(status_code=404, detail="Item not found.") - else: - item_name: str | None = redis_client.hget(f"item_id:{item_id}", "item_name") - redis_client.hdel("item_name_to_id", f"{item_name}") - redis_client.delete(f"item_id:{item_id}") - return {"result": "Item deleted."} - - - # Route to remove some quantity of a specific item by id but using Redis + del grocery_list[item_id] + return {"result": "Item deleted."} + + + # Route to remove some quantity of a specific item by id @app.delete("/items/{item_id}/{quantity}") - def remove_quantity(item_id: int, quantity: int): - if not redis_client.hexists(f"item_id:{item_id}", "item_id"): + def remove_quantity(item_id: int, quantity: int) -> dict[str, str]: + if item_id not in grocery_list: raise HTTPException(status_code=404, detail="Item not found.") - - item_quantity: str | None = redis_client.hget(f"item_id:{item_id}", "quantity") - # if quantity to be removed is higher or equal to item's quantity, delete the item - if item_quantity is None: - existing_quantity: int = 0 - else: - existing_quantity: int = int(item_quantity) - if existing_quantity <= quantity: - item_name: str | None = redis_client.hget(f"item_id:{item_id}", "item_name") - redis_client.hdel("item_name_to_id", f"{item_name}") - redis_client.delete(f"item_id:{item_id}") + if grocery_list[item_id].quantity <= quantity: + del grocery_list[item_id] return {"result": "Item deleted."} else: - redis_client.hincrby(f"item_id:{item_id}", "quantity", -quantity) - return {"result": f"{quantity} items removed."} + grocery_list[item_id].quantity -= quantity + return {"result": f"{quantity} items removed."} ``` 12. Save the file (`kb(workbench.action.files.save)`). The application should automatically reload. From c1a88980486dba03835fc7ec778dde8f24cb29d2 Mon Sep 17 00:00:00 2001 From: Luciana Abud <45497113+luabud@users.noreply.github.com> Date: Fri, 20 Oct 2023 17:55:01 -0700 Subject: [PATCH 13/34] Apply suggestions from code review Co-authored-by: Erik De Bonte --- docs/python/tutorial-fastapi.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/docs/python/tutorial-fastapi.md b/docs/python/tutorial-fastapi.md index b5733a879f..79661f7df9 100644 --- a/docs/python/tutorial-fastapi.md +++ b/docs/python/tutorial-fastapi.md @@ -11,7 +11,7 @@ MetaDescription: Python FastAPI tutorial showing IntelliSense and debugging supp [FastAPI](https://fastapi.tiangolo.com/) is a modern high-performant web framework for building APIs with Python. It is designed to make it easy to build APIs quickly and efficiently while providing features like automatic validation, serialization, and documentation of your API, making it a popular choice for building web services and microservices. -In this FastAPI tutorial, we will create a grocery list app using FastAPI. By the end of the tutorial, you will understand how to work with FastAPI in the VS Code terminal, editor, and debugger. This tutorial does not explore various details about FastAPI itself. For that, you can refer to the [FastAPI documentation link at the end of this tutorial](#next-steps). +In this FastAPI tutorial, we will create a grocery list app using FastAPI. By the end of the tutorial, you will understand how to work with FastAPI in the VS Code terminal, editor, and debugger. This tutorial is not a FastAPI deep dive. For that, you can refer to the [FastAPI documentation link at the end of this tutorial](#next-steps). The completed code project from this FastAPI tutorial can be found on GitHub: @@ -32,13 +32,13 @@ To successfully complete this tutorial in [VS Code](https://code.visualstudio.co - Python 3 (check the [installation guide](python-tutorial.md#install-a-python-interpreter) if you don’t have it installed) - [Python extension for VS Code](https://marketplace.visualstudio.com/items?itemName=ms-python.python) (For additional details on installing extensions, see [Extension Marketplace](https://code.visualstudio.com/docs/editor/extension-marketplace)) -In this section, we will create a folder to be open as a workspace in VS Code, set up a Python virtual environment, install the project’s dependencies and write the first lines of our FastAPI app. +In this section, we will create a folder to be opened as a workspace in VS Code, set up a Python virtual environment, install the project’s dependencies and write the first lines of our FastAPI app. -1. On your file system, create a project folder for this tutorial, such as `groceries-plugin``. +1. In your file system, create a project folder for this tutorial, such as `groceries-plugin``. 2. Open this new folder in VS Code (**File** > **Open Folder…** or `kb(workbench.action.files.openFolder)`). -Now, let’s create a requirements.txt file listing the dependencies we wish to install for our application. The requirements.txt file is a common practice in Python development, used to specify the libraries and their versions that your project relies on. This file helps ensure that anyone working on the project can recreate a similar development environment, making it a convenient component for maintaining consistency across different development environments. +Now, let’s create a requirements.txt file listing the dependencies we wish to install for our application. The requirements.txt file is a common practice in Python development, used to specify the libraries that your project relies on and their versions. This file helps ensure that anyone working on the project can recreate a similar development environment, making it a convenient component for maintaining consistency across different development environments. We will want to install FastAPI for creating our app, uvicorn to work as the server, and redis and type-redis for handling data storage and interacting with a Redis database. @@ -94,7 +94,7 @@ Let’s create our application! Now that we have our FastAPI app working, let’s define our grocery list items by creating a Pydantic model. Pydantic is a data validation and parsing library that integrates seamlessly with FastAPI. It allows you to define data models using Python classes with [type hints](https://docs.python.org/3/library/typing.html) for automatic validation and parsing of incoming data (which are called payloads) in API requests. -Pylance, the default language server for Python in VS Code, supports a few type hinting features that can be helpful for working with Pydantic models and FastAPI. Let's enable a few of them: +Pylance, the default language server for Python in VS Code, supports type hinting features that can be helpful for working with Pydantic models and FastAPI. Let's enable a few of them: 1. Open the Settings UI page (`kb(workbench.action.openSettings)`) 2. Search for "python type checking mode" and set it to "basic" to enable basic type checking. This will enable Pylance to show diagnostics and warnings to catch simple type-related errors. Alternetivaly, you can set it to "strict" to enable more advanced [type checking rules](https://microsoft.github.io/pyright/#/configuration?id=diagnostic-rule-defaults). ![Python Analysis Type Checking Mode options (off, basic and strict) in settings UI page.](images/fastapi-tutorial/type_checking_mode_setting.png) @@ -104,7 +104,7 @@ Pylance, the default language server for Python in VS Code, supports a few type Now let's create a Pydantic model for grocery list items. 1. Create a new Python file (`kb(workbench.action.files.newFile)` and select "Python File") 2. Add the following lines to the file, and then save it in the `groceries-plugin` folder as `models.py` (`kb(workbench.action.files.saveAs)`): - ``` + ```python from typing import Optional from pydantic import BaseModel @@ -117,14 +117,14 @@ Now let's create a Pydantic model for grocery list items. ## Create Routes Now we need a place to store the grocery list items. For simplicity, let’s start with an empty dictionary. 1. First, let's import all the packages we will need for our sample. Open the main.py file and replace the first import line with the following ones: - ``` + ```python from fastapi import FastAPI, HTTPException from models import ItemPayload ``` 2. Now add the following line right below `app = FastAPI()`: - ``` - grocery_list: Dict[int, ItemPayload] = {} + ```python + grocery_list: dict[int, ItemPayload] = {} ``` This creates a new empty dictionary that will receive keys of type int (which will be each item id) and values of the ItemPayload type. @@ -270,6 +270,8 @@ Finally, let's add the remaining routes for our application so we can list all i You can now open the `/docs` page again and test the new routes, leveraging the debugger and the debug console to better understand the code execution. ## Set up the data storage +At this point, you already have a working version of the application with the base functionality. This section will guide you through setting up data storage for persistence, but you can choose to skip it if you're happy with what you've learned already. + So far we are storing our data in a dictionary, which is not ideal because all ouf our data will be lost when the application is restarted. To persist our data, we will use [Redis](https://redis.io/), which is an open source in-memory data structure store. Due to its speed and versatility, Redis is commonly used as a data storage system in a wide range of applications, including web applications, real-time analytics systems, caching layers, this tutorial, and more. From 25d78e7126eb7b533ad2b28b0756ca7955dd0ad9 Mon Sep 17 00:00:00 2001 From: Luciana Abud <45497113+luabud@users.noreply.github.com> Date: Fri, 20 Oct 2023 17:55:48 -0700 Subject: [PATCH 14/34] Apply suggestions from code review Co-authored-by: Erik De Bonte --- docs/python/tutorial-fastapi.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/python/tutorial-fastapi.md b/docs/python/tutorial-fastapi.md index 79661f7df9..ec3c9b30d1 100644 --- a/docs/python/tutorial-fastapi.md +++ b/docs/python/tutorial-fastapi.md @@ -272,7 +272,7 @@ You can now open the `/docs` page again and test the new routes, leveraging the ## Set up the data storage At this point, you already have a working version of the application with the base functionality. This section will guide you through setting up data storage for persistence, but you can choose to skip it if you're happy with what you've learned already. -So far we are storing our data in a dictionary, which is not ideal because all ouf our data will be lost when the application is restarted. +So far we are storing our data in a dictionary, which is not ideal because all of our data will be lost when the application is restarted. To persist our data, we will use [Redis](https://redis.io/), which is an open source in-memory data structure store. Due to its speed and versatility, Redis is commonly used as a data storage system in a wide range of applications, including web applications, real-time analytics systems, caching layers, this tutorial, and more. @@ -280,7 +280,7 @@ If you are already working on **GitHub Codespaces** with our existing template, If you are on Windows, you can work with Redis by setting up either a [Docker container](https://www.docker.com/products/docker-desktop/), a [GitHub Codespace](https://github.com/features/codespaces) or through [WSL](https://learn.microsoft.com/en-us/windows/wsl/) (Windows Subsystem for Linux). In this tutorial we will cover the set up using Docker containers, but you can refer to the [section above](#github-codespaces) for instructions on how to set up a GitHub Codespace. -Otherwise, if you are on a Linux or a macOS machine, you can just install Redis by following the [instructions on their website](https://redis.io/docs/getting-started/), and then skip to the ["Replace the database"](#replace-the-database) section. +Otherwise, if you are on a Linux or a macOS machine, you can install Redis by following the [instructions on their website](https://redis.io/docs/getting-started/), and then skip to the ["Replace the database"](#replace-the-database) section. ### Setting up a Docker Container on Windows The Dev Container extension for VS Code offers a simple way to configure a Linux-based workspace in your Windows machine, putting your entire project and its dependencies as well as tools you need for your project into a tidy container. @@ -303,9 +303,9 @@ We can optionally install features to be included in the container. For this tut This will create a `.devcontainer` folder in your workspace, with a `devcontainer.json` file. -5. Select the "Reopen in Container" button from the notification that will show up on the bottom right corner once the dev container configuration is added to your workspace, under the `.devcontainer` folder. +5. Select the "Reopen in Container" button from the notification that will show up on the bottom right corner. - You can learn more about dev containers configuration in the [documentation](https://code.visualstudio.com/docs/devcontainers/containers#_create-a-devcontainerjson-file). + You can learn more about dev container configuration in the [documentation](https://code.visualstudio.com/docs/devcontainers/containers#_create-a-devcontainerjson-file). Once the container is set up, you will notice an indicator on the bottom left corner of VS Code: ![Dev Container indicator displayed on the bottom left corner of VS Code.](images/fastapi-tutorial/devcontainer_indicator.png) @@ -356,15 +356,15 @@ Note that Pylance raises a problem with this change. This is because the `hget` ```python if item_id_str is not None: ``` -Now that we have the `item_id` in a string, we just need to convert it to an `int` and add the quantity provided to the item. Because the redis hash we have so far only maps item names to their IDs, we now need to to map item IDs to their names and quantity. One way to do that is to create hashes for each item, in the format `"item_id:{item_id}"`, and provide it with "name" and "quantity" fields. +Now that we have the item id in a string, we just need to convert it to an `int` and add the quantity provided to the item. Because the redis hash we have so far only maps item names to their IDs, we now need to map item IDs to their names and quantity. One way to do that is to create hashes for each item, in the format `"item_id:{item_id}"`, and provide it with "name" and "quantity" fields. 7. Replace lines 19 and 20 with the following, to convert the `item_id` to an `int`, and then incrementing the quantity of our item by calling the `hincrby` method from redis: ```python - item_id = int(item_id_bytes.decode()) + item_id = int(item_id_str) redis_client.hincrby(f"item_id:{item_id}", "quantity", item.quantity) ``` -We now only need to replace the code for when the item does not exist, i.e. `item_id_str` is None. In this case, we will generate a new `item_id`, create a new redis hash for our item, and then add the provided item names and quantity. +We now only need to replace the code for when the item does not exist, i.e. `item_id_str` is `None`. In this case, we will generate a new `item_id`, create a new redis hash for our item, and then add the provided item names and quantity. To generate a new `item_id`, let’s use the `incr` method from redis, passing a new hash "item_ids". When this method is run for the first time, it will create the item_ids hash with a unique number, and then each time it's run it will generate an incremental number and store it in this hash. 8. Delete line 21 (TODO: verify line): @@ -390,7 +390,7 @@ Now we will add our item to our redis hash, using the `hset` method and by provi "item_id": item_id, "item_name": item.item_name, "quantity": item.quantity, - }, + }) ``` Now we only need to map our newly created id to the item name by setting the hash we referenced in the beginning, `item_name_to_id`. From a9d31cbdca9d1da6638985ebe6a48bb939e7639f Mon Sep 17 00:00:00 2001 From: John Murray Date: Sat, 21 Oct 2023 02:38:37 +0100 Subject: [PATCH 15/34] Clarify where the `scm/sourceControl` menu appears (#6754) --- api/extension-guides/scm-provider.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/extension-guides/scm-provider.md b/api/extension-guides/scm-provider.md index d4b0bc1975..3349977176 100644 --- a/api/extension-guides/scm-provider.md +++ b/api/extension-guides/scm-provider.md @@ -133,7 +133,7 @@ When creating them, `SourceControl` and `SourceControlResourceGroup` instances r } ``` -The `scm/sourceControl` menu is located contextually near SourceControl instances: +The `scm/sourceControl` menu is the context menu on each SourceControl instance in the Source Control Repositories view (previously named Source Control Providers): ![source control menu](images/scm-provider/sourcecontrol-menu.png) From 78dff3c42d271d777e7e7495377b41440711ea69 Mon Sep 17 00:00:00 2001 From: Greg Van Liew Date: Sun, 22 Oct 2023 16:56:58 -0700 Subject: [PATCH 16/34] Update image with context menu (#6758) --- .../images/scm-provider/sourcecontrol-menu.png | 4 ++-- api/extension-guides/scm-provider.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/extension-guides/images/scm-provider/sourcecontrol-menu.png b/api/extension-guides/images/scm-provider/sourcecontrol-menu.png index 996e0fa6db..8405d46d9d 100644 --- a/api/extension-guides/images/scm-provider/sourcecontrol-menu.png +++ b/api/extension-guides/images/scm-provider/sourcecontrol-menu.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bc9c63a4aeae194bc8ae984d9ac7a3fbcd0f663abb0c6317a78955992e60e780 -size 44782 +oid sha256:7c46bcb741233e33d97a052fa0015c261d62627a47d798e94a5139d615e391fa +size 68157 diff --git a/api/extension-guides/scm-provider.md b/api/extension-guides/scm-provider.md index 3349977176..2b990fc6bc 100644 --- a/api/extension-guides/scm-provider.md +++ b/api/extension-guides/scm-provider.md @@ -133,11 +133,11 @@ When creating them, `SourceControl` and `SourceControlResourceGroup` instances r } ``` -The `scm/sourceControl` menu is the context menu on each SourceControl instance in the Source Control Repositories view (previously named Source Control Providers): +The `scm/sourceControl` menu is the context menu on each `SourceControl` instance in the **Source Control Repositories** view: ![source control menu](images/scm-provider/sourcecontrol-menu.png) -The `scm/change/title` allows you to contribute commands to the title bar of the [Quick Diff](/api/references/contribution-points#QuickDiffProvider) inline diff editor, described [further ahead](#quick-diff). The command will be passed as arguments the URI of the document, the array of changes within it, and the index of the change which the inline change diff editor is currently focused on. For example, here's the declaration of the `stageChange` Git command which is contributed to this menu with a `when` clause testing that the `originalResourceScheme` [context key](/api/references/when-clause-contexts) equals `git`: +The `scm/change/title` allows you to contribute commands to the title bar of the [Quick Diff](/api/references/vscode-api#QuickDiffProvider) inline diff editor, described [further ahead](#quick-diff). The command will be passed as arguments the URI of the document, the array of changes within it, and the index of the change which the inline change diff editor is currently focused on. For example, here's the declaration of the `stageChange` Git command which is contributed to this menu with a `when` clause testing that the `originalResourceScheme` [context key](/api/references/when-clause-contexts) equals `git`: ```ts async stageChange(uri: Uri, changes: LineChange[], index: number): Promise; From 9c317417b32f8b04ddf9628000ce183ead9ff640 Mon Sep 17 00:00:00 2001 From: Luciana Abud Date: Mon, 23 Oct 2023 07:23:32 -0700 Subject: [PATCH 17/34] Address code review comments --- docs/python/tutorial-fastapi.md | 140 +++++++++++++++++++------------- 1 file changed, 85 insertions(+), 55 deletions(-) diff --git a/docs/python/tutorial-fastapi.md b/docs/python/tutorial-fastapi.md index ec3c9b30d1..06148bfb3f 100644 --- a/docs/python/tutorial-fastapi.md +++ b/docs/python/tutorial-fastapi.md @@ -38,11 +38,11 @@ In this section, we will create a folder to be opened as a workspace in VS Code, 2. Open this new folder in VS Code (**File** > **Open Folder…** or `kb(workbench.action.files.openFolder)`). -Now, let’s create a requirements.txt file listing the dependencies we wish to install for our application. The requirements.txt file is a common practice in Python development, used to specify the libraries that your project relies on and their versions. This file helps ensure that anyone working on the project can recreate a similar development environment, making it a convenient component for maintaining consistency across different development environments. +Now, let’s create a requirements.txt file listing the dependencies we wish to install for the application. The requirements.txt file is a common practice in Python development, used to specify the libraries that your project relies on and their versions. This file helps ensure that anyone working on the project can recreate a similar development environment, making it a convenient component for maintaining consistency across different development environments. -We will want to install FastAPI for creating our app, uvicorn to work as the server, and redis and type-redis for handling data storage and interacting with a Redis database. +We will want to install FastAPI for creating the app, uvicorn to work as the server, and redis and type-redis for handling data storage and interacting with a Redis database. -3. Create a new file in VS Code (**File** > **New File** or `kb(workbench.action.files.newUntitledFile)`). +3. Create a new file in VS Code (**File** > **New Text File** or `kb(workbench.action.files.newUntitledFile)`). 4. Add the following content to it: ``` fastapi @@ -52,20 +52,21 @@ We will want to install FastAPI for creating our app, uvicorn to work as the ser ``` 5. Save the file (`kb(workbench.action.files.save)`) and name it requirements.txt 6. Create a virtual environment by opening the Command Palette (`kb(workbench.action.showCommands)`) and running the **Python: Create Environment** command. + > Note: this step may take a couple of minutes to complete. 7. When asked by the environment type, select **Venv**: ![Drop down with "Venv" or "Conda" as options for environments that can be created with the Python: Create Environment command.](images/environments/create_environment_dropdown.png) 8. Then select the latest version of Python available on your machine: ![List of available global environments that can be used to create a virtual environment.](images/fastapi-tutorial/create_environment_interpreters_list.png) -9. Select the requirements.txt file from the drop-down list, so our dependencies are automatically installed, and then select "OK": +9. Select the requirements.txt file from the drop-down list, so the dependencies are automatically installed, and then select "OK": ![Check box selected to install dependencies from requirements.txt file](images/fastapi-tutorial/create_environment_select_requirements.png) -The virtual environment will be created, our dependencies will be automatically installed and the environment will be selected for your workspace to be used by the Python extension. +The virtual environment will be created, the dependencies will be automatically installed and the environment will be selected for your workspace to be used by the Python extension. ## Start Coding -Let’s create our application! +Let’s create the application! 1. Create a new Python file by clicking on **File** > **New File…** (`kb(workbench.action.files.newFile)`), and then select "Python File". 2. Save it as `main.py` (`kb(workbench.action.files.saveAs)`) in the `groceries-plugin` folder. 3. Add the following code to `main.py` and save the file: @@ -91,7 +92,7 @@ Let’s create our application! 7. Stop the debugger by clicking on the stop icon in the debug toolbar, or through `kb(workbench.action.debug.stop)`. ## Create a model for grocery list items -Now that we have our FastAPI app working, let’s define our grocery list items by creating a Pydantic model. +Now that we have the FastAPI app working, let’s define our grocery list items by creating a Pydantic model. Pydantic is a data validation and parsing library that integrates seamlessly with FastAPI. It allows you to define data models using Python classes with [type hints](https://docs.python.org/3/library/typing.html) for automatic validation and parsing of incoming data (which are called payloads) in API requests. Pylance, the default language server for Python in VS Code, supports type hinting features that can be helpful for working with Pydantic models and FastAPI. Let's enable a few of them: @@ -116,7 +117,7 @@ Now let's create a Pydantic model for grocery list items. ## Create Routes Now we need a place to store the grocery list items. For simplicity, let’s start with an empty dictionary. -1. First, let's import all the packages we will need for our sample. Open the main.py file and replace the first import line with the following ones: +1. First, let's import all the packages we will need for the sample. Open the main.py file and replace the first import line with the following ones: ```python from fastapi import FastAPI, HTTPException @@ -147,7 +148,7 @@ We’ll now define routes that will allow us to add and retrieve individual item grocery_list[item_id].quantity += quantity # otherwise, create a new item else: - # generate an id for the item based on the highest ID in the grocery_list + # generate an ID for the item based on the highest ID in the grocery_list item_id = max(grocery_list.keys()) + 1 if grocery_list else 0 grocery_list[item_id] = ItemPayload(item_id=item_id, item_name=item_name, quantity=quantity) @@ -158,20 +159,20 @@ We’ll now define routes that will allow us to add and retrieve individual item You might notice Pylance adds inlay hints with the function return type, as well as the types for `item_names` and `item_id`. You can double click on each suggestion to insert them into the code: ![Inlay function return and variable type hints being displayed by Pylance throughout the sample code.](images/fastapi-tutorial/pylance_inlay_hints.png) -Now let's see if this route is working as expected. The fastest way to do so is to leverage both VS Code's debugger as well as FastAPI's `/docs` endpoint, which provides information about all the available API routes and allows you to interact with the API to explore their parameters and responses. This documentation is generated dynamically based on the metadata and type hints defined in your FastAPI application. +Now let's see if this route is working as expected. The fastest way to do so is to leverage both VS Code's debugger as well as FastAPI's `/docs` endpoint, which provides information about all the available API routes and allows you to interact with the API to explore their parameters and responses. This documentation is generated dynamically based on the metadata and type hints defined in the FastAPI application. 4. Add a breakpoint next to the if item.quantity <= 0 statement, by clicking on the left margin of the line number (or `kb(editor.debug.action.toggleBreakpoint)`). This will make the debugger stop prior to the execution of that line, allowing us to inspect the code line by line. ![Breakpoint set next to the first line in the add_item function.](images/fastapi-tutorial/debugger_breakpoint.png) 5. Start the debugger (`kb(workbench.action.debug.start)`), and then navigate to http://127.0.0.1:8000/docs in the browser. - You'll see a Swagger-like interface with the two endpoints available in your app: `/items` and root (`/`). + You'll see a Swagger interface with the two endpoints available in the app: `/items` and root (`/`). ![Swagger UI displaying two endpoints: /items and /](images/fastapi-tutorial/fastapi_first_swagger_page.png) 6. Click on the down arrow next to the `/items` route to expand it, and click on the "Try it out" button that appears on the right side. ![Try it out button displayed next to the /items route in the Swagger UI.](images/fastapi-tutorial/fastapi_tryitout_button.png) -7. Add a grocery list item by passing a string to the "item" field, and a number to "quantity". For example, you could provide `{"item_id": 0, "item" : "apple", "quantity": 2 }`. +7. Add a grocery list item by passing a string to the "item" field, and a number to "quantity". For example, you could provide `{"item" : "apple", "quantity": 2 }`. 8. Click on the "Execute" button. ![Execute button displayed below the /items route.](images/fastapi-tutorial/fastapi_execute_button.png) @@ -192,19 +193,19 @@ Now let’s leverage VS Code's Debug Console to do some exploration. The Debug Console can be a powerful tool to quickly test expressions and better understand the state of your code at the time of the breakpoint. You can also use it to run arbitrary code, such as calling functions or printing variables. Learn more about Python debugging in VS Code in the [Python tutorial](python-tutorial.md#configure-and-run-the-debugger). -Finally, let's add the remaining routes for our application so we can list all items or specific items, as well as remove them from our grocery list. +Finally, let's add the remaining routes for the application so we can list all items or specific items, as well as remove them from our grocery list. You can leave the debugger running as it will automatically reload the application when you save the changes you'll make in the next step. 11. Replace the content in `main.py` with the code below: ```python from fastapi import FastAPI, HTTPException - + from models import ItemPayload - + app = FastAPI() - + grocery_list: dict[int, ItemPayload] = {} - - + + # Route to add an item @app.post("/items/{item_name}/{quantity}") def add_item(item_name: str, quantity: int) -> dict[str, ItemPayload]: @@ -219,29 +220,29 @@ Finally, let's add the remaining routes for our application so we can list all i grocery_list[item_id].quantity += quantity # otherwise, create a new item else: - # generate an id for the item based on the highest ID in the grocery_list + # generate an ID for the item based on the highest ID in the grocery_list item_id: int = max(grocery_list.keys()) + 1 if grocery_list else 0 grocery_list[item_id] = ItemPayload( item_id=item_id, item_name=item_name, quantity=quantity ) - + return {"item": grocery_list[item_id]} - - + + # Route to list a specific item by id @app.get("/items/{item_id}") def list_item(item_id: int) -> dict[str, ItemPayload]: if item_id not in grocery_list: raise HTTPException(status_code=404, detail="Item not found.") return {"item": grocery_list[item_id]} - - + + # Route to list all items @app.get("/items") def list_items() -> dict[str, dict[int, ItemPayload]]: return {"items": grocery_list} - - + + # Route to delete a specific item by id @app.delete("/items/{item_id}") def delete_item(item_id: int) -> dict[str, str]: @@ -249,8 +250,8 @@ Finally, let's add the remaining routes for our application so we can list all i raise HTTPException(status_code=404, detail="Item not found.") del grocery_list[item_id] return {"result": "Item deleted."} - - + + # Route to remove some quantity of a specific item by id @app.delete("/items/{item_id}/{quantity}") def remove_quantity(item_id: int, quantity: int) -> dict[str, str]: @@ -267,14 +268,14 @@ Finally, let's add the remaining routes for our application so we can list all i 12. Save the file (`kb(workbench.action.files.save)`). The application should automatically reload. -You can now open the `/docs` page again and test the new routes, leveraging the debugger and the debug console to better understand the code execution. +You can now open the `/docs` page again and test the new routes, leveraging the debugger and the debug console to better understand the code execution. Once you're done, you can stop the debugger (`kb(workbench.action.debug.stop)`). ## Set up the data storage At this point, you already have a working version of the application with the base functionality. This section will guide you through setting up data storage for persistence, but you can choose to skip it if you're happy with what you've learned already. -So far we are storing our data in a dictionary, which is not ideal because all of our data will be lost when the application is restarted. +So far we are storing the data in a dictionary, which is not ideal because all of the data will be lost when the application is restarted. -To persist our data, we will use [Redis](https://redis.io/), which is an open source in-memory data structure store. Due to its speed and versatility, Redis is commonly used as a data storage system in a wide range of applications, including web applications, real-time analytics systems, caching layers, this tutorial, and more. +To persist the data, we will use [Redis](https://redis.io/), which is an open source in-memory data structure store. Due to its speed and versatility, Redis is commonly used as a data storage system in a wide range of applications, including web applications, real-time analytics systems, caching layers, this tutorial, and more. If you are already working on **GitHub Codespaces** with our existing template, you can skip directly to the ["Replace the database"](#replace-the-database) section. @@ -298,19 +299,48 @@ For the steps below, make sure you have the following requirements installed on We can optionally install features to be included in the container. For this tutorial, we will install [Redis Server](https://github.com/itsmechlark/features/tree/main/src/redis-server), which is a community contributed feature that installs and adds the proper dev container set up for Redis. -4. Select "Redis Server" as an additional feature to be installed, and then press "OK". +4. Select "Redis Server" as an additional feature to be installed, press "OK", and then select "Keep Defaults". ![Redis Server option selected in the Dev Container configuration files list.](images/fastapi-tutorial/devcontainers_redis_server_feature.png) -This will create a `.devcontainer` folder in your workspace, with a `devcontainer.json` file. +This will create a `.devcontainer` folder in your workspace, with a `devcontainer.json` file. Let's make some edits to this file so the container setup can include steps such as installing the VS Code extensions we'll need as well as the project dependencies. + +5. Open the `devcontainer.json` file. + +6. Locate the content below and remove the comment (`//`) from that line, so the dependencies can be installed once the container is created: + ``` + "postCreateCommand": "pip3 install --user -r requirements.txt", + ``` +7. Add the following setting to `devcontainer.json`: + ``` + // 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.vscode-pylance", + "esbenp.prettier-vscode", + "ms-python.black-formatter", + "charliermarsh.ruff" + ] + } + } + ``` +8. Save the file. -5. Select the "Reopen in Container" button from the notification that will show up on the bottom right corner. +9. Select the "Reopen in Container" button from the notification that will show up on the bottom right corner. You can learn more about dev container configuration in the [documentation](https://code.visualstudio.com/docs/devcontainers/containers#_create-a-devcontainerjson-file). + > Note: it may take several minutes to build the container, depending on internet speed and machine performance. + +Once it's done, you will have a fully configured Linux-based workspace with Python 3 and Redis Server installed. Once the container is set up, you will notice an indicator on the bottom left corner of VS Code: ![Dev Container indicator displayed on the bottom left corner of VS Code.](images/fastapi-tutorial/devcontainer_indicator.png) -You should now be ready to move on to the next section, where we will replace our data storage. +You should now be ready to move on to the next section, where we will replace the data storage. ## Replace the database 1. In the `main.py` file, replace the `grocery_list = {}` in the beginning of the file with the lines below: @@ -328,9 +358,9 @@ We now have a Redis client object that connects to a Redis server running on the This client can now be used to perform operations such as getting, setting, and deleting objects in the Redis database. -Let's do some more replacements in our first route `add_item`. Instead of looking into all the keys from our dictionary to find the item name that has been provided, we can fetch that information directly from a redis hash. Let’s assume we will create a hash called `item_name_to_id`, with item names mapped into their IDs once they are added in our app. We can then get the id of the item name we’re receiving by invoking the `hget` method from redis. +Let's do some more replacements in the first route `add_item`. Instead of looking into all the keys from the dictionary to find the item name that has been provided, we can fetch that information directly from a redis hash. Let’s assume we will create a hash called `item_name_to_id`, with item names mapped into their IDs once they are added in the app. We can then get the ID of the item name we’re receiving by invoking the `hget` method from redis. -3. Delete line 17 (TODO: verify line number): +3. Delete the line with the content below: ```python items_names = list(grocery_list.values()) ``` @@ -339,7 +369,7 @@ Let's do some more replacements in our first route `add_item`. Instead of lookin item_id = redis_client.hget("item_name_to_id", item.item_name) ``` -Note that Pylance raises a problem with this change. This is because the `hget` method returns either `str`, or `None` (if the item doesn’t exist). However, the lines below our code that we haven’t replaced yet expect `item_id` to be of type `int`. Let’s address it by renaming the `item_id` symbol. +Note that Pylance raises a problem with this change. This is because the `hget` method returns either `str`, or `None` (if the item doesn’t exist). However, the lines below the code that we haven’t replaced yet expect `item_id` to be of type `int`. Let’s address it by renaming the `item_id` symbol. 4. Rename `item_id` with `item_id_str` by selecting the symbol and running the **Rename Symbol** command (`kb(editor.action.rename)`). @@ -348,7 +378,7 @@ Note that Pylance raises a problem with this change. This is because the `hget` 5. Pylance should show a variable type hint next to `item_id_str`. Double click to accept it: ![Variable type hint displayed next to the item_id_str variable.](images/fastapi-tutorial/pylance_redis_typehint.png) -6. If the item doesn't exist, then `item_id_str` will be None. So now we can delete line 18 (TODO: verify line): +6. If the item doesn't exist, then `item_id_str` will be None. So now we can delete the line with the following content: ```python if item.item_name in items_names: ``` @@ -356,18 +386,18 @@ Note that Pylance raises a problem with this change. This is because the `hget` ```python if item_id_str is not None: ``` -Now that we have the item id in a string, we just need to convert it to an `int` and add the quantity provided to the item. Because the redis hash we have so far only maps item names to their IDs, we now need to map item IDs to their names and quantity. One way to do that is to create hashes for each item, in the format `"item_id:{item_id}"`, and provide it with "name" and "quantity" fields. +Now that we have the item ID in a string, we just need to convert it to an `int` and add the quantity provided to the item. Because the redis hash we have so far only maps item names to their IDs, we now need to map item IDs to their names and quantity. One way to do that is to create hashes for each item, in the format `"item_id:{item_id}"`, and provide it with "name" and "quantity" fields. -7. Replace lines 19 and 20 with the following, to convert the `item_id` to an `int`, and then incrementing the quantity of our item by calling the `hincrby` method from redis: +7. Replace lines 19 and 20 with the following, to convert the `item_id` to an `int`, and then incrementing the quantity of the item by calling the `hincrby` method from redis: ```python item_id = int(item_id_str) redis_client.hincrby(f"item_id:{item_id}", "quantity", item.quantity) ``` -We now only need to replace the code for when the item does not exist, i.e. `item_id_str` is `None`. In this case, we will generate a new `item_id`, create a new redis hash for our item, and then add the provided item names and quantity. +We now only need to replace the code for when the item does not exist, i.e. `item_id_str` is `None`. In this case, we will generate a new `item_id`, create a new redis hash for the item, and then add the provided item names and quantity. To generate a new `item_id`, let’s use the `incr` method from redis, passing a new hash "item_ids". When this method is run for the first time, it will create the item_ids hash with a unique number, and then each time it's run it will generate an incremental number and store it in this hash. -8. Delete line 21 (TODO: verify line): +8. Delete the line with the following content: ```python item_id: int = len(grocery_list) ``` @@ -376,9 +406,9 @@ To generate a new `item_id`, let’s use the `incr` method from redis, passing a item_id: int = redis_client.incr("item_ids") ``` -Now we will add our item to our redis hash, using the `hset` method and by providing a mapping of fields or keys (`item_id`, `quantity` and `name`), and the values (the item’s newly created id, and its provided name and quantity). +Now we will add the item to the redis hash, using the `hset` method and by providing a mapping of fields or keys (`item_id`, `quantity` and `name`), and the values (the item’s newly created id, and its provided name and quantity). -9. Delete line 23 (TODO: verify line): +9. Delete the line with the following content: ```python grocery_list[item_id] = item ``` @@ -393,14 +423,14 @@ Now we will add our item to our redis hash, using the `hset` method and by provi }) ``` -Now we only need to map our newly created id to the item name by setting the hash we referenced in the beginning, `item_name_to_id`. +Now we only need to map the newly created ID to the item name by setting the hash we referenced in the beginning, `item_name_to_id`. -10. Add this line to the end of the route, right before the return code: +10. Add this line to the end of the route, inside the `else` block: ```python redis_client.hset("item_name_to_id", item.item_name, item_id) ``` -11. If you would like, you can try to do a similar replacement for the other routes. Otherwise, you can just replace the entire content of your file with the lines below: +11. If you would like, you can try to do a similar replacement for the other routes. Otherwise, you can just replace the entire content of the file with the lines below: ```python @app.get("/") def home(request: Request) -> dict[str, str]: @@ -420,7 +450,7 @@ Now we only need to map our newly created id to the item name by setting the has item_id = int(item_id_str) redis_client.hincrby(f"item_id:{item_id}", "quantity", item.quantity) else: - # Generate an id for the item + # Generate an ID for the item item_id: int = redis_client.incr("item_id") redis_client.hset( f"item_id:{item_id}", @@ -436,7 +466,7 @@ Now we only need to map our newly created id to the item name by setting the has return {"item": item} - # Route to list a specific item by id but using Redis + # Route to list a specific item by ID but using Redis @app.get("/items/{item_id}") def list_item(item_id: int): if not redis_client.hexists(f"item_id:{item_id}", "item_id"): @@ -476,7 +506,7 @@ Now we only need to map our newly created id to the item name by setting the has return {"items": items} - # Route to delete a specific item by id but using Redis + # Route to delete a specific item by ID but using Redis @app.delete("/items/{item_id}") def delete_item(item_id: int) -> dict[str, str]: if not redis_client.hexists(f"item_id:{item_id}", "item_id"): @@ -488,7 +518,7 @@ Now we only need to map our newly created id to the item name by setting the has return {"result": "Item deleted."} - # Route to remove some quantity of a specific item by id but using Redis + # Route to remove some quantity of a specific item by ID but using Redis @app.delete("/items/{item_id}/{quantity}") def remove_quantity(item_id: int, quantity: int): if not redis_client.hexists(f"item_id:{item_id}", "item_id"): @@ -514,7 +544,7 @@ Now we only need to map our newly created id to the item name by setting the has 12. Re-run the debugger to test this application by interacting with the `/docs` route. ## Optional: Set up database deletion -Because our data will be now be persisted by redis, you may want to create a script that will allow you to erase all testing data. For that you can create a new file called `flushdb.py` with the following content: +Because the data will be now be persisted by redis, you may want to create a script that will allow you to erase all testing data. For that you can create a new file called `flushdb.py` with the following content: ```python import redis @@ -522,7 +552,7 @@ redis_client = redis.StrictRedis(host='0.0.0.0', port=6379, db=0, decode_respons redis_client.flushdb() ``` -Then when you want to reset your database, you can open the flushdb.py file in VS Code and click on the run button on the top-right corner of the editor or run the **Python: Run Python File in Terminal** command. +Then when you want to reset the database, you can open the flushdb.py file in VS Code and click on the run button on the top-right corner of the editor, or run the **Python: Run Python File in Terminal** command from the Command Palette. Note that this should be done with caution because it will delete all the keys in the current database, which could lead to data loss if done in production. @@ -538,7 +568,7 @@ The completed code project from this tutorial can be found on GitHub: [python-sa Learn more about FastAPI on the [official documentation](https://fastapi.tiangolo.com/). -To try your app on a production website, check out the tutorial [Deploy Python apps to Azure App Service using Docker Containers](https://learn.microsoft.com/azure/developer/python/tutorial-deploy-containers-01). +To try the app on a production website, check out the tutorial [Deploy Python apps to Azure App Service using Docker Containers](https://learn.microsoft.com/azure/developer/python/tutorial-deploy-containers-01). You may also want to review the following articles in the VS Code docs that are relevant to Python: From 23eea049a89e2560ac57d6b7594bb562eb5ea22a Mon Sep 17 00:00:00 2001 From: Luciana Abud Date: Mon, 23 Oct 2023 07:31:20 -0700 Subject: [PATCH 18/34] Add link to docs and remove other extensions --- docs/python/tutorial-fastapi.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/python/tutorial-fastapi.md b/docs/python/tutorial-fastapi.md index 06148bfb3f..6980cef34d 100644 --- a/docs/python/tutorial-fastapi.md +++ b/docs/python/tutorial-fastapi.md @@ -11,7 +11,7 @@ MetaDescription: Python FastAPI tutorial showing IntelliSense and debugging supp [FastAPI](https://fastapi.tiangolo.com/) is a modern high-performant web framework for building APIs with Python. It is designed to make it easy to build APIs quickly and efficiently while providing features like automatic validation, serialization, and documentation of your API, making it a popular choice for building web services and microservices. -In this FastAPI tutorial, we will create a grocery list app using FastAPI. By the end of the tutorial, you will understand how to work with FastAPI in the VS Code terminal, editor, and debugger. This tutorial is not a FastAPI deep dive. For that, you can refer to the [FastAPI documentation link at the end of this tutorial](#next-steps). +In this FastAPI tutorial, we will create a grocery list app using FastAPI. By the end of the tutorial, you will understand how to work with FastAPI in the VS Code terminal, editor, and debugger. This tutorial is not a FastAPI deep dive. For that, you can refer to the [official FastAPI documentation](https://fastapi.tiangolo.com/). The completed code project from this FastAPI tutorial can be found on GitHub: @@ -319,11 +319,8 @@ This will create a `.devcontainer` folder in your workspace, with a `devcontaine "customizations": { "vscode": { "extensions": [ - "ms-python.python", - "ms-python.vscode-pylance", - "esbenp.prettier-vscode", - "ms-python.black-formatter", - "charliermarsh.ruff" + "ms-python.python", //Python extension ID + "ms-python.vscode-pylance" //Pylance extension ID ] } } From f328e0043fbefb6ae8e1e6d5dfcbf937d32bc111 Mon Sep 17 00:00:00 2001 From: Luciana Abud Date: Mon, 23 Oct 2023 07:33:58 -0700 Subject: [PATCH 19/34] Tweak wording --- docs/python/tutorial-fastapi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/python/tutorial-fastapi.md b/docs/python/tutorial-fastapi.md index 6980cef34d..e5e591d75a 100644 --- a/docs/python/tutorial-fastapi.md +++ b/docs/python/tutorial-fastapi.md @@ -383,7 +383,7 @@ Note that Pylance raises a problem with this change. This is because the `hget` ```python if item_id_str is not None: ``` -Now that we have the item ID in a string, we just need to convert it to an `int` and add the quantity provided to the item. Because the redis hash we have so far only maps item names to their IDs, we now need to map item IDs to their names and quantity. One way to do that is to create hashes for each item, in the format `"item_id:{item_id}"`, and provide it with "name" and "quantity" fields. +Now that we have the item ID in a string, we just need to convert it to an `int` and add the quantity provided to the item. Because the redis hash we have so far only maps item names to their IDs, we now need to map item IDs to their names and quantity. One way to do that is to create hashes for each item, in the format `"item_id:{item_id}"`, and add "name" and "quantity" fields to it. 7. Replace lines 19 and 20 with the following, to convert the `item_id` to an `int`, and then incrementing the quantity of the item by calling the `hincrby` method from redis: ```python From f1160b5a9f10a3a8088a9084749679acda53bd84 Mon Sep 17 00:00:00 2001 From: Luciana Abud Date: Mon, 23 Oct 2023 07:50:53 -0700 Subject: [PATCH 20/34] Fix screenshots and inputs for add_item --- .../fastapi-tutorial/debugger_breakpoint.png | 4 +- .../fastapi_breakpoint_hit.png | 4 +- .../fastapi_evaluate_debug_console.png | 4 +- docs/python/tutorial-fastapi.md | 47 ++++++++++--------- 4 files changed, 30 insertions(+), 29 deletions(-) diff --git a/docs/python/images/fastapi-tutorial/debugger_breakpoint.png b/docs/python/images/fastapi-tutorial/debugger_breakpoint.png index 821d247b9f..3ebe76ccbe 100644 --- a/docs/python/images/fastapi-tutorial/debugger_breakpoint.png +++ b/docs/python/images/fastapi-tutorial/debugger_breakpoint.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9f11695535da4a0e17e09b45695ee2b0e1d232db7501210504e4ba7c877357bb -size 28795 +oid sha256:254aabb59c5136fe9a6d675cf8e3cc4b4f376f998bd71548e5d5fae273c9c9ab +size 56761 diff --git a/docs/python/images/fastapi-tutorial/fastapi_breakpoint_hit.png b/docs/python/images/fastapi-tutorial/fastapi_breakpoint_hit.png index 967fe6c967..6ea145dd81 100644 --- a/docs/python/images/fastapi-tutorial/fastapi_breakpoint_hit.png +++ b/docs/python/images/fastapi-tutorial/fastapi_breakpoint_hit.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:960a3442358bda66696d5180eb1d6c5b46b851b34ec51b1619304eda4be2bc28 -size 32906 +oid sha256:659e00f686058091117a46183f17c6780df61ba32ad8d2f4dd73749665d6c74c +size 57572 diff --git a/docs/python/images/fastapi-tutorial/fastapi_evaluate_debug_console.png b/docs/python/images/fastapi-tutorial/fastapi_evaluate_debug_console.png index f0b63c7f2a..e81b234b40 100644 --- a/docs/python/images/fastapi-tutorial/fastapi_evaluate_debug_console.png +++ b/docs/python/images/fastapi-tutorial/fastapi_evaluate_debug_console.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:678cf1ef75ea061c8ee41d627f9cfad0cc8409a74a5e8202a225919c5e229547 -size 218380 +oid sha256:27bd36120280ad58fc0fabc8a0ae35939aaaf12619dd9563df384b6e706d1cd9 +size 272517 diff --git a/docs/python/tutorial-fastapi.md b/docs/python/tutorial-fastapi.md index e5e591d75a..0eada96294 100644 --- a/docs/python/tutorial-fastapi.md +++ b/docs/python/tutorial-fastapi.md @@ -435,32 +435,33 @@ Now we only need to map the newly created ID to the item name by setting the has return {"message": f"Add /docs to the end of the URL to access the Swagger UI."} # Route to add an item - @app.post("/items") - def add_item(item: ItemPayload) -> dict[str, ItemPayload]: - if item.quantity <= 0: - raise HTTPException(status_code=400, detail="Quantity must be greater than 0.") + @app.post("/items/{item_name}/{quantity}") + def add_item(item_name: str, quantity: int) -> dict[str, ItemPayload]: + if quantity <= 0: + raise HTTPException(status_code=400, detail="Quantity must be greater than 0.") - # Check if item already exists - item_id_str: str | None = redis_client.hget("item_name_to_id", item.item_name) + # Check if item already exists + item_id_str: str | None = redis_client.hget("item_name_to_id", item_name) - if item_id_str is not None: - item_id = int(item_id_str) - redis_client.hincrby(f"item_id:{item_id}", "quantity", item.quantity) - else: - # Generate an ID for the item - item_id: int = redis_client.incr("item_id") - redis_client.hset( - f"item_id:{item_id}", - mapping={ - "item_id": item_id, - "item_name": item.item_name, - "quantity": item.quantity, - }, - ) - # Create a set so we can search by name too - redis_client.hset("item_name_to_id", item.item_name, item_id) + if item_id_str is not None: + item_id = int(item_id_str) + redis_client.hincrby(f"item_id:{item_id}", "quantity", quantity) + else: + # Generate an id for the item + item_id: int = redis_client.incr("item_id") + redis_client.hset( + f"item_id:{item_id}", + mapping={ + "item_id": item_id, + "item_name": item_name, + "quantity": quantity, + }, + ) + # Create a set so we can search by name too + redis_client.hset("item_name_to_id", item_name, item_id) + + return {"item": ItemPayload(item_id=item_id, item_name=item_name, quantity=quantity)} - return {"item": item} # Route to list a specific item by ID but using Redis From 168385820fc7a755043d55913cc86ff7b2c40cf2 Mon Sep 17 00:00:00 2001 From: Luciana Abud Date: Mon, 23 Oct 2023 07:57:35 -0700 Subject: [PATCH 21/34] Fix dict based sample --- docs/python/tutorial-fastapi.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/docs/python/tutorial-fastapi.md b/docs/python/tutorial-fastapi.md index 0eada96294..3f781aaa4c 100644 --- a/docs/python/tutorial-fastapi.md +++ b/docs/python/tutorial-fastapi.md @@ -141,22 +141,23 @@ We’ll now define routes that will allow us to add and retrieve individual item raise HTTPException(status_code=400, detail="Quantity must be greater than 0.") # if item already exists, we'll just add the quantity. # get all item names - items_names = [item.item_name for item in grocery_list.values()] - if item_name in items_names: - # get index of item.item_name in item_names, which is the item_id - item_id = items_names.index(item_name) + items_ids = {item.item_name: item.item_id if item.item_id is not None else 0 for item in grocery_list.values()} + if item_name in items_ids.keys(): + # get index of item_name in item_ids, which is the item_id + item_id = items_ids[item_name] grocery_list[item_id].quantity += quantity - # otherwise, create a new item + # otherwise, create a new item else: - # generate an ID for the item based on the highest ID in the grocery_list + # generate an id for the item based on the highest ID in the grocery_list item_id = max(grocery_list.keys()) + 1 if grocery_list else 0 - grocery_list[item_id] = ItemPayload(item_id=item_id, item_name=item_name, quantity=quantity) - - return {"item": grocery_list[item_id]} + grocery_list[item_id] = ItemPayload( + item_id=item_id, item_name=item_name, quantity=quantity + ) + return {"item": grocery_list[item_id]} ``` - You might notice Pylance adds inlay hints with the function return type, as well as the types for `item_names` and `item_id`. You can double click on each suggestion to insert them into the code: + You might notice Pylance adds inlay hints with the function return type, as well as the types for `item_ids` and `item_id`. You can double click on each suggestion to insert them into the code: ![Inlay function return and variable type hints being displayed by Pylance throughout the sample code.](images/fastapi-tutorial/pylance_inlay_hints.png) Now let's see if this route is working as expected. The fastest way to do so is to leverage both VS Code's debugger as well as FastAPI's `/docs` endpoint, which provides information about all the available API routes and allows you to interact with the API to explore their parameters and responses. This documentation is generated dynamically based on the metadata and type hints defined in the FastAPI application. @@ -215,7 +216,7 @@ Finally, let's add the remaining routes for the application so we can list all i # get all item names items_ids = {item.item_name: item.item_id if item.item_id is not None else 0 for item in grocery_list.values()} if item_name in items_ids.keys(): - # get index of item.item_name in item_names, which is the item_id + # get index of item_name in item_ids, which is the item_id item_id: int = items_ids[item_name] grocery_list[item_id].quantity += quantity # otherwise, create a new item From 43d05e41aed3fbb5f0da4813f610b01f91cfd0b9 Mon Sep 17 00:00:00 2001 From: Luciana Abud Date: Mon, 23 Oct 2023 08:36:36 -0700 Subject: [PATCH 22/34] Make multiple fixes --- .../fastapi_debugger_variables.png | 4 +- .../fastapi-tutorial/pylance_inlay_hints.png | 4 +- .../pylance_redis_typehint.png | 4 +- docs/python/tutorial-fastapi.md | 52 +++++++++++++------ 4 files changed, 41 insertions(+), 23 deletions(-) diff --git a/docs/python/images/fastapi-tutorial/fastapi_debugger_variables.png b/docs/python/images/fastapi-tutorial/fastapi_debugger_variables.png index ff17513824..86ac656cec 100644 --- a/docs/python/images/fastapi-tutorial/fastapi_debugger_variables.png +++ b/docs/python/images/fastapi-tutorial/fastapi_debugger_variables.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ee9a8ec1590c96589805e9555ecb370343fefa84943d888d7b6df277dddab8c3 -size 39029 +oid sha256:98be66f161eddec28603d3f32db0ba91083fb3a4ca7fb74b59f284ce2a049b97 +size 87337 diff --git a/docs/python/images/fastapi-tutorial/pylance_inlay_hints.png b/docs/python/images/fastapi-tutorial/pylance_inlay_hints.png index 98835ceb76..83aa97692d 100644 --- a/docs/python/images/fastapi-tutorial/pylance_inlay_hints.png +++ b/docs/python/images/fastapi-tutorial/pylance_inlay_hints.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e6c86a35905344280cc5e2b8c3d01ec56e56ecf1b648e7e0e9a2b5c1c6473936 -size 132177 +oid sha256:77f7778e46d2e714ac19a635dd0ae8a8438f6fff116a822f96b67ade263c8570 +size 184339 diff --git a/docs/python/images/fastapi-tutorial/pylance_redis_typehint.png b/docs/python/images/fastapi-tutorial/pylance_redis_typehint.png index 21b620550b..ee1db229f8 100644 --- a/docs/python/images/fastapi-tutorial/pylance_redis_typehint.png +++ b/docs/python/images/fastapi-tutorial/pylance_redis_typehint.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:227ba1fdccd3de7bbc2d65378a2951f8cd696cbfe30b15e62c0c40fbec09f3b6 -size 14016 +oid sha256:b82a5cc0bbc819d2bc29c501022a7e0dd2e90ef36f58b38b7ca56a05ea0d6a0f +size 19234 diff --git a/docs/python/tutorial-fastapi.md b/docs/python/tutorial-fastapi.md index 3f781aaa4c..e5fd187fb0 100644 --- a/docs/python/tutorial-fastapi.md +++ b/docs/python/tutorial-fastapi.md @@ -142,7 +142,7 @@ We’ll now define routes that will allow us to add and retrieve individual item # if item already exists, we'll just add the quantity. # get all item names items_ids = {item.item_name: item.item_id if item.item_id is not None else 0 for item in grocery_list.values()} - if item_name in items_ids.keys(): + if item_name in items_ids.keys(): # get index of item_name in item_ids, which is the item_id item_id = items_ids[item_name] grocery_list[item_id].quantity += quantity @@ -162,7 +162,7 @@ We’ll now define routes that will allow us to add and retrieve individual item Now let's see if this route is working as expected. The fastest way to do so is to leverage both VS Code's debugger as well as FastAPI's `/docs` endpoint, which provides information about all the available API routes and allows you to interact with the API to explore their parameters and responses. This documentation is generated dynamically based on the metadata and type hints defined in the FastAPI application. -4. Add a breakpoint next to the if item.quantity <= 0 statement, by clicking on the left margin of the line number (or `kb(editor.debug.action.toggleBreakpoint)`). This will make the debugger stop prior to the execution of that line, allowing us to inspect the code line by line. +4. Add a breakpoint next to the if quantity <= 0 statement, by clicking on the left margin of the line number (or `kb(editor.debug.action.toggleBreakpoint)`). This will make the debugger stop prior to the execution of that line, allowing us to inspect the code line by line. ![Breakpoint set next to the first line in the add_item function.](images/fastapi-tutorial/debugger_breakpoint.png) 5. Start the debugger (`kb(workbench.action.debug.start)`), and then navigate to http://127.0.0.1:8000/docs in the browser. @@ -182,11 +182,12 @@ Now let's see if this route is working as expected. The fastest way to do so is ![Debugger stopped at the breakpoint set in the add_item function.](images/fastapi-tutorial/fastapi_breakpoint_hit.png) On the left side, you will see all local and global variables defined at this point in the Variables window, under the **Run and Debug** view: + ![Variables window displayed in the Run and Debug view, with the item and grocery_list variables highlighted.](images/fastapi-tutorial/fastapi_debugger_variables.png) Now let’s leverage VS Code's Debug Console to do some exploration. -10. Select the `item.quantity <= 0` statement, right-click on the editor and select **Evaluate in Debug Console**: +10. Select the `quantity <= 0` statement, right-click on the editor and select **Evaluate in Debug Console**: ![Evaluate in Debug Console option displayed in the context menu when right-clicking on a line of code.](images/fastapi-tutorial/fastapi_evaluate_debug_console.png) This will open the Debug Console and run the selected expression. As expected, the expression will be evaluated to `False`. @@ -360,11 +361,11 @@ Let's do some more replacements in the first route `add_item`. Instead of lookin 3. Delete the line with the content below: ```python - items_names = list(grocery_list.values()) + items_ids = {item.item_name: item.item_id if item.item_id is not None else 0 for item in grocery_list.values()} ``` And replace it with: ```python - item_id = redis_client.hget("item_name_to_id", item.item_name) + item_id = redis_client.hget("item_name_to_id", item_name) ``` Note that Pylance raises a problem with this change. This is because the `hget` method returns either `str`, or `None` (if the item doesn’t exist). However, the lines below the code that we haven’t replaced yet expect `item_id` to be of type `int`. Let’s address it by renaming the `item_id` symbol. @@ -378,7 +379,7 @@ Note that Pylance raises a problem with this change. This is because the `hget` 6. If the item doesn't exist, then `item_id_str` will be None. So now we can delete the line with the following content: ```python - if item.item_name in items_names: + if item_name in items_names: ``` And replace it with: ```python @@ -386,10 +387,16 @@ Note that Pylance raises a problem with this change. This is because the `hget` ``` Now that we have the item ID in a string, we just need to convert it to an `int` and add the quantity provided to the item. Because the redis hash we have so far only maps item names to their IDs, we now need to map item IDs to their names and quantity. One way to do that is to create hashes for each item, in the format `"item_id:{item_id}"`, and add "name" and "quantity" fields to it. -7. Replace lines 19 and 20 with the following, to convert the `item_id` to an `int`, and then incrementing the quantity of the item by calling the `hincrby` method from redis: +7. Delete the code within the `if` block: + ```python + item_id: int = items_ids[item_name] + grocery_list[item_id].quantity += quantity + ``` + And add the following, to convert the `item_id` to an `int`, and then to increment the quantity of the item by calling the `hincrby` method from redis: + ```python item_id = int(item_id_str) - redis_client.hincrby(f"item_id:{item_id}", "quantity", item.quantity) + redis_client.hincrby(f"item_id:{item_id}", "quantity", quantity) ``` We now only need to replace the code for when the item does not exist, i.e. `item_id_str` is `None`. In this case, we will generate a new `item_id`, create a new redis hash for the item, and then add the provided item names and quantity. @@ -397,7 +404,7 @@ To generate a new `item_id`, let’s use the `incr` method from redis, passing a 8. Delete the line with the following content: ```python - item_id: int = len(grocery_list) + item_id: int = max(grocery_list.keys()) + 1 if grocery_list else 0 ``` And add the following: ```python @@ -408,16 +415,18 @@ Now we will add the item to the redis hash, using the `hset` method and by provi 9. Delete the line with the following content: ```python - grocery_list[item_id] = item + grocery_list[item_id] = ItemPayload( + item_id=item_id, item_name=item_name, quantity=quantity + ) ``` - And replace it with: + And replace it with the following: ```python redis_client.hset( f"item_id:{item_id}", mapping={ "item_id": item_id, - "item_name": item.item_name, - "quantity": item.quantity, + "item_name": item_name, + "quantity": quantity, }) ``` @@ -425,10 +434,19 @@ Now we only need to map the newly created ID to the item name by setting the has 10. Add this line to the end of the route, inside the `else` block: ```python - redis_client.hset("item_name_to_id", item.item_name, item_id) + redis_client.hset("item_name_to_id", item_name, item_id) + ``` + +11. Delete the line with the following content: + ```python + return {"item": grocery_list[item_id]} + ``` + And replace it with: + ```python + return {"item": ItemPayload(item_id=item_id, item_name=item_name, quantity=quantity)} ``` -11. If you would like, you can try to do a similar replacement for the other routes. Otherwise, you can just replace the entire content of the file with the lines below: +12. If you would like, you can try to do a similar replacement for the other routes. Otherwise, you can just replace the entire content of the file with the lines below: ```python @app.get("/") def home(request: Request) -> dict[str, str]: @@ -513,7 +531,7 @@ Now we only need to map the newly created ID to the item name by setting the has else: item_name: str | None = redis_client.hget(f"item_id:{item_id}", "item_name") redis_client.hdel("item_name_to_id", f"{item_name}") - redis_client.hdel(f"item_id:{item_id}", "item_id") + redis_client.delete(f"item_id:{item_id}") return {"result": "Item deleted."} @@ -533,7 +551,7 @@ Now we only need to map the newly created ID to the item name by setting the has if existing_quantity <= quantity: item_name: str | None = redis_client.hget(f"item_id:{item_id}", "item_name") redis_client.hdel("item_name_to_id", f"{item_name}") - redis_client.hdel(f"item_id:{item_id}", "item_id") + redis_client.delete(f"item_id:{item_id}") return {"result": "Item deleted."} else: redis_client.hincrby(f"item_id:{item_id}", "quantity", -quantity) From f072d72d4767bd5121b0e5f7391cace3d93ca832 Mon Sep 17 00:00:00 2001 From: Brigit Murtaugh Date: Mon, 23 Oct 2023 12:09:46 -0700 Subject: [PATCH 23/34] Add browser version info (#6755) * Add browser version info * feedback --- docs/editor/vscode-web.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/editor/vscode-web.md b/docs/editor/vscode-web.md index b6c4dd0ef5..fbcdc7eb1c 100644 --- a/docs/editor/vscode-web.md +++ b/docs/editor/vscode-web.md @@ -175,7 +175,9 @@ Edge and Chrome today support the [File System API](https://developer.mozilla.or ### Browser support -You can use VS Code for the Web in Chrome, Edge, Firefox, and Safari. +You can use VS Code for the Web in the latest versions of Chrome, Edge, Firefox, and Safari. Older versions of each browser may not work - we only guarantee support for the latest version. + +> **Tip:** One way to check the compatible browser version is to look at the version of [Playright](https://playwright.dev/) currently used for testing VS Code and review its supported browser versions. You can find the currently used Playwright version in the VS Code repo's [package.json](https://github.com/microsoft/vscode/blob/main/package.json) file at `devDependencies/@playwright/test`. Once you know the Playwright version, for example `1.37`, you can then review the **Browser Versions** section in their [Release notes](https://playwright.dev/docs/release-notes). Webviews might appear differently or have some unexpected behavior in Firefox and Safari. You can view issue queries in the VS Code GitHub repo to track issues related to specific browsers, such as with the [Safari label](https://github.com/microsoft/vscode/labels/safari) and [Firefox label](https://github.com/microsoft/vscode/labels/firefox). From 011185f3332522b05b253a60b3047b7c6dab9175 Mon Sep 17 00:00:00 2001 From: Luciana Abud Date: Mon, 23 Oct 2023 14:22:52 -0700 Subject: [PATCH 24/34] Add beginners disclaimer --- docs/python/tutorial-fastapi.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/python/tutorial-fastapi.md b/docs/python/tutorial-fastapi.md index e5fd187fb0..4429e67d8a 100644 --- a/docs/python/tutorial-fastapi.md +++ b/docs/python/tutorial-fastapi.md @@ -13,6 +13,8 @@ MetaDescription: Python FastAPI tutorial showing IntelliSense and debugging supp In this FastAPI tutorial, we will create a grocery list app using FastAPI. By the end of the tutorial, you will understand how to work with FastAPI in the VS Code terminal, editor, and debugger. This tutorial is not a FastAPI deep dive. For that, you can refer to the [official FastAPI documentation](https://fastapi.tiangolo.com/). +If this is your first time using Python, we recommend you to start with our [Python tutorial](python-tutorial.md) to get familiar with the language and VS Code's Python support. This tutorial is more suited for those who are already familiar with Python and want to learn how to work with FastAPI in VS Code. + The completed code project from this FastAPI tutorial can be found on GitHub: If you have any problems, you can search for answers or ask a question on the [Python extension Discussions Q&A](https://github.com/microsoft/vscode-python/discussions/categories/q-a). From f35e2ea648f445edb3eb296361e64b5469a393af Mon Sep 17 00:00:00 2001 From: Luciana Abud Date: Mon, 23 Oct 2023 15:04:35 -0700 Subject: [PATCH 25/34] Address PR comments --- .../fastapi_execute_button.png | 4 +- docs/python/tutorial-fastapi.md | 87 ++++++++++--------- 2 files changed, 46 insertions(+), 45 deletions(-) diff --git a/docs/python/images/fastapi-tutorial/fastapi_execute_button.png b/docs/python/images/fastapi-tutorial/fastapi_execute_button.png index 88f98a6d99..84703dfecf 100644 --- a/docs/python/images/fastapi-tutorial/fastapi_execute_button.png +++ b/docs/python/images/fastapi-tutorial/fastapi_execute_button.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ebd6086c58198d00f131c67a9b936e7b363ea520beeb6bf55f54b870c35f63fe -size 17389 +oid sha256:0b0b355d01fde16e00411c2c34a5ff3dd70562a903d6b61c7db6ce5443deae48 +size 68881 diff --git a/docs/python/tutorial-fastapi.md b/docs/python/tutorial-fastapi.md index 4429e67d8a..2e340b1cd9 100644 --- a/docs/python/tutorial-fastapi.md +++ b/docs/python/tutorial-fastapi.md @@ -85,7 +85,7 @@ Let’s create the application! 5. From the drop-down menu, select the **FastAPI** configuration option from the drop-down list: ![Drop down with debugger configuration options, with FastAPI being highlighted.](images/fastapi-tutorial/fastapi_debug_config_option.png) - This will invoke uvicorn to start the application server through the debugger, which will allow us to step through the code to inspect its behaviour. You will see something like the following in the terminal: + This will invoke uvicorn to start the application server through the debugger, which will allow us to step through the code to inspect its behavior. You will see something like the following in the terminal: ![Uvicorn server running message displayed in the terminal, with an URL to access the app.](images/fastapi-tutorial/fastapi_debug_terminal.png) @@ -130,9 +130,11 @@ Now we need a place to store the grocery list items. For simplicity, let’s sta grocery_list: dict[int, ItemPayload] = {} ``` - This creates a new empty dictionary that will receive keys of type int (which will be each item id) and values of the ItemPayload type. + This creates a new empty dictionary that will receive keys of type int (which will be item ids) and values of the ItemPayload type. -We’ll now define routes that will allow us to add and retrieve individual items, as well as list all items in the grocery list. +We'll now define routes in our FastAPI application. In the context of web applications, routes are like pathways that map specific URLs to the code that handles them. These routes serve as the entry points for different functionalities within our application. When a client, such as a web browser or another program, sends a request to our application with a particular URL, FastAPI routes that request to the appropriate function (also known as route handler or view function) based on the URL, and that function processes the request and generates a response. + +Let's proceed with defining routes that will allow us to add and retrieve individual items, as well as list all items in the grocery list. 3. Add the following route at the end of the `main.py` file: ```python @@ -156,7 +158,7 @@ We’ll now define routes that will allow us to add and retrieve individual item item_id=item_id, item_name=item_name, quantity=quantity ) - return {"item": grocery_list[item_id]} + return {"item": grocery_list[item_id]} ``` You might notice Pylance adds inlay hints with the function return type, as well as the types for `item_ids` and `item_id`. You can double click on each suggestion to insert them into the code: @@ -175,7 +177,7 @@ Now let's see if this route is working as expected. The fastest way to do so is 6. Click on the down arrow next to the `/items` route to expand it, and click on the "Try it out" button that appears on the right side. ![Try it out button displayed next to the /items route in the Swagger UI.](images/fastapi-tutorial/fastapi_tryitout_button.png) -7. Add a grocery list item by passing a string to the "item" field, and a number to "quantity". For example, you could provide `{"item" : "apple", "quantity": 2 }`. +7. Add a grocery list item by passing a string to the "item" field, and a number to "quantity". For example, you could provide apple as the `item_name` and 2 as the `quantity`. 8. Click on the "Execute" button. ![Execute button displayed below the /items route.](images/fastapi-tutorial/fastapi_execute_button.png) @@ -183,7 +185,7 @@ Now let's see if this route is working as expected. The fastest way to do so is 9. Open VS Code again and notice the debugger has stopped at the breakpoint we set earlier. ![Debugger stopped at the breakpoint set in the add_item function.](images/fastapi-tutorial/fastapi_breakpoint_hit.png) -On the left side, you will see all local and global variables defined at this point in the Variables window, under the **Run and Debug** view: +On the left side, you will see all local and global variables defined at this point in the Variables window, under the **Run and Debug** view. In our example, we can see `item_name` set to apple and `quantity` set to 2 under the locals variable view, as well as an empty `grocery_list` dictionary under the globals variable view. ![Variables window displayed in the Run and Debug view, with the item and grocery_list variables highlighted.](images/fastapi-tutorial/fastapi_debugger_variables.png) @@ -192,10 +194,11 @@ Now let’s leverage VS Code's Debug Console to do some exploration. 10. Select the `quantity <= 0` statement, right-click on the editor and select **Evaluate in Debug Console**: ![Evaluate in Debug Console option displayed in the context menu when right-clicking on a line of code.](images/fastapi-tutorial/fastapi_evaluate_debug_console.png) - This will open the Debug Console and run the selected expression. As expected, the expression will be evaluated to `False`. + This will open the Debug Console and run the selected expression. As expected in our example, the expression will be evaluated to `False`. -The Debug Console can be a powerful tool to quickly test expressions and better understand the state of your code at the time of the breakpoint. You can also use it to run arbitrary code, such as calling functions or printing variables. Learn more about Python debugging in VS Code in the [Python tutorial](python-tutorial.md#configure-and-run-the-debugger). +The Debug Console can be a powerful tool to quickly test expressions and better understand the state of your code at the time of a breakpoint. You can also use it to run arbitrary code, such as calling functions or printing variables. Learn more about Python debugging in VS Code in the [Python tutorial](python-tutorial.md#configure-and-run-the-debugger). +You can now continue the execution of the code by clicking on the "Continue" button in the debug toolbar, or by pressing `F5` (`kb(workbench.action.debug.continue)`). Finally, let's add the remaining routes for the application so we can list all items or specific items, as well as remove them from our grocery list. You can leave the debugger running as it will automatically reload the application when you save the changes you'll make in the next step. @@ -272,7 +275,7 @@ Finally, let's add the remaining routes for the application so we can list all i 12. Save the file (`kb(workbench.action.files.save)`). The application should automatically reload. -You can now open the `/docs` page again and test the new routes, leveraging the debugger and the debug console to better understand the code execution. Once you're done, you can stop the debugger (`kb(workbench.action.debug.stop)`). +You can now open the `/docs` page again and test the new routes, leveraging the debugger and the debug console to better understand the code execution. Once you're done, you can stop the debugger (`kb(workbench.action.debug.stop)`). You may also remove the breakpoint we added on step 4 by clicking on it. ## Set up the data storage At this point, you already have a working version of the application with the base functionality. This section will guide you through setting up data storage for persistence, but you can choose to skip it if you're happy with what you've learned already. @@ -283,7 +286,7 @@ To persist the data, we will use [Redis](https://redis.io/), which is an open so If you are already working on **GitHub Codespaces** with our existing template, you can skip directly to the ["Replace the database"](#replace-the-database) section. -If you are on Windows, you can work with Redis by setting up either a [Docker container](https://www.docker.com/products/docker-desktop/), a [GitHub Codespace](https://github.com/features/codespaces) or through [WSL](https://learn.microsoft.com/en-us/windows/wsl/) (Windows Subsystem for Linux). In this tutorial we will cover the set up using Docker containers, but you can refer to the [section above](#github-codespaces) for instructions on how to set up a GitHub Codespace. +If you are on Windows, you can work with Redis by setting up either a [Docker container](https://www.docker.com/products/docker-desktop/), a [GitHub Codespace](https://github.com/features/codespaces) or through [WSL](https://learn.microsoft.com/en-us/windows/wsl/) (Windows Subsystem for Linux). In this tutorial we will use a Docker container, but you can refer to the [section above](#github-codespaces) for instructions on how to set up a GitHub Codespace. Otherwise, if you are on a Linux or a macOS machine, you can install Redis by following the [instructions on their website](https://redis.io/docs/getting-started/), and then skip to the ["Replace the database"](#replace-the-database) section. @@ -306,15 +309,15 @@ We can optionally install features to be included in the container. For this tut 4. Select "Redis Server" as an additional feature to be installed, press "OK", and then select "Keep Defaults". ![Redis Server option selected in the Dev Container configuration files list.](images/fastapi-tutorial/devcontainers_redis_server_feature.png) -This will create a `.devcontainer` folder in your workspace, with a `devcontainer.json` file. Let's make some edits to this file so the container setup can include steps such as installing the VS Code extensions we'll need as well as the project dependencies. +This will create a `.devcontainer` folder in your workspace, with a `devcontainer.json` file. Let's make some edits to this file so the container setup will include steps such as installing the VS Code extensions we'll need as well as the project dependencies. 5. Open the `devcontainer.json` file. - -6. Locate the content below and remove the comment (`//`) from that line, so the dependencies can be installed once the container is created: +6. Add a "," after the `"features" : { ... }` entry, so we can add more settings to the file. +7. Locate the content below and remove the comment (`//`) from that line, so the dependencies can be installed once the container is created: ``` "postCreateCommand": "pip3 install --user -r requirements.txt", ``` -7. Add the following setting to `devcontainer.json`: +8. Add the following setting to `devcontainer.json`: ``` // Use 'postCreateCommand' to run commands after the container is created. "postCreateCommand": "pip3 install --user -r requirements.txt", @@ -329,9 +332,9 @@ This will create a `.devcontainer` folder in your workspace, with a `devcontaine } } ``` -8. Save the file. +9. Save the file. -9. Select the "Reopen in Container" button from the notification that will show up on the bottom right corner. +10. Select the "Reopen in Container" button from the notification that will show up on the bottom right corner, or run the **Dev Containers: Reopen in Container** command from the Command Palette. You can learn more about dev container configuration in the [documentation](https://code.visualstudio.com/docs/devcontainers/containers#_create-a-devcontainerjson-file). > Note: it may take several minutes to build the container, depending on internet speed and machine performance. @@ -344,7 +347,7 @@ Once the container is set up, you will notice an indicator on the bottom left co You should now be ready to move on to the next section, where we will replace the data storage. ## Replace the database -1. In the `main.py` file, replace the `grocery_list = {}` in the beginning of the file with the lines below: +1. In the `main.py` file, replace the `grocery_list: dict[int, ItemPayload] = {}` in the beginning of the file with the lines below: ```python redis_client = redis.StrictRedis(host='0.0.0.0', port=6379, db=0, decode_responses=True) ``` @@ -372,9 +375,7 @@ Let's do some more replacements in the first route `add_item`. Instead of lookin Note that Pylance raises a problem with this change. This is because the `hget` method returns either `str`, or `None` (if the item doesn’t exist). However, the lines below the code that we haven’t replaced yet expect `item_id` to be of type `int`. Let’s address it by renaming the `item_id` symbol. -4. Rename `item_id` with `item_id_str` by selecting the symbol and running the **Rename Symbol** command (`kb(editor.action.rename)`). - - This will rename all occurrences of the symbol in the file, including the one in the `return` statement. +4. Rename `item_id` to `item_id_str`. 5. Pylance should show a variable type hint next to `item_id_str`. Double click to accept it: ![Variable type hint displayed next to the item_id_str variable.](images/fastapi-tutorial/pylance_redis_typehint.png) @@ -458,30 +459,30 @@ Now we only need to map the newly created ID to the item name by setting the has # Route to add an item @app.post("/items/{item_name}/{quantity}") def add_item(item_name: str, quantity: int) -> dict[str, ItemPayload]: - if quantity <= 0: - raise HTTPException(status_code=400, detail="Quantity must be greater than 0.") + if quantity <= 0: + raise HTTPException(status_code=400, detail="Quantity must be greater than 0.") - # Check if item already exists - item_id_str: str | None = redis_client.hget("item_name_to_id", item_name) + # Check if item already exists + item_id_str: str | None = redis_client.hget("item_name_to_id", item_name) - if item_id_str is not None: - item_id = int(item_id_str) - redis_client.hincrby(f"item_id:{item_id}", "quantity", quantity) - else: - # Generate an id for the item - item_id: int = redis_client.incr("item_id") - redis_client.hset( - f"item_id:{item_id}", - mapping={ - "item_id": item_id, - "item_name": item_name, - "quantity": quantity, - }, - ) - # Create a set so we can search by name too - redis_client.hset("item_name_to_id", item_name, item_id) + if item_id_str is not None: + item_id = int(item_id_str) + redis_client.hincrby(f"item_id:{item_id}", "quantity", quantity) + else: + # Generate an id for the item + item_id: int = redis_client.incr("item_ids") + redis_client.hset( + f"item_id:{item_id}", + mapping={ + "item_id": item_id, + "item_name": item_name, + "quantity": quantity, + }, + ) + # Create a set so we can search by name too + redis_client.hset("item_name_to_id", item_name, item_id) - return {"item": ItemPayload(item_id=item_id, item_name=item_name, quantity=quantity)} + return {"item": ItemPayload(item_id=item_id, item_name=item_name, quantity=quantity)} @@ -534,7 +535,7 @@ Now we only need to map the newly created ID to the item name by setting the has item_name: str | None = redis_client.hget(f"item_id:{item_id}", "item_name") redis_client.hdel("item_name_to_id", f"{item_name}") redis_client.delete(f"item_id:{item_id}") - return {"result": "Item deleted."} + return {"result": "Item deleted."} # Route to remove some quantity of a specific item by ID but using Redis @@ -557,7 +558,7 @@ Now we only need to map the newly created ID to the item name by setting the has return {"result": "Item deleted."} else: redis_client.hincrby(f"item_id:{item_id}", "quantity", -quantity) - return {"result": f"{quantity} items removed."} + return {"result": f"{quantity} items removed."} ``` 12. Re-run the debugger to test this application by interacting with the `/docs` route. From ecf6bc69cacd9ccd35766a66cc8cdc8e8ba2bd01 Mon Sep 17 00:00:00 2001 From: Luciana Abud Date: Mon, 23 Oct 2023 22:22:28 -0700 Subject: [PATCH 26/34] Add more context around redis --- .../fastapi_evaluate_debug_console.png | 4 ++-- docs/python/tutorial-fastapi.md | 23 +++++++++++-------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/docs/python/images/fastapi-tutorial/fastapi_evaluate_debug_console.png b/docs/python/images/fastapi-tutorial/fastapi_evaluate_debug_console.png index e81b234b40..c23cbfdcab 100644 --- a/docs/python/images/fastapi-tutorial/fastapi_evaluate_debug_console.png +++ b/docs/python/images/fastapi-tutorial/fastapi_evaluate_debug_console.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:27bd36120280ad58fc0fabc8a0ae35939aaaf12619dd9563df384b6e706d1cd9 -size 272517 +oid sha256:6654c71bf9e2d1d37fd90e2869bf6f838695f5570162c1e8489c3f836992a6f6 +size 247921 diff --git a/docs/python/tutorial-fastapi.md b/docs/python/tutorial-fastapi.md index 2e340b1cd9..4ed7332a1c 100644 --- a/docs/python/tutorial-fastapi.md +++ b/docs/python/tutorial-fastapi.md @@ -42,7 +42,7 @@ In this section, we will create a folder to be opened as a workspace in VS Code, Now, let’s create a requirements.txt file listing the dependencies we wish to install for the application. The requirements.txt file is a common practice in Python development, used to specify the libraries that your project relies on and their versions. This file helps ensure that anyone working on the project can recreate a similar development environment, making it a convenient component for maintaining consistency across different development environments. -We will want to install FastAPI for creating the app, uvicorn to work as the server, and redis and type-redis for handling data storage and interacting with a Redis database. +We will want to install FastAPI for creating the app, uvicorn to work as the server, and Redis and type-redis for handling data storage and interacting with a Redis database. 3. Create a new file in VS Code (**File** > **New Text File** or `kb(workbench.action.files.newUntitledFile)`). 4. Add the following content to it: @@ -130,7 +130,7 @@ Now we need a place to store the grocery list items. For simplicity, let’s sta grocery_list: dict[int, ItemPayload] = {} ``` - This creates a new empty dictionary that will receive keys of type int (which will be item ids) and values of the ItemPayload type. + This creates a new empty dictionary that will receive keys of type int (which will be item IDs) and values of the ItemPayload type. We'll now define routes in our FastAPI application. In the context of web applications, routes are like pathways that map specific URLs to the code that handles them. These routes serve as the entry points for different functionalities within our application. When a client, such as a web browser or another program, sends a request to our application with a particular URL, FastAPI routes that request to the appropriate function (also known as route handler or view function) based on the URL, and that function processes the request and generates a response. @@ -351,10 +351,10 @@ You should now be ready to move on to the next section, where we will replace th ```python redis_client = redis.StrictRedis(host='0.0.0.0', port=6379, db=0, decode_responses=True) ``` -Pylance will display an error message because redis hasn’t been imported yet. +Pylance will display an error message because Redis hasn’t been imported yet. 2. Click on "redis" in the editor, and select the light bulb the shows up (or `kb(editor.action.quickFix)`). Then select "Add 'import redis'". - ![Light bulb displayed next to the redis variable, with the option to add the import statement.](images/fastapi-tutorial/fastapi_add_redis_quickfix.png) + ![Light bulb displayed next to the Redis variable, with the option to add the import statement.](images/fastapi-tutorial/fastapi_add_redis_quickfix.png) You can also set up Pylance to automatically add imports by looking for the "Auto Import Completions" setting in the Settings UI page (`kb(workbench.action.openSettings)`) and enabling it. @@ -362,7 +362,9 @@ We now have a Redis client object that connects to a Redis server running on the This client can now be used to perform operations such as getting, setting, and deleting objects in the Redis database. -Let's do some more replacements in the first route `add_item`. Instead of looking into all the keys from the dictionary to find the item name that has been provided, we can fetch that information directly from a redis hash. Let’s assume we will create a hash called `item_name_to_id`, with item names mapped into their IDs once they are added in the app. We can then get the ID of the item name we’re receiving by invoking the `hget` method from redis. +Let's do some more replacements in the first route `add_item`. Instead of looking into all the keys from the dictionary to find the item name that has been provided, we can fetch that information directly from a Redis hash. + +In Redis, a hash is a data structure that can store multiple key-value pairs. Let's assue we'll create a hash called `item_name_to_id` to store item names (keys) mapped into their IDs (values) once they are added in the app. We can then get the ID of the item name we’re receiving by invoking the `hget` method from Redis. 3. Delete the line with the content below: ```python @@ -388,22 +390,23 @@ Note that Pylance raises a problem with this change. This is because the `hget` ```python if item_id_str is not None: ``` -Now that we have the item ID in a string, we just need to convert it to an `int` and add the quantity provided to the item. Because the redis hash we have so far only maps item names to their IDs, we now need to map item IDs to their names and quantity. One way to do that is to create hashes for each item, in the format `"item_id:{item_id}"`, and add "name" and "quantity" fields to it. + +Now that we have the item ID as a string, we need to convert it to an `int` and update the quantity for the item. Currently, our Redis hash only maps item names to their IDs. To also map item IDs to their names and quantities, we can create a separate Redis hash for each item. We'll use the format `"item_id:{item_id}"` as our hash name to make retrieval by ID easier, and add "name" and "quantity" fields for each hash. 7. Delete the code within the `if` block: ```python item_id: int = items_ids[item_name] grocery_list[item_id].quantity += quantity ``` - And add the following, to convert the `item_id` to an `int`, and then to increment the quantity of the item by calling the `hincrby` method from redis: + And add the following, to convert the `item_id` to an `int`, and then to increment the quantity of the item by calling the `hincrby` method from Redis: ```python item_id = int(item_id_str) redis_client.hincrby(f"item_id:{item_id}", "quantity", quantity) ``` -We now only need to replace the code for when the item does not exist, i.e. `item_id_str` is `None`. In this case, we will generate a new `item_id`, create a new redis hash for the item, and then add the provided item names and quantity. -To generate a new `item_id`, let’s use the `incr` method from redis, passing a new hash "item_ids". When this method is run for the first time, it will create the item_ids hash with a unique number, and then each time it's run it will generate an incremental number and store it in this hash. +We now only need to replace the code for when the item does not exist, i.e. `item_id_str` is `None`. In this case, we will generate a new `item_id`, create a new Redis hash for the item, and then add the provided item names and quantity. +To generate a new `item_id`, let’s use the `incr` method from Redis, passing a new hash "item_ids". When this method is run for the first time, it will create the item_ids hash with a unique number, and then each time it's run it will generate an incremental number and store it in this hash. 8. Delete the line with the following content: ```python @@ -414,7 +417,7 @@ To generate a new `item_id`, let’s use the `incr` method from redis, passing a item_id: int = redis_client.incr("item_ids") ``` -Now we will add the item to the redis hash, using the `hset` method and by providing a mapping of fields or keys (`item_id`, `quantity` and `name`), and the values (the item’s newly created id, and its provided name and quantity). +Now we will add the item to the Redis hash, using the `hset` method and by providing a mapping of fields or keys (`item_id`, `quantity` and `name`), and the values (the item’s newly created id, and its provided name and quantity). 9. Delete the line with the following content: ```python From cf542d61853472fdda031172f41c6e620a1059d7 Mon Sep 17 00:00:00 2001 From: Luciana Abud <45497113+luabud@users.noreply.github.com> Date: Mon, 23 Oct 2023 22:23:49 -0700 Subject: [PATCH 27/34] Update docs/python/tutorial-fastapi.md Co-authored-by: Erik De Bonte --- docs/python/tutorial-fastapi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/python/tutorial-fastapi.md b/docs/python/tutorial-fastapi.md index 4ed7332a1c..0a5fb42457 100644 --- a/docs/python/tutorial-fastapi.md +++ b/docs/python/tutorial-fastapi.md @@ -134,7 +134,7 @@ Now we need a place to store the grocery list items. For simplicity, let’s sta We'll now define routes in our FastAPI application. In the context of web applications, routes are like pathways that map specific URLs to the code that handles them. These routes serve as the entry points for different functionalities within our application. When a client, such as a web browser or another program, sends a request to our application with a particular URL, FastAPI routes that request to the appropriate function (also known as route handler or view function) based on the URL, and that function processes the request and generates a response. -Let's proceed with defining routes that will allow us to add and retrieve individual items, as well as list all items in the grocery list. +Let's proceed with defining routes that will allow us to add and retrieve individual items, as well as list all items in the grocery list. 3. Add the following route at the end of the `main.py` file: ```python From 6273480067954e1ca21de49abfbeae9798d6ff3c Mon Sep 17 00:00:00 2001 From: Luciana Abud Date: Mon, 23 Oct 2023 22:46:18 -0700 Subject: [PATCH 28/34] Fix typos --- docs/python/tutorial-fastapi.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/python/tutorial-fastapi.md b/docs/python/tutorial-fastapi.md index 0a5fb42457..8c4ce90d41 100644 --- a/docs/python/tutorial-fastapi.md +++ b/docs/python/tutorial-fastapi.md @@ -36,7 +36,7 @@ To successfully complete this tutorial in [VS Code](https://code.visualstudio.co In this section, we will create a folder to be opened as a workspace in VS Code, set up a Python virtual environment, install the project’s dependencies and write the first lines of our FastAPI app. -1. In your file system, create a project folder for this tutorial, such as `groceries-plugin``. +1. In your file system, create a project folder for this tutorial, such as `groceries-plugin`. 2. Open this new folder in VS Code (**File** > **Open Folder…** or `kb(workbench.action.files.openFolder)`). @@ -89,7 +89,7 @@ Let’s create the application! ![Uvicorn server running message displayed in the terminal, with an URL to access the app.](images/fastapi-tutorial/fastapi_debug_terminal.png) -6. `Ctrl`` + click the http://127.0.0.1:8000/ URL in the terminal to open your default browser to that address: +6. `Ctrl` + click the http://127.0.0.1:8000/ URL in the terminal to open your default browser to that address: ![Hello World message displayed in the browser.](images/fastapi-tutorial/helloworld_browser.png) 7. Stop the debugger by clicking on the stop icon in the debug toolbar, or through `kb(workbench.action.debug.stop)`. @@ -300,7 +300,7 @@ For the steps below, make sure you have the following requirements installed on #### Create the Dev Container configuration 1. Open the Command Palette and run the “Dev Container: Add Dev Container Configuration Files…" -2. Select "Python 3" +2. Select "Python 3": ![Python 3 option selected in the Dev Container configuration files list.](images/fastapi-tutorial/devcontainers_python3.png) 3. Select the default version. From 5b8cdfcf5f2f647ca92188167b6efdb98f657a4a Mon Sep 17 00:00:00 2001 From: Luciana Abud Date: Tue, 24 Oct 2023 19:20:56 -0700 Subject: [PATCH 29/34] Address feedback comments --- .../devcontainer_indicator.png | 4 +- .../devcontainer_python_interpreter.png | 3 + .../devcontainers_python3.png | 4 +- .../dictionarybased_github_branch.png | 3 + .../fastapi_execute_button.png | 4 +- .../fastapi_first_swagger_page.png | 4 +- .../fastapi_tryitout_button.png | 4 +- docs/python/tutorial-fastapi.md | 173 ++++++++++++------ 8 files changed, 128 insertions(+), 71 deletions(-) create mode 100644 docs/python/images/fastapi-tutorial/devcontainer_python_interpreter.png create mode 100644 docs/python/images/fastapi-tutorial/dictionarybased_github_branch.png diff --git a/docs/python/images/fastapi-tutorial/devcontainer_indicator.png b/docs/python/images/fastapi-tutorial/devcontainer_indicator.png index 3fa5dd6641..1445253115 100644 --- a/docs/python/images/fastapi-tutorial/devcontainer_indicator.png +++ b/docs/python/images/fastapi-tutorial/devcontainer_indicator.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:13d6528a58e710fe498edf98150523fa5c972b67cc9ba96d5eedf25f4707fcd2 -size 1969 +oid sha256:1504abb764d3f481295bc72bbc093a47d2e8632f4e708fb5be48b3d11f885baf +size 2673 diff --git a/docs/python/images/fastapi-tutorial/devcontainer_python_interpreter.png b/docs/python/images/fastapi-tutorial/devcontainer_python_interpreter.png new file mode 100644 index 0000000000..66638ca42e --- /dev/null +++ b/docs/python/images/fastapi-tutorial/devcontainer_python_interpreter.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:48db8cf8f8df07ce3aa9e5cfde8f62955611853fb973ecb8d3ad0dedd2dbcb15 +size 4747 diff --git a/docs/python/images/fastapi-tutorial/devcontainers_python3.png b/docs/python/images/fastapi-tutorial/devcontainers_python3.png index cd53484e3b..091b728e5a 100644 --- a/docs/python/images/fastapi-tutorial/devcontainers_python3.png +++ b/docs/python/images/fastapi-tutorial/devcontainers_python3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d2909aa6c070591b7601ed8c497fcf347d687aa7665bda9a3b6a0850c4d8b5c -size 19469 +oid sha256:7f7b988a65b889e605e306332619a604b00057a58798d42b09c4a412dbc5c2c6 +size 24334 diff --git a/docs/python/images/fastapi-tutorial/dictionarybased_github_branch.png b/docs/python/images/fastapi-tutorial/dictionarybased_github_branch.png new file mode 100644 index 0000000000..5e64f31955 --- /dev/null +++ b/docs/python/images/fastapi-tutorial/dictionarybased_github_branch.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c2fda314724aba70ed5073f423386d147acf1fec826bb5b3300d8bb660b0a94 +size 33796 diff --git a/docs/python/images/fastapi-tutorial/fastapi_execute_button.png b/docs/python/images/fastapi-tutorial/fastapi_execute_button.png index 84703dfecf..1b6a3415cb 100644 --- a/docs/python/images/fastapi-tutorial/fastapi_execute_button.png +++ b/docs/python/images/fastapi-tutorial/fastapi_execute_button.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0b0b355d01fde16e00411c2c34a5ff3dd70562a903d6b61c7db6ce5443deae48 -size 68881 +oid sha256:bcf0a0cb9d61d1eb83ba24af33ba8ee98fb95d813724e45dc429b16c8f5481bd +size 94785 diff --git a/docs/python/images/fastapi-tutorial/fastapi_first_swagger_page.png b/docs/python/images/fastapi-tutorial/fastapi_first_swagger_page.png index 1765df0426..432fb6ae42 100644 --- a/docs/python/images/fastapi-tutorial/fastapi_first_swagger_page.png +++ b/docs/python/images/fastapi-tutorial/fastapi_first_swagger_page.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c0a36093050aec8c7c450fa9146c28abb2645af91974d5720e7035ddfe26b75a -size 18501 +oid sha256:6e70ddc3bd40d7bfa4a87f88d79e6e66cb4290636dc0a9c4320481a2d9e7854e +size 52787 diff --git a/docs/python/images/fastapi-tutorial/fastapi_tryitout_button.png b/docs/python/images/fastapi-tutorial/fastapi_tryitout_button.png index 8632afcf0f..627f764e1d 100644 --- a/docs/python/images/fastapi-tutorial/fastapi_tryitout_button.png +++ b/docs/python/images/fastapi-tutorial/fastapi_tryitout_button.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cc1c58d18a5fe84f999e0f99bbf56d1e86523f4188ae31f04bfa55a62288edf3 -size 15419 +oid sha256:d8695767ed077096e3dfef915f67d621712c6bd3d508a6b0de28e8b29934f308 +size 60211 diff --git a/docs/python/tutorial-fastapi.md b/docs/python/tutorial-fastapi.md index 8c4ce90d41..6c208e8105 100644 --- a/docs/python/tutorial-fastapi.md +++ b/docs/python/tutorial-fastapi.md @@ -15,56 +15,67 @@ In this FastAPI tutorial, we will create a grocery list app using FastAPI. By th If this is your first time using Python, we recommend you to start with our [Python tutorial](python-tutorial.md) to get familiar with the language and VS Code's Python support. This tutorial is more suited for those who are already familiar with Python and want to learn how to work with FastAPI in VS Code. -The completed code project from this FastAPI tutorial can be found on GitHub: +The completed code project from this FastAPI tutorial can be found on GitHub: [python-sample-vscode-fastapi-tutorial](https://github.com/microsoft/python-sample-vscode-fastapi-tutorial). If you have any problems, you can search for answers or ask a question on the [Python extension Discussions Q&A](https://github.com/microsoft/vscode-python/discussions/categories/q-a). ## Set up the project -There are different ways you can set up your project for this tutorial. We will cover how you can set it up in [GitHub Codespaces](TODO: add link to section below) and in [VS Code on your local machine](TODO: ADD Link to section below). +There are different ways you can set up your project for this tutorial. We will cover how you can set it up in [GitHub Codespaces](#github-codespaces) and in [VS Code on your local machine](#locally-in-vs-code). ### GitHub Codespaces You can set up this project to develop in [GitHub Codespaces](https://github.com/features/codespaces), where you can code, debug, and run your app remotely in a Codespace. A Codespace allows you to access a fully-configured development environment hosted in the cloud, eliminating the need for local setup. This environment includes your project's dependencies, tools, and extensions, ensuring a consistent and reproducible development experience. It streamlines collaboration by providing real-time editing, integrated version control, and easy access to debugging and testing tools, all while maintaining the security and reliability of your project. -To set it up for this tutorial, navigate to the [FastAPI template repository]. This template contains all the necessary configurations and dependencies to quickly get started with FastAPI development in a GitHub Codespace. Next, select **Code** > **Codespaces** > **Create Codespace on \** to create and open a Codespace for your project. +> Note: All GitHub.com accounts have a monthly quota of free use of GitHub Codespaces included in the Free or Pro plan. For more information, see "[About billing for GitHub Codespaces](https://docs.github.com/en/billing/managing-billing-for-github-codespaces/about-billing-for-github-codespaces)". -Once you're done, you can continue with the [Start Coding](#start-coding) section below. +To set it up for this tutorial, navigate to [this project's GitHub repository](https://github.com/microsoft/python-sample-vscode-fastapi-tutorial). This Codespace will contain all the necessary configurations and dependencies to quickly get started with FastAPI development. + +To get the most of this tutorial, select the **dictionarybased** branch: +![dictionarybased branch selected in the python-sample-vscode-fastapi-tutorial GitHub repo.](images/fastapi-tutorial/dictionarybased_github_branch.png) + +Then, select **Code** > **Codespaces** > **Create Codespace on \** branch to create and open a Codespace for your project. + +Once you're done, you can continue with the [Replace the database](#replace-the-database) section below. ### Locally in VS Code To successfully complete this tutorial in [VS Code](https://code.visualstudio.com/), you need to first setup your Python development environment. Specifically, this tutorial requires: - Python 3 (check the [installation guide](python-tutorial.md#install-a-python-interpreter) if you don’t have it installed) - [Python extension for VS Code](https://marketplace.visualstudio.com/items?itemName=ms-python.python) (For additional details on installing extensions, see [Extension Marketplace](https://code.visualstudio.com/docs/editor/extension-marketplace)) -In this section, we will create a folder to be opened as a workspace in VS Code, set up a Python virtual environment, install the project’s dependencies and write the first lines of our FastAPI app. +In this section, we will create a folder to be opened as a workspace in VS Code, set up a Python virtual environment, and install the project’s dependencies. 1. In your file system, create a project folder for this tutorial, such as `groceries-plugin`. 2. Open this new folder in VS Code (**File** > **Open Folder…** or `kb(workbench.action.files.openFolder)`). +3. When the [Workspace Trust](/docs/editor/workspace-trust) prompt shows up, click on the "Yes, I trust the authors" button to allow the workspace to access necessary resources and extensions. You can learn more about Workspace Trust in the [documentation](/docs/editor/workspace-trust). - -Now, let’s create a requirements.txt file listing the dependencies we wish to install for the application. The requirements.txt file is a common practice in Python development, used to specify the libraries that your project relies on and their versions. This file helps ensure that anyone working on the project can recreate a similar development environment, making it a convenient component for maintaining consistency across different development environments. +Now, let’s create a `requirements.txt` file listing the dependencies we wish to install for the application. The `requirements.txt` file is a common practice in Python development, used to specify the libraries that your project relies on and their versions. This file helps ensure that anyone working on the project can recreate a similar development environment, making it a convenient component for maintaining consistency across different development environments. We will want to install FastAPI for creating the app, uvicorn to work as the server, and Redis and type-redis for handling data storage and interacting with a Redis database. -3. Create a new file in VS Code (**File** > **New Text File** or `kb(workbench.action.files.newUntitledFile)`). -4. Add the following content to it: +4. Create a new file in VS Code (**File** > **New Text File** or `kb(workbench.action.files.newUntitledFile)`). +5. Add the following content to it: ``` fastapi redis types-redis uvicorn ``` -5. Save the file (`kb(workbench.action.files.save)`) and name it requirements.txt -6. Create a virtual environment by opening the Command Palette (`kb(workbench.action.showCommands)`) and running the **Python: Create Environment** command. +6. Save the file (`kb(workbench.action.files.save)`) and name it `requirements.txt` +7. Create a virtual environment by opening the Command Palette (`kb(workbench.action.showCommands)`) and running the **Python: Create Environment** command. > Note: this step may take a couple of minutes to complete. -7. When asked by the environment type, select **Venv**: +8. When asked by the environment type, select **Venv**: ![Drop down with "Venv" or "Conda" as options for environments that can be created with the Python: Create Environment command.](images/environments/create_environment_dropdown.png) -8. Then select the latest version of Python available on your machine: +9. Then select the latest version of Python available on your machine: ![List of available global environments that can be used to create a virtual environment.](images/fastapi-tutorial/create_environment_interpreters_list.png) -9. Select the requirements.txt file from the drop-down list, so the dependencies are automatically installed, and then select "OK": +10. Select the `requirements.txt` file from the drop-down list, so the dependencies are automatically installed, and then select "OK": ![Check box selected to install dependencies from requirements.txt file](images/fastapi-tutorial/create_environment_select_requirements.png) -The virtual environment will be created, the dependencies will be automatically installed and the environment will be selected for your workspace to be used by the Python extension. +The virtual environment will be created, the dependencies will be automatically installed and the environment will be selected for your workspace to be used by the Python extension. You can confirm it's been selected by checking the bottom right corner of VS Code: + +![Environment in the status bar](images/shared/environment-in-status-bar.png) + +> Note: If you don't see the newly created environment information on the status bar, you can click on the Python interpreter indicator (or run the **Python: Select Interpreter** command from the Command Palette) and manually select the virtual environment. ## Start Coding @@ -91,21 +102,28 @@ Let’s create the application! 6. `Ctrl` + click the http://127.0.0.1:8000/ URL in the terminal to open your default browser to that address: ![Hello World message displayed in the browser.](images/fastapi-tutorial/helloworld_browser.png) + + Congrats! Your FastAPI app is up and running! + 7. Stop the debugger by clicking on the stop icon in the debug toolbar, or through `kb(workbench.action.debug.stop)`. ## Create a model for grocery list items Now that we have the FastAPI app working, let’s define our grocery list items by creating a Pydantic model. Pydantic is a data validation and parsing library that integrates seamlessly with FastAPI. It allows you to define data models using Python classes with [type hints](https://docs.python.org/3/library/typing.html) for automatic validation and parsing of incoming data (which are called payloads) in API requests. -Pylance, the default language server for Python in VS Code, supports type hinting features that can be helpful for working with Pydantic models and FastAPI. Let's enable a few of them: + +Pylance, the default language server for Python in VS Code, supports type hinting features that can be helpful for working with Pydantic models and FastAPI. This is because Pylance is built on top of [Pyright](https://github.com/microsoft/pyright), which is a static type checker for Python that can detect type errors in your code to prevent bugs and improve code quality. + +The 3 steps below are optional, but given FastAPI leverages type hints extensively to improve code readability and validation, we can take advantage of Pylance's type checking features to catch errors early on: + 1. Open the Settings UI page (`kb(workbench.action.openSettings)`) -2. Search for "python type checking mode" and set it to "basic" to enable basic type checking. This will enable Pylance to show diagnostics and warnings to catch simple type-related errors. Alternetivaly, you can set it to "strict" to enable more advanced [type checking rules](https://microsoft.github.io/pyright/#/configuration?id=diagnostic-rule-defaults). +2. Search for "python type checking mode" and set it to "basic" to enable basic type checking. This will enable Pylance to show diagnostics and warnings to catch simple type-related errors. Alternatively, you can set it to "strict" to enable more advanced [type checking rules](https://microsoft.github.io/pyright/#/configuration?id=diagnostic-rule-defaults). ![Python Analysis Type Checking Mode options (off, basic and strict) in settings UI page.](images/fastapi-tutorial/type_checking_mode_setting.png) 3. Next, search for "Python inlay type hints", and enable inlay hints for Variable Types and Function Return Types: ![Two Python Analysis Type Hints settings being enabled in the settings UI page: for Function Return Types and for Variable Types.](images/fastapi-tutorial/function_and_variable_return_type_hint_settings.png) Now let's create a Pydantic model for grocery list items. -1. Create a new Python file (`kb(workbench.action.files.newFile)` and select "Python File") +1. Create a new Python file (`kb(workbench.action.files.newFile)` and select "Python File" 2. Add the following lines to the file, and then save it in the `groceries-plugin` folder as `models.py` (`kb(workbench.action.files.saveAs)`): ```python from typing import Optional @@ -119,7 +137,7 @@ Now let's create a Pydantic model for grocery list items. ## Create Routes Now we need a place to store the grocery list items. For simplicity, let’s start with an empty dictionary. -1. First, let's import all the packages we will need for the sample. Open the main.py file and replace the first import line with the following ones: +1. First, let's import all the packages we will need for the sample. Open the `main.py` file and replace the first import line with the following ones: ```python from fastapi import FastAPI, HTTPException @@ -130,7 +148,7 @@ Now we need a place to store the grocery list items. For simplicity, let’s sta grocery_list: dict[int, ItemPayload] = {} ``` - This creates a new empty dictionary that will receive keys of type int (which will be item IDs) and values of the ItemPayload type. + This creates a new empty dictionary that will receive keys of type `int` (which will be item IDs) and values of the `ItemPayload` type. We'll now define routes in our FastAPI application. In the context of web applications, routes are like pathways that map specific URLs to the code that handles them. These routes serve as the entry points for different functionalities within our application. When a client, such as a web browser or another program, sends a request to our application with a particular URL, FastAPI routes that request to the appropriate function (also known as route handler or view function) based on the URL, and that function processes the request and generates a response. @@ -161,7 +179,7 @@ Let's proceed with defining routes that will allow us to add and retrieve indivi return {"item": grocery_list[item_id]} ``` - You might notice Pylance adds inlay hints with the function return type, as well as the types for `item_ids` and `item_id`. You can double click on each suggestion to insert them into the code: + If you have enabled type hints, you might notice Pylance adds inlay hints with the function return type, as well as the types for `item_ids` and `item_id`. You can optionally double click on each suggestion to insert them into the code: ![Inlay function return and variable type hints being displayed by Pylance throughout the sample code.](images/fastapi-tutorial/pylance_inlay_hints.png) Now let's see if this route is working as expected. The fastest way to do so is to leverage both VS Code's debugger as well as FastAPI's `/docs` endpoint, which provides information about all the available API routes and allows you to interact with the API to explore their parameters and responses. This documentation is generated dynamically based on the metadata and type hints defined in the FastAPI application. @@ -196,7 +214,7 @@ Now let’s leverage VS Code's Debug Console to do some exploration. This will open the Debug Console and run the selected expression. As expected in our example, the expression will be evaluated to `False`. -The Debug Console can be a powerful tool to quickly test expressions and better understand the state of your code at the time of a breakpoint. You can also use it to run arbitrary code, such as calling functions or printing variables. Learn more about Python debugging in VS Code in the [Python tutorial](python-tutorial.md#configure-and-run-the-debugger). +The Debug Console can be a powerful tool to quickly test expressions and better understand the state of your code at the time of a breakpoint. You can also use it to run arbitrary code, such as calling functions or printing variables. You can learn more about Python debugging in VS Code in the [Python tutorial](python-tutorial.md#configure-and-run-the-debugger). You can now continue the execution of the code by clicking on the "Continue" button in the debug toolbar, or by pressing `F5` (`kb(workbench.action.debug.continue)`). @@ -212,7 +230,6 @@ Finally, let's add the remaining routes for the application so we can list all i grocery_list: dict[int, ItemPayload] = {} - # Route to add an item @app.post("/items/{item_name}/{quantity}") def add_item(item_name: str, quantity: int) -> dict[str, ItemPayload]: @@ -220,7 +237,10 @@ Finally, let's add the remaining routes for the application so we can list all i raise HTTPException(status_code=400, detail="Quantity must be greater than 0.") # if item already exists, we'll just add the quantity. # get all item names - items_ids = {item.item_name: item.item_id if item.item_id is not None else 0 for item in grocery_list.values()} + items_ids: dict[str, int] = { + item.item_name: item.item_id if item.item_id is not None else 0 + for item in grocery_list.values() + } if item_name in items_ids.keys(): # get index of item_name in item_ids, which is the item_id item_id: int = items_ids[item_name] @@ -271,12 +291,15 @@ Finally, let's add the remaining routes for the application so we can list all i else: grocery_list[item_id].quantity -= quantity return {"result": f"{quantity} items removed."} + ``` 12. Save the file (`kb(workbench.action.files.save)`). The application should automatically reload. You can now open the `/docs` page again and test the new routes, leveraging the debugger and the debug console to better understand the code execution. Once you're done, you can stop the debugger (`kb(workbench.action.debug.stop)`). You may also remove the breakpoint we added on step 4 by clicking on it. +Congratulations! You now have a working FastAPI application with routes to add, list, and delete items from a grocery list. + ## Set up the data storage At this point, you already have a working version of the application with the base functionality. This section will guide you through setting up data storage for persistence, but you can choose to skip it if you're happy with what you've learned already. @@ -301,13 +324,14 @@ For the steps below, make sure you have the following requirements installed on #### Create the Dev Container configuration 1. Open the Command Palette and run the “Dev Container: Add Dev Container Configuration Files…" 2. Select "Python 3": - ![Python 3 option selected in the Dev Container configuration files list.](images/fastapi-tutorial/devcontainers_python3.png) + + Python 3 option selected in the Dev Container configuration files list. 3. Select the default version. We can optionally install features to be included in the container. For this tutorial, we will install [Redis Server](https://github.com/itsmechlark/features/tree/main/src/redis-server), which is a community contributed feature that installs and adds the proper dev container set up for Redis. 4. Select "Redis Server" as an additional feature to be installed, press "OK", and then select "Keep Defaults". - ![Redis Server option selected in the Dev Container configuration files list.](images/fastapi-tutorial/devcontainers_redis_server_feature.png) + Redis Server option selected in the Dev Container configuration files list. This will create a `.devcontainer` folder in your workspace, with a `devcontainer.json` file. Let's make some edits to this file so the container setup will include steps such as installing the VS Code extensions we'll need as well as the project dependencies. @@ -336,35 +360,50 @@ This will create a `.devcontainer` folder in your workspace, with a `devcontaine 10. Select the "Reopen in Container" button from the notification that will show up on the bottom right corner, or run the **Dev Containers: Reopen in Container** command from the Command Palette. - You can learn more about dev container configuration in the [documentation](https://code.visualstudio.com/docs/devcontainers/containers#_create-a-devcontainerjson-file). + You can learn more about dev containers configuration in the [documentation](https://code.visualstudio.com/docs/devcontainers/containers#_create-a-devcontainerjson-file). > Note: it may take several minutes to build the container, depending on internet speed and machine performance. Once it's done, you will have a fully configured Linux-based workspace with Python 3 and Redis Server installed. Once the container is set up, you will notice an indicator on the bottom left corner of VS Code: + ![Dev Container indicator displayed on the bottom left corner of VS Code.](images/fastapi-tutorial/devcontainer_indicator.png) -You should now be ready to move on to the next section, where we will replace the data storage. +> Note: Double check that the Python and Pylance extensions have been successfully installed in the container by opening the Extensions view (`kb(workbench.view.extensions)`) and searching for them. If not, you can install them by clicking on the "Install in Dev Container" button. + +You should also see the selected Python interpreter information on the status bar at the bottom right corner, matching the version specified in the `devcontainer.json` file: + +Python interpreter selection + +> Note: If you don't see the Python interpreter information on the status bar, you can click on the Python interpreter indicator (or run the **Python: Select Interpreter** command from the Command Palette) and manually select the Python interpreter in the container. + +We are now ready to move on to the next section, where we will replace the data storage. ## Replace the database + +We have a dictionary that stores the grocery list items, but we want to replace it with a Redis database. In this tutorial, we will use Redis hashes to store our data, which is a data structure that can store multiple key-value pairs. + +Unlike a traditional database where you can retrieve an item without knowing its ID, you need to know the Redis hash key in order to retrieve a value from it. In this tutorial, we will create a hash called `item_name_to_id` that will allow us to retrieve items by name, and map them to their IDs. In addition, we'll create other hashes that will allow us to retrieve items by ID, mapping them to their names and quantities. Each item hash will be named `item_id:{item_id}` and will have two fields: `item_name` and `quantity`. + + +First, let's start by replacing the dictionary with a Redis client object that connects to a Redis server. + 1. In the `main.py` file, replace the `grocery_list: dict[int, ItemPayload] = {}` in the beginning of the file with the lines below: ```python redis_client = redis.StrictRedis(host='0.0.0.0', port=6379, db=0, decode_responses=True) ``` Pylance will display an error message because Redis hasn’t been imported yet. -2. Click on "redis" in the editor, and select the light bulb the shows up (or `kb(editor.action.quickFix)`). Then select "Add 'import redis'". +2. Click on "redis" in the editor, and select the light bulb that shows up (or `kb(editor.action.quickFix)`). Then select **Add 'import redis'**. ![Light bulb displayed next to the Redis variable, with the option to add the import statement.](images/fastapi-tutorial/fastapi_add_redis_quickfix.png) -You can also set up Pylance to automatically add imports by looking for the "Auto Import Completions" setting in the Settings UI page (`kb(workbench.action.openSettings)`) and enabling it. + > **Tip**: You can set up Pylance to automatically add imports by looking for the "Auto Import Completions" setting in the Settings UI page (`kb(workbench.action.openSettings)`) and enabling it. We now have a Redis client object that connects to a Redis server running on the local host (`host="0.0.0.0"`) and listening on port 6379 (`port=6379`). The `db` parameter specifies the Redis database to use. Redis supports multiple databases, and in this code we’re setting to use database 0, which is the default database. We're also passing `decode_responses=True` for the responses to be decoded as strings (instead of bytes). -This client can now be used to perform operations such as getting, setting, and deleting objects in the Redis database. - Let's do some more replacements in the first route `add_item`. Instead of looking into all the keys from the dictionary to find the item name that has been provided, we can fetch that information directly from a Redis hash. -In Redis, a hash is a data structure that can store multiple key-value pairs. Let's assue we'll create a hash called `item_name_to_id` to store item names (keys) mapped into their IDs (values) once they are added in the app. We can then get the ID of the item name we’re receiving by invoking the `hget` method from Redis. +We'll assume that the `item_name_to_id` hash already exists, mapping item names to their IDs (don't worry, we'll add this code shortly!). We can then get the ID of the item name we’re receiving in the request by invoking the `hget` method from Redis, which will return the item ID if the requested name already exists in the hash, or `None` if it doesn't. 3. Delete the line with the content below: ```python @@ -379,34 +418,36 @@ Note that Pylance raises a problem with this change. This is because the `hget` 4. Rename `item_id` to `item_id_str`. -5. Pylance should show a variable type hint next to `item_id_str`. Double click to accept it: +5. If you have inlay hints enabled, Pylance should show a variable type hint next to `item_id_str`. You can optionally double click to accept it: + ![Variable type hint displayed next to the item_id_str variable.](images/fastapi-tutorial/pylance_redis_typehint.png) 6. If the item doesn't exist, then `item_id_str` will be None. So now we can delete the line with the following content: ```python - if item_name in items_names: + if item_name in items_ids.keys(): ``` And replace it with: ```python if item_id_str is not None: ``` -Now that we have the item ID as a string, we need to convert it to an `int` and update the quantity for the item. Currently, our Redis hash only maps item names to their IDs. To also map item IDs to their names and quantities, we can create a separate Redis hash for each item. We'll use the format `"item_id:{item_id}"` as our hash name to make retrieval by ID easier, and add "name" and "quantity" fields for each hash. +Now that we have the item ID as a string, we need to convert it to an `int` and update the quantity for the item. Currently, our Redis hash only maps item names to their IDs. To also map item IDs to their names and quantities, we will create a separate Redis hash for each item, using `"item_id:{item_id}"` as our hash name to make retrieval by ID easier. We'll also add `item_name` and `quantity` fields for each of these hashes. 7. Delete the code within the `if` block: ```python item_id: int = items_ids[item_name] grocery_list[item_id].quantity += quantity ``` - And add the following, to convert the `item_id` to an `int`, and then to increment the quantity of the item by calling the `hincrby` method from Redis: + And add the following, to convert the `item_id` to an `int`, and then to increment the quantity of the item by calling the `hincrby` method from Redis. This method will increment the value of the `"quantity"` field by the given amount in the request (`quantity`): ```python item_id = int(item_id_str) redis_client.hincrby(f"item_id:{item_id}", "quantity", quantity) ``` -We now only need to replace the code for when the item does not exist, i.e. `item_id_str` is `None`. In this case, we will generate a new `item_id`, create a new Redis hash for the item, and then add the provided item names and quantity. -To generate a new `item_id`, let’s use the `incr` method from Redis, passing a new hash "item_ids". When this method is run for the first time, it will create the item_ids hash with a unique number, and then each time it's run it will generate an incremental number and store it in this hash. + +We now only need to replace the code for when the item does not exist, i.e. `item_id_str` is `None`. In this case, we will generate a new `item_id`, create a new Redis hash for the item, and then add the provided item name and quantity. +To generate a new `item_id`, let’s use the `incr` method from Redis, passing a new hash called `"item_ids"`. This hash will be used to store the last generated ID, so we can increment it each time we create a new item, ensuring that they all have a unique ID. 8. Delete the line with the following content: ```python @@ -417,7 +458,9 @@ To generate a new `item_id`, let’s use the `incr` method from Redis, passing a item_id: int = redis_client.incr("item_ids") ``` -Now we will add the item to the Redis hash, using the `hset` method and by providing a mapping of fields or keys (`item_id`, `quantity` and `name`), and the values (the item’s newly created id, and its provided name and quantity). + When this `incr` call is run for the first time with the `item_ids` key, Redis will create the key and map it to the value `1`. Then, each subsequent time it's run, it will increment the stored value by 1. + +Now we will add the item to the Redis hash, using the `hset` method and by providing a mapping the fields (`item_id`, `quantity` and `name`), and the values (the item’s newly created id, and its provided name and quantity). 9. Delete the line with the following content: ```python @@ -454,10 +497,14 @@ Now we only need to map the newly created ID to the item name by setting the has 12. If you would like, you can try to do a similar replacement for the other routes. Otherwise, you can just replace the entire content of the file with the lines below: ```python - @app.get("/") - def home(request: Request) -> dict[str, str]: - url = str(request.base_url) - return {"message": f"Add /docs to the end of the URL to access the Swagger UI."} + import redis + from fastapi import FastAPI, HTTPException + + from models import ItemPayload + + app = FastAPI() + + redis_client = redis.StrictRedis(host="0.0.0.0", port=6379, db=0, decode_responses=True) # Route to add an item @app.post("/items/{item_name}/{quantity}") @@ -485,13 +532,14 @@ Now we only need to map the newly created ID to the item name by setting the has # Create a set so we can search by name too redis_client.hset("item_name_to_id", item_name, item_id) - return {"item": ItemPayload(item_id=item_id, item_name=item_name, quantity=quantity)} - + return { + "item": ItemPayload(item_id=item_id, item_name=item_name, quantity=quantity) + } - # Route to list a specific item by ID but using Redis + # Route to list a specific item by id but using Redis @app.get("/items/{item_id}") - def list_item(item_id: int): + def list_item(item_id: int) -> dict[str, dict[str, str]]: if not redis_client.hexists(f"item_id:{item_id}", "item_id"): raise HTTPException(status_code=404, detail="Item not found.") else: @@ -506,9 +554,7 @@ Now we only need to map the newly created ID to the item name by setting the has for name, id_str in stored_items.items(): item_id: int = int(id_str) - item_name_str: str | None = redis_client.hget( - f"item_id:{item_id}", "item_name" - ) + item_name_str: str | None = redis_client.hget(f"item_id:{item_id}", "item_name") if item_name_str is not None: item_name: str = item_name_str else: @@ -529,7 +575,7 @@ Now we only need to map the newly created ID to the item name by setting the has return {"items": items} - # Route to delete a specific item by ID but using Redis + # Route to delete a specific item by id but using Redis @app.delete("/items/{item_id}") def delete_item(item_id: int) -> dict[str, str]: if not redis_client.hexists(f"item_id:{item_id}", "item_id"): @@ -538,12 +584,12 @@ Now we only need to map the newly created ID to the item name by setting the has item_name: str | None = redis_client.hget(f"item_id:{item_id}", "item_name") redis_client.hdel("item_name_to_id", f"{item_name}") redis_client.delete(f"item_id:{item_id}") - return {"result": "Item deleted."} + return {"result": "Item deleted."} - # Route to remove some quantity of a specific item by ID but using Redis + # Route to remove some quantity of a specific item by id but using Redis @app.delete("/items/{item_id}/{quantity}") - def remove_quantity(item_id: int, quantity: int): + def remove_quantity(item_id: int, quantity: int) -> dict[str, str]: if not redis_client.hexists(f"item_id:{item_id}", "item_id"): raise HTTPException(status_code=404, detail="Item not found.") @@ -561,13 +607,16 @@ Now we only need to map the newly created ID to the item name by setting the has return {"result": "Item deleted."} else: redis_client.hincrby(f"item_id:{item_id}", "quantity", -quantity) - return {"result": f"{quantity} items removed."} + return {"result": f"{quantity} items removed."} + ``` -12. Re-run the debugger to test this application by interacting with the `/docs` route. +13. Re-run the debugger to test this application by interacting with the `/docs` route. You can stop the debugger once you're done. + +Congrats! You now have a working FastAPI application with routes to add, list, and delete items from a grocery list, and the data being persisted in a Redis database. ## Optional: Set up database deletion -Because the data will be now be persisted by redis, you may want to create a script that will allow you to erase all testing data. For that you can create a new file called `flushdb.py` with the following content: +Because the data will be now be persisted by Redis, you may want to create a script that will allow you to erase all testing data. For that you can create a new file called `flushdb.py` with the following content: ```python import redis @@ -580,14 +629,16 @@ Then when you want to reset the database, you can open the flushdb.py file in VS Note that this should be done with caution because it will delete all the keys in the current database, which could lead to data loss if done in production. ## Optional: Create a ChatGPT plugin using GitHub Codespaces -With GitHub Codespaces, we can host our application for testing purposes to use with [ChagGPT Plugins](https://platform.openai.com/docs/plugins/introduction), which are tools that enable [ChatGPT](https://chat.openai.com/) to interact with existing APIs to enhance ChatGPT’s abilities, allowing it to perform a wide range of actions. ChatGPT Plugins are not currently publicly available, but you can join their [waitlist](https://openai.com/waitlist/plugins) to get access. Once you do, you can follow along the livestream recording below to create your own grocery list plugin for ChatGPT: +With GitHub Codespaces, we can host our application for testing purposes to use with [ChatGPT Plugins](https://platform.openai.com/docs/plugins/introduction), which are tools that enable [ChatGPT](https://chat.openai.com/) to interact with existing APIs to enhance ChatGPT’s abilities, allowing it to perform a wide range of actions. ChatGPT Plugins are not currently publicly available, but you can join their [wait list](https://openai.com/waitlist/plugins) to get access. Once you do, you can follow along the live stream recording below to create your own grocery list plugin for ChatGPT: -Note that all personal GitHub.com accounts have a monthly quota of free use of GitHub Codespaces included in the Free or Pro plan. For more information, see "[About billing for GitHub Codespaces](https://docs.github.com/en/billing/managing-billing-for-github-codespaces/about-billing-for-github-codespaces)". +> Note: All personal GitHub.com accounts have a monthly quota of free use of GitHub Codespaces included in the Free or Pro plan. For more information, see "[About billing for GitHub Codespaces](https://docs.github.com/en/billing/managing-billing-for-github-codespaces/about-billing-for-github-codespaces)". ## Next Steps -The completed code project from this tutorial can be found on GitHub: [python-sample-vscode-fastapi-tutorial](TODO: Add link). +Thank you for following along this tutorial! We hope you learned something new about FastAPI and how to use it with VS Code. + +The completed code project from this tutorial can be found on GitHub: [python-sample-vscode-fastapi-tutorial](https://github.com/microsoft/python-sample-vscode-fastapi-tutorial). Learn more about FastAPI on the [official documentation](https://fastapi.tiangolo.com/). From 7eb1da2d45cb7bab56b3096268f5ff565d2dca83 Mon Sep 17 00:00:00 2001 From: Greg Van Liew Date: Wed, 25 Oct 2023 08:36:35 -0700 Subject: [PATCH 30/34] Add "Custom Layout" topic (#6763) * scaffold new Custom Layout topic * Locked editor group basics Part of #6568 * add a couple of images * Add Panel alignment section and image * Add more sections * Locked editor groups * Add pinned tab image * avoid a line break --------- Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com> --- build/sitemap.xml | 5 + docs/editor/custom-layout.md | 239 ++++++++++++++++++ .../custom-layout/autolockgroup-setting.png | 3 + .../customize-layout-dropdown.png | 3 + .../custom-layout/grid-editor-layout.png | 3 + .../custom-layout/lock-group-command.png | 3 + .../custom-layout/locked-editor-group.png | 3 + .../custom-layout/maximize-panel-size.png | 3 + .../custom-layout/non-default-layout.png | 3 + .../output-in-explorer-group.png | 3 + .../panel-alignment-context-menu.png | 3 + .../panel-context-menu-position.png | 3 + .../custom-layout/pinned-editor-tab.png | 3 + .../custom-layout/reset-tool-bar-menu.png | 3 + .../custom-layout/search-view-toolbar.png | 3 + .../custom-layout/secondary-side-bar.png | 3 + .../custom-layout/split-editor-commands.png | 3 + .../custom-layout/toggle-UI-visibility.png | 3 + docs/editor/port-forwarding.md | 2 +- docs/getstarted/userinterface.md | 5 + 20 files changed, 298 insertions(+), 1 deletion(-) create mode 100644 docs/editor/custom-layout.md create mode 100644 docs/editor/images/custom-layout/autolockgroup-setting.png create mode 100644 docs/editor/images/custom-layout/customize-layout-dropdown.png create mode 100644 docs/editor/images/custom-layout/grid-editor-layout.png create mode 100644 docs/editor/images/custom-layout/lock-group-command.png create mode 100644 docs/editor/images/custom-layout/locked-editor-group.png create mode 100644 docs/editor/images/custom-layout/maximize-panel-size.png create mode 100644 docs/editor/images/custom-layout/non-default-layout.png create mode 100644 docs/editor/images/custom-layout/output-in-explorer-group.png create mode 100644 docs/editor/images/custom-layout/panel-alignment-context-menu.png create mode 100644 docs/editor/images/custom-layout/panel-context-menu-position.png create mode 100644 docs/editor/images/custom-layout/pinned-editor-tab.png create mode 100644 docs/editor/images/custom-layout/reset-tool-bar-menu.png create mode 100644 docs/editor/images/custom-layout/search-view-toolbar.png create mode 100644 docs/editor/images/custom-layout/secondary-side-bar.png create mode 100644 docs/editor/images/custom-layout/split-editor-commands.png create mode 100644 docs/editor/images/custom-layout/toggle-UI-visibility.png diff --git a/build/sitemap.xml b/build/sitemap.xml index d396fc1dcc..6de69b2160 100644 --- a/build/sitemap.xml +++ b/build/sitemap.xml @@ -270,6 +270,11 @@ weekly 0.8 + + https://code.visualstudio.com/docs/editor/custom-layout + weekly + 0.8 + https://code.visualstudio.com/docs/sourcecontrol/overview weekly diff --git a/docs/editor/custom-layout.md b/docs/editor/custom-layout.md new file mode 100644 index 0000000000..9221e2259a --- /dev/null +++ b/docs/editor/custom-layout.md @@ -0,0 +1,239 @@ +--- +Order: 23 +Area: editor +TOCTitle: Custom Layout +ContentId: 71e2c9c1-fb19-469a-9620-877d4b08fb0d +PageTitle: Custom layout of Visual Studio Code +DateApproved: 8/3/2023 +MetaDescription: Visual Studio Code custom user interface layout. +--- +# Custom Layout + +Visual Studio Code comes with a simple user interface and convenient default layout. At the same time, VS Code provides options and settings to let you customize the UI layout to suite your preferences and work style. In this topic, we'll highlight various UI customizations so you can display views, editors, and panels in the way that's most productive for you. + +>**Note**: If you are new to VS Code, you may want to start with the [user interface overview](/docs/getstarted/userinterface.md) or take a look at the [Tips and Tricks](/docs/getstarted/tips-and-tricks.md) article. + +This article starts by discussing [Workbench](#workbench) customizations to rearrange UI elements such as the side bars, views, and panels. Later in the article, we'll cover customization of the [Editor](#editor) region with editor groups, split editors, and editor tabs. + +## Workbench + +### Primary side bar + +The default (Primary) side bar shows views such as the File Explorer, Search, and Source Control on the left of the workbench. If you'd prefer it on the right, you can: + +* Right-click the Activity bar and select **Move Primary Side Bar Right**. +* Run **View: Toggle Primary Side Bar Position** to switch the Primary side bar right and left. +* Set the **Workbench > Side Bar: Location** (`workbench.sideBar.location`) [setting](/docs/getstarted/settings.md) to `right`. + +### Secondary side bar + +By default, VS Code shows all views in the **Primary Side Bar** located to the left of the editor region. If you like another place to display views, you can open the **Secondary Side Bar** to the right and drag and drop views into that side bar. + +This can be useful if you'd like to see two views at the same time, for example, the File Explorer on the left and Source Control view on the right: + +![Source Control view in the Secondary Side bar to the right](images/custom-layout/secondary-side-bar.png) + +To display the Secondary side bar, you can: + +* Run **View: Toggle Secondary Side Bar Visibility** (`kb(workbench.action.toggleAuxiliaryBar)`). +* Check the **View** > **Appearance** > **Secondary Side Bar** menu item. + +The Secondary side bar is initially empty but you can drag and drop views and panels into it and the layout is preserved across your VS Code sessions. + +>**Note**: You can reset views and panels back to their default locations with the **View: Reset View Locations** command. + +### Panel + +The Panel region displays UI elements such as the Problems, Terminal, and Output panels and by default is located under the editor region. + +### Panel position + +You can also move the region to the left or right of the editor with the **Move Panel** commands: + +* **View: Move Panel Left** (`workbench.action.positionPanelLeft`) +* **View: Move Panel Right** (`workbench.action.positionPanelRight`) +* **View: Move Panel To Bottom** (`workbench.action.positionPanelBottom`) + +You can configure these options in the menu under **View** > **Appearance** > **Panel Position**, Panel title bar context menu, or using the new **View: Move Panel** commands. + +![Panel title bar context menu with Panel Position options](images/custom-layout/panel-context-menu-position.png) + +### Panel alignment + +This option lets you configure how far the bottom Panel spans across your window. There are four options: + +* **Center** - This is the default behavior. The panel spans the width of the editor area only. +* **Justify** - The panel spans the full width of the window. +* **Left** - The panel spans from the left edge of the window to the right edge of the editor area. +* **Right** - The panel spans from the right edge of the window to the left edge of the editor area. + +With all Panel alignment options, the Activity Bar is considered the edge of the window. + +You can configure these options in the menu under **View** > **Appearance** > **Align Panel**, Panel title context menu, or using the new **Set Panel Alignment to...** commands. + +![Align Panel options from Panel title context menu](images/custom-layout/panel-alignment-context-menu.png) + +### Maximize Panel size + +You can quickly toggle the Panel region to fill the entire editor area with the **Maximize Panel Size** chevron button in the upper right of the Panel region. The chevron button points downwards in the maximized panel to restore the panel to the original size. + +![Maximize Panel Size button in the upper right of the Panel region](images/custom-layout/maximize-panel-size.png) + +You can also maximize the Panel region via the **View: Toggle Maximized Panel** command. + +> **Note**: Besides customizing the overall Panel region display, individual panels may have their own layout customizations. For example, the Terminal lets you have [multiple open tabs](/docs/terminal/basics.md#managing-terminals) and [split existing terminals](/docs/terminal/basics.md#groups-split-panes). + +### Customize Layout control + +The VS Code title bar also has buttons to toggle the visibility of the main UI elements (Side bars and Panel region). + +![Title bar buttons to toggle main UI elements, with hover on Toggle Panel](images/custom-layout/toggle-UI-visibility.png) + +The rightmost button brings up the **Customize Layout** dropdown, where you can further change the visibility and layout of various UI elements and includes several layout modes: + +![Customize Layout dropdown shown via the Customize Layout button in the title bar](images/custom-layout/customize-layout-dropdown.png) + +The layout modes are: + +* **Full Screen** - Set the editor to fill the full display screen. **View: Toggle Full Screen** (`kb(workbench.action.toggleFullScreen)`). +* **Zen Mode** - Hide all UI except for the editor area. **View: Toggle Zen Mode** (`kb(workbench.action.toggleZenMode)`). +* **Centered Layout** - Centers the editor inside the editor region. **View: Toggle Centered Layout**. + +### Drag and drop views and panels + +VS Code has a default layout of views and panels in the Primary Side bar and Panel region but you can drag and drop views and panels between these regions. For example, you can drag and drop the Source Control view into the Panel region or put the Problems panel into the Primary Side bar: + +![The Source Control view in the Panel region and Problem panel in the Primary Side bar](images/custom-layout/non-default-layout.png) + +>**Note**: Reminder that you can reset a view and panel back to its default location with the **Reset Location** context menu item or all views and panels with the general **View: Reset View Locations** command. + +You can also add views and panels to existing view or panel to create groups. For example, you could move the Output panel to the Explore view group by dragging over the Explorer Activity bar item and then dropping into the view: + +![Output panel moved to the Explorer view group](images/custom-layout/output-in-explorer-group.png) + +You are not limited to using the mouse for moving views and panels. You can also customize layouts via the keyboard with the **View: Move View** and **View: Move Focused View** commands, where dropdowns let you pick the UI element to move and the destination, either a location like the Side bar or Panel region or an existing view or panel to create a group. + +## Tool bars + +Most VS Code views and panels have tool bars displayed on the top right of their UI. For example, the Search view has a tool bar with actions such as **Refresh**, **Clear Search Results**, etc.: + +![Search view tool bar with hover over Clear Search Results action](images/custom-layout/search-view-toolbar.png) + +### Hide items in tool bars + +If you think a tool bar is too busy and you'd like to hide less frequently used actions, you can right-click on any action and select its **Hide** command (for example **Hide 'Clear Search Results'**) or uncheck any of the actions from the dropdown. Hidden actions are moved to the `...` **More Actions** menu and can be invoked from there. + +To restore an action to the tool bar, right-click the tool bar button area and select the **Reset Menu** command or recheck the hidden action. To restore all menus in VS Code, run **View: Reset All Menus** from the Command Palette (`kb(workbench.action.showCommands)`). + +![Search tool bar context menu with Reset menu command](images/custom-layout/reset-tool-bar-menu.png) + +## Editor + +You can customize the layout of the VS Code editor region independent of the workbench user interface. By default, the editor region displays useful features such as the minimap, breadcrumbs, editor tabs, and has optional UI such as Sticky Scroll. You can also adjust the layout of the editors themselves. + +### Minimap and breadcrumbs + +The **View** > **Appearance** menu has a section for customizing the editor region. There you'll find toggles for: + +* **Minimap** - A [visual overview](/docs/getstarted/userinterface.md#minimap) of your current file. **View: Toggle Minimap**. +* **Breadcrumbs** - Display [folder, file, and current symbol](/docs/getstarted/userinterface.md#breadcrumbs) information for the active file. **View: Toggle Breadcrumbs**. +* **Sticky Scroll** - Display nested symbol scopes in the active file. **View: Toggle Sticky Scroll**. + +### Editor groups + +By default, all opened editors go into the same **editor group** and add a new editor tab to the right. You can create new editor groups in order to group similar or related files or allow [side by side editing](/docs/getstarted/userinterface.md#side-by-side-editing) of the same file. Create a new editor group by dragging an editor to the side or using one of the **Split** commands in the context menu to split the current edit and start a new editor group to the left, right, above, or below. + +![Split editor commands in the editor tab context menu](images/custom-layout/split-editor-commands.png) + +The **Split** editor commands are also available from the **View** > **Editor Layout** menu and through the Command Palette. + +If you'd like to go quickly between vertical and horizontal editor group layout, you can use the **Toggle Vertical/Horizontal Editor Layout** command (`kb(workbench.action.toggleEditorGroupLayout)`). + +### Split in group + +You can also split an editor in the same group for side by side editing with the **View: Split Editor in Group** command (`kb(workbench.action.splitEditorInGroup)`). + +When using the split in group feature, there are specific commands for toggling this mode and navigating between the two split editors: + +* **View: Split Editor in Group** - Split the current editor. +* **View: Toggle Split Editor in Group** - Toggle between split mode for the active editor. +* **View: Join Editor in Group** - Go back to a single editor for the active file. +* **View: Toggle Layout of Split Editor in Group** - Toggle between horizontal and vertical layout. + +To navigate between the sides: + +* **View: Focus First Side in Active Editor** - Move focus to the first (left or top) side of split editor. +* **View: Focus Second Side in Active Editor** - Move focus to the second (right or bottom) side. +* **View: Focus Other Side in Active Editor** - Toggle between the split editor sides. + +The **Workbench > Editor: Split in Group Layout** (`workbench.editor.splitInGroupLayout`) [setting](/docs/getstarted/settings.md) lets you set the preferred split editor layout to either horizontal (default) or vertical. + +### Grid layout + +If you'd like more control over the editor layout, you can use the [grid layout](/docs/getstarted/userinterface.md#grid-editor-layout), where you can have multiple rows and columns of editors visible. The **View** > **Editor Layout** menu lists various editor layout options (for example, **Two Columns**, **Three Columns**, **Grid (2x2)**) and you can adjust the editor sizes by grabbing and moving the sash between editors. + +![Editor Grid 2x2 layout with the sash highlighted](images/custom-layout/grid-editor-layout.png) + +### Pinned tabs + +If you'd like an editor to always be visible, you can pin it to the editor tool bar. You can pin an editor tab from either the context menu or using the command **View: Pin Editor** (`kb(workbench.action.pinEditor)`). + +![Pinned editor tab with pin button highlighted](images/custom-layout/pinned-editor-tab.png) + +Pinned tabs help display files that are important to you as: + +* Pinned tabs always appear first before non-pinned tabs. +* They do not scroll out of view if you have many tabs opened. +* They do not close when using editor tab commands such as **Close Others** or **Close All**. +* They do not close even when you exceed a set limit of opened editors. + +Unpin an editor by clicking on the pin icon, using the **Unpin** editor tab context menu item, or the **View: Unpin Editor** command. + +You can choose how you'd like to display pinned editors with the **Workbench > Editor: Pinned Tab Sizing** (`workbench.editor.pinnedTabSizing`) setting. The options are: + +* `normal`: A pinned tab inherits the look of other tabs (default) +* `shrink`: A pinned tab shrinks to a fixed size showing parts of the editor label. +* `compact`: A pinned tab will only show as icon or first letter of the editor label. + +You can also show pinned editor tabs on a separate row above the regular editor tool bar by setting **Workbench > Editor: Pinned Tabs On Separate Row**. You can pin and unpin editors by dragging and dropping their tabs between the two rows. + +### Locked editor groups + +When using multiple editors, it's common to have one or more that you want to always keep visible. The locked editor group feature, where an entire editor group is locked and visible, provides a stable display and any request to open a new editor will create it in another group. You can tell whether an editor group is locked by the lock icon in the editor group tool bar. + +![Locked editor group with lock icon highlighted](images/custom-layout/locked-editor-group.png) + +You can lock an editor group by selecting **Lock Group** from the editor tool bar **More Actions** `...` dropdown or running the **View: Lock Editor Group** command. + +![Lock Group command in the editor tool bar More Actions dropdown](images/custom-layout/lock-group-command.png) + +You can unlock an editor group by clicking on the lock icon or running the **View: Unlock Editor Group** command. + +Locked groups behave differently than unlocked groups: + +* New editors will not open in a locked group unless explicitly moved there (for example, via drag and drop). +* If a new editor skips a locked group, it will either open in the most recently used unlocked group or create a new group to the side of the locked one. +* The locked state of an editor group is persisted and restored across restarts. +* You can lock empty groups as well allowing for a more stable editor layout. + +The primary use case is for [terminals in the editor area](/docs/terminal/basics.md#terminals-in-editor-area). For example, you might want to edit text on the left and have a terminal on the right that is always visible. When a terminal editor is created and moved to the side, it will automatically lock. This means that even when the terminal on the right is focused, opening a file will open it on the left side without needing to manually change focus first. + +Auto locking groups can be configured using the `workbench.editor.autoLockGroups` setting, which defaults to only terminal editors but any editor type can be added to get the same behavior. + +![Auto Lock Groups setting with Terminal checked in the Settings editor](images/custom-layout/autolockgroup-setting.png) + +The commands related to editor group locking: + +* **View: Lock Editor Group** - Lock the active editor group. +* **View: Unlock Editor Group** - Unlock the active locked editor group. +* **View: Toggle Editor Group Lock** - Lock or unlock the active editor group. + +You must have more that one editor group for these commands to be available. + +## Next steps + +Read on to find out about: + +* [Visual Studio Code User Interface](/docs/getstarted/userinterface.md) - A quick orientation to VS Code. +* [Basic Editing](/docs/editor/codebasics.md) - Learn about the powerful VS Code editor. +* [Code Navigation](/docs/editor/editingevolved.md) - Move quickly through your source code. diff --git a/docs/editor/images/custom-layout/autolockgroup-setting.png b/docs/editor/images/custom-layout/autolockgroup-setting.png new file mode 100644 index 0000000000..a8ec789c7a --- /dev/null +++ b/docs/editor/images/custom-layout/autolockgroup-setting.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9d3a06ee9894beaee1fa038d611bd1e5d94995d6d6b6458c07f6f654144b55f3 +size 143833 diff --git a/docs/editor/images/custom-layout/customize-layout-dropdown.png b/docs/editor/images/custom-layout/customize-layout-dropdown.png new file mode 100644 index 0000000000..c69e745273 --- /dev/null +++ b/docs/editor/images/custom-layout/customize-layout-dropdown.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3b6c36848d499d6defc83d781bad5f32edfa5606cebea77b7dbb0d598073b0d1 +size 51920 diff --git a/docs/editor/images/custom-layout/grid-editor-layout.png b/docs/editor/images/custom-layout/grid-editor-layout.png new file mode 100644 index 0000000000..08efd0414c --- /dev/null +++ b/docs/editor/images/custom-layout/grid-editor-layout.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cf3130c5c051324c78b7b92b04ee4953af6e07bf86aa2b4c2943bc7ee8ce07ba +size 351969 diff --git a/docs/editor/images/custom-layout/lock-group-command.png b/docs/editor/images/custom-layout/lock-group-command.png new file mode 100644 index 0000000000..c684df606b --- /dev/null +++ b/docs/editor/images/custom-layout/lock-group-command.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ce85a48fc3f222695a59cb0df917081eff609d20c5459fdf9d2d1a90f41d4802 +size 59839 diff --git a/docs/editor/images/custom-layout/locked-editor-group.png b/docs/editor/images/custom-layout/locked-editor-group.png new file mode 100644 index 0000000000..820a0fbf9e --- /dev/null +++ b/docs/editor/images/custom-layout/locked-editor-group.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2fa981b9bac0fb5f27525d02be186d9f18e274e33086d51097e607ffd9a03391 +size 43955 diff --git a/docs/editor/images/custom-layout/maximize-panel-size.png b/docs/editor/images/custom-layout/maximize-panel-size.png new file mode 100644 index 0000000000..5bdf6151f6 --- /dev/null +++ b/docs/editor/images/custom-layout/maximize-panel-size.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:375465a3616558e083e3fb5feaa7a86731162f5354c6584d9babe2f3be7b4cc4 +size 13108 diff --git a/docs/editor/images/custom-layout/non-default-layout.png b/docs/editor/images/custom-layout/non-default-layout.png new file mode 100644 index 0000000000..55a73636b5 --- /dev/null +++ b/docs/editor/images/custom-layout/non-default-layout.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c6b99cb497ce2937357024d4da43cd8a82459c82fa1448d359a321cbb187880b +size 277508 diff --git a/docs/editor/images/custom-layout/output-in-explorer-group.png b/docs/editor/images/custom-layout/output-in-explorer-group.png new file mode 100644 index 0000000000..b859121d6d --- /dev/null +++ b/docs/editor/images/custom-layout/output-in-explorer-group.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a2ff4606fb2b911319c4b6338296cb8d2c26fcc0e3499faf4b570158995610af +size 298571 diff --git a/docs/editor/images/custom-layout/panel-alignment-context-menu.png b/docs/editor/images/custom-layout/panel-alignment-context-menu.png new file mode 100644 index 0000000000..3a2b4f31dd --- /dev/null +++ b/docs/editor/images/custom-layout/panel-alignment-context-menu.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7418ad08711a6a3fb722df7734ec5ac9ec6948d7094fd83547a90c164d747d0a +size 47638 diff --git a/docs/editor/images/custom-layout/panel-context-menu-position.png b/docs/editor/images/custom-layout/panel-context-menu-position.png new file mode 100644 index 0000000000..840f52d5ac --- /dev/null +++ b/docs/editor/images/custom-layout/panel-context-menu-position.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1c32eb44c40bef2eecc898fef7708f90e83126dab1bf883b92cb4acddf9d04e0 +size 48236 diff --git a/docs/editor/images/custom-layout/pinned-editor-tab.png b/docs/editor/images/custom-layout/pinned-editor-tab.png new file mode 100644 index 0000000000..3110457e2f --- /dev/null +++ b/docs/editor/images/custom-layout/pinned-editor-tab.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a6b333b997c83b20124d315b8f6d7267d01e6f1473e24f5916eb7a0d6c91a10f +size 40858 diff --git a/docs/editor/images/custom-layout/reset-tool-bar-menu.png b/docs/editor/images/custom-layout/reset-tool-bar-menu.png new file mode 100644 index 0000000000..29918380e4 --- /dev/null +++ b/docs/editor/images/custom-layout/reset-tool-bar-menu.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8ab7f0df73a5a9a74ad00d35b5c641a91b17160200bcde1dbd1426e38c51c845 +size 57412 diff --git a/docs/editor/images/custom-layout/search-view-toolbar.png b/docs/editor/images/custom-layout/search-view-toolbar.png new file mode 100644 index 0000000000..24dd928ae5 --- /dev/null +++ b/docs/editor/images/custom-layout/search-view-toolbar.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3262b2971f20c8247f4f315159abb7164ff2167331883800227edaeac52d4506 +size 45897 diff --git a/docs/editor/images/custom-layout/secondary-side-bar.png b/docs/editor/images/custom-layout/secondary-side-bar.png new file mode 100644 index 0000000000..5d94be3c30 --- /dev/null +++ b/docs/editor/images/custom-layout/secondary-side-bar.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:43d2a8eb797865fb853a02563abd016f4a8db9226e7dc8a15cd8ef1b6c0255bc +size 360793 diff --git a/docs/editor/images/custom-layout/split-editor-commands.png b/docs/editor/images/custom-layout/split-editor-commands.png new file mode 100644 index 0000000000..cbd2299760 --- /dev/null +++ b/docs/editor/images/custom-layout/split-editor-commands.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5df7f1409b5d4c0c982a7c377465bc4b57556e5fb063b00a40280cf198aabf78 +size 92671 diff --git a/docs/editor/images/custom-layout/toggle-UI-visibility.png b/docs/editor/images/custom-layout/toggle-UI-visibility.png new file mode 100644 index 0000000000..56d622d923 --- /dev/null +++ b/docs/editor/images/custom-layout/toggle-UI-visibility.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca5bfb5f401e63f2b5838c5b68b9e855b254c5f55ac16680b43be27684b2acfe +size 5235 diff --git a/docs/editor/port-forwarding.md b/docs/editor/port-forwarding.md index ee3f24ce01..d9cff69b15 100644 --- a/docs/editor/port-forwarding.md +++ b/docs/editor/port-forwarding.md @@ -1,5 +1,5 @@ --- -Order: 23 +Order: 24 Area: editor TOCTitle: Port Forwarding ContentId: d7a80c88-c091-4d13-9240-d432c12407a7 diff --git a/docs/getstarted/userinterface.md b/docs/getstarted/userinterface.md index 9a11794018..b88caaedc7 100644 --- a/docs/getstarted/userinterface.md +++ b/docs/getstarted/userinterface.md @@ -53,6 +53,10 @@ When you have more than one editor open you can switch between them quickly by h >**Tip:** You can resize editors and reorder them. Drag and drop the editor title area to reposition or resize the editor. +### Split in group + +You can also split the current editor without creating a new editor group with the **View: Split Editor in Group** command (`kb(workbench.action.splitEditorInGroup)`). To learn more about this editor mode and specific commands for navigating between the two sides, you can read the section in [Custom Layout](/docs/editor/custom-layout.md#split-in-group) topic. + ## Minimap A Minimap (code outline) gives you a high-level overview of your source code, which is useful for quick navigation and code understanding. A file's minimap is shown on the right side of the editor. You can click or drag the shaded area to quickly jump to different sections of your file. @@ -369,6 +373,7 @@ The `window.restoreWindows` setting tells VS Code how to restore the opened wind Now that you know the overall layout of VS Code, start to customize the editor to how you like to work by looking at the following topics: * [Changing the Theme](/docs/getstarted/themes.md) - Set a Color and/or File Icon theme to your preference. +* [Custom Layout](/docs/editor/custom-layout.md) - Learn more about VS Code's workbench and editor layout customizations. ## Common questions From e97c88539ec956a7f5d6381fde691d45944dbe73 Mon Sep 17 00:00:00 2001 From: Luciana Abud <45497113+luabud@users.noreply.github.com> Date: Wed, 25 Oct 2023 14:58:42 -0700 Subject: [PATCH 31/34] Apply suggestions from code review Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Co-authored-by: Courtney Webster <60238438+cwebster-99@users.noreply.github.com> --- docs/python/tutorial-fastapi.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/python/tutorial-fastapi.md b/docs/python/tutorial-fastapi.md index 6c208e8105..0bb61152c1 100644 --- a/docs/python/tutorial-fastapi.md +++ b/docs/python/tutorial-fastapi.md @@ -150,7 +150,7 @@ Now we need a place to store the grocery list items. For simplicity, let’s sta This creates a new empty dictionary that will receive keys of type `int` (which will be item IDs) and values of the `ItemPayload` type. -We'll now define routes in our FastAPI application. In the context of web applications, routes are like pathways that map specific URLs to the code that handles them. These routes serve as the entry points for different functionalities within our application. When a client, such as a web browser or another program, sends a request to our application with a particular URL, FastAPI routes that request to the appropriate function (also known as route handler or view function) based on the URL, and that function processes the request and generates a response. +We'll now define routes in our FastAPI application. In the context of web applications, routes are like pathways that map specific URLs to the code that handles them. These routes serve as the entry points for the different functionality within our application. When a client, such as a web browser or another program, sends a request to our application with a particular URL, FastAPI routes that request to the appropriate function (also known as route handler or view function) based on the URL, and that function processes the request and generates a response. Let's proceed with defining routes that will allow us to add and retrieve individual items, as well as list all items in the grocery list. @@ -179,12 +179,12 @@ Let's proceed with defining routes that will allow us to add and retrieve indivi return {"item": grocery_list[item_id]} ``` - If you have enabled type hints, you might notice Pylance adds inlay hints with the function return type, as well as the types for `item_ids` and `item_id`. You can optionally double click on each suggestion to insert them into the code: + If you have enabled type hints in the previous section, you might notice Pylance adds inlay hints with the function return type, as well as the types for `item_ids` and `item_id`. You can optionally double-click on each suggestion to insert them into the code: ![Inlay function return and variable type hints being displayed by Pylance throughout the sample code.](images/fastapi-tutorial/pylance_inlay_hints.png) Now let's see if this route is working as expected. The fastest way to do so is to leverage both VS Code's debugger as well as FastAPI's `/docs` endpoint, which provides information about all the available API routes and allows you to interact with the API to explore their parameters and responses. This documentation is generated dynamically based on the metadata and type hints defined in the FastAPI application. -4. Add a breakpoint next to the if quantity <= 0 statement, by clicking on the left margin of the line number (or `kb(editor.debug.action.toggleBreakpoint)`). This will make the debugger stop prior to the execution of that line, allowing us to inspect the code line by line. +4. Add a breakpoint next to the if `quantity <= 0` statement, by clicking on the left margin of the line number (or `kb(editor.debug.action.toggleBreakpoint)`). This will make the debugger stop prior to the execution of that line, allowing us to inspect the code line by line. ![Breakpoint set next to the first line in the add_item function.](images/fastapi-tutorial/debugger_breakpoint.png) 5. Start the debugger (`kb(workbench.action.debug.start)`), and then navigate to http://127.0.0.1:8000/docs in the browser. From bfc9ab8fe047da195838375ef1cb7e95c664fbe5 Mon Sep 17 00:00:00 2001 From: Luciana Abud <45497113+luabud@users.noreply.github.com> Date: Wed, 25 Oct 2023 17:47:15 -0700 Subject: [PATCH 32/34] Apply suggestions from code review Co-authored-by: Brigit Murtaugh --- docs/python/tutorial-fastapi.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/python/tutorial-fastapi.md b/docs/python/tutorial-fastapi.md index 0bb61152c1..24c496c016 100644 --- a/docs/python/tutorial-fastapi.md +++ b/docs/python/tutorial-fastapi.md @@ -319,7 +319,7 @@ For the steps below, make sure you have the following requirements installed on #### Requirements - [Docker for Windows](https://www.docker.com/) -- [Dev Containers extension for VS Code](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) +- [VS Code Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) #### Create the Dev Container configuration 1. Open the Command Palette and run the “Dev Container: Add Dev Container Configuration Files…" @@ -328,7 +328,7 @@ For the steps below, make sure you have the following requirements installed on Python 3 option selected in the Dev Container configuration files list. 3. Select the default version. -We can optionally install features to be included in the container. For this tutorial, we will install [Redis Server](https://github.com/itsmechlark/features/tree/main/src/redis-server), which is a community contributed feature that installs and adds the proper dev container set up for Redis. +We can optionally install Features to be included in the container. For this tutorial, we will install [Redis Server](https://github.com/itsmechlark/features/tree/main/src/redis-server), which is a community contributed Feature that installs and adds the proper dev container set up for Redis. 4. Select "Redis Server" as an additional feature to be installed, press "OK", and then select "Keep Defaults". Redis Server option selected in the Dev Container configuration files list. From c70250a43729ef190f5a99654052970fff55359e Mon Sep 17 00:00:00 2001 From: Luciana Abud Date: Wed, 25 Oct 2023 17:58:38 -0700 Subject: [PATCH 33/34] Fix dev container references --- docs/python/tutorial-fastapi.md | 40 ++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/python/tutorial-fastapi.md b/docs/python/tutorial-fastapi.md index 24c496c016..79f09b5936 100644 --- a/docs/python/tutorial-fastapi.md +++ b/docs/python/tutorial-fastapi.md @@ -108,22 +108,11 @@ Let’s create the application! 7. Stop the debugger by clicking on the stop icon in the debug toolbar, or through `kb(workbench.action.debug.stop)`. ## Create a model for grocery list items -Now that we have the FastAPI app working, let’s define our grocery list items by creating a Pydantic model. -Pydantic is a data validation and parsing library that integrates seamlessly with FastAPI. It allows you to define data models using Python classes with [type hints](https://docs.python.org/3/library/typing.html) for automatic validation and parsing of incoming data (which are called payloads) in API requests. +Now that we have the FastAPI app working, we can define our grocery list items by using [Pydantic](https://docs.pydantic.dev/latest/), which is a data validation and parsing library that integrates seamlessly with FastAPI. Pydantic allows you to define data models using Python classes with [type hints](https://docs.python.org/3/library/typing.html) for automatic validation and parsing of incoming data (which are called payloads) in API requests. +Let's create a model for our grocery list items. We will use the `ItemPayload` model to define the data structure of the items we will be adding to the grocery list. This model will have three fields: `item_id`, `item_name`, and `quantity`. -Pylance, the default language server for Python in VS Code, supports type hinting features that can be helpful for working with Pydantic models and FastAPI. This is because Pylance is built on top of [Pyright](https://github.com/microsoft/pyright), which is a static type checker for Python that can detect type errors in your code to prevent bugs and improve code quality. - -The 3 steps below are optional, but given FastAPI leverages type hints extensively to improve code readability and validation, we can take advantage of Pylance's type checking features to catch errors early on: - -1. Open the Settings UI page (`kb(workbench.action.openSettings)`) -2. Search for "python type checking mode" and set it to "basic" to enable basic type checking. This will enable Pylance to show diagnostics and warnings to catch simple type-related errors. Alternatively, you can set it to "strict" to enable more advanced [type checking rules](https://microsoft.github.io/pyright/#/configuration?id=diagnostic-rule-defaults). - ![Python Analysis Type Checking Mode options (off, basic and strict) in settings UI page.](images/fastapi-tutorial/type_checking_mode_setting.png) -3. Next, search for "Python inlay type hints", and enable inlay hints for Variable Types and Function Return Types: - ![Two Python Analysis Type Hints settings being enabled in the settings UI page: for Function Return Types and for Variable Types.](images/fastapi-tutorial/function_and_variable_return_type_hint_settings.png) - -Now let's create a Pydantic model for grocery list items. -1. Create a new Python file (`kb(workbench.action.files.newFile)` and select "Python File" +1. Create a new Python file by clicking on **File** > **New File…** (`kb(workbench.action.files.newFile)`), and then select "Python File". 2. Add the following lines to the file, and then save it in the `groceries-plugin` folder as `models.py` (`kb(workbench.action.files.saveAs)`): ```python from typing import Optional @@ -135,6 +124,16 @@ Now let's create a Pydantic model for grocery list items. quantity: int ``` +[Pylance](https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance), the default language server for Python in VS Code, supports type hinting features that can be helpful for working with Pydantic models and FastAPI. This is because Pylance is built on top of [Pyright](https://github.com/microsoft/pyright), which is a static type checker for Python that can detect type errors in your code to prevent bugs and improve code quality. + +The 3 steps below are optional, but given FastAPI leverages type hints extensively to improve code readability and validation, we can take advantage of Pylance's type checking features to catch errors early on: + +1. Open the Settings UI page (`kb(workbench.action.openSettings)`) +2. Search for "python type checking mode" and set it to "basic" to enable basic type checking. This will enable Pylance to show diagnostics and warnings to catch simple type-related errors. Alternatively, you can set it to "strict" to enable more advanced [type checking rules](https://microsoft.github.io/pyright/#/configuration?id=diagnostic-rule-defaults). + ![Python Analysis Type Checking Mode options (off, basic and strict) in settings UI page.](images/fastapi-tutorial/type_checking_mode_setting.png) +3. Next, search for "Python inlay type hints", and enable inlay hints for Variable Types and Function Return Types: + ![Two Python Analysis Type Hints settings being enabled in the settings UI page: for Function Return Types and for Variable Types.](images/fastapi-tutorial/function_and_variable_return_type_hint_settings.png) + ## Create Routes Now we need a place to store the grocery list items. For simplicity, let’s start with an empty dictionary. 1. First, let's import all the packages we will need for the sample. Open the `main.py` file and replace the first import line with the following ones: @@ -314,24 +313,25 @@ If you are on Windows, you can work with Redis by setting up either a [Docker co Otherwise, if you are on a Linux or a macOS machine, you can install Redis by following the [instructions on their website](https://redis.io/docs/getting-started/), and then skip to the ["Replace the database"](#replace-the-database) section. ### Setting up a Docker Container on Windows -The Dev Container extension for VS Code offers a simple way to configure a Linux-based workspace in your Windows machine, putting your entire project and its dependencies as well as tools you need for your project into a tidy container. +The [VS Code Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) offers a streamlined approach to consolidate your project, its dependencies and all necessary tools into one tidy container, creating a full-featured development environment. The extension enables you to open your project inside (or mounted into) the container in VS Code, leveraging its complete range of feature set. + For the steps below, make sure you have the following requirements installed on your machine: #### Requirements - [Docker for Windows](https://www.docker.com/) - [VS Code Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) -#### Create the Dev Container configuration -1. Open the Command Palette and run the “Dev Container: Add Dev Container Configuration Files…" +#### Create the Dev container configuration +1. Open the Command Palette and run the "Dev Containers: Add Dev Container Configuration Files…" 2. Select "Python 3": - Python 3 option selected in the Dev Container configuration files list. + Python 3 option selected in the Dev Containers configuration files list. 3. Select the default version. We can optionally install Features to be included in the container. For this tutorial, we will install [Redis Server](https://github.com/itsmechlark/features/tree/main/src/redis-server), which is a community contributed Feature that installs and adds the proper dev container set up for Redis. 4. Select "Redis Server" as an additional feature to be installed, press "OK", and then select "Keep Defaults". - Redis Server option selected in the Dev Container configuration files list. + Redis Server option selected in the Dev Containers configuration files list. This will create a `.devcontainer` folder in your workspace, with a `devcontainer.json` file. Let's make some edits to this file so the container setup will include steps such as installing the VS Code extensions we'll need as well as the project dependencies. @@ -367,7 +367,7 @@ Once it's done, you will have a fully configured Linux-based workspace with Pyth Once the container is set up, you will notice an indicator on the bottom left corner of VS Code: -![Dev Container indicator displayed on the bottom left corner of VS Code.](images/fastapi-tutorial/devcontainer_indicator.png) +![Dev Containers indicator displayed on the bottom left corner of VS Code.](images/fastapi-tutorial/devcontainer_indicator.png) > Note: Double check that the Python and Pylance extensions have been successfully installed in the container by opening the Extensions view (`kb(workbench.view.extensions)`) and searching for them. If not, you can install them by clicking on the "Install in Dev Container" button. From ee05d9ba8f2ae9f6a2f481ba24cdea2b11160dd1 Mon Sep 17 00:00:00 2001 From: Luciana Abud Date: Wed, 25 Oct 2023 18:39:07 -0700 Subject: [PATCH 34/34] Address more PR comments --- docs/python/tutorial-fastapi.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/python/tutorial-fastapi.md b/docs/python/tutorial-fastapi.md index 79f09b5936..dc54465c31 100644 --- a/docs/python/tutorial-fastapi.md +++ b/docs/python/tutorial-fastapi.md @@ -328,7 +328,7 @@ For the steps below, make sure you have the following requirements installed on Python 3 option selected in the Dev Containers configuration files list. 3. Select the default version. -We can optionally install Features to be included in the container. For this tutorial, we will install [Redis Server](https://github.com/itsmechlark/features/tree/main/src/redis-server), which is a community contributed Feature that installs and adds the proper dev container set up for Redis. +We can optionally install [Features](https://github.com/devcontainers/features) to be included in the container. For this tutorial, we will install [Redis Server](https://github.com/itsmechlark/features/tree/main/src/redis-server), which is a community contributed Feature that installs and adds the proper dev container set up for Redis. 4. Select "Redis Server" as an additional feature to be installed, press "OK", and then select "Keep Defaults". Redis Server option selected in the Dev Containers configuration files list. @@ -337,10 +337,17 @@ This will create a `.devcontainer` folder in your workspace, with a `devcontaine 5. Open the `devcontainer.json` file. 6. Add a "," after the `"features" : { ... }` entry, so we can add more settings to the file. + +Next, we will add the necessary dependency installation commands to the `postCreateCommand` property in the `devcontainer.json` file, so our application will be ready to run once the container is set up. + 7. Locate the content below and remove the comment (`//`) from that line, so the dependencies can be installed once the container is created: ``` "postCreateCommand": "pip3 install --user -r requirements.txt", ``` + You can learn more about the `postCreateCommand` and more in the [documentation](https://containers.dev/implementors/json_reference/#lifecycle-scripts). + + Now we will use the `customizations` property to add the VS Code extensions we will want to be installed in the container. + 8. Add the following setting to `devcontainer.json`: ``` // Use 'postCreateCommand' to run commands after the container is created. @@ -360,9 +367,10 @@ This will create a `.devcontainer` folder in your workspace, with a `devcontaine 10. Select the "Reopen in Container" button from the notification that will show up on the bottom right corner, or run the **Dev Containers: Reopen in Container** command from the Command Palette. - You can learn more about dev containers configuration in the [documentation](https://code.visualstudio.com/docs/devcontainers/containers#_create-a-devcontainerjson-file). > Note: it may take several minutes to build the container, depending on internet speed and machine performance. + You can learn more about dev containers configuration in the [documentation](https://code.visualstudio.com/docs/devcontainers/containers#_create-a-devcontainerjson-file). + Once it's done, you will have a fully configured Linux-based workspace with Python 3 and Redis Server installed. Once the container is set up, you will notice an indicator on the bottom left corner of VS Code: