From 39667714a1523c8f92de26831f17b1cc2e7f1324 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Wed, 22 Jul 2026 00:40:28 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9D=20Recommend=20uv=20projects=20by?= =?UTF-8?q?=20default=20in=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 27 +- docs/environment-variables.md | 297 +------ docs/index.md | 27 +- docs/tutorial/app-dir.md | 2 +- docs/tutorial/arguments/default.md | 16 +- docs/tutorial/arguments/envvar.md | 35 +- docs/tutorial/arguments/help.md | 16 +- docs/tutorial/arguments/optional.md | 8 +- docs/tutorial/commands/arguments.md | 6 +- docs/tutorial/commands/callback.md | 16 +- docs/tutorial/commands/context.md | 14 +- docs/tutorial/commands/help.md | 34 +- docs/tutorial/commands/index.md | 10 +- docs/tutorial/commands/name.md | 4 +- docs/tutorial/commands/one-or-multiple.md | 20 +- docs/tutorial/commands/options.md | 14 +- docs/tutorial/exceptions.md | 12 +- docs/tutorial/first-steps.md | 44 +- docs/tutorial/index.md | 2 +- docs/tutorial/install.md | 91 +- docs/tutorial/launch.md | 4 +- .../arguments-with-multiple-values.md | 10 +- .../multiple-values/multiple-options.md | 12 +- .../options-with-multiple-values.md | 8 +- docs/tutorial/one-file-per-command.md | 4 +- docs/tutorial/options/callback-and-context.md | 6 +- docs/tutorial/options/help.md | 12 +- docs/tutorial/options/name.md | 24 +- docs/tutorial/options/password.md | 4 +- docs/tutorial/options/prompt.md | 8 +- docs/tutorial/options/required.md | 6 +- docs/tutorial/options/version.md | 10 +- docs/tutorial/package.md | 43 +- docs/tutorial/parameter-types/bool.md | 30 +- docs/tutorial/parameter-types/datetime.md | 10 +- docs/tutorial/parameter-types/enum.md | 26 +- docs/tutorial/parameter-types/file.md | 14 +- docs/tutorial/parameter-types/index.md | 6 +- docs/tutorial/parameter-types/number.md | 28 +- docs/tutorial/parameter-types/path.md | 14 +- docs/tutorial/parameter-types/uuid.md | 4 +- docs/tutorial/printing.md | 10 +- docs/tutorial/progressbar.md | 10 +- docs/tutorial/prompt.md | 12 +- docs/tutorial/subcommands/add-typer.md | 16 +- .../tutorial/subcommands/callback-override.md | 8 +- docs/tutorial/subcommands/name-and-help.md | 32 +- .../subcommands/nested-subcommands.md | 36 +- docs/tutorial/subcommands/single-file.md | 16 +- docs/tutorial/terminating.md | 12 +- docs/tutorial/testing.md | 6 +- docs/tutorial/typer-app.md | 8 +- docs/tutorial/typer-command.md | 53 +- docs/virtual-environments.md | 831 +----------------- mkdocs.yml | 2 - 55 files changed, 514 insertions(+), 1516 deletions(-) diff --git a/README.md b/README.md index 1edc910146..f2e09384e1 100644 --- a/README.md +++ b/README.md @@ -43,18 +43,21 @@ The key features are: ## Installation -Create and activate a [virtual environment](https://typer.tiangolo.com/virtual-environments/) and then install **Typer**: +First, [install `uv`](https://docs.astral.sh/uv/getting-started/installation/), and then add **Typer** to your project:
```console -$ pip install typer +$ uv add typer ---> 100% -Successfully installed typer rich shellingham ```
+This installs both the Typer library and the `typer` command in the project's virtual environment. To use `typer` directly with shell completion, [activate the project environment and install completion](tutorial/install.md#activate-the-virtual-environment). + +If you prefer to use `pip`, install `typer` inside a virtual environment. See the [installation guide](tutorial/install.md) for the alternative steps. + ## Example ### The absolute minimum @@ -136,7 +139,7 @@ Now you could run it with Python directly: ```console // Run your application -$ python main.py +$ uv run python main.py // You get a nice error, you are missing 'name' Usage: main.py [OPTIONS] {name} @@ -147,7 +150,7 @@ Try 'main.py --help' for help. // You get a --help for free -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] {name} @@ -159,7 +162,7 @@ Usage: main.py [OPTIONS] {name} ╰───────────────────────────────────────────────────╯ // Now pass the 'name' argument -$ python main.py Camila +$ uv run python main.py Camila Hello Camila @@ -219,7 +222,7 @@ Check the new help:
```console -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] COMMAND [ARGS]... @@ -252,7 +255,7 @@ Now check the help for the `hello` command:
```console -$ python main.py hello --help +$ uv run python main.py hello --help Usage: main.py hello [OPTIONS] {name} @@ -271,7 +274,7 @@ And now check the help for the `goodbye` command:
```console -$ python main.py goodbye --help +$ uv run python main.py goodbye --help Usage: main.py goodbye [OPTIONS] {name} @@ -296,19 +299,19 @@ Now you can try out the new command line application: ```console // Use it with the hello command -$ python main.py hello Camila +$ uv run python main.py hello Camila Hello Camila // And with the goodbye command -$ python main.py goodbye Camila +$ uv run python main.py goodbye Camila Bye Camila! // And with --formal -$ python main.py goodbye --formal Camila +$ uv run python main.py goodbye --formal Camila Goodbye Ms. Camila. Have a good day. ``` diff --git a/docs/environment-variables.md b/docs/environment-variables.md index f351b4aab7..e32e0fced5 100644 --- a/docs/environment-variables.md +++ b/docs/environment-variables.md @@ -1,298 +1,9 @@ # Environment Variables -Before we jump into **Typer** code, let's cover a bit some of the **basics** that we'll need to understand how to work with Python (and programming) in general. Let's check a bit about **environment variables**. +An **environment variable** (also known as an **env var**) is a value that lives outside of your Python code, in the operating system, and can be read by your application and other programs. -/// tip +Typer can use environment variables as alternative values for command-line parameters. You will learn how in [CLI Arguments with Environment Variables](tutorial/arguments/envvar.md). -If you already know what "environment variables" are and how to use them, feel free to skip this. +## Learn More { #learn-more } -/// - -An environment variable (also known as "**env var**") is a variable that lives **outside** of the Python code, in the **operating system**, and could be read by your Python code (or by other programs as well). - -Environment variables could be useful for handling application **settings**, as part of the **installation** of Python, etc. - -## Create and Use Env Vars - -You can **create** and use environment variables in the **shell (terminal)**, without needing Python: - -//// tab | Linux, macOS, Windows Bash - -
- -```console -// You could create an env var MY_NAME with -$ export MY_NAME="Wade Wilson" - -// Then you could use it with other programs, like -$ echo "Hello $MY_NAME" - -Hello Wade Wilson -``` - -
- -//// - -//// tab | Windows PowerShell - -
- -```console -// Create an env var MY_NAME -$ $Env:MY_NAME = "Wade Wilson" - -// Use it with other programs, like -$ echo "Hello $Env:MY_NAME" - -Hello Wade Wilson -``` - -
- -//// - -## Read env vars in Python - -You could also create environment variables **outside** of Python, in the terminal (or with any other method), and then **read them in Python**. - -For example you could have a file `main.py` with: - -```Python hl_lines="3" -import os - -name = os.getenv("MY_NAME", "World") -print(f"Hello {name} from Python") -``` - -/// tip - -The second argument to [`os.getenv()`](https://docs.python.org/3.8/library/os.html#os.getenv) is the default value to return. - -If not provided, it's `None` by default, here we provide `"World"` as the default value to use. - -/// - -Then you could call that Python program: - -//// tab | Linux, macOS, Windows Bash - -
- -```console -// Here we don't set the env var yet -$ python main.py - -// As we didn't set the env var, we get the default value - -Hello World from Python - -// But if we create an environment variable first -$ export MY_NAME="Wade Wilson" - -// And then call the program again -$ python main.py - -// Now it can read the environment variable - -Hello Wade Wilson from Python -``` - -
- -//// - -//// tab | Windows PowerShell - -
- -```console -// Here we don't set the env var yet -$ python main.py - -// As we didn't set the env var, we get the default value - -Hello World from Python - -// But if we create an environment variable first -$ $Env:MY_NAME = "Wade Wilson" - -// And then call the program again -$ python main.py - -// Now it can read the environment variable - -Hello Wade Wilson from Python -``` - -
- -//// - -As environment variables can be set outside of the code, but can be read by the code, and don't have to be stored (committed to `git`) with the rest of the files, it's common to use them for configurations or **settings**. - -You can also create an environment variable only for a **specific program invocation**, that is only available to that program, and only for its duration. - -To do that, create it right before the program itself, on the same line: - -
- -```console -// Create an env var MY_NAME in line for this program call -$ MY_NAME="Wade Wilson" python main.py - -// Now it can read the environment variable - -Hello Wade Wilson from Python - -// The env var no longer exists afterwards -$ python main.py - -Hello World from Python -``` - -
- -/// tip - -You can read more about it at [The Twelve-Factor App: Config](https://12factor.net/config). - -/// - -## Types and Validation - -These environment variables can only handle **text strings**, as they are external to Python and have to be compatible with other programs and the rest of the system (and even with different operating systems, as Linux, Windows, macOS). - -That means that **any value** read in Python from an environment variable **will be a `str`**, and any conversion to a different type or any validation has to be done in code. - -You will learn more about using environment variables for your CLI applications later in the section about [CLI Arguments with Environment Variables](./tutorial/arguments/envvar.md). - -## `PATH` Environment Variable - -There is a **special** environment variable called **`PATH`** that is used by the operating systems (Linux, macOS, Windows) to find programs to run. - -The value of the variable `PATH` is a long string that is made of directories separated by a colon `:` on Linux and macOS, and by a semicolon `;` on Windows. - -For example, the `PATH` environment variable could look like this: - -//// tab | Linux, macOS - -```plaintext -/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin -``` - -This means that the system should look for programs in the directories: - -* `/usr/local/bin` -* `/usr/bin` -* `/bin` -* `/usr/sbin` -* `/sbin` - -//// - -//// tab | Windows - -```plaintext -C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32 -``` - -This means that the system should look for programs in the directories: - -* `C:\Program Files\Python312\Scripts` -* `C:\Program Files\Python312` -* `C:\Windows\System32` - -//// - -When you type a **command** in the terminal, the operating system **looks for** the program in **each of those directories** listed in the `PATH` environment variable. - -For example, when you type `python` in the terminal, the operating system looks for a program called `python` in the **first directory** in that list. - -If it finds it, then it will **use it**. Otherwise it keeps looking in the **other directories**. - -### Installing Python and Updating the `PATH` - -When you install Python, you might be asked if you want to update the `PATH` environment variable. - -//// tab | Linux, macOS - -Let's say you install Python and it ends up in a directory `/opt/custompython/bin`. - -If you say yes to update the `PATH` environment variable, then the installer will add `/opt/custompython/bin` to the `PATH` environment variable. - -It could look like this: - -```plaintext -/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/custompython/bin -``` - -//// - -//// tab | Windows - -Let's say you install Python and it ends up in a directory `C:\opt\custompython\bin`. - -If you say yes to update the `PATH` environment variable, then the installer will add `C:\opt\custompython\bin` to the `PATH` environment variable. - -```plaintext -C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32;C:\opt\custompython\bin -``` - -//// - -So, if you type: - -
- -```console -$ python -``` - -
- -//// tab | Linux, macOS - -The system will **find** the `python` program in `/opt/custompython/bin` (the last directory) and run it. - -It would be roughly equivalent to typing: - -
- -```console -$ /opt/custompython/bin/python -``` - -
- -//// - -//// tab | Windows - -The system will **find** the `python` program in `C:\opt\custompython\bin\python` (the last directory) and run it. - -It would be roughly equivalent to typing: - -
- -```console -$ C:\opt\custompython\bin\python -``` - -
- -//// - -This information will be useful when learning about [Virtual Environments](virtual-environments.md). - -It will also be useful when you **create your own CLI programs** as, for them to be available for your users, they will need to be somewhere in the `PATH` environment variable. - -## Conclusion - -With this you should have a basic understanding of what **environment variables** are and how to use them in Python. - -You can also read more about them in the [Wikipedia for Environment Variable](https://en.wikipedia.org/wiki/Environment_variable). - -In many cases it's not very obvious how environment variables would be useful and applicable right away. But they keep showing up in many different scenarios when you are developing, so it's good to know about them. - -For example, you will need this information in the next section, about [Virtual Environments](virtual-environments.md). +Read the [Environment Variables guide](https://tiangolo.com/guides/environment-variables/) for a detailed, cross-platform explanation, including how to create and read environment variables and how the `PATH` environment variable works. diff --git a/docs/index.md b/docs/index.md index 874cc6df87..196d94db6f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -51,18 +51,21 @@ The key features are: ## Installation -Create and activate a [virtual environment](https://typer.tiangolo.com/virtual-environments/) and then install **Typer**: +First, [install `uv`](https://docs.astral.sh/uv/getting-started/installation/), and then add **Typer** to your project:
```console -$ pip install typer +$ uv add typer ---> 100% -Successfully installed typer rich shellingham ```
+This installs both the Typer library and the `typer` command in the project's virtual environment. To use `typer` directly with shell completion, [activate the project environment and install completion](tutorial/install.md#activate-the-virtual-environment). + +If you prefer to use `pip`, install `typer` inside a virtual environment. See the [installation guide](tutorial/install.md) for the alternative steps. + ## Example ### The absolute minimum @@ -144,7 +147,7 @@ Now you could run it with Python directly: ```console // Run your application -$ python main.py +$ uv run python main.py // You get a nice error, you are missing 'name' Usage: main.py [OPTIONS] {name} @@ -155,7 +158,7 @@ Try 'main.py --help' for help. // You get a --help for free -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] {name} @@ -167,7 +170,7 @@ Usage: main.py [OPTIONS] {name} ╰───────────────────────────────────────────────────╯ // Now pass the 'name' argument -$ python main.py Camila +$ uv run python main.py Camila Hello Camila @@ -227,7 +230,7 @@ Check the new help:
```console -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] COMMAND [ARGS]... @@ -260,7 +263,7 @@ Now check the help for the `hello` command:
```console -$ python main.py hello --help +$ uv run python main.py hello --help Usage: main.py hello [OPTIONS] {name} @@ -279,7 +282,7 @@ And now check the help for the `goodbye` command:
```console -$ python main.py goodbye --help +$ uv run python main.py goodbye --help Usage: main.py goodbye [OPTIONS] {name} @@ -304,19 +307,19 @@ Now you can try out the new command line application: ```console // Use it with the hello command -$ python main.py hello Camila +$ uv run python main.py hello Camila Hello Camila // And with the goodbye command -$ python main.py goodbye Camila +$ uv run python main.py goodbye Camila Bye Camila! // And with --formal -$ python main.py goodbye --formal Camila +$ uv run python main.py goodbye --formal Camila Goodbye Ms. Camila. Have a good day. ``` diff --git a/docs/tutorial/app-dir.md b/docs/tutorial/app-dir.md index e79fcc9587..1fa6caad27 100644 --- a/docs/tutorial/app-dir.md +++ b/docs/tutorial/app-dir.md @@ -11,7 +11,7 @@ Check it:
```console -$ python main.py +$ uv run python main.py Config file doesn't exist yet ``` diff --git a/docs/tutorial/arguments/default.md b/docs/tutorial/arguments/default.md index 248891126b..abaac61c37 100644 --- a/docs/tutorial/arguments/default.md +++ b/docs/tutorial/arguments/default.md @@ -24,7 +24,7 @@ Check it: ```console // Check the help -$ python main.py --help +$ uv run python main.py --help // Notice the [default: Wade Wilson] ✨ Usage: main.py [OPTIONS] [name] @@ -36,12 +36,12 @@ Options: --help Show this message and exit. // With no optional CLI argument -$ python main.py +$ uv run python main.py Hello Wade Wilson // With one CLI argument -$ python main.py Camila +$ uv run python main.py Camila Hello Camila ``` @@ -70,7 +70,7 @@ Check it: ```console // Check the help -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] [name] @@ -81,20 +81,20 @@ Options: --help Show this message and exit. // Try it several times, it will use a random default each time -$ python main.py +$ uv run python main.py Hello Deadpool -$ python main.py +$ uv run python main.py Hello Hiro -$ python main.py +$ uv run python main.py Hello Rick // Now pass a value for the CLI argument -$ python main.py Camila +$ uv run python main.py Camila Hello Camila ``` diff --git a/docs/tutorial/arguments/envvar.md b/docs/tutorial/arguments/envvar.md index 126f160ac6..047ea6a21c 100644 --- a/docs/tutorial/arguments/envvar.md +++ b/docs/tutorial/arguments/envvar.md @@ -2,9 +2,11 @@ You can also configure a *CLI argument* to read a value from an environment variable if it is not provided in the command line as a *CLI argument*. +An **environment variable** (also known as an **env var**) is a value that lives outside of your Python code, in the operating system, and can be read by your application and other programs. + /// tip -You can learn more about environment variables in the [Environment Variables](../../environment-variables.md) page. +Read the [Environment Variables guide](https://tiangolo.com/guides/environment-variables/) for a detailed, cross-platform explanation. /// @@ -18,7 +20,7 @@ In this case, the *CLI argument* `name` will have a default value of `"World"`, ```console // Check the help -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] [name] @@ -29,28 +31,39 @@ Options: --help Show this message and exit. // Call it without a CLI argument -$ python main.py +$ uv run python main.py Hello Mr. World // Now pass a value for the CLI argument -$ python main.py Czernobog +$ uv run python main.py Czernobog Hello Mr. Czernobog // And now use the environment variable -$ AWESOME_NAME=Wednesday python main.py +$ AWESOME_NAME=Wednesday uv run python main.py Hello Mr. Wednesday // CLI arguments take precedence over env vars -$ AWESOME_NAME=Wednesday python main.py Czernobog +$ AWESOME_NAME=Wednesday uv run python main.py Czernobog Hello Mr. Czernobog ```
+/// tip | Windows PowerShell + +In PowerShell, set the environment variable first and then run the command: + +```console +$ $Env:AWESOME_NAME = "Wednesday" +$ uv run python main.py +``` + +/// + ## Multiple environment variables You are not restricted to a single environment variable, you can declare a list of environment variables that could be used to get a value if it was not passed in the command line: @@ -63,7 +76,7 @@ Check it: ```console // Check the help -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] [name] @@ -74,12 +87,12 @@ Options: --help Show this message and exit. // Try the first env var -$ AWESOME_NAME=Wednesday python main.py +$ AWESOME_NAME=Wednesday uv run python main.py Hello Mr. Wednesday // Try the second env var -$ GOD_NAME=Anubis python main.py +$ GOD_NAME=Anubis uv run python main.py Hello Mr. Anubis ``` @@ -98,7 +111,7 @@ Check it: ```console //Check the help -$ python main.py --help +$ uv run python main.py --help // It won't show the env var Usage: main.py [OPTIONS] [name] @@ -110,7 +123,7 @@ Options: --help Show this message and exit. // But it will still be able to use it -$ AWESOME_NAME=Wednesday python main.py +$ AWESOME_NAME=Wednesday uv run python main.py Hello Mr. Wednesday ``` diff --git a/docs/tutorial/arguments/help.md b/docs/tutorial/arguments/help.md index 7f8d0e62bf..4a86fecb97 100644 --- a/docs/tutorial/arguments/help.md +++ b/docs/tutorial/arguments/help.md @@ -19,7 +19,7 @@ And it will be used in the automatic `--help` option:
```console -$ python main.py --help +$ uv run python main.py --help // Check the section with Arguments below 🚀 Usage: main.py [OPTIONS] {name} @@ -44,7 +44,7 @@ And the `--help` option will combine all the information:
```console -$ python main.py --help +$ uv run python main.py --help // Notice that we have the help text from the docstring and also the Arguments 📝 Usage: main.py [OPTIONS] {name} @@ -71,7 +71,7 @@ It will show that default value in the help text:
```console -$ python main.py --help +$ uv run python main.py --help // Notice the [default: World] 🔍 Usage: main.py [OPTIONS] [name] @@ -96,7 +96,7 @@ And then it won't show the default value:
```console -$ python main.py --help +$ uv run python main.py --help // Notice the there's no [default: World] now 🔥 Usage: main.py [OPTIONS] [name] @@ -123,7 +123,7 @@ And it will be used in the help text:
```console -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] [name] @@ -168,7 +168,7 @@ Now the generated help text will have `✨username✨` instead of `name`:
```console -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] [✨username✨] @@ -196,7 +196,7 @@ And next you will see other panels for the *CLI arguments* that have a custom pa
```console -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] {name} [lastname] [age] @@ -237,7 +237,7 @@ Check it:
```console -$ python main.py --help +$ uv run python main.py --help // Notice there's no Arguments section at all 🔥 Usage: main.py [OPTIONS] [name] diff --git a/docs/tutorial/arguments/optional.md b/docs/tutorial/arguments/optional.md index cda10c46e9..6d2d34714b 100644 --- a/docs/tutorial/arguments/optional.md +++ b/docs/tutorial/arguments/optional.md @@ -82,7 +82,7 @@ All we did there achieves the same thing as before, a **required** *CLI argument
```console -$ python main.py +$ uv run python main.py Usage: main.py [OPTIONS] {name} Try "main.py --help" for help. @@ -130,7 +130,7 @@ Check the help: ```console // First check the help -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] [name] @@ -157,12 +157,12 @@ Now run it and test it: ```console // With no CLI argument -$ python main.py +$ uv run python main.py Hello World! // With one optional CLI argument -$ python main.py Camila +$ uv run python main.py Camila Hello Camila ``` diff --git a/docs/tutorial/commands/arguments.md b/docs/tutorial/commands/arguments.md index d6bbb61824..34badfdabf 100644 --- a/docs/tutorial/commands/arguments.md +++ b/docs/tutorial/commands/arguments.md @@ -8,7 +8,7 @@ The same way as with a CLI application with a single command, subcommands (or ju ```console // Check the help for create -$ python main.py create --help +$ uv run python main.py create --help Usage: main.py create [OPTIONS] {username} @@ -19,12 +19,12 @@ Options: --help Show this message and exit. // Call it with a CLI argument -$ python main.py create Camila +$ uv run python main.py create Camila Creating user: Camila // The same for delete -$ python main.py delete Camila +$ uv run python main.py delete Camila Deleting user: Camila ``` diff --git a/docs/tutorial/commands/callback.md b/docs/tutorial/commands/callback.md index 2d3488de5e..8b42ec7b8e 100644 --- a/docs/tutorial/commands/callback.md +++ b/docs/tutorial/commands/callback.md @@ -32,7 +32,7 @@ Check it: ```console // Check the help -$ python main.py --help +$ uv run python main.py --help // Notice the main help text, extracted from the callback function: "Manage users in the awesome CLI app." Usage: main.py [OPTIONS] COMMAND [ARGS]... @@ -52,12 +52,12 @@ Commands: // Check the new top level CLI option --verbose // Try it normally -$ python main.py create Camila +$ uv run python main.py create Camila Creating user: Camila // And now with --verbose -$ python main.py --verbose create Camila +$ uv run python main.py --verbose create Camila Will write verbose output About to create a user @@ -65,7 +65,7 @@ Creating user: Camila Just created a user // Notice that --verbose belongs to the callback, it has to go before create or delete ⛔️ -$ python main.py create --verbose Camila +$ uv run python main.py create --verbose Camila Usage: main.py create [OPTIONS] {username} Try "main.py create --help" for help. @@ -88,7 +88,7 @@ Check it:
```console -$ python main.py create Camila +$ uv run python main.py create Camila Running a command Creating user: Camila @@ -109,7 +109,7 @@ Check it:
```console -$ python main.py create Camila +$ uv run python main.py create Camila // Notice that the message is the one from new_callback() Override callback, running a command @@ -133,7 +133,7 @@ Check it:
```console -$ python main.py --help +$ uv run python main.py --help // Notice all the help text extracted from the callback docstring Usage: main.py [OPTIONS] COMMAND [ARGS]... @@ -153,7 +153,7 @@ Commands: create // And it just works as normally -$ python main.py create Camila +$ uv run python main.py create Camila Creating user: Camila ``` diff --git a/docs/tutorial/commands/context.md b/docs/tutorial/commands/context.md index 190a2b08c2..8ae454d5f3 100644 --- a/docs/tutorial/commands/context.md +++ b/docs/tutorial/commands/context.md @@ -21,13 +21,13 @@ Check it:
```console -$ python main.py create Camila +$ uv run python main.py create Camila // We get the message from the callback About to execute command: create Creating user: Camila -$ python main.py delete Camila +$ uv run python main.py delete Camila // We get the message from the callback, this time with delete About to execute command: delete @@ -51,13 +51,13 @@ Check it:
```console -$ python main.py +$ uv run python main.py // The callback is executed, we don't get the default help message Initializing database // Try with a command -$ python main.py create Camila +$ uv run python main.py create Camila // The callback is still executed Initializing database @@ -81,13 +81,13 @@ Check it:
```console -$ python main.py +$ uv run python main.py // The callback is executed Initializing database // Check it with a subcommand -$ python main.py create Camila +$ uv run python main.py create Camila // This time the callback is not executed Creating user: Camila @@ -108,7 +108,7 @@ Then you can access those extra raw *CLI parameters* as a `list` of `str` in `ct
```console -$ python main.py --name Camila --city Berlin +$ uv run python main.py --name Camila --city Berlin Got extra arg: --name Got extra arg: Camila diff --git a/docs/tutorial/commands/help.md b/docs/tutorial/commands/help.md index 908ce51c70..7a0d34b025 100644 --- a/docs/tutorial/commands/help.md +++ b/docs/tutorial/commands/help.md @@ -12,7 +12,7 @@ Check it: ```console // Check the new help -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] COMMAND [ARGS]... @@ -32,7 +32,7 @@ Commands: // Now the commands have inline help 🎉 // Check the help for create -$ python main.py create --help +$ uv run python main.py create --help Usage: main.py create [OPTIONS] {username} @@ -45,7 +45,7 @@ Options: --help Show this message and exit. // Check the help for delete -$ python main.py delete --help +$ uv run python main.py delete --help Usage: main.py delete [OPTIONS] {username} @@ -61,7 +61,7 @@ Options: --help Show this message and exit. // Check the help for delete-all -$ python main.py delete-all --help +$ uv run python main.py delete-all --help Usage: main.py delete-all [OPTIONS] @@ -74,7 +74,7 @@ Options: --help Show this message and exit. // Check the help for init -$ python main.py init --help +$ uv run python main.py init --help Usage: main.py init [OPTIONS] @@ -106,7 +106,7 @@ Check it: ```console // Check the help -$ python main.py --help +$ uv run python main.py --help // Notice it uses the help passed to @app.command() Usage: main.py [OPTIONS] COMMAND [ARGS]... @@ -139,7 +139,7 @@ And when you show the `--help` option you will see it's marked as "`deprecated`"
```console -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] COMMAND [ARGS]... @@ -164,7 +164,7 @@ And if you check the `--help` for the deprecated command (in this example, the c
```console -$ python main.py delete --help +$ uv run python main.py delete --help Usage: main.py delete [OPTIONS] {username} @@ -193,7 +193,7 @@ If a user mistypes a command, they'll see a helpful suggestion:
```console -$ python main.py crate +$ uv run python main.py crate Usage: main.py [OPTIONS] COMMAND [ARGS]... Try 'main.py --help' for help. @@ -236,7 +236,7 @@ Check the help for the `create` command:
```console -$ python main.py create --help +$ uv run python main.py create --help Usage: main.py create [OPTIONS] {username} @@ -259,7 +259,7 @@ And check the help for the `delete` command:
```console -$ python main.py delete --help +$ uv run python main.py delete --help Usage: main.py delete [OPTIONS] {username} @@ -293,7 +293,7 @@ Check the help for the `create` command:
```console -$ python main.py create --help +$ uv run python main.py create --help Usage: main.py create [OPTIONS] {username} @@ -321,7 +321,7 @@ And the same for the `delete` command:
```console -$ python main.py delete --help +$ uv run python main.py delete --help Usage: main.py delete [OPTIONS] {username} @@ -363,7 +363,7 @@ Commands without a panel will be shown in the default panel `Commands`, and the
```console -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] COMMAND [ARGS]... @@ -411,7 +411,7 @@ You can check the `--help` option for the command `create`:
```console -$ python main.py create --help +$ uv run python main.py create --help Usage: main.py create [OPTIONS] {username} [lastname] @@ -445,7 +445,7 @@ And those panels will be shown when you use the main `--help` option.
```console -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] COMMAND [ARGS]... @@ -480,7 +480,7 @@ And when you check the `--help` option it will look like:
```console -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] {username} diff --git a/docs/tutorial/commands/index.md b/docs/tutorial/commands/index.md index 0ba1d8882a..f4037a1fd6 100644 --- a/docs/tutorial/commands/index.md +++ b/docs/tutorial/commands/index.md @@ -66,7 +66,7 @@ Now we have a CLI application with 2 commands, `create` and `delete`: ```console // Check the help -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] COMMAND [ARGS]... @@ -80,11 +80,11 @@ Commands: delete // Test them -$ python main.py create +$ uv run python main.py create Creating user: Hiro Hamada -$ python main.py delete +$ uv run python main.py delete Deleting user: Hiro Hamada @@ -115,7 +115,7 @@ Now we can run this: ```console // Check the help without having to type --help -$ python main.py +$ uv run python main.py Usage: main.py [OPTIONS] COMMAND [ARGS]... @@ -146,7 +146,7 @@ Then we will see the `delete` command first in the help output: ```console // Check the help -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] COMMAND [ARGS]... diff --git a/docs/tutorial/commands/name.md b/docs/tutorial/commands/name.md index 13b25ba7a3..ac2bd4d35e 100644 --- a/docs/tutorial/commands/name.md +++ b/docs/tutorial/commands/name.md @@ -24,7 +24,7 @@ Now, even though the functions are named `cli_create_user()` and `cli_delete_use
```console -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] COMMAND [ARGS]... @@ -38,7 +38,7 @@ Commands: delete // Test it -$ python main.py create Camila +$ uv run python main.py create Camila Creating user: Camila ``` diff --git a/docs/tutorial/commands/one-or-multiple.md b/docs/tutorial/commands/one-or-multiple.md index 976ede4d96..c763c34a08 100644 --- a/docs/tutorial/commands/one-or-multiple.md +++ b/docs/tutorial/commands/one-or-multiple.md @@ -10,7 +10,7 @@ You might have noticed that if you create a single command, as in the following ```console // Without a CLI argument -$ python main.py +$ uv run python main.py Usage: main.py [OPTIONS] {name} Try "main.py --help" for help. @@ -18,12 +18,12 @@ Try "main.py --help" for help. Error: Missing argument 'name'. // With the 'name' CLI argument -$ python main.py Camila +$ uv run python main.py Camila Hello Camila // Asking for help -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] {name} @@ -51,7 +51,7 @@ Here we have 2 commands `create` and `delete`: ```console // Check the help -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] COMMAND [ARGS]... @@ -65,11 +65,11 @@ Commands: delete // Test the commands -$ python main.py create +$ uv run python main.py create Creating user: Hiro Hamada -$ python main.py delete +$ uv run python main.py delete Deleting user: Hiro Hamada ``` @@ -90,7 +90,7 @@ Check it: ```console // Check the help -$ python main.py --help +$ uv run python main.py --help // Notice the single command create Usage: main.py [OPTIONS] COMMAND [ARGS]... @@ -104,7 +104,7 @@ Commands: create // Try it -$ python main.py create +$ uv run python main.py create Creating user: Hiro Hamada ``` @@ -122,7 +122,7 @@ And now the docstring from the callback will be used as the help text:
```console -$ python main.py --help +$ uv run python main.py --help // Notice the help text from the docstring Usage: main.py [OPTIONS] COMMAND [ARGS]... @@ -140,7 +140,7 @@ Commands: create // And it still works the same, the callback does nothing -$ python main.py create +$ uv run python main.py create Creating user: Hiro Hamada ``` diff --git a/docs/tutorial/commands/options.md b/docs/tutorial/commands/options.md index dd70a2aef5..3d5acac84c 100644 --- a/docs/tutorial/commands/options.md +++ b/docs/tutorial/commands/options.md @@ -52,18 +52,18 @@ Test it: ```console // Check the command create -$ python main.py create Camila +$ uv run python main.py create Camila Creating user: Camila // Now test the command delete -$ python main.py delete Camila +$ uv run python main.py delete Camila # Are you sure you want to delete the user? [y/n]: $ y Deleting user: Camila -$ python main.py delete Wade +$ uv run python main.py delete Wade # Are you sure you want to delete the user? [y/n]: $ n @@ -72,13 +72,13 @@ Operation cancelled // And finally, the command delete-all // Notice it doesn't have CLI arguments, only a CLI option -$ python main.py delete-all +$ uv run python main.py delete-all # Are you sure you want to delete ALL users? [y/n]: $ y Deleting all users -$ python main.py delete-all +$ uv run python main.py delete-all # Are you sure you want to delete ALL users? [y/n]: $ n @@ -86,12 +86,12 @@ Operation cancelled // And if you pass the --force CLI option, it doesn't need to confirm -$ python main.py delete-all --force +$ uv run python main.py delete-all --force Deleting all users // And init that doesn't take any CLI parameter -$ python main.py init +$ uv run python main.py init Initializing user database ``` diff --git a/docs/tutorial/exceptions.md b/docs/tutorial/exceptions.md index 18c3374ff3..4cbe53c284 100644 --- a/docs/tutorial/exceptions.md +++ b/docs/tutorial/exceptions.md @@ -23,7 +23,7 @@ So, the error you see will be **much clearer** and simpler, to help you detect t
```console -$ python main.py +$ uv run python main.py ╭──────────────── Traceback (most recent call last) ────────────────╮ /home/user/code/superapp/main.py:8 in main @@ -50,7 +50,7 @@ In this case, Typer will still do some tricks to show you the information **as c
```console -$ python main.py +$ uv run python main.py Traceback (most recent call last): @@ -84,7 +84,7 @@ Now, when using Rich, you will see the error with the local variables:
```console -$ python main.py +$ uv run python main.py ╭──────────────── Traceback (most recent call last) ────────────────╮ /home/user/code/superapp/main.py:5 in main @@ -121,7 +121,7 @@ Now when you run it, you will see the whole output:
```console -$ python main.py +$ uv run python main.py ╭──────────────── Traceback (most recent call last) ────────────────╮ /home/user/code/superapp/main.py:12 in <module> @@ -203,7 +203,7 @@ And now you will see the full standard exception as with any other Python progra
```console -$ python main.py +$ uv run python main.py Traceback (most recent call last): File "main.py", line 12, in @@ -239,7 +239,7 @@ This will work for any other Typer program too, in case you need to debug a prob ```console export TYPER_STANDARD_TRACEBACK=1 -$ python main.py +$ uv run python main.py Traceback (most recent call last): diff --git a/docs/tutorial/first-steps.md b/docs/tutorial/first-steps.md index c7b088157b..c22453d6ac 100644 --- a/docs/tutorial/first-steps.md +++ b/docs/tutorial/first-steps.md @@ -13,14 +13,14 @@ Test it:
```console -$ python main.py +$ uv run python main.py Hello World // It just prints "Hello World". // Now check the --help -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] @@ -67,7 +67,7 @@ Update the previous example with an argument `name`: ```console -$ python main.py +$ uv run python main.py // If you run it without the argument, it shows a nice error Usage: main.py [OPTIONS] {name} @@ -77,14 +77,14 @@ $ python main.py ╰───────────────────────────────────────────────────╯ // Now pass that 'name' CLI argument -$ python main.py Camila +$ uv run python main.py Camila Hello Camila // Here "Camila" is the CLI argument // To pass a name with spaces for the same CLI argument, use quotes -$ python main.py "Camila Gutiérrez" +$ uv run python main.py "Camila Gutiérrez" Hello Camila Gutiérrez ``` @@ -109,7 +109,7 @@ So, extend that to have 2 arguments, `name` and `lastname`: ```console // Check the main --help -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] {name} {lastname} @@ -125,7 +125,7 @@ $ python main.py --help // There are now 2 CLI arguments, name and lastname // Now pass a single name argument -$ python main.py Camila +$ uv run python main.py Camila Usage: main.py [OPTIONS] {name} {lastname} Try 'main.py --help' for help. @@ -134,7 +134,7 @@ $ python main.py Camila ╰───────────────────────────────────────────────────╯ // These 2 arguments are required, so, pass both: -$ python main.py Camila Gutiérrez +$ uv run python main.py Camila Gutiérrez Hello Camila Gutiérrez ``` @@ -148,7 +148,7 @@ Notice that the order is important. The last name has to go after the first name If you called it with: ``` -$ python main.py Gutiérrez Camila +$ uv run python main.py Gutiérrez Camila ``` your app wouldn't have a way to know which is the `name` and which the `lastname`. It expects the first *CLI argument* to be the `name` and the second *CLI argument* to be the `lastname`. @@ -233,7 +233,7 @@ Here `formal` is a `bool` that is `False` by default. ```console // Get the help -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] {name} {lastname} @@ -261,21 +261,21 @@ Now call it normally:
```console -$ python main.py Camila Gutiérrez +$ uv run python main.py Camila Gutiérrez Hello Camila Gutiérrez // But if you pass --formal -$ python main.py Camila Gutiérrez --formal +$ uv run python main.py Camila Gutiérrez --formal Good day Ms. Camila Gutiérrez. // And as --formal is a CLI option you can put it anywhere in this command -$ python main.py Camila --formal Gutiérrez +$ uv run python main.py Camila --formal Gutiérrez Good day Ms. Camila Gutiérrez. -$ python main.py --formal Camila Gutiérrez +$ uv run python main.py --formal Camila Gutiérrez Good day Ms. Camila Gutiérrez. ``` @@ -293,7 +293,7 @@ As `lastname` now has a default value of `""` (an empty string) it is no longer
```console -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] {name} @@ -322,12 +322,12 @@ A *CLI option* with a value like `--lastname` (contrary to a *CLI option* withou ```console // Call it without a --lastname -$ python main.py Camila +$ uv run python main.py Camila Hello Camila // Pass the --lastname -$ python main.py Camila --lastname Gutiérrez +$ uv run python main.py Camila --lastname Gutiérrez Hello Camila Gutiérrez ``` @@ -345,7 +345,7 @@ And as `--lastname` is now a *CLI option* that doesn't depend on the order, you
```console -$ python main.py --lastname Gutiérrez Camila +$ uv run python main.py --lastname Gutiérrez Camila // and it will still work normally Hello Camila Gutiérrez @@ -364,7 +364,7 @@ Now see it with the `--help` option:
```console -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] {name} @@ -465,16 +465,16 @@ We will use ***CLI parameter*** to refer to both, *CLI arguments* and *CLI optio ## The `typer` Command -When you install `typer`, by default it adds a `typer` command to your shell. +When you [activate the project's virtual environment](install.md#activate-the-virtual-environment), the `typer` command installed in that project becomes available directly in your shell. -This `typer` command allows you to run your scripts with ✨ auto completion ✨ in your terminal. +After you install completion for it, this `typer` command allows you to run your scripts with ✨ auto completion ✨ in your terminal. As an alternative to running with Python:
```console -$ python main.py +$ uv run python main.py Hello World ``` diff --git a/docs/tutorial/index.md b/docs/tutorial/index.md index ab1a7a3381..0f70f29295 100644 --- a/docs/tutorial/index.md +++ b/docs/tutorial/index.md @@ -48,7 +48,7 @@ To run any of the examples, copy the code to a file `main.py`, and run it:
```console -$ python main.py +$ uv run python main.py ✨ The magic happens here ✨ ``` diff --git a/docs/tutorial/install.md b/docs/tutorial/install.md index 3658e84283..3540c9062f 100644 --- a/docs/tutorial/install.md +++ b/docs/tutorial/install.md @@ -1,17 +1,100 @@ # Install **Typer** -The first step is to install **Typer**. +The first step is to set up your project and add **Typer**. -First, make sure you create your [virtual environment](../virtual-environments.md), activate it, and then install it, for example with: +Install [`uv`](https://docs.astral.sh/uv/getting-started/installation/), then create a project and add Typer:
```console -$ pip install typer +$ uv init awesome-project --bare +$ cd awesome-project +$ uv add typer ---> 100% -Successfully installed typer shellingham rich ```
+`uv add` creates the project's virtual environment in `.venv`, adds Typer to `pyproject.toml`, and creates `uv.lock` so the same package versions can be installed later. + +/// details | What these commands do + +* `uv init`: create a new Python project. +* `awesome-project`: create the project in a new directory with this name. +* `--bare`: create only the minimal `pyproject.toml` file, without generating a sample `main.py`, `README.md`, or other files. You will create the application files yourself in the next steps of this tutorial. + +Then `cd awesome-project` enters the new project directory before adding Typer. + +`uv` will use a compatible Python version already installed on your system, or download one if needed. + +When you run `uv add`, it selects compatible versions of Typer and all the packages Typer depends on. It records the exact versions in `uv.lock`, making it possible to install the same package versions later on another computer or when distributing the application. + +Creating or updating this file is called [**locking** the project dependencies](https://docs.astral.sh/uv/concepts/projects/sync/). `uv` does this automatically when you add a package. + +/// + By default, `typer` comes with `rich` and `shellingham`. + +## Activate the Virtual Environment + +`uv add typer` installs both the Typer library and the `typer` command in the project's `.venv`. + +You can normally run project commands with `uv run`. But to configure and use shell completion for the first-class `typer` command, activate the project's virtual environment so that `typer` is available directly in your shell. + +//// tab | Linux, macOS + +
+ +```console +$ source .venv/bin/activate +``` + +
+ +//// + +//// tab | Windows PowerShell + +
+ +```console +$ .venv\Scripts\Activate.ps1 +``` + +
+ +//// + +//// tab | Windows Bash + +
+ +```console +$ source .venv/Scripts/activate +``` + +
+ +//// + +Enable completion for your current shell with: + +
+ +```console +$ typer --install-completion +``` + +
+ +Restart your terminal for the new completion configuration to take effect. + +Activate the project's virtual environment again in each new terminal session where you want to use the `typer` command directly. + +/// details | Using `pip` instead + +If you prefer to manage a virtual environment and packages manually, create and activate a virtual environment and then install Typer with `pip install typer`. + +Read the [Virtual Environments guide](https://tiangolo.com/guides/virtual-environments/) for the detailed steps. + +/// diff --git a/docs/tutorial/launch.md b/docs/tutorial/launch.md index 2f98d73905..b52f19ff9d 100644 --- a/docs/tutorial/launch.md +++ b/docs/tutorial/launch.md @@ -11,7 +11,7 @@ Check it:
```console -$ python main.py +$ uv run python main.py Opening Typer docs @@ -39,7 +39,7 @@ Check it:
```console -$ python main.py +$ uv run python main.py Opening config directory diff --git a/docs/tutorial/multiple-values/arguments-with-multiple-values.md b/docs/tutorial/multiple-values/arguments-with-multiple-values.md index 8a178f4d37..b1227a2228 100644 --- a/docs/tutorial/multiple-values/arguments-with-multiple-values.md +++ b/docs/tutorial/multiple-values/arguments-with-multiple-values.md @@ -11,7 +11,7 @@ And then you can pass it as many *CLI arguments* of that type as you want:
```console -$ python main.py ./index.md ./first-steps.md woohoo! +$ uv run python main.py ./index.md ./first-steps.md woohoo! This file exists: index.md woohoo! @@ -45,7 +45,7 @@ Check it: ```console // Check the help -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] [names]... @@ -56,19 +56,19 @@ Options: --help Show this message and exit. // Use it with its defaults -$ python main.py +$ uv run python main.py Hello Harry Hello Hermione Hello Ron // If you pass an invalid number of arguments you will get an error -$ python main.py Draco Hagrid +$ uv run python main.py Draco Hagrid Error: Argument 'names' takes 3 values // And if you pass the exact number of values it will work correctly -$ python main.py Draco Hagrid Dobby +$ uv run python main.py Draco Hagrid Dobby Hello Draco Hello Hagrid diff --git a/docs/tutorial/multiple-values/multiple-options.md b/docs/tutorial/multiple-values/multiple-options.md index abb198a9ee..d974d75172 100644 --- a/docs/tutorial/multiple-values/multiple-options.md +++ b/docs/tutorial/multiple-values/multiple-options.md @@ -16,18 +16,18 @@ Check it: ```console // The default value is 'None' -$ python main.py +$ uv run python main.py No provided users (raw input = None) Aborted! // Now pass a user -$ python main.py --user Camila +$ uv run python main.py --user Camila Processing user: Camila // And now try with several users -$ python main.py --user Camila --user Rick --user Morty +$ uv run python main.py --user Camila --user Rick --user Morty Processing user: Camila Processing user: Rick @@ -47,17 +47,17 @@ Check it:
```console -$ python main.py +$ uv run python main.py The sum is 0 // Try with some numbers -$ python main.py --number 2 +$ uv run python main.py --number 2 The sum is 2.0 // Try with some numbers -$ python main.py --number 2 --number 3 --number 4.5 +$ uv run python main.py --number 2 --number 3 --number 4.5 The sum is 9.5 ``` diff --git a/docs/tutorial/multiple-values/options-with-multiple-values.md b/docs/tutorial/multiple-values/options-with-multiple-values.md index 29753eac7c..688f25c9aa 100644 --- a/docs/tutorial/multiple-values/options-with-multiple-values.md +++ b/docs/tutorial/multiple-values/options-with-multiple-values.md @@ -56,7 +56,7 @@ Now let's see how this works in the terminal: ```console // check the help -$ python main.py --help +$ uv run python main.py --help // Notice the Usage: main.py [OPTIONS] @@ -66,18 +66,18 @@ Options: --help Show this message and exit. // Now try it -$ python main.py --user Camila 50 yes +$ uv run python main.py --user Camila 50 yes The username Camila has 50 coins And this user is a wizard! // With other values -$ python main.py --user Morty 3 no +$ uv run python main.py --user Morty 3 no The username Morty has 3 coins // Try with invalid values (not enough) -$ python main.py --user Camila 50 +$ uv run python main.py --user Camila 50 Error: Option '--user' requires 3 arguments ``` diff --git a/docs/tutorial/one-file-per-command.md b/docs/tutorial/one-file-per-command.md index d9ac0c9b51..d320a7d5b9 100644 --- a/docs/tutorial/one-file-per-command.md +++ b/docs/tutorial/one-file-per-command.md @@ -108,11 +108,11 @@ To run the application, you can execute it as a Python module:
```console -$ python -m mycli.main version +$ uv run python -m mycli.main version My CLI Version 1.0 -$ python -m mycli.main users add Camila +$ uv run python -m mycli.main users add Camila Adding user: Camila ``` diff --git a/docs/tutorial/options/callback-and-context.md b/docs/tutorial/options/callback-and-context.md index cdccd13e7c..7532522114 100644 --- a/docs/tutorial/options/callback-and-context.md +++ b/docs/tutorial/options/callback-and-context.md @@ -23,11 +23,11 @@ Check it:
```console -$ python main.py --name Camila +$ uv run python main.py --name Camila Hello Camila -$ python main.py --name Rick +$ uv run python main.py --name Rick Usage: main.py [OPTIONS] @@ -170,7 +170,7 @@ Check it:
```console -$ python main.py --name Camila +$ uv run python main.py --name Camila Validating param: name Hello Camila diff --git a/docs/tutorial/options/help.md b/docs/tutorial/options/help.md index 4104077964..6dc3e94cb5 100644 --- a/docs/tutorial/options/help.md +++ b/docs/tutorial/options/help.md @@ -29,7 +29,7 @@ Test it:
```console -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] {name} @@ -65,7 +65,7 @@ And below you will see other panels for the *CLI options* that have a custom pan
```console -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] {name} @@ -109,12 +109,12 @@ And it will no longer show the default value in the help text:
```console -$ python main.py +$ uv run python main.py Hello Wade Wilson // Show the help -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] @@ -138,12 +138,12 @@ And it will be used in the help text:
```console -$ python main.py +$ uv run python main.py Hello Wade Wilson // Show the help -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] diff --git a/docs/tutorial/options/name.md b/docs/tutorial/options/name.md index 031c3ec431..e523642180 100644 --- a/docs/tutorial/options/name.md +++ b/docs/tutorial/options/name.md @@ -47,7 +47,7 @@ Check it:
```console -$ python main.py --help +$ uv run python main.py --help // Notice the --name instead of --user-name Usage: main.py [OPTIONS] @@ -57,7 +57,7 @@ Options: --help Show this message and exit. // Try it -$ python main.py --name Camila +$ uv run python main.py --name Camila Hello Camila ``` @@ -195,7 +195,7 @@ Check it: ```console // Check the help -$ python main.py --help +$ uv run python main.py --help // Notice the two CLI option names -n and --name Usage: main.py [OPTIONS] @@ -205,7 +205,7 @@ Options: --help Show this message and exit. // Try the short version -$ python main.py -n Camila +$ uv run python main.py -n Camila Hello Camila ``` @@ -223,7 +223,7 @@ Check it:
```console -$ python main.py --help +$ uv run python main.py --help // Notice there's no --name nor --user-name, only -n Usage: main.py [OPTIONS] @@ -233,7 +233,7 @@ Options: --help Show this message and exit. // Try it -$ python main.py -n Camila +$ uv run python main.py -n Camila Hello Camila ``` @@ -251,7 +251,7 @@ Check it:
```console -$ python main.py --help +$ uv run python main.py --help // Notice that we have the long version --user-name back // and we also have the short version -n @@ -262,12 +262,12 @@ Options: --help Show this message and exit. // Try it -$ python main.py --user-name Camila +$ uv run python main.py --user-name Camila Hello Camila // And try the short version -$ python main.py -n Camila +$ uv run python main.py -n Camila ```
@@ -291,7 +291,7 @@ Check it:
```console -$ python main.py --help +$ uv run python main.py --help // We now have short versions -n and -f // And also long versions --name and --formal @@ -303,13 +303,13 @@ Options: --help Show this message and exit. // Try the short versions -$ python main.py -n Camila -f +$ uv run python main.py -n Camila -f Good day Ms. Camila. // And try the 2 short versions together // See how -n has to go last, to be able to get the value -$ python main.py -fn Camila +$ uv run python main.py -fn Camila Good day Ms. Camila. ``` diff --git a/docs/tutorial/options/password.md b/docs/tutorial/options/password.md index 9d74f4634e..1989b1f76d 100644 --- a/docs/tutorial/options/password.md +++ b/docs/tutorial/options/password.md @@ -9,7 +9,7 @@ And the CLI program will ask for confirmation:
```console -$ python main.py Camila +$ uv run python main.py Camila // It prompts for the email # Email: $ camila@example.com @@ -37,7 +37,7 @@ Check it:
```console -$ python main.py Camila +$ uv run python main.py Camila // It prompts for the password, but doesn't show anything when you type # Password: $ diff --git a/docs/tutorial/options/prompt.md b/docs/tutorial/options/prompt.md index 09156f4cb5..395229d1f0 100644 --- a/docs/tutorial/options/prompt.md +++ b/docs/tutorial/options/prompt.md @@ -10,7 +10,7 @@ And then your program will ask the user for it in the terminal: ```console // Call it with the 'name' CLI argument -$ python main.py Camila +$ uv run python main.py Camila // It asks for the missing CLI option --lastname # Lastname: $ Gutiérrez @@ -32,7 +32,7 @@ And then your program will ask for it using with your custom prompt: ```console // Call it with the 'name' CLI argument -$ python main.py Camila +$ uv run python main.py Camila // It uses the custom prompt # Please tell me your last name: $ Gutiérrez @@ -57,7 +57,7 @@ And it will prompt the user for a value and then for the confirmation:
```console -$ python main.py +$ uv run python main.py // Your app will first prompt for the project name, and then for the confirmation # Project name: $ Old Project @@ -66,7 +66,7 @@ $ python main.py Deleting project Old Project // If the user doesn't type the same, receives an error and a new prompt -$ python main.py +$ uv run python main.py # Project name: $ Old Project # Repeat for confirmation: $ New Spice diff --git a/docs/tutorial/options/required.md b/docs/tutorial/options/required.md index 24aa4c7df5..a3ca5f3736 100644 --- a/docs/tutorial/options/required.md +++ b/docs/tutorial/options/required.md @@ -43,7 +43,7 @@ And test it: ```console // Pass the 'name' CLI argument -$ python main.py Camila +$ uv run python main.py Camila // We didn't pass the now required --lastname CLI option Usage: main.py [OPTIONS] {name} @@ -52,12 +52,12 @@ Try "main.py --help" for help. Error: Missing option '--lastname'. // Now update it to pass the required --lastname CLI option -$ python main.py Camila --lastname Gutiérrez +$ uv run python main.py Camila --lastname Gutiérrez Hello Camila Gutiérrez // And if you check the help -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] {name} diff --git a/docs/tutorial/options/version.md b/docs/tutorial/options/version.md index 9b4303182d..6b4db75127 100644 --- a/docs/tutorial/options/version.md +++ b/docs/tutorial/options/version.md @@ -27,7 +27,7 @@ Check it:
```console -$ python main.py --help +$ uv run python main.py --help // We get a --version, and don't get an awkward --no-version 🎉 Usage: main.py [OPTIONS] @@ -39,12 +39,12 @@ Options: // We can call it normally -$ python main.py --name Camila +$ uv run python main.py --name Camila Hello Camila // And we can get the version -$ python main.py --version +$ uv run python main.py --version Awesome CLI Version: 0.1.0 @@ -64,7 +64,7 @@ Then our CLI program could not work as expected in some cases as it is *right no
```console -$ python main.py --name Rick --version +$ uv run python main.py --name Rick --version Only Camila is allowed Aborted! @@ -103,7 +103,7 @@ Check it:
```console -$ python main.py --name Rick --version +$ uv run python main.py --name Rick --version // Now we only get the version, and the name is not used Awesome CLI Version: 0.1.0 diff --git a/docs/tutorial/package.md b/docs/tutorial/package.md index ce87570fd6..b238ebfe02 100644 --- a/docs/tutorial/package.md +++ b/docs/tutorial/package.md @@ -298,29 +298,21 @@ The `.whl` is the wheel file. You can send that wheel file to anyone and they ca ## Test your wheel package -Now you can open another terminal and install that package from the file for your own user with: +Now you can open another terminal and install that package as an isolated command-line tool with:
```console -$ pip install --user /home/rick/rick-portal-gun/dist/rick_portal_gun-0.1.0-py3-none-any.whl +$ uv tool install /home/rick/rick-portal-gun/dist/rick_portal_gun-0.1.0-py3-none-any.whl ---> 100% ```
-/// warning - -The `--user` is important, that ensures you install it in your user's directory and not in the global system. - -If you installed it in the global system (e.g. with `sudo`) you could install a version of a library (e.g. a sub-dependency) that is incompatible with your system. - -/// - /// tip -Bonus points if you use [uvx](https://docs.astral.sh/uv/) to install it while keeping an isolated environment for your Python CLI programs 🚀 +`uv` installs the tool in its own isolated environment, avoiding conflicts with your projects and system Python. /// @@ -541,40 +533,17 @@ Now to see that we can install it from PyPI, open another terminal, and uninstal
```console -$ pip uninstall rick-portal-gun - -Found existing installation: rick-portal-gun 0.1.0 -Uninstalling rick-portal-gun-0.1.0: - Would remove: - /home/rick/.local/bin/rick-portal-gun - /home/rick/.local/lib/python3.10/site-packages/rick_portal_gun-0.1.0.dist-info/* - /home/rick/.local/lib/python3.10/site-packages/rick_portal_gun/* -# Proceed (Y/n)? $ Y - Successfully uninstalled rick-portal-gun-0.1.0 +$ uv tool uninstall rick-portal-gun ```
-And now install it again, but this time using just the name, so that `pip` pulls it from PyPI: +And now install it again, but this time using just the name, so that `uv` pulls it from PyPI:
```console -$ pip install --user rick-portal-gun - -// Notice that it says "Downloading" 🚀 -Collecting rick-portal-gun - Downloading rick_portal_gun-0.1.0-py3-none-any.whl.metadata (435 bytes) -Requirement already satisfied: typer<0.13.0,>=0.12.3 in ./.local/lib/python3.10/site-packages (from rick-portal-gun==0.1.0) (0.12.3) -Requirement already satisfied: typing-extensions>=3.7.4.3 in ./.local/lib/python3.10/site-packages (from typer<0.13.0,>=0.12.3->rick-portal-gun==0.1.0) (4.11.0) -Requirement already satisfied: shellingham>=1.3.0 in ./.local/lib/python3.10/site-packages (from typer<0.13.0,>=0.12.3->rick-portal-gun==0.1.0) (1.5.4) -Requirement already satisfied: rich>=10.11.0 in ./.local/lib/python3.10/site-packages (from typer<0.13.0,>=0.12.3->rick-portal-gun==0.1.0) (13.7.1) -Requirement already satisfied: pygments<3.0.0,>=2.13.0 in ./.local/lib/python3.10/site-packages (from rich>=10.11.0->typer<0.13.0,>=0.12.3->rick-portal-gun==0.1.0) (2.17.2) -Requirement already satisfied: markdown-it-py>=2.2.0 in ./.local/lib/python3.10/site-packages (from rich>=10.11.0->typer<0.13.0,>=0.12.3->rick-portal-gun==0.1.0) (3.0.0) -Requirement already satisfied: mdurl~=0.1 in ./.local/lib/python3.10/site-packages (from markdown-it-py>=2.2.0->rich>=10.11.0->typer<0.13.0,>=0.12.3->rick-portal-gun==0.1.0) (0.1.2) -Downloading rick_portal_gun-0.1.0-py3-none-any.whl (1.8 kB) -Installing collected packages: rick-portal-gun -Successfully installed rick-portal-gun-0.1.0 +$ uv tool install rick-portal-gun ```
diff --git a/docs/tutorial/parameter-types/bool.md b/docs/tutorial/parameter-types/bool.md index 072d83dbca..ec1bdd213d 100644 --- a/docs/tutorial/parameter-types/bool.md +++ b/docs/tutorial/parameter-types/bool.md @@ -18,7 +18,7 @@ Now there's only a `--force` *CLI option*: ```console // Check the help -$ python main.py --help +$ uv run python main.py --help // Notice there's only --force, we no longer have --no-force Usage: main.py [OPTIONS] @@ -28,17 +28,17 @@ Options: --help Show this message and exit. // Try it: -$ python main.py +$ uv run python main.py Not forcing // Now add --force -$ python main.py --force +$ uv run python main.py --force Forcing operation // And --no-force no longer exists ⛔️ -$ python main.py --no-force +$ uv run python main.py --no-force Usage: main.py [OPTIONS] Try "main.py --help" for help. @@ -66,7 +66,7 @@ Check it: ```console // Check the help -$ python main.py --help +$ uv run python main.py --help // Notice the --accept / --reject Usage: main.py [OPTIONS] @@ -76,17 +76,17 @@ Options: --help Show this message and exit. // Try it -$ python main.py +$ uv run python main.py I don't know what you want yet // Now pass --accept -$ python main.py --accept +$ uv run python main.py --accept Accepting! // And --reject -$ python main.py --reject +$ uv run python main.py --reject Rejecting! ``` @@ -107,7 +107,7 @@ Check it: ```console // Check the help -$ python main.py --help +$ uv run python main.py --help // Notice the -f, --force / -F, --no-force Usage: main.py [OPTIONS] @@ -117,12 +117,12 @@ Options: --help Show this message and exit. // Try with the short name -f -$ python main.py -f +$ uv run python main.py -f Forcing operation // Try with the short name -F -$ python main.py -F +$ uv run python main.py -F Not forcing ``` @@ -151,7 +151,7 @@ Check it: ```console // Check the help -$ python main.py --help +$ uv run python main.py --help // Notice the / -d, --demo Usage: main.py [OPTIONS] @@ -161,17 +161,17 @@ Options: --help Show this message and exit. // Try it -$ python main.py +$ uv run python main.py Running in production // Now pass --demo -$ python main.py --demo +$ uv run python main.py --demo Running demo // And the short version -$ python main.py -d +$ uv run python main.py -d Running demo ``` diff --git a/docs/tutorial/parameter-types/datetime.md b/docs/tutorial/parameter-types/datetime.md index 7b7756cc91..cb3d37bc5a 100644 --- a/docs/tutorial/parameter-types/datetime.md +++ b/docs/tutorial/parameter-types/datetime.md @@ -17,7 +17,7 @@ Check it:
```console -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] {birth}:<%Y-%m-%d|%Y-%m-%dT%H:%M:%S|%Y-%m-%d %H:%M:%S> @@ -28,13 +28,13 @@ Options: --help Show this message and exit. // Pass a datetime -$ python main.py 1956-01-31T10:00:00 +$ uv run python main.py 1956-01-31T10:00:00 Interesting day to be born: 1956-01-31 10:00:00 Birth hour: 10 // An invalid date -$ python main.py july-19-1989 +$ uv run python main.py july-19-1989 Usage: main.py [OPTIONS] {birth}:<%Y-%m-%d|%Y-%m-%dT%H:%M:%S|%Y-%m-%d %H:%M:%S> Try 'main.py --help' for help. @@ -73,12 +73,12 @@ Check it: ```console // ISO dates work -$ python main.py 1969-10-29 +$ uv run python main.py 1969-10-29 Launch will be at: 1969-10-29 00:00:00 // But the strange custom format also works -$ python main.py 10/29/1969 +$ uv run python main.py 10/29/1969 Launch will be at: 1969-10-29 00:00:00 ``` diff --git a/docs/tutorial/parameter-types/enum.md b/docs/tutorial/parameter-types/enum.md index bfa5d53aee..b386177fc1 100644 --- a/docs/tutorial/parameter-types/enum.md +++ b/docs/tutorial/parameter-types/enum.md @@ -17,7 +17,7 @@ Check it:
```console -$ python main.py --help +$ uv run python main.py --help // Notice the predefined values Usage: main.py [OPTIONS] @@ -27,12 +27,12 @@ Options: --help Show this message and exit. // Try it -$ python main.py --network conv +$ uv run python main.py --network conv Training neural network of type: conv // Invalid value -$ python main.py --network capsule +$ uv run python main.py --network capsule Usage: main.py [OPTIONS] Try "main.py --help" for help. @@ -40,7 +40,7 @@ Try "main.py --help" for help. Error: Invalid value for '--network': 'capsule' is not one of 'simple', 'conv', 'lstm'. // Note that enums are case sensitive by default -$ python main.py --network CONV +$ uv run python main.py --network CONV Usage: main.py [OPTIONS] Try "main.py --help" for help. @@ -62,12 +62,12 @@ And then the values of the `Enum` will be checked no matter if lower case, upper ```console // Notice the upper case CONV -$ python main.py --network CONV +$ uv run python main.py --network CONV Training neural network of type: conv // A mix also works -$ python main.py --network LsTm +$ uv run python main.py --network LsTm Training neural network of type: lstm ``` @@ -85,7 +85,7 @@ This works just like any other parameter value taking a list of things:
```console -$ python main.py --help +$ uv run python main.py --help // Notice the default values being shown Usage: main.py [OPTIONS] @@ -95,17 +95,17 @@ Options: --help Show this message and exit. // Try it with the default values -$ python main.py +$ uv run python main.py Buying groceries: Eggs, Cheese // Try it with a single value -$ python main.py --groceries "Eggs" +$ uv run python main.py --groceries "Eggs" Buying groceries: Eggs // Try it with multiple values -$ python main.py --groceries "Eggs" --groceries "Bacon" +$ uv run python main.py --groceries "Eggs" --groceries "Bacon" Buying groceries: Eggs, Bacon ``` @@ -121,7 +121,7 @@ You can also use `Literal` to represent a set of possible predefined choices, wi
```console -$ python main.py --help +$ uv run python main.py --help // Notice the predefined values Usage: main.py [OPTIONS] @@ -131,12 +131,12 @@ Options: --help Show this message and exit. // Try it -$ python main.py --network conv +$ uv run python main.py --network conv Training neural network of type: conv // Invalid value -$ python main.py --network capsule +$ uv run python main.py --network capsule Usage: main.py [OPTIONS] Try "main.py --help" for help. diff --git a/docs/tutorial/parameter-types/file.md b/docs/tutorial/parameter-types/file.md index 76a92749ad..2f5c7aae38 100644 --- a/docs/tutorial/parameter-types/file.md +++ b/docs/tutorial/parameter-types/file.md @@ -61,7 +61,7 @@ $ echo "some settings" > config.txt $ echo "some more settings" >> config.txt // Now run your program -$ python main.py --config config.txt +$ uv run python main.py --config config.txt Config line: some settings @@ -90,7 +90,7 @@ Check it:
```console -$ python main.py --config text.txt +$ uv run python main.py --config text.txt Config written @@ -125,7 +125,7 @@ Check it:
```console -$ python main.py --file lena.jpg +$ uv run python main.py --file lena.jpg Processed bytes total: 512 Processed bytes total: 1024 @@ -152,7 +152,7 @@ If you have a `str`, you have to encode it first to get `bytes`.
```console -$ python main.py --file binary.dat +$ uv run python main.py --file binary.dat Binary file written @@ -210,16 +210,16 @@ Check it:
```console -$ python main.py --config config.txt +$ uv run python main.py --config config.txt Config line written // Run your program a couple more times to see how it appends instead of overwriting -$ python main.py --config config.txt +$ uv run python main.py --config config.txt Config line written -$ python main.py --config config.txt +$ uv run python main.py --config config.txt Config line written diff --git a/docs/tutorial/parameter-types/index.md b/docs/tutorial/parameter-types/index.md index 88ebe45dfe..6289407ca9 100644 --- a/docs/tutorial/parameter-types/index.md +++ b/docs/tutorial/parameter-types/index.md @@ -21,7 +21,7 @@ And here's how it looks like:
```console -$ python main.py --help +$ uv run python main.py --help // Notice how --age is an 'int' (integer) and --height-meters is a 'float' Usage: main.py [OPTIONS] {name} @@ -36,7 +36,7 @@ Options: --help Show this message and exit. // Call it with CLI parameters -$ python main.py Camila --age 15 --height-meters 1.70 --female +$ uv run python main.py Camila --age 15 --height-meters 1.70 --female // All the data has the correct Python type name is Camila, of type: class 'str' @@ -45,7 +45,7 @@ name is Camila, of type: class 'str' --female is True, of type: class 'bool' // And if you pass an incorrect type -$ python main.py Camila --age 15.3 +$ uv run python main.py Camila --age 15.3 Usage: main.py [OPTIONS] {name} Try "main.py --help" for help. diff --git a/docs/tutorial/parameter-types/number.md b/docs/tutorial/parameter-types/number.md index 2e2da4a9e8..aaae367a22 100644 --- a/docs/tutorial/parameter-types/number.md +++ b/docs/tutorial/parameter-types/number.md @@ -13,7 +13,7 @@ Check it:
```console -$ python main.py --help +$ uv run python main.py --help // Notice the extra range information in the help text for --age and --score Usage: main.py [OPTIONS] {ID} @@ -27,14 +27,14 @@ Options: --help Show this message and exit. // Pass all the CLI parameters -$ python main.py 5 --age 20 --score 90 +$ uv run python main.py 5 --age 20 --score 90 ID is 5 --age is 20 --score is 90.0 // Pass an invalid ID -$ python main.py 1002 +$ uv run python main.py 1002 Usage: main.py [OPTIONS] {ID} Try "main.py --help" for help. @@ -42,7 +42,7 @@ Try "main.py --help" for help. Error: Invalid value for 'ID': 1002 is not in the range 0<=x<=1000. // Pass an invalid age -$ python main.py 5 --age 15 +$ uv run python main.py 5 --age 15 Usage: main.py [OPTIONS] {ID} Try "main.py --help" for help. @@ -50,7 +50,7 @@ Try "main.py --help" for help. Error: Invalid value for '--age': 15 is not in the range x>=18. // Pass an invalid score -$ python main.py 5 --age 20 --score 100.5 +$ uv run python main.py 5 --age 20 --score 100.5 Usage: main.py [OPTIONS] {ID} Try "main.py --help" for help. @@ -58,7 +58,7 @@ Try "main.py --help" for help. Error: Invalid value for '--score': 100.5 is not in the range x<=100. // But as we didn't specify a minimum score, this is accepted -$ python main.py 5 --age 20 --score -5 +$ uv run python main.py 5 --age 20 --score -5 ID is 5 --age is 20 @@ -81,7 +81,7 @@ And then, when you pass data that is out of the valid range, it will be "clamped ```console // ID doesn't have clamp, so it shows an error -$ python main.py 1002 +$ uv run python main.py 1002 Usage: main.py [OPTIONS] {ID} Try "main.py --help" for help. @@ -89,7 +89,7 @@ Try "main.py --help" for help. Error: Invalid value for 'ID': 1002 is not in the range 0<=x<=1000. // But --rank and --score use clamp -$ python main.py 5 --rank 11 --score -5 +$ uv run python main.py 5 --rank 11 --score -5 ID is 5 --rank is 10 @@ -112,32 +112,32 @@ And the value you receive in the function will be the amount of times that `--ve ```console // Check it -$ python main.py +$ uv run python main.py Verbose level is 0 // Now use one --verbose -$ python main.py --verbose +$ uv run python main.py --verbose Verbose level is 1 // Now 3 --verbose -$ python main.py --verbose --verbose --verbose +$ uv run python main.py --verbose --verbose --verbose Verbose level is 3 // And with the short name -$ python main.py -v +$ uv run python main.py -v Verbose level is 1 // And with the short name 3 times -$ python main.py -v -v -v +$ uv run python main.py -v -v -v Verbose level is 3 // As short names can be put together, this also works -$ python main.py -vvv +$ uv run python main.py -vvv Verbose level is 3 ``` diff --git a/docs/tutorial/parameter-types/path.md b/docs/tutorial/parameter-types/path.md index dcaada8969..9a05d0826c 100644 --- a/docs/tutorial/parameter-types/path.md +++ b/docs/tutorial/parameter-types/path.md @@ -14,13 +14,13 @@ Check it: ```console // No config -$ python main.py +$ uv run python main.py Aborted. No config file // Pass a config that doesn't exist -$ python main.py --config config.txt +$ uv run python main.py --config config.txt The config doesn't exist @@ -28,12 +28,12 @@ The config doesn't exist $ echo "some settings" > config.txt // And try again -$ python main.py --config config.txt +$ uv run python main.py --config config.txt Config file contents: some settings // And with a directory -$ python main.py --config ./ +$ uv run python main.py --config ./ Config is a directory, will use all its config files ``` @@ -66,7 +66,7 @@ Check it:
```console -$ python main.py --config config.txt +$ uv run python main.py --config config.txt Usage: main.py [OPTIONS] Try "main.py --help" for help. @@ -77,12 +77,12 @@ Error: Invalid value for '--config': File 'config.txt' does not exist. $ echo "some settings" > config.txt // And try again -$ python main.py --config config.txt +$ uv run python main.py --config config.txt Config file contents: some settings // And with a directory -$ python main.py --config ./ +$ uv run python main.py --config ./ Usage: main.py [OPTIONS] Try "main.py --help" for help. diff --git a/docs/tutorial/parameter-types/uuid.md b/docs/tutorial/parameter-types/uuid.md index 187a79b0e7..4199c7a373 100644 --- a/docs/tutorial/parameter-types/uuid.md +++ b/docs/tutorial/parameter-types/uuid.md @@ -32,13 +32,13 @@ Check it: ```console // Pass a valid UUID v4 -$ python main.py d48edaa6-871a-4082-a196-4daab372d4a1 +$ uv run python main.py d48edaa6-871a-4082-a196-4daab372d4a1 User ID is d48edaa6-871a-4082-a196-4daab372d4a1 UUID version is: 4 // An invalid value -$ python main.py 7479706572-72756c6573 +$ uv run python main.py 7479706572-72756c6573 Usage: main.py [OPTIONS] {user_id} Try "main.py --help" for help. diff --git a/docs/tutorial/printing.md b/docs/tutorial/printing.md index 62c83205d0..d12a352468 100644 --- a/docs/tutorial/printing.md +++ b/docs/tutorial/printing.md @@ -9,7 +9,7 @@ It will show the output normally:
```console -$ python main.py World +$ uv run python main.py World Hello World ``` @@ -31,7 +31,7 @@ Just with that, **Rich** will be able to print your data with nice colors and st
```console -$ python main.py +$ uv run python main.py Here's the data { @@ -57,7 +57,7 @@ Rich also supports a [custom markup syntax](https://rich.readthedocs.io/en/stabl
```console -$ python main.py +$ uv run python main.py Alert! Portal gun shooting! 💥 ``` @@ -85,7 +85,7 @@ If you run it, you will see a nicely formatted table:
```console -$ python main.py +$ uv run python main.py ┏━━━━━━━┳━━━━━━━━━━━━┓ ┃ Name Item ┃ @@ -159,7 +159,7 @@ When you try it in the terminal, it will probably just look the same:
```console -$ python main.py +$ uv run python main.py Here is something written to standard error ``` diff --git a/docs/tutorial/progressbar.md b/docs/tutorial/progressbar.md index a907e42625..ddedbffd4b 100644 --- a/docs/tutorial/progressbar.md +++ b/docs/tutorial/progressbar.md @@ -15,7 +15,7 @@ Check it:
```console -$ python main.py +$ uv run python main.py ---> 100% @@ -31,7 +31,7 @@ The colors and information will look something like this:
```console -$ python main.py +$ uv run python main.py Processing... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸━━━━━━━━━━ 74% 0:00:01 ``` @@ -55,7 +55,7 @@ But at some point in time it will look like this (imagine it's spinning). 🤓
```console -$ python main.py +$ uv run python main.py Processing... Preparing... @@ -136,7 +136,7 @@ Check it:
```console -$ python main.py +$ uv run python main.py ---> 100% @@ -164,7 +164,7 @@ Check it:
```console -$ python main.py +$ uv run python main.py ---> 100% diff --git a/docs/tutorial/prompt.md b/docs/tutorial/prompt.md index 522f6ba693..3bd53af1ac 100644 --- a/docs/tutorial/prompt.md +++ b/docs/tutorial/prompt.md @@ -11,7 +11,7 @@ Check it:
```console -$ python main.py +$ uv run python main.py # What's your name?:$ Camila @@ -31,14 +31,14 @@ Check it:
```console -$ python main.py +$ uv run python main.py # Are you sure you want to delete it? [y/N]:$ y Deleting it! // This time cancel it -$ python main.py +$ uv run python main.py # Are you sure you want to delete it? [y/N]:$ n @@ -57,14 +57,14 @@ As it's very common to abort if the user doesn't confirm, there's an integrated
```console -$ python main.py +$ uv run python main.py # Are you sure you want to delete it? [y/N]:$ y Deleting it! // This time cancel it -$ python main.py +$ uv run python main.py # Are you sure you want to delete it? [y/N]:$ n @@ -84,7 +84,7 @@ And when you run it, it will look like:
```console -$ python main.py +$ uv run python main.py # Enter your name 😎:$ Morty diff --git a/docs/tutorial/subcommands/add-typer.md b/docs/tutorial/subcommands/add-typer.md index c8d77ad596..7e110f6d24 100644 --- a/docs/tutorial/subcommands/add-typer.md +++ b/docs/tutorial/subcommands/add-typer.md @@ -17,7 +17,7 @@ And you would use it like:
```console -$ python items.py create Wand +$ uv run python items.py create Wand Creating item: Wand ``` @@ -37,7 +37,7 @@ And you would use it like:
```console -$ python users.py create Camila +$ uv run python users.py create Camila Creating user: Camila ``` @@ -74,7 +74,7 @@ Check it: ```console // Check the help -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] COMMAND [ARGS]... @@ -98,7 +98,7 @@ Let's check the `items` command: ```console // Check the help for items -$ python main.py items --help +$ uv run python main.py items --help // It shows its own commands (subcommands): create, delete, sell Usage: main.py items [OPTIONS] COMMAND [ARGS]... @@ -112,11 +112,11 @@ Commands: sell // Try it -$ python main.py items create Wand +$ uv run python main.py items create Wand Creating item: Wand -$ python main.py items sell Vase +$ uv run python main.py items sell Vase Selling item: Vase ``` @@ -134,7 +134,7 @@ And now check the command `users`, with all its subcommands:
```console -$ python main.py users --help +$ uv run python main.py users --help Usage: main.py users [OPTIONS] COMMAND [ARGS]... @@ -146,7 +146,7 @@ Commands: delete // Try it -$ python main.py users create Camila +$ uv run python main.py users create Camila Creating user: Camila ``` diff --git a/docs/tutorial/subcommands/callback-override.md b/docs/tutorial/subcommands/callback-override.md index 8ab05df993..cf67a9219b 100644 --- a/docs/tutorial/subcommands/callback-override.md +++ b/docs/tutorial/subcommands/callback-override.md @@ -15,7 +15,7 @@ Check it:
```console -$ python main.py users create Camila +$ uv run python main.py users create Camila // Notice the first message is not created by the command function but by the callback Running a users command @@ -37,7 +37,7 @@ Check it:
```console -$ python main.py users create Camila +$ uv run python main.py users create Camila Running a users command Creating user: Camila @@ -62,7 +62,7 @@ Check it:
```console -$ python main.py users create Camila +$ uv run python main.py users create Camila // Notice the message from the new callback Callback override, running users command @@ -88,7 +88,7 @@ Check it:
```console -$ python users create Camila +$ uv run python users create Camila // Notice the message from the callback added in add_typer() I have the high land! Running users command diff --git a/docs/tutorial/subcommands/name-and-help.md b/docs/tutorial/subcommands/name-and-help.md index 16829ebb9c..87e141f526 100644 --- a/docs/tutorial/subcommands/name-and-help.md +++ b/docs/tutorial/subcommands/name-and-help.md @@ -20,7 +20,7 @@ And then we get that help text for that command in the *CLI program*: ```console // Check the main help -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] COMMAND [ARGS]... @@ -33,7 +33,7 @@ Commands: users Manage users in the app. // Check the help for the users command -$ python main.py users --help +$ uv run python main.py users --help Usage: main.py users [OPTIONS] COMMAND [ARGS]... @@ -97,7 +97,7 @@ Check it: ```console // Check the main help -$ python main.py --help +$ uv run python main.py --help // Notice the help text "Manage users in the app." Usage: main.py [OPTIONS] COMMAND [ARGS]... @@ -111,7 +111,7 @@ Commands: users Manage users in the app. // Check the help for the users command -$ python main.py users --help +$ uv run python main.py users --help // Notice the main description: "Manage users in the app." Usage: main.py users [OPTIONS] COMMAND [ARGS]... @@ -151,7 +151,7 @@ Check it: ```console // Check the main help -$ python main.py --help +$ uv run python main.py --help // Notice the help text "Manage users in the app." Usage: main.py [OPTIONS] COMMAND [ARGS]... @@ -165,7 +165,7 @@ Commands: users Manage users in the app. // Check the help for the users command -$ python main.py users --help +$ uv run python main.py users --help // Notice the main description: "Manage users in the app." Usage: main.py users [OPTIONS] COMMAND [ARGS]... @@ -195,7 +195,7 @@ Check it: ```console // Check the main help -$ python main.py --help +$ uv run python main.py --help // Notice the help text "Manage users in the app." Usage: main.py [OPTIONS] COMMAND [ARGS]... @@ -209,7 +209,7 @@ Commands: users Manage users in the app. // Check the help for the users command -$ python main.py users --help +$ uv run python main.py users --help // Notice the main description: "Manage users in the app." Usage: main.py users [OPTIONS] COMMAND [ARGS]... @@ -243,7 +243,7 @@ Check it: ```console // Check the main help -$ python main.py --help +$ uv run python main.py --help // Check the command new-users and its help text Usage: main.py [OPTIONS] COMMAND [ARGS]... @@ -257,7 +257,7 @@ Commands: new-users I have the highland! Create some users. // Now check the help for the new-users command -$ python main.py new-users --help +$ uv run python main.py new-users --help // Notice the help text Usage: main.py new-users [OPTIONS] COMMAND [ARGS]... @@ -321,7 +321,7 @@ Check it: ```console // Check the main help -$ python main.py --help +$ uv run python main.py --help // Notice the command name is exp-users and the help text is "Explicit help." Usage: main.py [OPTIONS] COMMAND [ARGS]... @@ -335,7 +335,7 @@ Commands: exp-users Explicit help. // Check the help for the exp-users command -$ python main.py exp-users --help +$ uv run python main.py exp-users --help // Notice the main help text Usage: main.py exp-users [OPTIONS] COMMAND [ARGS]... @@ -367,7 +367,7 @@ Check it: ```console // Check the help -$ python main.py --help +$ uv run python main.py --help // The help text is now "Help from callback for users.". Usage: main.py [OPTIONS] COMMAND [ARGS]... @@ -381,7 +381,7 @@ Commands: users Help from callback for users. // Check the users command help -$ python main.py users --help +$ uv run python main.py users --help // Notice the main help text Usage: main.py users [OPTIONS] COMMAND [ARGS]... @@ -411,7 +411,7 @@ Check it: ```console // Check the help -$ python main.py --help +$ uv run python main.py --help // Notice the command name cake-sith-users and the new help text "Unlimited powder! Eh, users." Usage: main.py [OPTIONS] COMMAND [ARGS]... @@ -425,7 +425,7 @@ Commands: cake-sith-users Unlimited powder! Eh, users. // And check the help for the command cake-sith-users -$ python main.py cake-sith-users --help +$ uv run python main.py cake-sith-users --help // Notice the main help text Usage: main.py cake-sith-users [OPTIONS] COMMAND [ARGS]... diff --git a/docs/tutorial/subcommands/nested-subcommands.md b/docs/tutorial/subcommands/nested-subcommands.md index 13e5a7dc94..a856cae4ef 100644 --- a/docs/tutorial/subcommands/nested-subcommands.md +++ b/docs/tutorial/subcommands/nested-subcommands.md @@ -20,7 +20,7 @@ This is already a simple *CLI program* to manage reigns: ```console // Check the help -$ python reigns.py --help +$ uv run python reigns.py --help Usage: reigns.py [OPTIONS] COMMAND [ARGS]... @@ -34,11 +34,11 @@ Commands: destroy // Try it -$ python reigns.py conquer Cintra +$ uv run python reigns.py conquer Cintra Conquering reign: Cintra -$ python reigns.py destroy Mordor +$ uv run python reigns.py destroy Mordor Destroying reign: Mordor ``` @@ -57,7 +57,7 @@ With it, you can manage towns: ```console // Check the help -$ python towns.py --help +$ uv run python towns.py --help Usage: towns.py [OPTIONS] COMMAND [ARGS]... @@ -71,11 +71,11 @@ Commands: burn // Try it -$ python towns.py found "New Asgard" +$ uv run python towns.py found "New Asgard" Founding town: New Asgard -$ python towns.py burn Vizima +$ uv run python towns.py burn Vizima Burning town: Vizima ``` @@ -96,7 +96,7 @@ Check it: ```console // Check the help -$ python lands.py --help +$ uv run python lands.py --help Usage: lands.py [OPTIONS] COMMAND [ARGS]... @@ -110,7 +110,7 @@ Commands: towns // We still have the help for reigns -$ python lands.py reigns --help +$ uv run python lands.py reigns --help Usage: lands.py reigns [OPTIONS] COMMAND [ARGS]... @@ -122,7 +122,7 @@ Commands: destroy // And the help for towns -$ python lands.py towns --help +$ uv run python lands.py towns --help Usage: lands.py towns [OPTIONS] COMMAND [ARGS]... @@ -142,20 +142,20 @@ Now try it, manage the lands through the CLI: ```console // Try the reigns command -$ python lands.py reigns conquer Gondor +$ uv run python lands.py reigns conquer Gondor Conquering reign: Gondor -$ python lands.py reigns destroy Nilfgaard +$ uv run python lands.py reigns destroy Nilfgaard Destroying reign: Nilfgaard // Try the towns command -$ python lands.py towns found Springfield +$ uv run python lands.py towns found Springfield Founding town: Springfield -$ python lands.py towns burn Atlantis +$ uv run python lands.py towns burn Atlantis Burning town: Atlantis ``` @@ -195,7 +195,7 @@ And now we have everything in a single *CLI program*: ```console // Check the main help -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] COMMAND [ARGS]... @@ -210,22 +210,22 @@ Commands: lands // Try some users commands -$ python main.py users create Camila +$ uv run python main.py users create Camila Creating user: Camila // Now try some items commands -$ python main.py items create Sword +$ uv run python main.py items create Sword Creating item: Sword // And now some lands commands for reigns -$ python main.py lands reigns conquer Gondor +$ uv run python main.py lands reigns conquer Gondor Conquering reign: Gondor // And for towns -$ python main.py lands towns found Cartagena +$ uv run python main.py lands towns found Cartagena Founding town: Cartagena ``` diff --git a/docs/tutorial/subcommands/single-file.md b/docs/tutorial/subcommands/single-file.md index bd54b02037..880a6b48d5 100644 --- a/docs/tutorial/subcommands/single-file.md +++ b/docs/tutorial/subcommands/single-file.md @@ -38,7 +38,7 @@ To call them like: ```console // We want this ✔️ -$ python main.py items create +$ uv run python main.py items create ```
@@ -49,7 +49,7 @@ instead of: ```console // We don't want this ⛔️ -$ python main.py items items-create +$ uv run python main.py items items-create ```
@@ -67,7 +67,7 @@ It still works the same: ```console // Check the help -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] COMMAND [ARGS]... @@ -91,7 +91,7 @@ Check the `items` command: ```console // Check the help for items -$ python main.py items --help +$ uv run python main.py items --help // It shows its own commands (subcommands): create, delete, sell Usage: main.py items [OPTIONS] COMMAND [ARGS]... @@ -105,11 +105,11 @@ Commands: sell // Try it -$ python main.py items create Wand +$ uv run python main.py items create Wand Creating item: Wand -$ python main.py items sell Vase +$ uv run python main.py items sell Vase Selling item: Vase ``` @@ -122,7 +122,7 @@ And the same for the `users` command:
```console -$ python main.py users --help +$ uv run python main.py users --help Usage: main.py users [OPTIONS] COMMAND [ARGS]... @@ -134,7 +134,7 @@ Commands: delete // Try it -$ python main.py users create Camila +$ uv run python main.py users create Camila Creating user: Camila ``` diff --git a/docs/tutorial/terminating.md b/docs/tutorial/terminating.md index 0dc0c35f86..ce000b655e 100644 --- a/docs/tutorial/terminating.md +++ b/docs/tutorial/terminating.md @@ -25,13 +25,13 @@ Check it:
```console -$ python main.py Camila +$ uv run python main.py Camila User created: Camila Notification sent for new user: Camila // Try with an existing user -$ python main.py rick +$ uv run python main.py rick The user already exists @@ -63,7 +63,7 @@ Check it:
```console -$ python main.py Camila +$ uv run python main.py Camila New user created: Camila @@ -73,7 +73,7 @@ $ echo $? 0 // Now make it exit with an error -$ python main.py root +$ uv run python main.py root The root user is reserved @@ -106,12 +106,12 @@ Check it:
```console -$ python main.py Camila +$ uv run python main.py Camila New user created: Camila // Now make it exit with an error -$ python main.py root +$ uv run python main.py root The root user is reserved Aborted! diff --git a/docs/tutorial/testing.md b/docs/tutorial/testing.md index 26219e0340..0b67d3c560 100644 --- a/docs/tutorial/testing.md +++ b/docs/tutorial/testing.md @@ -11,7 +11,7 @@ So, you would use it like:
```console -$ python main.py Camila --city Berlin +$ uv run python main.py Camila --city Berlin Hello Camila Let's have a coffee in Berlin @@ -88,7 +88,7 @@ Then you can call `pytest` in your directory and it will run your tests:
```console -$ pytest +$ uv run pytest ================ test session starts ================ platform linux -- Python 3.10, pytest-5.3.5, py-1.8.1, pluggy-0.13.1 @@ -116,7 +116,7 @@ That you would use like:
```console -$ python main.py Camila +$ uv run python main.py Camila # Email: $ camila@example.com diff --git a/docs/tutorial/typer-app.md b/docs/tutorial/typer-app.md index f095a2e19b..6bb60fb1e9 100644 --- a/docs/tutorial/typer-app.md +++ b/docs/tutorial/typer-app.md @@ -49,7 +49,7 @@ If you run the second example, with the explicit `app`, it works exactly the sam ```console // Without a CLI argument -$ python main.py +$ uv run python main.py Usage: main.py [OPTIONS] {name} Try "main.py --help" for help. @@ -57,12 +57,12 @@ Try "main.py --help" for help. Error: Missing argument 'name'. // With the 'name' CLI argument -$ python main.py Camila +$ uv run python main.py Camila Hello Camila // Asking for help -$ python main.py --help +$ uv run python main.py --help Usage: main.py [OPTIONS] {name} @@ -93,7 +93,7 @@ So instead of running a Python script like:
```console -$ python main.py +$ uv run python main.py ✨ Some magic here ✨ ``` diff --git a/docs/tutorial/typer-command.md b/docs/tutorial/typer-command.md index 0c7884bc3c..661dc5dcf7 100644 --- a/docs/tutorial/typer-command.md +++ b/docs/tutorial/typer-command.md @@ -2,7 +2,7 @@ The `typer` command provides ✨ completion ✨ in the Terminal for your own small scripts. Even if they don't use Typer internally. Of course, it works better if you use **Typer** in your script. -It's probably most useful if you have a small custom Python script using **Typer** (maybe as part of some project), for some small tasks, and it's not complex/important enough to create a whole installable Python package for it (something to be installed with `pip`). +It's probably most useful if you have a small custom Python script using **Typer** (maybe as part of some project), for some small tasks, and it's not complex/important enough to create a whole installable Python package. In that case, you can run your program with the `typer` command in your Terminal, and it will provide completion for your script. @@ -10,18 +10,46 @@ The `typer` command also has functionality to generate Markdown documentation fo ## Install -When you install **Typer** with: +Add Typer to your project: -```bash -pip install typer +```console +$ uv add typer +``` + +This installs both the Typer library and the `typer` command in the project's `.venv`. + +Activate that environment to make `typer` available as a first-class command in your terminal: + +//// tab | Linux, macOS + +```console +$ source .venv/bin/activate ``` -...it includes the `typer` command. +//// + +//// tab | Windows PowerShell + +```console +$ .venv\Scripts\Activate.ps1 +``` + +//// + +//// tab | Windows Bash + +```console +$ source .venv/Scripts/activate +``` + +//// + +Using the `typer` command from the project environment ensures it uses the same Typer version and dependencies as the project. If you don't want to use the `typer` command, you can call the Typer library as a module with: -```bash -python -m typer +```console +$ uv run python -m typer ``` ## Install completion @@ -76,9 +104,8 @@ For it to work, you would also install **Typer**:
```console -$ python -m pip install typer +$ uv add typer ---> 100% -Successfully installed typer ```
@@ -90,15 +117,15 @@ Then you could run your script with normal Python:
```console -$ python my_custom_script.py hello +$ uv run python my_custom_script.py hello Hello World! -$ python my_custom_script.py hello --name Camila +$ uv run python my_custom_script.py hello --name Camila Hello Camila! -$ python my_custom_script.py bye --name Camila +$ uv run python my_custom_script.py bye --name Camila Bye Camila ``` @@ -258,7 +285,7 @@ $ typer some_script.py utils docs If you don't want to use the `typer` command, you can still generate docs with: ```console -$ python -m typer some_script.py utils docs +$ uv run python -m typer some_script.py utils docs ``` /// diff --git a/docs/virtual-environments.md b/docs/virtual-environments.md index 4c7c9b291f..ef960399d8 100644 --- a/docs/virtual-environments.md +++ b/docs/virtual-environments.md @@ -1,844 +1,35 @@ # Virtual Environments -When you work in Python projects you probably should use a **virtual environment** (or a similar mechanism) to isolate the packages you install for each project. +When you work with Python projects, you should use a **virtual environment** to isolate the packages installed for each project. -/// note +For Typer projects, I recommend using [uv](https://docs.astral.sh/uv/) to manage the project, its dependencies, and its virtual environment. -If you already know about virtual environments, how to create them and use them, you might want to skip this section. 🤓 +## Create a Project { #create-a-project } -/// - -/// tip - -A **virtual environment** is different than an **environment variable**. - -An **environment variable** is a variable in the system that can be used by programs. - -A **virtual environment** is a directory with some files in it. - -/// - -/// note - -This page will teach you how to use **virtual environments** and how they work. - -If you are ready to adopt a **tool that manages everything** for you (including installing Python), try [uv](https://github.com/astral-sh/uv). - -/// - -## Create a Project - -First, create a directory for your project. - -What I normally do is that I create a directory named `code` inside my home/user directory. - -And inside of that I create one directory per project. +Install `uv` using the [official installation guide](https://docs.astral.sh/uv/getting-started/installation/), and then create a project:
```console -// Go to the home directory -$ cd -// Create a directory for all your code projects -$ mkdir code -// Enter into that code directory -$ cd code -// Create a directory for this project -$ mkdir awesome-project -// Enter into that project directory +$ uv init awesome-project --bare $ cd awesome-project +$ uv add typer ```
-## Create a Virtual Environment - -When you start working on a Python project **for the first time**, create a virtual environment **inside your project**. - -/// tip - -You only need to do this **once per project**, not every time you work. - -/// - -//// tab | `venv` - -To create a virtual environment, you can use the `venv` module that comes with Python. - -
- -```console -$ python -m venv .venv -``` - -
- -/// details | What that command means - -* `python`: use the program called `python` -* `-m`: call a module as a script, we'll tell it which module next -* `venv`: use the module called `venv` that normally comes installed with Python -* `.venv`: create the virtual environment in the new directory `.venv` - -/// - -//// - -//// tab | `uv` - -If you have [`uv`](https://github.com/astral-sh/uv) installed, you can use it to create a virtual environment. - -
- -```console -$ uv venv -``` - -
- -/// tip - -By default, `uv` will create a virtual environment in a directory called `.venv`. - -But you could customize it passing an additional argument with the directory name. - -/// - -//// - -That command creates a new virtual environment in a directory called `.venv`. - -/// details | `.venv` or other name - -You could create the virtual environment in a different directory, but there's a convention of calling it `.venv`. - -/// - -## Activate the Virtual Environment - -Activate the new virtual environment so that any Python command you run or package you install uses it. - -/// tip - -Do this **every time** you start a **new terminal session** to work on the project. - -/// - -//// tab | Linux, macOS - -
- -```console -$ source .venv/bin/activate -``` - -
- -//// - -//// tab | Windows PowerShell - -
- -```console -$ .venv\Scripts\Activate.ps1 -``` - -
- -//// - -//// tab | Windows Bash - -Or if you use Bash for Windows (e.g. [Git Bash](https://gitforwindows.org/)): - -
- -```console -$ source .venv/Scripts/activate -``` - -
- -//// - -/// tip - -Every time you install a **new package** in that environment, **activate** the environment again. - -This makes sure that if you use a **terminal (CLI) program** installed by that package, you use the one from your virtual environment and not any other that could be installed globally, probably with a different version than what you need. - -/// - -## Check the Virtual Environment is Active - -Check that the virtual environment is active (the previous command worked). - -/// tip - -This is **optional**, but it's a good way to **check** that everything is working as expected and you are using the virtual environment you intended. - -/// - -//// tab | Linux, macOS, Windows Bash - -
- -```console -$ which python - -/home/user/code/awesome-project/.venv/bin/python -``` - -
- -If it shows the `python` binary at `.venv/bin/python`, inside of your project (in this case `awesome-project`), then it worked. 🎉 - -//// - -//// tab | Windows PowerShell - -
- -```console -$ Get-Command python - -C:\Users\user\code\awesome-project\.venv\Scripts\python -``` - -
- -If it shows the `python` binary at `.venv\Scripts\python`, inside of your project (in this case `awesome-project`), then it worked. 🎉 - -//// - -## Upgrade `pip` - -/// tip - -If you use [`uv`](https://github.com/astral-sh/uv) you would use it to install things instead of `pip`, so you don't need to upgrade `pip`. 😎 - -/// - -If you are using `pip` to install packages (it comes by default with Python), you should **upgrade** it to the latest version. - -Many exotic errors while installing a package are solved by just upgrading `pip` first. - -/// tip - -You would normally do this **once**, right after you create the virtual environment. - -/// - -Make sure the virtual environment is active (with the command above) and then run: - -
- -```console -$ python -m pip install --upgrade pip - ----> 100% -``` - -
- -## Add `.gitignore` - -If you are using **Git** (you should), add a `.gitignore` file to exclude everything in your `.venv` from Git. - -/// tip - -If you used [`uv`](https://github.com/astral-sh/uv) to create the virtual environment, it already did this for you, you can skip this step. 😎 - -/// - -/// tip - -Do this **once**, right after you create the virtual environment. - -/// - -
- -```console -$ echo "*" > .venv/.gitignore -``` - -
- -/// details | What that command means - -* `echo "*"`: will "print" the text `*` in the terminal (the next part changes that a bit) -* `>`: anything printed to the terminal by the command to the left of `>` should not be printed but instead written to the file that goes to the right of `>` -* `.gitignore`: the name of the file where the text should be written - -And `*` for Git means "everything". So, it will ignore everything in the `.venv` directory. - -That command will create a file `.gitignore` with the content: - -```gitignore -* -``` - -/// - -## Install Packages - -After activating the environment, you can install packages in it. - -/// tip - -Do this **once** when installing or upgrading the packages your project needs. - -If you need to upgrade a version or add a new package you would **do this again**. - -/// - -### Install Packages Directly - -If you're in a hurry and don't want to use a file to declare your project's package requirements, you can install them directly. - -/// tip - -It's a (very) good idea to put the packages and versions your program needs in a file (for example `requirements.txt` or `pyproject.toml`). - -/// - -//// tab | `pip` - -
- -```console -$ pip install typer - ----> 100% -``` - -
- -//// - -//// tab | `uv` - -If you have [`uv`](https://github.com/astral-sh/uv): - -
- -```console -$ uv pip install typer ----> 100% -``` - -
- -//// - -### Install from `requirements.txt` - -If you have a `requirements.txt`, you can now use it to install its packages. - -//// tab | `pip` - -
- -```console -$ pip install -r requirements.txt ----> 100% -``` - -
- -//// - -//// tab | `uv` - -If you have [`uv`](https://github.com/astral-sh/uv): - -
- -```console -$ uv pip install -r requirements.txt ----> 100% -``` - -
- -//// - -/// details | `requirements.txt` - -A `requirements.txt` with some packages could look like: - -```requirements.txt -typer==0.13.0 -rich==13.7.1 -``` - -/// - -## Run Your Program - -After you activated the virtual environment, you can run your program, and it will use the Python inside of your virtual environment with the packages you installed there. - -
- -```console -$ python main.py - -Hello World -``` - -
- -## Configure Your Editor - -You would probably use an editor, make sure you configure it to use the same virtual environment you created (it will probably autodetect it) so that you can get autocompletion and inline errors. - -For example: - -* [VS Code](https://code.visualstudio.com/docs/python/environments#_select-and-activate-an-environment) -* [PyCharm](https://www.jetbrains.com/help/pycharm/creating-virtual-environment.html) - -/// tip - -You normally have to do this only **once**, when you create the virtual environment. - -/// - -## Deactivate the Virtual Environment - -Once you are done working on your project you can **deactivate** the virtual environment. - -
- -```console -$ deactivate -``` - -
- -This way, when you run `python` it won't try to run it from that virtual environment with the packages installed there. - -## Ready to Work - -Now you're ready to start working on your project. - - - -/// tip - -Do you want to understand what's all that above? - -Continue reading. 👇🤓 - -/// - -## Why Virtual Environments - -To work with Typer you need to install [Python](https://www.python.org/). - -After that, you would need to **install** Typer and any other **packages** you want to use. - -To install packages you would normally use the `pip` command that comes with Python (or similar alternatives). - -Nevertheless, if you just use `pip` directly, the packages would be installed in your **global Python environment** (the global installation of Python). - -### The Problem - -So, what's the problem with installing packages in the global Python environment? - -At some point, you will probably end up writing many different programs that depend on **different packages**. And some of these projects you work on will depend on **different versions** of the same package. 😱 - -For example, you could create a project called `philosophers-stone`, this program depends on another package called **`harry`, using the version `1`**. So, you need to install `harry`. - -```mermaid -flowchart LR - stone(philosophers-stone) -->|requires| harry-1[harry v1] -``` - -Then, at some point later, you create another project called `prisoner-of-azkaban`, and this project also depends on `harry`, but this project needs **`harry` version `3`**. - -```mermaid -flowchart LR - azkaban(prisoner-of-azkaban) --> |requires| harry-3[harry v3] -``` - -But now the problem is, if you install the packages globally (in the global environment) instead of in a local **virtual environment**, you will have to choose which version of `harry` to install. - -If you want to run `philosophers-stone` you will need to first install `harry` version `1`, for example with: - -
- -```console -$ pip install "harry==1" -``` - -
- -And then you would end up with `harry` version `1` installed in your global Python environment. - -```mermaid -flowchart LR - subgraph global[global env] - harry-1[harry v1] - end - subgraph stone-project[philosophers-stone project] - stone(philosophers-stone) -->|requires| harry-1 - end -``` - -But then if you want to run `prisoner-of-azkaban`, you will need to uninstall `harry` version `1` and install `harry` version `3` (or just installing version `3` would automatically uninstall version `1`). - -
- -```console -$ pip install "harry==3" -``` - -
- -And then you would end up with `harry` version `3` installed in your global Python environment. - -And if you try to run `philosophers-stone` again, there's a chance it would **not work** because it needs `harry` version `1`. - -```mermaid -flowchart LR - subgraph global[global env] - harry-1[harry v1] - style harry-1 fill:#ccc,stroke-dasharray: 5 5 - harry-3[harry v3] - end - subgraph stone-project[philosophers-stone project] - stone(philosophers-stone) -.-x|⛔️| harry-1 - end - subgraph azkaban-project[prisoner-of-azkaban project] - azkaban(prisoner-of-azkaban) --> |requires| harry-3 - end -``` - -/// tip - -It's very common in Python packages to try the best to **avoid breaking changes** in **new versions**, but it's better to be safe, and install newer versions intentionally and when you can run the tests to check everything is working correctly. - -/// - -Now, imagine that with **many** other **packages** that all your **projects depend on**. That's very difficult to manage. And you would probably end up running some projects with some **incompatible versions** of the packages, and not knowing why something isn't working. - -Also, depending on your operating system (e.g. Linux, Windows, macOS), it could have come with Python already installed. And in that case it probably had some packages pre-installed with some specific versions **needed by your system**. If you install packages in the global Python environment, you could end up **breaking** some of the programs that came with your operating system. - -## Where are Packages Installed - -When you install Python, it creates some directories with some files in your computer. - -Some of these directories are the ones in charge of having all the packages you install. - -When you run: - -
- -```console -// Don't run this now, it's just an example 🤓 -$ pip install typer ----> 100% -``` - -
- -That will download a compressed file with the Typer code, normally from [PyPI](https://pypi.org/project/typer/). - -It will also **download** files for other packages that Typer depends on. - -Then it will **extract** all those files and put them in a directory in your computer. - -By default, it will put those files downloaded and extracted in the directory that comes with your Python installation, that's the **global environment**. - -## What are Virtual Environments - -The solution to the problems of having all the packages in the global environment is to use a **virtual environment for each project** you work on. - -A virtual environment is a **directory**, very similar to the global one, where you can install the packages for a project. - -This way, each project will have its own virtual environment (`.venv` directory) with its own packages. - -```mermaid -flowchart TB - subgraph stone-project[philosophers-stone project] - stone(philosophers-stone) --->|requires| harry-1 - subgraph venv1[.venv] - harry-1[harry v1] - end - end - subgraph azkaban-project[prisoner-of-azkaban project] - azkaban(prisoner-of-azkaban) --->|requires| harry-3 - subgraph venv2[.venv] - harry-3[harry v3] - end - end - stone-project ~~~ azkaban-project -``` - -## What Does Activating a Virtual Environment Mean - -When you activate a virtual environment, for example with: - -//// tab | Linux, macOS - -
- -```console -$ source .venv/bin/activate -``` - -
- -//// - -//// tab | Windows PowerShell - -
- -```console -$ .venv\Scripts\Activate.ps1 -``` - -
- -//// - -//// tab | Windows Bash - -Or if you use Bash for Windows (e.g. [Git Bash](https://gitforwindows.org/)): - -
- -```console -$ source .venv/Scripts/activate -``` - -
- -//// - -That command will create or modify some [environment variables](environment-variables.md) that will be available for the next commands. - -One of those variables is the `PATH` variable. - -/// tip - -You can learn more about the `PATH` environment variable in the [Environment Variables](environment-variables.md#path-environment-variable) section. - -/// - -Activating a virtual environment adds its path `.venv/bin` (on Linux and macOS) or `.venv\Scripts` (on Windows) to the `PATH` environment variable. - -Let's say that before activating the environment, the `PATH` variable looked like this: - -//// tab | Linux, macOS - -```plaintext -/usr/bin:/bin:/usr/sbin:/sbin -``` - -That means that the system would look for programs in: - -* `/usr/bin` -* `/bin` -* `/usr/sbin` -* `/sbin` - -//// - -//// tab | Windows - -```plaintext -C:\Windows\System32 -``` - -That means that the system would look for programs in: - -* `C:\Windows\System32` - -//// - -After activating the virtual environment, the `PATH` variable would look something like this: - -//// tab | Linux, macOS - -```plaintext -/home/user/code/awesome-project/.venv/bin:/usr/bin:/bin:/usr/sbin:/sbin -``` - -That means that the system will now start looking first look for programs in: - -```plaintext -/home/user/code/awesome-project/.venv/bin -``` - -before looking in the other directories. - -So, when you type `python` in the terminal, the system will find the Python program in - -```plaintext -/home/user/code/awesome-project/.venv/bin/python -``` - -and use that one. - -//// - -//// tab | Windows - -```plaintext -C:\Users\user\code\awesome-project\.venv\Scripts;C:\Windows\System32 -``` - -That means that the system will now start looking first look for programs in: - -```plaintext -C:\Users\user\code\awesome-project\.venv\Scripts -``` - -before looking in the other directories. - -So, when you type `python` in the terminal, the system will find the Python program in - -```plaintext -C:\Users\user\code\awesome-project\.venv\Scripts\python -``` - -and use that one. - -//// - -An important detail is that it will put the virtual environment path at the **beginning** of the `PATH` variable. The system will find it **before** finding any other Python available. This way, when you run `python`, it will use the Python **from the virtual environment** instead of any other `python` (for example, a `python` from a global environment). - -Activating a virtual environment also changes a couple of other things, but this is one of the most important things it does. - -## Checking a Virtual Environment - -When you check if a virtual environment is active, for example with: - -//// tab | Linux, macOS, Windows Bash - -
- -```console -$ which python - -/home/user/code/awesome-project/.venv/bin/python -``` - -
- -//// - -//// tab | Windows PowerShell - -
- -```console -$ Get-Command python - -C:\Users\user\code\awesome-project\.venv\Scripts\python -``` - -
- -//// - -That means that the `python` program that will be used is the one **in the virtual environment**. - -you use `which` in Linux and macOS and `Get-Command` in Windows PowerShell. - -The way that command works is that it will go and check in the `PATH` environment variable, going through **each path in order**, looking for the program called `python`. Once it finds it, it will **show you the path** to that program. - -The most important part is that when you call `python`, that is the exact "`python`" that will be executed. - -So, you can confirm if you are in the correct virtual environment. - -/// tip - -It's easy to activate one virtual environment, get one Python, and then **go to another project**. - -And the second project **wouldn't work** because you are using the **incorrect Python**, from a virtual environment for another project. - -It's useful being able to check what `python` is being used. 🤓 - -/// - -## Why Deactivate a Virtual Environment - -For example, you could be working on a project `philosophers-stone`, **activate that virtual environment**, install packages and work with that environment. - -And then you want to work on **another project** `prisoner-of-azkaban`. - -You go to that project: - -
- -```console -$ cd ~/code/prisoner-of-azkaban -``` - -
- -If you don't deactivate the virtual environment for `philosophers-stone`, when you run `python` in the terminal, it will try to use the Python from `philosophers-stone`. - -
- -```console -$ cd ~/code/prisoner-of-azkaban - -$ python main.py - -// Error importing sirius, it's not installed 😱 -Traceback (most recent call last): - File "main.py", line 1, in - import sirius -``` - -
+`uv` creates a virtual environment for the project automatically. You don't need to create or activate one yourself. -But if you deactivate the virtual environment and activate the new one for `prisoner-of-askaban` then when you run `python` it will use the Python from the virtual environment in `prisoner-of-azkaban`. +Run commands inside the project environment with `uv run`, for example:
```console -$ cd ~/code/prisoner-of-azkaban - -// You don't need to be in the old directory to deactivate, you can do it wherever you are, even after going to the other project 😎 -$ deactivate - -// Activate the virtual environment in prisoner-of-azkaban/.venv 🚀 -$ source .venv/bin/activate - -// Now when you run python, it will find the package sirius installed in this virtual environment ✨ -$ python main.py - -I solemnly swear 🐺 +$ uv run python main.py ```
-## Alternatives - -This is a simple guide to get you started and teach you how everything works **underneath**. - -There are many **alternatives** to managing virtual environments, package dependencies (requirements), projects. - -Once you are ready and want to use a tool to **manage the entire project**, packages dependencies, virtual environments, etc. I would suggest you try [uv](https://github.com/astral-sh/uv). - -`uv` can do a lot of things, it can: - -* **Install Python** for you, including different versions -* Manage the **virtual environment** for your projects -* Install **packages** -* Manage package **dependencies and versions** for your project -* Make sure you have an **exact** set of packages and versions to install, including their dependencies, so that you can be sure that you can run your project in production exactly the same as in your computer while developing, this is called **locking** -* And many other things - -## Conclusion - -If you read and understood all this, now **you know much more** about virtual environments than many developers out there. 🤓 +## Learn More { #learn-more } -Knowing these details will most probably be useful in a future time when you are debugging something that seems complex, but you will know **how it all works underneath**. 😎 +Read the [Virtual Environments guide](https://tiangolo.com/guides/virtual-environments/) to learn how virtual environments work underneath, including activation and the alternative `python -m venv` and `pip` workflow. diff --git a/mkdocs.yml b/mkdocs.yml index 75b9c43262..8fcdd8de4b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -82,8 +82,6 @@ nav: - features.md - "": - tutorial/index.md - - environment-variables.md - - virtual-environments.md - tutorial/install.md - tutorial/first-steps.md - tutorial/typer-app.md