From 73b969862e47cb3802876633779cd2df3cb8223b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Fri, 23 Aug 2024 12:56:40 -0500 Subject: [PATCH 01/73] =?UTF-8?q?=F0=9F=93=9D=20Add=20docs=20for=20virtual?= =?UTF-8?q?=20environments,=20environment=20variables,=20and=20update=20co?= =?UTF-8?q?ntributing=20(#946)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 + docs/contributing.md | 208 +++----- docs/environment-variables.md | 304 +++++++++++ docs/index.md | 2 + docs/tutorial/arguments/envvar.md | 6 + docs/tutorial/index.md | 50 +- docs/tutorial/install.md | 33 ++ docs/virtual-environments.md | 844 ++++++++++++++++++++++++++++++ mkdocs.yml | 3 + 9 files changed, 1262 insertions(+), 190 deletions(-) create mode 100644 docs/environment-variables.md create mode 100644 docs/tutorial/install.md create mode 100644 docs/virtual-environments.md diff --git a/README.md b/README.md index 49b441055f..b1e8c17eb2 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,8 @@ The key features are: ## Installation +Create and activate a virtual environment and then install **Typer**: +
```console diff --git a/docs/contributing.md b/docs/contributing.md index a4a158543a..bcb2a78960 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -4,197 +4,144 @@ First, you might want to see the basic ways to [help Typer and get help](help-ty ## Developing -If you already cloned the repository and you know that you need to deep dive in the code, here are some guidelines to set up your environment. +If you already cloned the typer repository and you want to deep dive in the code, here are some guidelines to set up your environment. -### Virtual environment with `venv` +### Virtual Environment -You can create a virtual environment in a directory using Python's `venv` module: +Follow the instructions to create and activate a [virtual environment](virtual-environments.md){.internal-link target=_blank} for the internal code of `fastapi`. -
- -```console -$ python -m venv env -``` - -
- -That will create a directory `./env/` with the Python binaries and then you will be able to install packages for that isolated environment. - -### Activate the environment +### Install Requirements Using `pip` -Activate the new environment with: - -//// tab | Linux, macOS +After activating the environment, install the required packages:
```console -$ source ./env/bin/activate +$ pip install -r requirements.txt + +---> 100% ```
-//// - -//// tab | Windows PowerShell - -
- -```console -$ .\env\Scripts\Activate.ps1 -``` +It will install all the dependencies and your local Typer in your local environment. -
+### Using your Local Typer -//// +If you create a Python file that imports and uses Typer, and run it with the Python from your local environment, it will use your cloned local Typer source code. -//// tab | Windows Bash +And if you update that local Typer source code when you run that Python file again, it will use the fresh version of Typer you just edited. -Or if you use Bash for Windows (e.g. Git Bash): +That way, you don't have to "install" your local version to be able to test every change. -
+/// note | "Technical Details" -```console -$ source ./env/Scripts/activate -``` +This only happens when you install using this included `requirements.txt` instead of running `pip install typer` directly. -
+That is because inside the `requirements.txt` file, the local version of Typer is marked to be installed in "editable" mode, with the `-e` option. -//// +/// -To check it worked, use: +### Format -//// tab | Linux, macOS, Windows Bash +There is a script that you can run that will format and clean all your code:
```console -$ which pip - -some/directory/typer/env/bin/pip +$ bash scripts/format.sh ```
-//// +It will also auto-sort all your imports. + +## Tests -//// tab | Windows PowerShell +There is a script that you can run locally to test all the code and generate coverage reports in HTML:
```console -$ Get-Command pip - -some/directory/typer/env/bin/pip +$ bash scripts/test-cov-html.sh ```
-//// - -If it shows the `pip` binary at `env/bin/pip` then it worked. 🎉 - -/// tip - -Every time you install a new package with `pip` under that environment, activate the environment again. - -This makes sure that if you use a terminal program installed by that package (like `flit`), you use the one from your local environment and not any other that could be installed globally. +This command generates a directory `./htmlcov/`, if you open the file `./htmlcov/index.html` in your browser, you can explore interactively the regions of code that are covered by the tests, and notice if there is any region missing. -/// +## Docs -### Flit +First, make sure you set up your environment as described above, that will install all the requirements. -**Typer** uses Flit to build, package and publish the project. +### Docs live -After activating the environment as described above, install `flit`: +During local development, there is a script that builds the site and checks for any changes, live-reloading:
```console -$ pip install flit +$ python ./scripts/docs.py live ----> 100% +[INFO] Serving on http://127.0.0.1:8008 +[INFO] Start watching changes +[INFO] Start detecting changes ```
-Now re-activate the environment to make sure you are using the `flit` you just installed (and not a global one). +It will serve the documentation on `http://127.0.0.1:8008`. -And now use `flit` to install the development dependencies: +That way, you can edit the documentation/source files and see the changes live. -//// tab | Linux, macOS +/// tip -
+Alternatively, you can perform the same steps that scripts does manually. -```console -$ flit install --deps develop --symlink +Go into the docs director at `docs/`: ----> 100% +```console +$ cd docs/ ``` -
- -//// - -//// tab | Windows - -If you are on Windows, use `--pth-file` instead of `--symlink`: - -
+Then run `mkdocs` in that directory: ```console -$ flit install --deps develop --pth-file - ----> 100% +$ mkdocs serve --dev-addr 8008 ``` -
- -//// - -It will install all the dependencies and your local Typer in your local environment. - -#### Using your local Typer +/// -If you create a Python file that imports and uses Typer, and run it with the Python from your local environment, it will use your local Typer source code. +#### Typer CLI (optional) -And if you update that local Typer source code, as it is installed with `--symlink` (or `--pth-file` on Windows), when you run that Python file again, it will use the fresh version of Typer you just edited. +The instructions here show you how to use the script at `./scripts/docs.py` with the `python` program directly. -That way, you don't have to "install" your local version to be able to test every change. +But you can also use Typer CLI, and you will get autocompletion in your terminal for the commands after installing completion. -### Format - -There is a script that you can run that will format and clean all your code: +If you install Typer CLI, you can install completion with:
```console -$ bash scripts/format.sh +$ typer --install-completion + +zsh completion installed in /home/user/.bashrc. +Completion will take effect once you restart the terminal. ```
-It will also auto-sort all your imports. - -For it to sort them correctly, you need to have Typer installed locally in your environment, with the command in the section above using `--symlink` (or `--pth-file` on Windows). - -### Format imports - -There is another script that formats all the imports and makes sure you don't have unused imports: +### Docs Structure -
- -```console -$ bash scripts/format-imports.sh -``` +The documentation uses MkDocs. -
+And there are extra tools/scripts in place in `./scripts/docs.py`. -As it runs one command after the other and modifies and reverts many files, it takes a bit longer to run, so it might be easier to use `scripts/format.sh` frequently and `scripts/format-imports.sh` only before committing. +/// tip -## Docs +You don't need to see the code in `./scripts/docs.py`, you just use it in the command line. -The documentation uses MkDocs. +/// All the documentation is in Markdown format in the directory `./docs`. @@ -206,45 +153,12 @@ In fact, those blocks of code are not written inside the Markdown, they are Pyth And those Python files are included/injected in the documentation when generating the site. -### Docs for tests +### Docs for Tests Most of the tests actually run against the example source files in the documentation. -This helps making sure that: +This helps to make sure that: -* The documentation is up to date. +* The documentation is up-to-date. * The documentation examples can be run as is. * Most of the features are covered by the documentation, ensured by test coverage. - -During local development, there is a script that builds the site and checks for any changes, live-reloading: - -
- -```console -$ bash scripts/docs-live.sh - -[INFO] - Building documentation... -[INFO] - Cleaning site directory -[INFO] - Documentation built in 2.74 seconds -[INFO] - Serving on http://127.0.0.1:8008 -``` - -
- -It will serve the documentation on `http://127.0.0.1:8008`. - -That way, you can edit the documentation/source files and see the changes live. - -## Tests - -There is a script that you can run locally to test all the code and generate coverage reports in HTML: - -
- -```console -$ bash scripts/test-cov-html.sh -``` - -
- -This command generates a directory `./htmlcov/`, if you open the file `./htmlcov/index.html` in your browser, you can explore interactively the regions of code that are covered by the tests, and notice if there is any region missing. diff --git a/docs/environment-variables.md b/docs/environment-variables.md new file mode 100644 index 0000000000..1cc66b928a --- /dev/null +++ b/docs/environment-variables.md @@ -0,0 +1,304 @@ +# 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**. + +/// tip + +If you already know what "environment variables" are and how to use them, feel free to skip this. + +/// + +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()` 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. + +/// + +## 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){.internal-link target=_blank}. + +## `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 +``` + +This way, when you type `python` in the terminal, the system will find the Python program in `/opt/custompython/bin` (the last directory) and use that one. + +//// + +//// 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 +``` + +This way, when you type `python` in the terminal, the system will find the Python program in `C:\opt\custompython\bin` (the last directory) and use that one. + +//// + +This way, when you type `python` in the terminal, the system will find the Python program in `/opt/custompython/bin` (the last directory) and use that one. + +So, if you type: + +
+ +```console +$ python +``` + +
+ +//// tab | Linux, macOS + +The system will **find** the `python` program in `/opt/custompython/bin` 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` 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){.internal-link target=_blank}. + +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. + +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). diff --git a/docs/index.md b/docs/index.md index 9996f64800..355380729a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -52,6 +52,8 @@ The key features are: ## Installation +Create and activate a virtual environment and then install **Typer**: +
```console diff --git a/docs/tutorial/arguments/envvar.md b/docs/tutorial/arguments/envvar.md index 970d1ee92a..a53d42685e 100644 --- a/docs/tutorial/arguments/envvar.md +++ b/docs/tutorial/arguments/envvar.md @@ -2,6 +2,12 @@ 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*. +/// tip + +You can learn more about environment variables in the [Environment Variables](../../environment-variables.md){.internal-link target=_blank} page. + +/// + To do that, use the `envvar` parameter for `typer.Argument()`: //// tab | Python 3.7+ diff --git a/docs/tutorial/index.md b/docs/tutorial/index.md index c8ea8a1cb5..bad806c88a 100644 --- a/docs/tutorial/index.md +++ b/docs/tutorial/index.md @@ -6,7 +6,7 @@ It covers everything you need to know from the **simplest scripts** to **complex You could consider this a **book**, a **course**, the **official** and recommended way to learn **Typer**. 😎 -## Python types +## Python Types If you need a refresher about how to use Python type hints, check the first part of FastAPI's Python types intro. @@ -31,17 +31,15 @@ These type hints are what give you autocomplete in your editor and several other **Typer** is based on these type hints. -## Intro +## About this Tutorial This tutorial shows you how to use **Typer** with all its features, step by step. Each section gradually builds on the previous ones, but it's structured to separate topics, so that you can go directly to any specific one to solve your specific CLI needs. -It is also built to work as a future reference. +It is also built to work as a future reference so you can come back and see exactly what you need. -So you can come back and see exactly what you need. - -## Run the code +## Run the Code All the code blocks can be copied and used directly (they are tested Python files). @@ -59,42 +57,8 @@ $ python main.py It is **HIGHLY encouraged** that you write or copy the code, edit it and run it locally. -Using it in your editor is what really shows you the benefits of **Typer**, seeing how little code you have to write, all the type checks, autocompletion, etc. - -And running the examples is what will really help you understand what is going on. - -You can learn a lot more by running some examples and playing around with them than by reading all the docs here. - ---- - -## Install **Typer** - -The first step is to install **Typer**: - -
- -```console -$ pip install typer ----> 100% -Successfully installed typer click shellingham rich -``` - -
- -By default, `typer` comes with `rich` and `shellingham`. - -/// note +Using it in your editor is what really shows you the benefits of **Typer**, seeing how little code you have to write, all the **inline errors**, **autocompletion**, etc. -If you are an advanced user and want to opt out of these default extra dependencies, you can instead install `typer-slim`. - -```bash -pip install typer -``` - -...includes the same optional dependencies as: - -```bash -pip install "typer-slim[standard]" -``` +And running the examples is what will really help you **understand** what is going on. -/// +You can learn a lot more by **running some examples** and **playing around** with them than by reading all the docs here. diff --git a/docs/tutorial/install.md b/docs/tutorial/install.md new file mode 100644 index 0000000000..09ee0cfc2c --- /dev/null +++ b/docs/tutorial/install.md @@ -0,0 +1,33 @@ +# Install **Typer** + +The first step is to install **Typer**. + +First, make sure you create your [virtual environment](../virtual-environments.md){.internal-link target=_blank}, activate it, and then install it, for example with: + +
+ +```console +$ pip install typer +---> 100% +Successfully installed typer click shellingham rich +``` + +
+ +By default, `typer` comes with `rich` and `shellingham`. + +/// note + +If you are an advanced user and want to opt out of these default extra dependencies, you can instead install `typer-slim`. + +```bash +pip install typer +``` + +...includes the same optional dependencies as: + +```bash +pip install "typer-slim[standard]" +``` + +/// diff --git a/docs/virtual-environments.md b/docs/virtual-environments.md new file mode 100644 index 0000000000..5a8ffd4013 --- /dev/null +++ b/docs/virtual-environments.md @@ -0,0 +1,844 @@ +# 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. + +/// info + +If you already know about virtual environments, how to create them and use them, you might want to skip this section. 🤓 + +/// + +/// 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. + +/// + +/// info + +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. + +/// + +## 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. + +
+ +```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 +$ cd awesome-project +``` + +
+ +## 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` 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): + +
+ +```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` 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` 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`: + +
+ +```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`: + +
+ +```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 +* PyCharm + +/// 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. + +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. + +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 it's 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): + +
+ +```console +$ source .venv/Scripts/activate +``` + +
+ +//// + +That command will create or modify some [environment variables](environment-variables.md){.internal-link target=_blank} 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){.internal-link target=_blank} 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 +``` + +
+ +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`. + +
+ +```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 🐺 +``` + +
+ +## 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. + +`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. 🤓 + +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**. 😎 diff --git a/mkdocs.yml b/mkdocs.yml index cf15b162d9..ead95508a0 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -72,6 +72,9 @@ nav: - features.md - Tutorial - User Guide: - tutorial/index.md + - environment-variables.md + - virtual-environments.md + - tutorial/install.md - tutorial/first-steps.md - tutorial/printing.md - tutorial/terminating.md From 20643c31fecdb1dfdf8c0a44e9dbaf987f716e12 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 23 Aug 2024 17:57:14 +0000 Subject: [PATCH 02/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index e16bf5488c..2ea245d7f5 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,6 +2,10 @@ ## Latest Changes +### Docs + +* 📝 Add docs for virtual environments, environment variables, and update contributing. PR [#946](https://github.com/fastapi/typer/pull/946) by [@tiangolo](https://github.com/tiangolo). + ### Internal * 🙈 Remove extra line in .gitignore. PR [#936](https://github.com/fastapi/typer/pull/936) by [@tiangolo](https://github.com/tiangolo). From cb196cf1027fad0336d67f20f8840da71d8b5cf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Fri, 23 Aug 2024 14:10:39 -0500 Subject: [PATCH 03/73] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix=20typo=20in=20`d?= =?UTF-8?q?ocs/contributing.md`=20(#947)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/contributing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contributing.md b/docs/contributing.md index bcb2a78960..047fec98a5 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -8,7 +8,7 @@ If you already cloned the +``` + +And you should see the completion working: + +```console +run -- Run the provided Typer app. +utils -- Extra utility commands for Typer apps. +``` + +And the same for the commands in your `demo.py` file: + +```console +$ typer demo.py run + +hello goodbye +``` + +You can also check the configuration file using `nano` or `vim`, for example: + +```bash +nano ~/.zshrc +``` + +It will show some content like: + +```bash +fpath+=~/.zfunc; autoload -Uz compinit; compinit + + +zstyle ':completion:*' menu select +``` + +If you exit from the container, you can start a new one, you will probably have to install the packages again and install completion again. + +Using this process, you can test all the shells, with their completions, being able to start from scratch quickly in a fresh container, and verifying that everything works as expected. + ## Docs First, make sure you set up your environment as described above, that will install all the requirements. diff --git a/scripts/docker/Dockerfile b/scripts/docker/Dockerfile new file mode 100644 index 0000000000..738867a954 --- /dev/null +++ b/scripts/docker/Dockerfile @@ -0,0 +1,22 @@ +FROM python:latest + +# Add Fish +RUN echo 'deb http://download.opensuse.org/repositories/shells:/fish:/release:/3/Debian_12/ /' | tee /etc/apt/sources.list.d/shells:fish:release:3.list +RUN curl -fsSL https://download.opensuse.org/repositories/shells:fish:release:3/Debian_12/Release.key | gpg --dearmor | tee /etc/apt/trusted.gpg.d/shells_fish_release_3.gpg > /dev/null + +# Install packages including Fish, Zsh, PowerShell +RUN apt-get update && apt-get install -y \ + wget \ + apt-transport-https \ + software-properties-common \ + nano \ + vim \ + fish \ + zsh \ + && wget https://github.com/PowerShell/PowerShell/releases/download/v7.4.4/powershell_7.4.4-1.deb_amd64.deb \ + && dpkg -i powershell_7.4.4-1.deb_amd64.deb + +# Install uv +COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv + +ENV UV_SYSTEM_PYTHON=1 diff --git a/scripts/docker/compose.yaml b/scripts/docker/compose.yaml new file mode 100644 index 0000000000..0f9d960d34 --- /dev/null +++ b/scripts/docker/compose.yaml @@ -0,0 +1,7 @@ +services: + typer: + build: . + volumes: + - ../../:/code + working_dir: /code + command: sleep infinity From 3b8199739841ea5f213b4d180d14cf1c8ebb56a4 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 24 Aug 2024 14:38:15 +0000 Subject: [PATCH 10/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 8240dfdce9..bfe03d8add 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -4,6 +4,7 @@ ### Docs +* 📝 Add docs and scripts to test completion in different shells. PR [#953](https://github.com/fastapi/typer/pull/953) by [@tiangolo](https://github.com/tiangolo). * ✏️ Fix a typo in `docs/virtual-environments.md`. PR [#952](https://github.com/fastapi/typer/pull/952) by [@tiangolo](https://github.com/tiangolo). * ✏️ Fix typo in `docs/contributing.md`. PR [#947](https://github.com/fastapi/typer/pull/947) by [@tiangolo](https://github.com/tiangolo). * 📝 Add docs for virtual environments, environment variables, and update contributing. PR [#946](https://github.com/fastapi/typer/pull/946) by [@tiangolo](https://github.com/tiangolo). From 7e944ac68d157e4d3da9637d3d26b94fb7bdd853 Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 24 Aug 2024 21:09:20 +0100 Subject: [PATCH 11/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20the=20Progress=20?= =?UTF-8?q?Bar=20tutorial=20with=20correct=20output=20(#199)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: svlandeg --- docs/tutorial/progressbar.md | 2 +- docs_src/progressbar/tutorial006.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/tutorial/progressbar.md b/docs/tutorial/progressbar.md index 250ea49382..c56cf50b29 100644 --- a/docs/tutorial/progressbar.md +++ b/docs/tutorial/progressbar.md @@ -246,5 +246,5 @@ Check it:
python main.py -Processed 100 things in batches. +Processed 1000 things in batches.
diff --git a/docs_src/progressbar/tutorial006.py b/docs_src/progressbar/tutorial006.py index ac94a3ed3e..d83b0da7cb 100644 --- a/docs_src/progressbar/tutorial006.py +++ b/docs_src/progressbar/tutorial006.py @@ -9,6 +9,8 @@ def main(): for batch in range(4): # Fake processing time time.sleep(1) + # Increment by 250 on each loop iteration + # (it will take 4 seconds to reach 1000) progress.update(250) print(f"Processed {total} things in batches.") From b98d0fecb9f8bdba81b1dabd2da2034948f4de8a Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 24 Aug 2024 20:09:40 +0000 Subject: [PATCH 12/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index bfe03d8add..e1df216ffd 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -4,6 +4,7 @@ ### Docs +* 📝 Update the Progress Bar tutorial with correct output. PR [#199](https://github.com/fastapi/typer/pull/199) by [@n1ckdm](https://github.com/n1ckdm). * 📝 Add docs and scripts to test completion in different shells. PR [#953](https://github.com/fastapi/typer/pull/953) by [@tiangolo](https://github.com/tiangolo). * ✏️ Fix a typo in `docs/virtual-environments.md`. PR [#952](https://github.com/fastapi/typer/pull/952) by [@tiangolo](https://github.com/tiangolo). * ✏️ Fix typo in `docs/contributing.md`. PR [#947](https://github.com/fastapi/typer/pull/947) by [@tiangolo](https://github.com/tiangolo). From 846dc41ffc4f6835851411ce40be5e3d8489868e Mon Sep 17 00:00:00 2001 From: Chris Burr Date: Sat, 24 Aug 2024 22:10:55 +0200 Subject: [PATCH 13/73] =?UTF-8?q?=F0=9F=9A=B8=20Improve=20assertion=20erro?= =?UTF-8?q?r=20message=20if=20a=20group=20is=20not=20a=20valid=20subclass?= =?UTF-8?q?=20(#425)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sebastián Ramírez Co-authored-by: svlandeg --- typer/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typer/main.py b/typer/main.py index f7d634d8bc..8d915527cd 100644 --- a/typer/main.py +++ b/typer/main.py @@ -505,7 +505,7 @@ def get_group_from_info( context_param_name, ) = get_params_convertors_ctx_param_name_from_function(solved_info.callback) cls = solved_info.cls or TyperGroup - assert issubclass(cls, TyperGroup) + assert issubclass(cls, TyperGroup), f"{cls} should be a subclass of {TyperGroup}" group = cls( name=solved_info.name or "", commands=commands, From a1e80f396fe921b611d8e613e1402ed9d6d5ee7b Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 24 Aug 2024 20:11:41 +0000 Subject: [PATCH 14/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index e1df216ffd..cc173956e7 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,6 +2,10 @@ ## Latest Changes +### Features + +* 🚸 Improve assertion error message if a group is not a valid subclass. PR [#425](https://github.com/fastapi/typer/pull/425) by [@chrisburr](https://github.com/chrisburr). + ### Docs * 📝 Update the Progress Bar tutorial with correct output. PR [#199](https://github.com/fastapi/typer/pull/199) by [@n1ckdm](https://github.com/n1ckdm). From 1da8e8691e0103b8e411d013371c39e984303a06 Mon Sep 17 00:00:00 2001 From: Kinuax Date: Sat, 24 Aug 2024 22:14:02 +0200 Subject: [PATCH 15/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20package=20docs=20?= =?UTF-8?q?with=20the=20latest=20versions=20of=20Typer=20and=20Poetry=20(#?= =?UTF-8?q?781)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: svlandeg --- docs/tutorial/package.md | 99 +++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 56 deletions(-) diff --git a/docs/tutorial/package.md b/docs/tutorial/package.md index cb04fd6d2c..617c93cd0b 100644 --- a/docs/tutorial/package.md +++ b/docs/tutorial/package.md @@ -49,41 +49,34 @@ cd ./rick-portal-gun ## Dependencies and environment -Add `typer[all]` to your dependencies: +Add `typer` to your dependencies:
```console -$ poetry add "typer[all]" +$ poetry add typer // It creates a virtual environment for your project Creating virtualenv rick-portal-gun-w31dJa0b-py3.10 in /home/rick/.cache/pypoetry/virtualenvs -Using version ^0.1.0 for typer +Using version ^0.12.0 for typer Updating dependencies Resolving dependencies... (1.2s) -Writing lock file - ---> 100% -Package operations: 15 installs, 0 updates, 0 removals - - - Installing zipp (3.1.0) - - Installing importlib-metadata (1.5.0) - - Installing pyparsing (2.4.6) - - Installing six (1.14.0) - - Installing attrs (19.3.0) - - Installing click (7.1.1) - - Installing colorama (0.4.3) - - Installing more-itertools (8.2.0) - - Installing packaging (20.3) - - Installing pluggy (0.13.1) - - Installing py (1.8.1) - - Installing shellingham (1.3.2) - - Installing wcwidth (0.1.8) - - Installing pytest (5.4.1) - - Installing typer (0.0.11) +Package operations: 8 installs, 0 updates, 0 removals + + - Installing mdurl (0.1.2) + - Installing markdown-it-py (3.0.0) + - Installing pygments (2.17.2) + - Installing click (8.1.7) + - Installing rich (13.7.1) + - Installing shellingham (1.5.4) + - Installing typing-extensions (4.11.0) + - Installing typer (0.12.3) + +Writing lock file // Activate that new virtual environment $ poetry shell @@ -106,8 +99,7 @@ You can see that you have a generated project structure that looks like: ├── rick_portal_gun │   └── __init__.py └── tests - ├── __init__.py - └── test_rick_portal_gun.py + └── __init__.py ``` ## Create your app @@ -183,14 +175,11 @@ rick-portal-gun = "rick_portal_gun.main:app" [tool.poetry.dependencies] python = "^3.10" -typer = {extras = ["all"], version = "^0.1.0"} - -[tool.poetry.dev-dependencies] -pytest = "^5.2" +typer = "^0.12.0" [build-system] -requires = ["poetry>=0.12"] -build-backend = "poetry.masonry.api" +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" ``` Here's what that line means: @@ -239,7 +228,7 @@ Installing dependencies from lock file No dependencies to install or update - - Installing rick-portal-gun (0.1.0) + - Installing the current project: rick-portal-gun (0.1.0) ```
@@ -258,7 +247,7 @@ $ which rick-portal-gun /home/rick/.cache/pypoetry/virtualenvs/rick-portal-gun-w31dJa0b-py3.10/bin/rick-portal-gun // Try it -$ rick-portal-gun +$ rick-portal-gun --help // You get all the standard help Usage: rick-portal-gun [OPTIONS] COMMAND [ARGS]... @@ -292,7 +281,6 @@ $ poetry build Building rick-portal-gun (0.1.0) - Building sdist - Built rick-portal-gun-0.1.0.tar.gz - - Building wheel - Built rick_portal_gun-0.1.0-py3-none-any.whl ``` @@ -320,7 +308,7 @@ Now you can open another terminal and install that package from the file for you
```console -$ pip install --user /home/rock/code/rick-portal-gun/dist/rick_portal_gun-0.1.0-py3-none-any.whl +$ pip install --user /home/rick/rick-portal-gun/dist/rick_portal_gun-0.1.0-py3-none-any.whl ---> 100% ``` @@ -361,7 +349,7 @@ Having it installed globally (and not in a single environment), you can now inst ```console $ rick-portal-gun --install-completion -zsh completion installed in /home/user/.zshrc. +zsh completion installed in /home/rick/.zshrc. Completion will take effect once you restart the terminal. ``` @@ -439,8 +427,7 @@ The file would live right beside `__init__.py`: │ ├── __main__.py │ └── main.py └── tests - ├── __init__.py - └── test_rick_portal_gun.py + └── __init__.py ``` No other file has to import it, you don't have to reference it in your `pyproject.toml` or anything else, it just works by default, as it is standard Python behavior. @@ -457,7 +444,7 @@ Now, after installing your package, if you call it with `python -m` it will work
```console -$ python -m rick_portal_gun +$ python -m rick_portal_gun --help Usage: __main__.py [OPTIONS] COMMAND [ARGS]... @@ -513,7 +500,7 @@ You can pass all the arguments and keyword arguments you could pass to a Click a
```console -$ python -m rick_portal_gun +$ python -m rick_portal_gun --help Usage: rick-portal-gun [OPTIONS] COMMAND [ARGS]... @@ -603,7 +590,6 @@ $ poetry publish --build Building rick-portal-gun (0.1.0) - Building sdist - Built rick-portal-gun-0.1.0.tar.gz - - Building wheel - Built rick_portal_gun-0.1.0-py3-none-any.whl @@ -630,10 +616,10 @@ $ pip uninstall rick-portal-gun Found existing installation: rick-portal-gun 0.1.0 Uninstalling rick-portal-gun-0.1.0: Would remove: - /home/user/.local/bin/rick-portal-gun - /home/user/.local/lib/python3.10/site-packages/rick_portal_gun-0.1.0.dist-info/* - /home/user/.local/lib/python3.10/site-packages/rick_portal_gun/* -# Proceed (y/n)? $ y + /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 ``` @@ -648,11 +634,16 @@ $ 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 (1.8 kB) -Requirement already satisfied: typer[all]<0.0.12,>=0.0.11 in ./.local/lib/python3.10/site-packages (from rick-portal-gun) (0.0.11) -Requirement already satisfied: click<7.2.0,>=7.1.1 in ./anaconda3/lib/python3.10/site-packages (from typer[all]<0.0.12,>=0.0.11->rick-portal-gun) (7.1.1) -Requirement already satisfied: colorama; extra == "all" in ./anaconda3/lib/python3.10/site-packages (from typer[all]<0.0.12,>=0.0.11->rick-portal-gun) (0.4.3) -Requirement already satisfied: shellingham; extra == "all" in ./anaconda3/lib/python3.10/site-packages (from typer[all]<0.0.12,>=0.0.11->rick-portal-gun) (1.3.1) + 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: click>=8.0.0 in ./.local/lib/python3.10/site-packages (from typer<0.13.0,>=0.12.3->rick-portal-gun==0.1.0) (8.1.7) +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 ``` @@ -715,14 +706,11 @@ rick-portal-gun = "rick_portal_gun.main:app" [tool.poetry.dependencies] python = "^3.10" -typer = {extras = ["all"], version = "^0.1.0"} - -[tool.poetry.dev-dependencies] -pytest = "^5.2" +typer = "^0.12.0" [build-system] -requires = ["poetry>=0.12"] -build-backend = "poetry.masonry.api" +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" ``` And in the file `rick_portal_gun/__init__.py`: @@ -743,7 +731,6 @@ $ poetry publish --build Building rick-portal-gun (0.2.0) - Building sdist - Built rick-portal-gun-0.2.0.tar.gz - - Building wheel - Built rick_portal_gun-0.2.0-py3-none-any.whl From 9c8b5f0663d8423a49853aa9118483e95c2c7438 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 24 Aug 2024 20:14:21 +0000 Subject: [PATCH 16/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index cc173956e7..1b30a8633b 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -8,6 +8,7 @@ ### Docs +* 📝 Update package docs with the latest versions of Typer and Poetry. PR [#781](https://github.com/fastapi/typer/pull/781) by [@kinuax](https://github.com/kinuax). * 📝 Update the Progress Bar tutorial with correct output. PR [#199](https://github.com/fastapi/typer/pull/199) by [@n1ckdm](https://github.com/n1ckdm). * 📝 Add docs and scripts to test completion in different shells. PR [#953](https://github.com/fastapi/typer/pull/953) by [@tiangolo](https://github.com/tiangolo). * ✏️ Fix a typo in `docs/virtual-environments.md`. PR [#952](https://github.com/fastapi/typer/pull/952) by [@tiangolo](https://github.com/tiangolo). From 113c13b555937f28b12271bf4dc9dc16b163f52d Mon Sep 17 00:00:00 2001 From: OhioDschungel6 <39698795+OhioDschungel6@users.noreply.github.com> Date: Sat, 24 Aug 2024 22:14:51 +0200 Subject: [PATCH 17/73] =?UTF-8?q?=F0=9F=93=9D=20Fix=20broken=20link=20(#83?= =?UTF-8?q?5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: svlandeg --- docs/alternatives.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/alternatives.md b/docs/alternatives.md index 656a4ef660..1f1b7904de 100644 --- a/docs/alternatives.md +++ b/docs/alternatives.md @@ -22,7 +22,7 @@ Provide a better development experience than just reading *CLI Parameters* by ha /// -### Hug +### Hug Hug is a library to create APIs and CLIs, it uses parameters in functions to declare the required data. From 6ce2feb6e2784d68ec042c3305f6c98279186cdf Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 24 Aug 2024 20:15:57 +0000 Subject: [PATCH 18/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 1b30a8633b..3e90ffde83 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -8,6 +8,7 @@ ### Docs +* 📝 Fix broken link. PR [#835](https://github.com/fastapi/typer/pull/835) by [@OhioDschungel6](https://github.com/OhioDschungel6). * 📝 Update package docs with the latest versions of Typer and Poetry. PR [#781](https://github.com/fastapi/typer/pull/781) by [@kinuax](https://github.com/kinuax). * 📝 Update the Progress Bar tutorial with correct output. PR [#199](https://github.com/fastapi/typer/pull/199) by [@n1ckdm](https://github.com/n1ckdm). * 📝 Add docs and scripts to test completion in different shells. PR [#953](https://github.com/fastapi/typer/pull/953) by [@tiangolo](https://github.com/tiangolo). From 8b7814d49d40ba01946ea24e5f278312be416447 Mon Sep 17 00:00:00 2001 From: Sofie Van Landeghem Date: Sat, 24 Aug 2024 22:16:36 +0200 Subject: [PATCH 19/73] =?UTF-8?q?=F0=9F=94=A5=20Clean=20up=20redundant=20c?= =?UTF-8?q?ode=20(#858)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_completion/test_completion_install.py | 5 ----- typer/main.py | 3 +-- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/tests/test_completion/test_completion_install.py b/tests/test_completion/test_completion_install.py index d1a4695bc8..7c9054e250 100644 --- a/tests/test_completion/test_completion_install.py +++ b/tests/test_completion/test_completion_install.py @@ -133,11 +133,6 @@ def test_completion_install_fish(): assert "Completion will take effect once you restart the terminal" in result.stdout -runner = CliRunner() -app = typer.Typer() -app.command()(mod.main) - - def test_completion_install_powershell(): completion_path: Path = ( Path.home() / ".config/powershell/Microsoft.PowerShell_profile.ps1" diff --git a/typer/main.py b/typer/main.py index 8d915527cd..20350e8bcc 100644 --- a/typer/main.py +++ b/typer/main.py @@ -436,7 +436,6 @@ def solve_typer_info_help(typer_info: TyperInfo) -> str: def solve_typer_info_defaults(typer_info: TyperInfo) -> TyperInfo: values: Dict[str, Any] = {} - name = None for name, value in typer_info.__dict__.items(): # Priority 1: Value was set in app.add_typer() if not isinstance(value, DefaultPlaceholder): @@ -816,7 +815,7 @@ def get_click_param( else: default_value = param.default parameter_info = OptionInfo() - annotation: Any = Any + annotation: Any if not param.annotation == param.empty: annotation = param.annotation else: From fe8b82a380d9aaa5cf97faaa399b5738f22cbc37 Mon Sep 17 00:00:00 2001 From: Fabio Ramalho <6180271+fsramalho@users.noreply.github.com> Date: Sat, 24 Aug 2024 21:17:30 +0100 Subject: [PATCH 20/73] =?UTF-8?q?=F0=9F=93=9DAdd=20missing=20`main.py`=20i?= =?UTF-8?q?n=20tutorial=20on=20CLI=20option=20names=20(#868)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: svlandeg --- docs/tutorial/options/name.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/options/name.md b/docs/tutorial/options/name.md index 7ece83f8b5..a724a5b563 100644 --- a/docs/tutorial/options/name.md +++ b/docs/tutorial/options/name.md @@ -81,7 +81,7 @@ Options: --help Show this message and exit. // Try it -$ python --name Camila +$ python main.py --name Camila Hello Camila ``` From 2ef93589d2e7dd679ced6b8c412b608ec9923952 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 24 Aug 2024 20:18:06 +0000 Subject: [PATCH 21/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 3e90ffde83..5ea02a5c04 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -6,6 +6,10 @@ * 🚸 Improve assertion error message if a group is not a valid subclass. PR [#425](https://github.com/fastapi/typer/pull/425) by [@chrisburr](https://github.com/chrisburr). +### Refactors + +* 🔥 Clean up redundant code. PR [#858](https://github.com/fastapi/typer/pull/858) by [@svlandeg](https://github.com/svlandeg). + ### Docs * 📝 Fix broken link. PR [#835](https://github.com/fastapi/typer/pull/835) by [@OhioDschungel6](https://github.com/OhioDschungel6). From f6cbe2545ad4fc563a96ef55532c6f8ef00921ae Mon Sep 17 00:00:00 2001 From: Sofie Van Landeghem Date: Sat, 24 Aug 2024 22:19:19 +0200 Subject: [PATCH 22/73] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Use=20F-strings=20in?= =?UTF-8?q?=20Click=20examples=20in=20docs=20(#891)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use f-string in Click examples --- docs_src/using_click/tutorial001.py | 2 +- docs_src/using_click/tutorial003.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs_src/using_click/tutorial001.py b/docs_src/using_click/tutorial001.py index 6fbf00649a..b3260c6494 100644 --- a/docs_src/using_click/tutorial001.py +++ b/docs_src/using_click/tutorial001.py @@ -7,7 +7,7 @@ def hello(count, name): """Simple program that greets NAME for a total of COUNT times.""" for x in range(count): - click.echo("Hello %s!" % name) + click.echo(f"Hello {name}!") if __name__ == "__main__": diff --git a/docs_src/using_click/tutorial003.py b/docs_src/using_click/tutorial003.py index a8f99e4623..5b3967c015 100644 --- a/docs_src/using_click/tutorial003.py +++ b/docs_src/using_click/tutorial003.py @@ -23,7 +23,7 @@ def callback(): @click.option("--name", prompt="Your name", help="The person to greet.") def hello(name): """Simple program that greets NAME for a total of COUNT times.""" - click.echo("Hello %s!" % name) + click.echo(f"Hello {name}!") typer_click_object = typer.main.get_command(app) From c86da2a7a4d649f8750d5ab36768c4f4499432ff Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 24 Aug 2024 20:20:03 +0000 Subject: [PATCH 23/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 5ea02a5c04..82af65b057 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -12,6 +12,7 @@ ### Docs +* 📝Add missing `main.py` in tutorial on CLI option names. PR [#868](https://github.com/fastapi/typer/pull/868) by [@fsramalho](https://github.com/fsramalho). * 📝 Fix broken link. PR [#835](https://github.com/fastapi/typer/pull/835) by [@OhioDschungel6](https://github.com/OhioDschungel6). * 📝 Update package docs with the latest versions of Typer and Poetry. PR [#781](https://github.com/fastapi/typer/pull/781) by [@kinuax](https://github.com/kinuax). * 📝 Update the Progress Bar tutorial with correct output. PR [#199](https://github.com/fastapi/typer/pull/199) by [@n1ckdm](https://github.com/n1ckdm). From a79fbafb17405d2bbdbc085c445d4b6c14189c2f Mon Sep 17 00:00:00 2001 From: Sofie Van Landeghem Date: Sat, 24 Aug 2024 22:20:58 +0200 Subject: [PATCH 24/73] =?UTF-8?q?=F0=9F=94=A5=20Remove=20Python=203.6=20sp?= =?UTF-8?q?ecific=20code=20paths=20(#850)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- typer/_typing.py | 94 +++++++++--------------------------------------- 1 file changed, 17 insertions(+), 77 deletions(-) diff --git a/typer/_typing.py b/typer/_typing.py index b1d2483da0..30cd612de9 100644 --- a/typer/_typing.py +++ b/typer/_typing.py @@ -3,6 +3,7 @@ # mypy: ignore-errors import sys +from collections.abc import Callable as Callable from os import PathLike from typing import ( TYPE_CHECKING, @@ -10,6 +11,7 @@ Any, ClassVar, Dict, + ForwardRef, Generator, List, Mapping, @@ -24,9 +26,13 @@ cast, get_type_hints, ) +from typing import Callable as TypingCallable from typing_extensions import Annotated, Literal +AnyCallable = TypingCallable[..., Any] +NoArgAnyCallable = TypingCallable[[], Any] + try: from typing import _TypingBase as typing_base # type: ignore except ImportError: @@ -45,28 +51,7 @@ TypesUnionType = () -if sys.version_info < (3, 7): - if TYPE_CHECKING: - - class ForwardRef: - def __init__(self, arg: Any): - pass - - def _eval_type(self, globalns: Any, localns: Any) -> Any: - pass - - else: - from typing import _ForwardRef as ForwardRef -else: - from typing import ForwardRef - - -if sys.version_info < (3, 7): - - def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Any: - return type_._eval_type(globalns, localns) - -elif sys.version_info < (3, 9): +if sys.version_info < (3, 9): def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Any: return type_._evaluate(globalns, localns) @@ -81,7 +66,7 @@ def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Any: if sys.version_info < (3, 9): # Ensure we always get all the whole `Annotated` hint, not just the annotated type. - # For 3.6 to 3.8, `get_type_hints` doesn't recognize `typing_extensions.Annotated`, + # For 3.7 to 3.8, `get_type_hints` doesn't recognize `typing_extensions.Annotated`, # so it already returns the full annotation get_all_type_hints = get_type_hints @@ -91,19 +76,6 @@ def get_all_type_hints(obj: Any, globalns: Any = None, localns: Any = None) -> A return get_type_hints(obj, globalns, localns, include_extras=True) -if sys.version_info < (3, 7): - from typing import Callable as Callable - - AnyCallable = Callable[..., Any] - NoArgAnyCallable = Callable[[], Any] -else: - from collections.abc import Callable as Callable - from typing import Callable as TypingCallable - - AnyCallable = TypingCallable[..., Any] - NoArgAnyCallable = TypingCallable[[], Any] - - # Annotated[...] is implemented by returning an instance of one of these classes, depending on # python/typing_extensions version. AnnotatedTypeNames = {"AnnotatedMeta", "_AnnotatedAlias"} @@ -133,22 +105,7 @@ def get_origin(tp: Type[Any]) -> Optional[Type[Any]]: return _typing_get_origin(tp) or getattr(tp, "__origin__", None) -if sys.version_info < (3, 7): # noqa: C901 (ignore complexity) - - def get_args(t: Type[Any]) -> Tuple[Any, ...]: - """Simplest get_args compatibility layer possible. - - The Python 3.6 typing module does not have `_GenericAlias` so - this won't work for everything. In particular this will not - support the `generics` module (we don't support generic models in - python 3.6). - - """ - if type(t).__name__ in AnnotatedTypeNames: - return t.__args__ + t.__metadata__ - return getattr(t, "__args__", ()) - -elif sys.version_info < (3, 8): # noqa: C901 +if sys.version_info < (3, 8): # noqa: C901 from typing import _GenericAlias def get_args(t: Type[Any]) -> Tuple[Any, ...]: @@ -352,8 +309,8 @@ def is_union(tp: Optional[Type[Any]]) -> bool: if sys.version_info < (3, 8): - # Even though this implementation is slower, we need it for python 3.6/3.7: - # In python 3.6/3.7 "Literal" is not a builtin type and uses a different + # Even though this implementation is slower, we need it for python 3.7: + # In python 3.7 "Literal" is not a builtin type and uses a different # mechanism. # for this reason `Literal[None] is Literal[None]` evaluates to `False`, # breaking the faster implementation used for the other python versions. @@ -431,10 +388,8 @@ def resolve_annotations( 1, ): value = ForwardRef(value, is_argument=False, is_class=True) - elif sys.version_info >= (3, 7): - value = ForwardRef(value, is_argument=False) else: - value = ForwardRef(value) + value = ForwardRef(value, is_argument=False) try: value = _eval_type(value, base_globals, None) except NameError: @@ -448,25 +403,12 @@ def is_callable_type(type_: Type[Any]) -> bool: return type_ is Callable or get_origin(type_) is Callable -if sys.version_info >= (3, 7): - - def is_literal_type(type_: Type[Any]) -> bool: - return Literal is not None and get_origin(type_) is Literal - - def literal_values(type_: Type[Any]) -> Tuple[Any, ...]: - return get_args(type_) +def is_literal_type(type_: Type[Any]) -> bool: + return Literal is not None and get_origin(type_) is Literal -else: - - def is_literal_type(type_: Type[Any]) -> bool: - return ( - Literal is not None - and hasattr(type_, "__values__") - and type_ == Literal[type_.__values__] - ) - def literal_values(type_: Type[Any]) -> Tuple[Any, ...]: - return type_.__values__ +def literal_values(type_: Type[Any]) -> Tuple[Any, ...]: + return get_args(type_) def all_literal_values(type_: Type[Any]) -> Tuple[Any, ...]: @@ -522,9 +464,7 @@ def _check_classvar(v: Optional[Type[Any]]) -> bool: if v is None: return False - return v.__class__ == ClassVar.__class__ and ( - sys.version_info < (3, 7) or getattr(v, "_name", None) == "ClassVar" - ) + return v.__class__ == ClassVar.__class__ and getattr(v, "_name", None) == "ClassVar" def is_classvar(ann_type: Type[Any]) -> bool: From 8ebcef82ee27fa8cb089b5245bf28e1a2543c49d Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 24 Aug 2024 20:22:32 +0000 Subject: [PATCH 25/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 82af65b057..ac07cf50b6 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -12,6 +12,7 @@ ### Docs +* ♻️ Use F-strings in Click examples in docs. PR [#891](https://github.com/fastapi/typer/pull/891) by [@svlandeg](https://github.com/svlandeg). * 📝Add missing `main.py` in tutorial on CLI option names. PR [#868](https://github.com/fastapi/typer/pull/868) by [@fsramalho](https://github.com/fsramalho). * 📝 Fix broken link. PR [#835](https://github.com/fastapi/typer/pull/835) by [@OhioDschungel6](https://github.com/OhioDschungel6). * 📝 Update package docs with the latest versions of Typer and Poetry. PR [#781](https://github.com/fastapi/typer/pull/781) by [@kinuax](https://github.com/kinuax). From 7420a15c3f179976dc4a93f46b36830fd4604d07 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 24 Aug 2024 20:24:54 +0000 Subject: [PATCH 26/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index ac07cf50b6..24ba7927a5 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -8,6 +8,7 @@ ### Refactors +* 🔥 Remove Python 3.6 specific code paths. PR [#850](https://github.com/fastapi/typer/pull/850) by [@svlandeg](https://github.com/svlandeg). * 🔥 Clean up redundant code. PR [#858](https://github.com/fastapi/typer/pull/858) by [@svlandeg](https://github.com/svlandeg). ### Docs From 1e327a8cd6491fb60778abbd2fe16d3f5e27420f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 24 Aug 2024 15:26:43 -0500 Subject: [PATCH 27/73] =?UTF-8?q?=E2=AC=86=20Bump=20pillow=20from=2010.3.0?= =?UTF-8?q?=20to=2010.4.0=20(#939)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [pillow](https://github.com/python-pillow/Pillow) from 10.3.0 to 10.4.0. - [Release notes](https://github.com/python-pillow/Pillow/releases) - [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst) - [Commits](https://github.com/python-pillow/Pillow/compare/10.3.0...10.4.0) --- updated-dependencies: - dependency-name: pillow dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-docs.txt b/requirements-docs.txt index 4ad8149417..e54908e1d2 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -7,7 +7,7 @@ pyyaml >=5.3.1,<7.0.0 # For Material for MkDocs, Chinese search # jieba==0.42.1 # For image processing by Material for MkDocs -pillow==10.3.0 +pillow==10.4.0 # For image processing by Material for MkDocs cairosvg==2.7.1 # mkdocstrings[python]==0.25.1 From 01b180ff2a68f0b696b94ed0756458b37b82b390 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 24 Aug 2024 15:27:02 -0500 Subject: [PATCH 28/73] =?UTF-8?q?=E2=AC=86=20Bump=20mkdocs-material=20from?= =?UTF-8?q?=209.5.18=20to=209.5.33=20(#945)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.18 to 9.5.33. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.18...9.5.33) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-docs.txt b/requirements-docs.txt index e54908e1d2..5635b9558e 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -1,6 +1,6 @@ -e . -mkdocs-material==9.5.18 +mkdocs-material==9.5.33 mdx-include >=1.4.1,<2.0.0 mkdocs-redirects>=1.2.1,<1.3.0 pyyaml >=5.3.1,<7.0.0 From bd1ce8a304c194da9f1e9b53e4d8170c35803650 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 24 Aug 2024 20:28:27 +0000 Subject: [PATCH 29/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 24ba7927a5..5ebc85d5f2 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -25,6 +25,7 @@ ### Internal +* ⬆ Bump pillow from 10.3.0 to 10.4.0. PR [#939](https://github.com/fastapi/typer/pull/939) by [@dependabot[bot]](https://github.com/apps/dependabot). * 👷 Fix issue-manager. PR [#948](https://github.com/fastapi/typer/pull/948) by [@tiangolo](https://github.com/tiangolo). * 🙈 Remove extra line in .gitignore. PR [#936](https://github.com/fastapi/typer/pull/936) by [@tiangolo](https://github.com/tiangolo). * ⬆ Update pytest-cov requirement from <5.0.0,>=2.10.0 to >=2.10.0,<6.0.0. PR [#844](https://github.com/fastapi/typer/pull/844) by [@dependabot[bot]](https://github.com/apps/dependabot). From 8c0c0032ce25bda9592a1a038144454a209090d1 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 24 Aug 2024 20:28:47 +0000 Subject: [PATCH 30/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 5ebc85d5f2..62462267ce 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -25,6 +25,7 @@ ### Internal +* ⬆ Bump mkdocs-material from 9.5.18 to 9.5.33. PR [#945](https://github.com/fastapi/typer/pull/945) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ Bump pillow from 10.3.0 to 10.4.0. PR [#939](https://github.com/fastapi/typer/pull/939) by [@dependabot[bot]](https://github.com/apps/dependabot). * 👷 Fix issue-manager. PR [#948](https://github.com/fastapi/typer/pull/948) by [@tiangolo](https://github.com/tiangolo). * 🙈 Remove extra line in .gitignore. PR [#936](https://github.com/fastapi/typer/pull/936) by [@tiangolo](https://github.com/tiangolo). From 493dd8e58c34b272dec24291807d98cf87c0ba1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Ra=C4=8Dinsk=C3=BD?= Date: Sat, 24 Aug 2024 22:36:12 +0200 Subject: [PATCH 31/73] =?UTF-8?q?=F0=9F=92=84=20Unify=20the=20width=20of?= =?UTF-8?q?=20the=20Rich=20console=20for=20help=20and=20errors=20(#788)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: svlandeg --- typer/main.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/typer/main.py b/typer/main.py index 20350e8bcc..06a6accbfe 100644 --- a/typer/main.py +++ b/typer/main.py @@ -39,10 +39,11 @@ try: import rich - from rich.console import Console from rich.traceback import Traceback - console_stderr = Console(stderr=True) + from . import rich_utils + + console_stderr = rich_utils._get_rich_console(stderr=True) except ImportError: # pragma: no cover rich = None # type: ignore @@ -70,12 +71,15 @@ def except_hook( supress_internal_dir_names = [typer_path, click_path] exc = exc_value if rich: + from .rich_utils import MAX_WIDTH + rich_tb = Traceback.from_exception( type(exc), exc, exc.__traceback__, show_locals=exception_config.pretty_exceptions_show_locals, suppress=supress_internal_dir_names, + width=MAX_WIDTH, ) console_stderr.print(rich_tb) return From 3f566edbeff698913df6022af69e7985ea17a4e2 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 24 Aug 2024 20:36:29 +0000 Subject: [PATCH 32/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 62462267ce..e35ebb7e81 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -4,6 +4,7 @@ ### Features +* 💄 Unify the width of the Rich console for help and errors. PR [#788](https://github.com/fastapi/typer/pull/788) by [@racinmat](https://github.com/racinmat). * 🚸 Improve assertion error message if a group is not a valid subclass. PR [#425](https://github.com/fastapi/typer/pull/425) by [@chrisburr](https://github.com/chrisburr). ### Refactors From 70ffd68cd962e4676e65a4c5cacdca532fbcc025 Mon Sep 17 00:00:00 2001 From: patricksurry Date: Sat, 24 Aug 2024 16:44:57 -0400 Subject: [PATCH 33/73] =?UTF-8?q?=F0=9F=90=9B=20Fix=20PowerShell=20complet?= =?UTF-8?q?ion=20with=20incomplete=20word=20(#360)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sebastián Ramírez Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: svlandeg --- .../test_tutorial003.py | 19 ++++++++++++++++++- .../test_tutorial003_an.py | 19 ++++++++++++++++++- typer/_completion_classes.py | 2 +- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/tests/test_tutorial/test_options_autocompletion/test_tutorial003.py b/tests/test_tutorial/test_options_autocompletion/test_tutorial003.py index ebd1e066ba..60304e9e55 100644 --- a/tests/test_tutorial/test_options_autocompletion/test_tutorial003.py +++ b/tests/test_tutorial/test_options_autocompletion/test_tutorial003.py @@ -9,7 +9,7 @@ runner = CliRunner() -def test_completion(): +def test_completion_zsh(): result = subprocess.run( [sys.executable, "-m", "coverage", "run", mod.__file__, " "], capture_output=True, @@ -25,6 +25,23 @@ def test_completion(): assert "Sebastian" in result.stdout +def test_completion_powershell(): + result = subprocess.run( + [sys.executable, "-m", "coverage", "run", mod.__file__, " "], + capture_output=True, + encoding="utf-8", + env={ + **os.environ, + "_TUTORIAL003.PY_COMPLETE": "complete_powershell", + "_TYPER_COMPLETE_ARGS": "tutorial003.py --name Seb", + "_TYPER_COMPLETE_WORD_TO_COMPLETE": "Seb", + }, + ) + assert "Camila" not in result.stdout + assert "Carlos" not in result.stdout + assert "Sebastian" in result.stdout + + def test_1(): result = runner.invoke(mod.app, ["--name", "Camila"]) assert result.exit_code == 0 diff --git a/tests/test_tutorial/test_options_autocompletion/test_tutorial003_an.py b/tests/test_tutorial/test_options_autocompletion/test_tutorial003_an.py index 8f12583e80..7688f108f5 100644 --- a/tests/test_tutorial/test_options_autocompletion/test_tutorial003_an.py +++ b/tests/test_tutorial/test_options_autocompletion/test_tutorial003_an.py @@ -9,7 +9,7 @@ runner = CliRunner() -def test_completion(): +def test_completion_zsh(): result = subprocess.run( [sys.executable, "-m", "coverage", "run", mod.__file__, " "], capture_output=True, @@ -25,6 +25,23 @@ def test_completion(): assert "Sebastian" in result.stdout +def test_completion_powershell(): + result = subprocess.run( + [sys.executable, "-m", "coverage", "run", mod.__file__, " "], + capture_output=True, + encoding="utf-8", + env={ + **os.environ, + "_TUTORIAL003_AN.PY_COMPLETE": "complete_powershell", + "_TYPER_COMPLETE_ARGS": "tutorial003.py --name Seb", + "_TYPER_COMPLETE_WORD_TO_COMPLETE": "Seb", + }, + ) + assert "Camila" not in result.stdout + assert "Carlos" not in result.stdout + assert "Sebastian" in result.stdout + + def test_1(): result = runner.invoke(mod.app, ["--name", "Camila"]) assert result.exit_code == 0 diff --git a/typer/_completion_classes.py b/typer/_completion_classes.py index 6bb25c13bd..71ba031860 100644 --- a/typer/_completion_classes.py +++ b/typer/_completion_classes.py @@ -175,7 +175,7 @@ def get_completion_args(self) -> Tuple[List[str], str]: completion_args = os.getenv("_TYPER_COMPLETE_ARGS", "") incomplete = os.getenv("_TYPER_COMPLETE_WORD_TO_COMPLETE", "") cwords = click.parser.split_arg_string(completion_args) - args = cwords[1:] + args = cwords[1:-1] if incomplete else cwords[1:] return args, incomplete def format_completion(self, item: click.shell_completion.CompletionItem) -> str: From 6b35a70cd128366fe70d019ebaea2556ab50d4f0 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 24 Aug 2024 20:45:13 +0000 Subject: [PATCH 34/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index e35ebb7e81..899595b15e 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -7,6 +7,10 @@ * 💄 Unify the width of the Rich console for help and errors. PR [#788](https://github.com/fastapi/typer/pull/788) by [@racinmat](https://github.com/racinmat). * 🚸 Improve assertion error message if a group is not a valid subclass. PR [#425](https://github.com/fastapi/typer/pull/425) by [@chrisburr](https://github.com/chrisburr). +### Fixes + +* 🐛 Fix PowerShell completion with incomplete word. PR [#360](https://github.com/fastapi/typer/pull/360) by [@patricksurry](https://github.com/patricksurry). + ### Refactors * 🔥 Remove Python 3.6 specific code paths. PR [#850](https://github.com/fastapi/typer/pull/850) by [@svlandeg](https://github.com/svlandeg). From afac2b80dca716f65e63f539b5885c37e031a0cf Mon Sep 17 00:00:00 2001 From: Sofie Van Landeghem Date: Sat, 24 Aug 2024 23:02:06 +0200 Subject: [PATCH 35/73] =?UTF-8?q?=F0=9F=90=9B=20=20Fix=20sourcing=20of=20c?= =?UTF-8?q?ompletion=20path=20for=20Git=20Bash=20(#801)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Sebastián Ramírez --- typer/_completion_shared.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/typer/_completion_shared.py b/typer/_completion_shared.py index a5ff7b196a..4f40c143c0 100644 --- a/typer/_completion_shared.py +++ b/typer/_completion_shared.py @@ -100,13 +100,13 @@ def install_bash(*, prog_name: str, complete_var: str, shell: str) -> Path: # It seems bash-completion is the official completion system for bash: # Ref: https://www.gnu.org/software/bash/manual/html_node/A-Programmable-Completion-Example.html # But installing in the locations from the docs doesn't seem to have effect - completion_path = Path.home() / f".bash_completions/{prog_name}.sh" + completion_path = Path.home() / ".bash_completions" / f"{prog_name}.sh" rc_path = Path.home() / ".bashrc" rc_path.parent.mkdir(parents=True, exist_ok=True) rc_content = "" if rc_path.is_file(): rc_content = rc_path.read_text() - completion_init_lines = [f"source {completion_path}"] + completion_init_lines = [f"source '{completion_path}'"] for line in completion_init_lines: if line not in rc_content: # pragma: no cover rc_content += f"\n{line}" From f17bb0675a33d23064b24c67754e8fa78e3dfc41 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 24 Aug 2024 21:02:25 +0000 Subject: [PATCH 36/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 899595b15e..3386a19686 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -9,6 +9,7 @@ ### Fixes +* 🐛 Fix sourcing of completion path for Git Bash. PR [#801](https://github.com/fastapi/typer/pull/801) by [@svlandeg](https://github.com/svlandeg). * 🐛 Fix PowerShell completion with incomplete word. PR [#360](https://github.com/fastapi/typer/pull/360) by [@patricksurry](https://github.com/patricksurry). ### Refactors From fc2c54f0aa9508b4a72ae50cb3bcb3bd0e6c1a21 Mon Sep 17 00:00:00 2001 From: Sofie Van Landeghem Date: Sat, 24 Aug 2024 23:04:45 +0200 Subject: [PATCH 37/73] =?UTF-8?q?=F0=9F=90=9B=20Ensure=20`rich=5Fmarkup=5F?= =?UTF-8?q?mode=3DNone`=20disables=20Rich=20formatting=20(#859)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/tutorial/commands/help.md | 2 +- tests/test_rich_markup_mode.py | 40 ++++++++++++++++++++++++++++++++++ typer/core.py | 22 ++++++++++++------- typer/main.py | 11 ++++++++-- 4 files changed, 64 insertions(+), 11 deletions(-) create mode 100644 tests/test_rich_markup_mode.py diff --git a/docs/tutorial/commands/help.md b/docs/tutorial/commands/help.md index b9501b72ee..5a6668038a 100644 --- a/docs/tutorial/commands/help.md +++ b/docs/tutorial/commands/help.md @@ -208,7 +208,7 @@ Then you can use more formatting in the docstrings and the `help` parameter for /// info -By default, `rich_markup_mode` is `None`, which disables any rich text formatting. +By default, `rich_markup_mode` is `None` if Rich is not installed, and `"rich"` if it is installed. In the latter case, you can set `rich_markup_mode` to `None` to disable rich text formatting. /// diff --git a/tests/test_rich_markup_mode.py b/tests/test_rich_markup_mode.py new file mode 100644 index 0000000000..d9fe5bae4b --- /dev/null +++ b/tests/test_rich_markup_mode.py @@ -0,0 +1,40 @@ +import typer +import typer.completion +from typer.testing import CliRunner + +runner = CliRunner() +rounded = ["╭", "─", "┬", "╮", "│", "├", "┼", "┤", "╰", "┴", "╯"] + + +def test_rich_markup_mode_none(): + app = typer.Typer(rich_markup_mode=None) + + @app.command() + def main(arg: str): + """Main function""" + print(f"Hello {arg}") + + assert app.rich_markup_mode is None + + result = runner.invoke(app, ["World"]) + assert "Hello World" in result.stdout + + result = runner.invoke(app, ["--help"]) + assert all(c not in result.stdout for c in rounded) + + +def test_rich_markup_mode_rich(): + app = typer.Typer(rich_markup_mode="rich") + + @app.command() + def main(arg: str): + """Main function""" + print(f"Hello {arg}") + + assert app.rich_markup_mode == "rich" + + result = runner.invoke(app, ["World"]) + assert "Hello World" in result.stdout + + result = runner.invoke(app, ["--help"]) + assert any(c in result.stdout for c in rounded) diff --git a/typer/core.py b/typer/core.py index 31fece5a76..c5cdbfee8d 100644 --- a/typer/core.py +++ b/typer/core.py @@ -31,15 +31,18 @@ else: from typing_extensions import Literal +MarkupMode = Literal["markdown", "rich", None] + try: import rich from . import rich_utils + DEFAULT_MARKUP_MODE: MarkupMode = "rich" + except ImportError: # pragma: no cover rich = None # type: ignore - -MarkupMode = Literal["markdown", "rich", None] + DEFAULT_MARKUP_MODE = None # Copy from click.parser._split_opt @@ -167,6 +170,7 @@ def _main( complete_var: Optional[str] = None, standalone_mode: bool = True, windows_expand_args: bool = True, + rich_markup_mode: MarkupMode = DEFAULT_MARKUP_MODE, **extra: Any, ) -> Any: # Typer override, duplicated from click.main() to handle custom rich exceptions @@ -208,7 +212,7 @@ def _main( if not standalone_mode: raise # Typer override - if rich: + if rich and rich_markup_mode is not None: rich_utils.rich_format_error(e) else: e.show() @@ -238,7 +242,7 @@ def _main( if not standalone_mode: raise # Typer override - if rich: + if rich and rich_markup_mode is not None: rich_utils.rich_abort_error() else: click.echo(_("Aborted!"), file=sys.stderr) @@ -614,7 +618,7 @@ def __init__( hidden: bool = False, deprecated: bool = False, # Rich settings - rich_markup_mode: MarkupMode = None, + rich_markup_mode: MarkupMode = DEFAULT_MARKUP_MODE, rich_help_panel: Union[str, None] = None, ) -> None: super().__init__( @@ -665,11 +669,12 @@ def main( complete_var=complete_var, standalone_mode=standalone_mode, windows_expand_args=windows_expand_args, + rich_markup_mode=self.rich_markup_mode, **extra, ) def format_help(self, ctx: click.Context, formatter: click.HelpFormatter) -> None: - if not rich: + if not rich or self.rich_markup_mode is None: return super().format_help(ctx, formatter) return rich_utils.rich_format_help( obj=self, @@ -687,7 +692,7 @@ def __init__( Union[Dict[str, click.Command], Sequence[click.Command]] ] = None, # Rich settings - rich_markup_mode: MarkupMode = None, + rich_markup_mode: MarkupMode = DEFAULT_MARKUP_MODE, rich_help_panel: Union[str, None] = None, **attrs: Any, ) -> None: @@ -727,11 +732,12 @@ def main( complete_var=complete_var, standalone_mode=standalone_mode, windows_expand_args=windows_expand_args, + rich_markup_mode=self.rich_markup_mode, **extra, ) def format_help(self, ctx: click.Context, formatter: click.HelpFormatter) -> None: - if not rich: + if not rich or self.rich_markup_mode is None: return super().format_help(ctx, formatter) return rich_utils.rich_format_help( obj=self, diff --git a/typer/main.py b/typer/main.py index 06a6accbfe..013939bf20 100644 --- a/typer/main.py +++ b/typer/main.py @@ -15,7 +15,14 @@ from ._typing import get_args, get_origin, is_union from .completion import get_completion_inspect_parameters -from .core import MarkupMode, TyperArgument, TyperCommand, TyperGroup, TyperOption +from .core import ( + DEFAULT_MARKUP_MODE, + MarkupMode, + TyperArgument, + TyperCommand, + TyperGroup, + TyperOption, +) from .models import ( AnyType, ArgumentInfo, @@ -137,7 +144,7 @@ def __init__( deprecated: bool = Default(False), add_completion: bool = True, # Rich settings - rich_markup_mode: MarkupMode = None, + rich_markup_mode: MarkupMode = Default(DEFAULT_MARKUP_MODE), rich_help_panel: Union[str, None] = Default(None), pretty_exceptions_enable: bool = True, pretty_exceptions_show_locals: bool = True, From 6cc1f9a361414c09db45a0ffd36823fdfd405636 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 24 Aug 2024 21:05:03 +0000 Subject: [PATCH 38/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 3386a19686..69319e4eb5 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -9,6 +9,7 @@ ### Fixes +* 🐛 Ensure `rich_markup_mode=None` disables Rich formatting. PR [#859](https://github.com/fastapi/typer/pull/859) by [@svlandeg](https://github.com/svlandeg). * 🐛 Fix sourcing of completion path for Git Bash. PR [#801](https://github.com/fastapi/typer/pull/801) by [@svlandeg](https://github.com/svlandeg). * 🐛 Fix PowerShell completion with incomplete word. PR [#360](https://github.com/fastapi/typer/pull/360) by [@patricksurry](https://github.com/patricksurry). From a5b7557140eda1f494050a791d51469ca6483805 Mon Sep 17 00:00:00 2001 From: Sofie Van Landeghem Date: Sat, 24 Aug 2024 23:08:27 +0200 Subject: [PATCH 39/73] =?UTF-8?q?=E2=9C=85=20Add=20`needs=5Fbash`=20test?= =?UTF-8?q?=20fixture=20(#888)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- tests/test_completion/test_completion.py | 4 +++- tests/utils.py | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/test_completion/test_completion.py b/tests/test_completion/test_completion.py index 703373b226..36581aba09 100644 --- a/tests/test_completion/test_completion.py +++ b/tests/test_completion/test_completion.py @@ -5,9 +5,10 @@ from docs_src.commands.index import tutorial001 as mod -from ..utils import needs_linux +from ..utils import needs_bash, needs_linux +@needs_bash @needs_linux def test_show_completion(): result = subprocess.run( @@ -23,6 +24,7 @@ def test_show_completion(): assert "_TUTORIAL001.PY_COMPLETE=complete_bash" in result.stdout +@needs_bash @needs_linux def test_install_completion(): bash_completion_path: Path = Path.home() / ".bashrc" diff --git a/tests/utils.py b/tests/utils.py index 9b503ff799..8f51332ee2 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -2,6 +2,18 @@ import pytest +try: + import shellingham + from shellingham import ShellDetectionFailure + + shell = shellingham.detect_shell()[0] +except ImportError: # pragma: no cover + shellingham = None + shell = None +except ShellDetectionFailure: # pragma: no cover + shell = None + + needs_py310 = pytest.mark.skipif( sys.version_info < (3, 10), reason="requires python3.10+" ) @@ -9,3 +21,7 @@ needs_linux = pytest.mark.skipif( not sys.platform.startswith("linux"), reason="Test requires Linux" ) + +needs_bash = pytest.mark.skipif( + not shellingham or not shell or "bash" not in shell, reason="Test requires Bash" +) From a3378509cf2cc949473daf3c144ed759051de6c9 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 24 Aug 2024 21:08:45 +0000 Subject: [PATCH 40/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 69319e4eb5..3656b71eef 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -32,6 +32,7 @@ ### Internal +* ✅ Add `needs_bash` test fixture. PR [#888](https://github.com/fastapi/typer/pull/888) by [@svlandeg](https://github.com/svlandeg). * ⬆ Bump mkdocs-material from 9.5.18 to 9.5.33. PR [#945](https://github.com/fastapi/typer/pull/945) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ Bump pillow from 10.3.0 to 10.4.0. PR [#939](https://github.com/fastapi/typer/pull/939) by [@dependabot[bot]](https://github.com/apps/dependabot). * 👷 Fix issue-manager. PR [#948](https://github.com/fastapi/typer/pull/948) by [@tiangolo](https://github.com/tiangolo). From d93c0ac714386b39df87597e30149bf2c2a7e824 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sat, 24 Aug 2024 16:14:02 -0500 Subject: [PATCH 41/73] =?UTF-8?q?=F0=9F=94=A8=20Pre-install=20dependencies?= =?UTF-8?q?=20in=20Docker=20so=20that=20testing=20in=20Docker=20is=20faste?= =?UTF-8?q?r=20(#954)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/docker/Dockerfile | 6 ++++++ scripts/docker/compose.yaml | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/scripts/docker/Dockerfile b/scripts/docker/Dockerfile index 738867a954..b158e2db04 100644 --- a/scripts/docker/Dockerfile +++ b/scripts/docker/Dockerfile @@ -20,3 +20,9 @@ RUN apt-get update && apt-get install -y \ COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv ENV UV_SYSTEM_PYTHON=1 + +COPY . /code + +WORKDIR /code + +RUN uv pip install -r requirements.txt diff --git a/scripts/docker/compose.yaml b/scripts/docker/compose.yaml index 0f9d960d34..efe17f9efd 100644 --- a/scripts/docker/compose.yaml +++ b/scripts/docker/compose.yaml @@ -1,7 +1,8 @@ services: typer: - build: . + build: + context: ../../ + dockerfile: scripts/docker/Dockerfile volumes: - ../../:/code - working_dir: /code command: sleep infinity From 3ac3644d480677ae44c50e436920def381aab915 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 24 Aug 2024 21:14:19 +0000 Subject: [PATCH 42/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 3656b71eef..0571ef345f 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -32,6 +32,7 @@ ### Internal +* 🔨 Pre-install dependencies in Docker so that testing in Docker is faster. PR [#954](https://github.com/fastapi/typer/pull/954) by [@tiangolo](https://github.com/tiangolo). * ✅ Add `needs_bash` test fixture. PR [#888](https://github.com/fastapi/typer/pull/888) by [@svlandeg](https://github.com/svlandeg). * ⬆ Bump mkdocs-material from 9.5.18 to 9.5.33. PR [#945](https://github.com/fastapi/typer/pull/945) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ Bump pillow from 10.3.0 to 10.4.0. PR [#939](https://github.com/fastapi/typer/pull/939) by [@dependabot[bot]](https://github.com/apps/dependabot). From 88aefd4492696bd1afa7bdb49477dec2c29d7e77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sat, 24 Aug 2024 16:16:36 -0500 Subject: [PATCH 43/73] =?UTF-8?q?=F0=9F=94=96=20Release=20version=200.12.5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 2 ++ typer/__init__.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index 0571ef345f..1938376e72 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,6 +2,8 @@ ## Latest Changes +## 0.12.5 + ### Features * 💄 Unify the width of the Rich console for help and errors. PR [#788](https://github.com/fastapi/typer/pull/788) by [@racinmat](https://github.com/racinmat). diff --git a/typer/__init__.py b/typer/__init__.py index dd2531263d..b422dd00d3 100644 --- a/typer/__init__.py +++ b/typer/__init__.py @@ -1,6 +1,6 @@ """Typer, build great CLIs. Easy to code. Based on Python type hints.""" -__version__ = "0.12.4" +__version__ = "0.12.5" from shutil import get_terminal_size as get_terminal_size From 14bab0a34db333f2047433ba84ee197ddea4f207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sun, 25 Aug 2024 21:18:17 -0500 Subject: [PATCH 44/73] =?UTF-8?q?=F0=9F=91=B7=20Update=20`latest-changes`?= =?UTF-8?q?=20GitHub=20Action=20(#955)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/latest-changes.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/latest-changes.yml b/.github/workflows/latest-changes.yml index 31c4ea724b..6a3ac059a5 100644 --- a/.github/workflows/latest-changes.yml +++ b/.github/workflows/latest-changes.yml @@ -34,8 +34,7 @@ jobs: if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }} with: limit-access-to-actor: true - - uses: docker://tiangolo/latest-changes:0.3.0 - # - uses: tiangolo/latest-changes@main + - uses: tiangolo/latest-changes@0.3.1 with: token: ${{ secrets.GITHUB_TOKEN }} latest_changes_file: docs/release-notes.md From 0f9b8fd40fc9f88734452c7ad33d0c6e1722f6fc Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 26 Aug 2024 02:19:33 +0000 Subject: [PATCH 45/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 1938376e72..72ee06e795 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,6 +2,10 @@ ## Latest Changes +### Internal + +* 👷 Update `latest-changes` GitHub Action. PR [#955](https://github.com/fastapi/typer/pull/955) by [@tiangolo](https://github.com/tiangolo). + ## 0.12.5 ### Features From 1f684d40d42e85525a6040a393e4c81ec637ef6f Mon Sep 17 00:00:00 2001 From: Sofie Van Landeghem Date: Mon, 26 Aug 2024 11:21:33 +0200 Subject: [PATCH 46/73] =?UTF-8?q?=E2=9C=A8=20Show=20help=20items=20in=20or?= =?UTF-8?q?der=20of=20definition=20(#944)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/tutorial/commands/index.md | 33 ++++++++++++++ docs_src/commands/index/tutorial004.py | 17 +++++++ tests/assets/cli/multiapp-docs-title.md | 32 ++++++------- tests/assets/cli/multiapp-docs.md | 32 ++++++------- .../test_index/test_tutorial004.py | 45 +++++++++++++++++++ typer/core.py | 6 +++ 6 files changed, 133 insertions(+), 32 deletions(-) create mode 100644 docs_src/commands/index/tutorial004.py create mode 100644 tests/test_tutorial/test_commands/test_index/test_tutorial004.py diff --git a/docs/tutorial/commands/index.md b/docs/tutorial/commands/index.md index d2693849d7..f531c7cfb3 100644 --- a/docs/tutorial/commands/index.md +++ b/docs/tutorial/commands/index.md @@ -257,6 +257,39 @@ Commands:
+ +## Sorting of the commands + +Note that by design, **Typer** shows the commands in the order they've been declared. + +So, if we take our original example, with `create` and `delete` commands, and reverse the order in the Python file: + +```Python hl_lines="7 12" +{!../docs_src/commands/index/tutorial004.py!} +``` + +Then we will see the `delete` command first in the help output: + +
+ +```console +// Check the help +$ python main.py --help + +Usage: main.py [OPTIONS] COMMAND [ARGS]... + +Options: + --install-completion Install completion for the current shell. + --show-completion Show completion for the current shell, to copy it or customize the installation. + --help Show this message and exit. + +Commands: + delete + create +``` + +
+ ## Click Group If you come from Click, a `typer.Typer` app with subcommands is more or less the equivalent of a Click Group. diff --git a/docs_src/commands/index/tutorial004.py b/docs_src/commands/index/tutorial004.py new file mode 100644 index 0000000000..83419b695f --- /dev/null +++ b/docs_src/commands/index/tutorial004.py @@ -0,0 +1,17 @@ +import typer + +app = typer.Typer() + + +@app.command() +def delete(): + print("Deleting user: Hiro Hamada") + + +@app.command() +def create(): + print("Creating user: Hiro Hamada") + + +if __name__ == "__main__": + app() diff --git a/tests/assets/cli/multiapp-docs-title.md b/tests/assets/cli/multiapp-docs-title.md index e688a9ba91..ffde843736 100644 --- a/tests/assets/cli/multiapp-docs-title.md +++ b/tests/assets/cli/multiapp-docs-title.md @@ -18,41 +18,41 @@ The end **Commands**: -* `sub` * `top`: Top command +* `sub` -## `multiapp sub` +## `multiapp top` + +Top command **Usage**: ```console -$ multiapp sub [OPTIONS] COMMAND [ARGS]... +$ multiapp top [OPTIONS] ``` **Options**: * `--help`: Show this message and exit. -**Commands**: - -* `bye`: Say bye -* `hello`: Say Hello -* `hi`: Say Hi - -### `multiapp sub bye` - -Say bye +## `multiapp sub` **Usage**: ```console -$ multiapp sub bye [OPTIONS] +$ multiapp sub [OPTIONS] COMMAND [ARGS]... ``` **Options**: * `--help`: Show this message and exit. +**Commands**: + +* `hello`: Say Hello +* `hi`: Say Hi +* `bye`: Say bye + ### `multiapp sub hello` Say Hello @@ -87,14 +87,14 @@ $ multiapp sub hi [OPTIONS] [USER] * `--help`: Show this message and exit. -## `multiapp top` +### `multiapp sub bye` -Top command +Say bye **Usage**: ```console -$ multiapp top [OPTIONS] +$ multiapp sub bye [OPTIONS] ``` **Options**: diff --git a/tests/assets/cli/multiapp-docs.md b/tests/assets/cli/multiapp-docs.md index ed4592f5c8..67d02568db 100644 --- a/tests/assets/cli/multiapp-docs.md +++ b/tests/assets/cli/multiapp-docs.md @@ -18,41 +18,41 @@ The end **Commands**: -* `sub` * `top`: Top command +* `sub` -## `multiapp sub` +## `multiapp top` + +Top command **Usage**: ```console -$ multiapp sub [OPTIONS] COMMAND [ARGS]... +$ multiapp top [OPTIONS] ``` **Options**: * `--help`: Show this message and exit. -**Commands**: - -* `bye`: Say bye -* `hello`: Say Hello -* `hi`: Say Hi - -### `multiapp sub bye` - -Say bye +## `multiapp sub` **Usage**: ```console -$ multiapp sub bye [OPTIONS] +$ multiapp sub [OPTIONS] COMMAND [ARGS]... ``` **Options**: * `--help`: Show this message and exit. +**Commands**: + +* `hello`: Say Hello +* `hi`: Say Hi +* `bye`: Say bye + ### `multiapp sub hello` Say Hello @@ -87,14 +87,14 @@ $ multiapp sub hi [OPTIONS] [USER] * `--help`: Show this message and exit. -## `multiapp top` +### `multiapp sub bye` -Top command +Say bye **Usage**: ```console -$ multiapp top [OPTIONS] +$ multiapp sub bye [OPTIONS] ``` **Options**: diff --git a/tests/test_tutorial/test_commands/test_index/test_tutorial004.py b/tests/test_tutorial/test_commands/test_index/test_tutorial004.py new file mode 100644 index 0000000000..ae0139e93a --- /dev/null +++ b/tests/test_tutorial/test_commands/test_index/test_tutorial004.py @@ -0,0 +1,45 @@ +import subprocess +import sys + +from typer.testing import CliRunner + +from docs_src.commands.index import tutorial004 as mod + +app = mod.app + +runner = CliRunner() + + +def test_help(): + result = runner.invoke(app, ["--help"]) + assert result.exit_code == 0 + assert "[OPTIONS] COMMAND [ARGS]..." in result.output + print(result.output) + assert "Commands" in result.output + assert "create" in result.output + assert "delete" in result.output + # Test that the 'delete' command precedes the 'create' command in the help output + create_char = result.output.index("create") + delete_char = result.output.index("delete") + assert delete_char < create_char + + +def test_create(): + result = runner.invoke(app, ["create"]) + assert result.exit_code == 0 + assert "Creating user: Hiro Hamada" in result.output + + +def test_delete(): + result = runner.invoke(app, ["delete"]) + assert result.exit_code == 0 + assert "Deleting user: Hiro Hamada" in result.output + + +def test_script(): + result = subprocess.run( + [sys.executable, "-m", "coverage", "run", mod.__file__, "--help"], + capture_output=True, + encoding="utf-8", + ) + assert "Usage" in result.stdout diff --git a/typer/core.py b/typer/core.py index c5cdbfee8d..00e21da869 100644 --- a/typer/core.py +++ b/typer/core.py @@ -744,3 +744,9 @@ def format_help(self, ctx: click.Context, formatter: click.HelpFormatter) -> Non ctx=ctx, markup_mode=self.rich_markup_mode, ) + + def list_commands(self, ctx: click.Context) -> List[str]: + """Returns a list of subcommand names. + Note that in Click's Group class, these are sorted. + In Typer, we wish to maintain the original order of creation (cf Issue #933)""" + return [n for n, c in self.commands.items()] From ae82de02adebc6b265d00068f4d2c16420ffbfbf Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 26 Aug 2024 09:22:03 +0000 Subject: [PATCH 47/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 72ee06e795..ad028087d5 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,6 +2,10 @@ ## Latest Changes +### Features + +* ✨ Show help items in order of definition. PR [#944](https://github.com/fastapi/typer/pull/944) by [@svlandeg](https://github.com/svlandeg). + ### Internal * 👷 Update `latest-changes` GitHub Action. PR [#955](https://github.com/fastapi/typer/pull/955) by [@tiangolo](https://github.com/tiangolo). From 7d6db83b2d9b8a62b2044908f701260e03d53f86 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2024 18:25:11 -0500 Subject: [PATCH 48/73] =?UTF-8?q?=E2=AC=86=20Bump=20ruff=20from=200.2.0=20?= =?UTF-8?q?to=200.6.1=20(#938)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ⬆ Bump ruff from 0.2.0 to 0.6.1 Bumps [ruff](https://github.com/astral-sh/ruff) from 0.2.0 to 0.6.1. - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/v0.2.0...0.6.1) --- updated-dependencies: - dependency-name: ruff dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: svlandeg Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- requirements-tests.txt | 2 +- tests/test_ambiguous_params.py | 15 +++++---------- typer/main.py | 8 ++++---- typer/params.py | 12 ++++-------- typer/rich_utils.py | 6 +++--- 6 files changed, 18 insertions(+), 27 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3289dd0950..4ea5b873a6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,7 +14,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.2.0 + rev: v0.6.1 hooks: - id: ruff args: diff --git a/requirements-tests.txt b/requirements-tests.txt index ac6377c10d..0cacd0ecbe 100644 --- a/requirements-tests.txt +++ b/requirements-tests.txt @@ -6,7 +6,7 @@ coverage[toml] >=6.2,<8.0 pytest-xdist >=1.32.0,<4.0.0 pytest-sugar >=0.9.4,<1.1.0 mypy ==1.4.1 -ruff ==0.2.0 +ruff ==0.6.1 # Needed explicitly by typer-slim rich >=10.11.0 shellingham >=1.3.0 diff --git a/tests/test_ambiguous_params.py b/tests/test_ambiguous_params.py index 4dbf09b569..0693c8e9aa 100644 --- a/tests/test_ambiguous_params.py +++ b/tests/test_ambiguous_params.py @@ -29,8 +29,7 @@ def test_forbid_default_value_in_annotated_argument(): # This test case only works with `typer.Argument`. `typer.Option` uses positionals # for param_decls too. @app.command() - def cmd(my_param: Annotated[str, typer.Argument("foo")]): - ... # pragma: no cover + def cmd(my_param: Annotated[str, typer.Argument("foo")]): ... # pragma: no cover with pytest.raises(AnnotatedParamWithDefaultValueError) as excinfo: runner.invoke(app) @@ -64,8 +63,7 @@ def test_forbid_annotated_param_and_default_param(param, param_info_type): app = typer.Typer() @app.command() - def cmd(my_param: Annotated[str, param()] = param("foo")): - ... # pragma: no cover + def cmd(my_param: Annotated[str, param()] = param("foo")): ... # pragma: no cover with pytest.raises(MixedAnnotatedAndDefaultStyleError) as excinfo: runner.invoke(app) @@ -83,8 +81,7 @@ def test_forbid_multiple_typer_params_in_annotated(): @app.command() def cmd( my_param: Annotated[str, typer.Argument(), typer.Argument()], - ): - ... # pragma: no cover + ): ... # pragma: no cover with pytest.raises(MultipleTyperAnnotationsError) as excinfo: runner.invoke(app) @@ -121,8 +118,7 @@ def make_string(): @app.command() def cmd( my_param: Annotated[str, param(default_factory=make_string)] = "hello", - ): - ... # pragma: no cover + ): ... # pragma: no cover with pytest.raises(DefaultFactoryAndDefaultValueError) as excinfo: runner.invoke(app) @@ -171,8 +167,7 @@ def make_string(): @app.command() def cmd( my_param: str = param("hi", default_factory=make_string), - ): - ... # pragma: no cover + ): ... # pragma: no cover with pytest.raises(DefaultFactoryAndDefaultValueError) as excinfo: runner.invoke(app) diff --git a/typer/main.py b/typer/main.py index 013939bf20..c385c48c5f 100644 --- a/typer/main.py +++ b/typer/main.py @@ -715,9 +715,9 @@ def get_click_type( elif parameter_info.parser is not None: return click.types.FuncParamType(parameter_info.parser) - elif annotation == str: + elif annotation is str: return click.STRING - elif annotation == int: + elif annotation is int: if parameter_info.min is not None or parameter_info.max is not None: min_ = None max_ = None @@ -728,7 +728,7 @@ def get_click_type( return click.IntRange(min=min_, max=max_, clamp=parameter_info.clamp) else: return click.INT - elif annotation == float: + elif annotation is float: if parameter_info.min is not None or parameter_info.max is not None: return click.FloatRange( min=parameter_info.min, @@ -737,7 +737,7 @@ def get_click_type( ) else: return click.FLOAT - elif annotation == bool: + elif annotation is bool: return click.BOOL elif annotation == UUID: return click.UUID diff --git a/typer/params.py b/typer/params.py index 710a4cf136..2fd025c90d 100644 --- a/typer/params.py +++ b/typer/params.py @@ -68,8 +68,7 @@ def Option( path_type: Union[None, Type[str], Type[bytes]] = None, # Rich settings rich_help_panel: Union[str, None] = None, -) -> Any: - ... +) -> Any: ... # Overload for Option created with custom type 'click_type' @@ -132,8 +131,7 @@ def Option( path_type: Union[None, Type[str], Type[bytes]] = None, # Rich settings rich_help_panel: Union[str, None] = None, -) -> Any: - ... +) -> Any: ... def Option( @@ -305,8 +303,7 @@ def Argument( path_type: Union[None, Type[str], Type[bytes]] = None, # Rich settings rich_help_panel: Union[str, None] = None, -) -> Any: - ... +) -> Any: ... # Overload for Argument created with custom type 'click_type' @@ -361,8 +358,7 @@ def Argument( path_type: Union[None, Type[str], Type[bytes]] = None, # Rich settings rich_help_panel: Union[str, None] = None, -) -> Any: - ... +) -> Any: ... def Argument( diff --git a/typer/rich_utils.py b/typer/rich_utils.py index cf0538e914..900f4b4e87 100644 --- a/typer/rich_utils.py +++ b/typer/rich_utils.py @@ -68,9 +68,9 @@ STYLE_ABORTED = "red" _TERMINAL_WIDTH = getenv("TERMINAL_WIDTH") MAX_WIDTH = int(_TERMINAL_WIDTH) if _TERMINAL_WIDTH else None -COLOR_SYSTEM: Optional[ - Literal["auto", "standard", "256", "truecolor", "windows"] -] = "auto" # Set to None to disable colors +COLOR_SYSTEM: Optional[Literal["auto", "standard", "256", "truecolor", "windows"]] = ( + "auto" # Set to None to disable colors +) _TYPER_FORCE_DISABLE_TERMINAL = getenv("_TYPER_FORCE_DISABLE_TERMINAL") FORCE_TERMINAL = ( True From 127d074446e267645d4ef42fd39c650735d4c304 Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 28 Aug 2024 23:25:30 +0000 Subject: [PATCH 49/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index ad028087d5..395a185361 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -8,6 +8,7 @@ ### Internal +* ⬆ Bump ruff from 0.2.0 to 0.6.1. PR [#938](https://github.com/fastapi/typer/pull/938) by [@dependabot[bot]](https://github.com/apps/dependabot). * 👷 Update `latest-changes` GitHub Action. PR [#955](https://github.com/fastapi/typer/pull/955) by [@tiangolo](https://github.com/tiangolo). ## 0.12.5 From 76824fc369c57e50c533888807559a5f2382258a Mon Sep 17 00:00:00 2001 From: Sofie Van Landeghem Date: Thu, 29 Aug 2024 01:26:35 +0200 Subject: [PATCH 50/73] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix=20typo=20in=20fu?= =?UTF-8?q?nction=20name=20`=5Fmake=5Frich=5Ftext`=20(#959)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- typer/rich_utils.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/typer/rich_utils.py b/typer/rich_utils.py index 900f4b4e87..905c5a3dbf 100644 --- a/typer/rich_utils.py +++ b/typer/rich_utils.py @@ -144,7 +144,7 @@ def _get_rich_console(stderr: bool = False) -> Console: ) -def _make_rich_rext( +def _make_rich_text( *, text: str, style: str = "", markup_mode: MarkupMode ) -> Union[Markdown, Text]: """Take a string, remove indentations, and return styled text. @@ -194,7 +194,7 @@ def _get_help_text( # Remove single linebreaks if markup_mode != MARKUP_MODE_MARKDOWN and not first_line.startswith("\b"): first_line = first_line.replace("\n", " ") - yield _make_rich_rext( + yield _make_rich_text( text=first_line.strip(), style=STYLE_HELPTEXT_FIRST_LINE, markup_mode=markup_mode, @@ -217,7 +217,7 @@ def _get_help_text( # Join with double linebreaks if markdown remaining_lines = "\n\n".join(remaining_paragraphs) - yield _make_rich_rext( + yield _make_rich_text( text=remaining_lines, style=STYLE_HELPTEXT, markup_mode=markup_mode, @@ -272,7 +272,7 @@ def _get_parameter_help( for x in paragraphs ] items.append( - _make_rich_rext( + _make_rich_text( text="\n".join(paragraphs).strip(), style=STYLE_OPTION_HELP, markup_mode=markup_mode, @@ -331,7 +331,7 @@ def _make_command_help( paragraphs[0] = paragraphs[0].replace("\n", " ") elif paragraphs[0].startswith("\b"): paragraphs[0] = paragraphs[0].replace("\b\n", "") - return _make_rich_rext( + return _make_rich_text( text=paragraphs[0].strip(), style=STYLE_OPTION_HELP, markup_mode=markup_mode, @@ -674,7 +674,7 @@ def rich_format_help( # Remove single linebreaks, replace double with single lines = obj.epilog.split("\n\n") epilogue = "\n".join([x.replace("\n", " ").strip() for x in lines]) - epilogue_text = _make_rich_rext(text=epilogue, markup_mode=markup_mode) + epilogue_text = _make_rich_text(text=epilogue, markup_mode=markup_mode) console.print(Padding(Align(epilogue_text, pad=False), 1)) From fc2a63462565c5f01f9c805de2fd63b73459878f Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 28 Aug 2024 23:27:10 +0000 Subject: [PATCH 51/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 395a185361..d0a1d428a7 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -6,6 +6,10 @@ * ✨ Show help items in order of definition. PR [#944](https://github.com/fastapi/typer/pull/944) by [@svlandeg](https://github.com/svlandeg). +### Refactors + +* ✏️ Fix typo in function name `_make_rich_text`. PR [#959](https://github.com/fastapi/typer/pull/959) by [@svlandeg](https://github.com/svlandeg). + ### Internal * ⬆ Bump ruff from 0.2.0 to 0.6.1. PR [#938](https://github.com/fastapi/typer/pull/938) by [@dependabot[bot]](https://github.com/apps/dependabot). From 67a9eee9967d553446cb09f6367073271434db01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Todorovich?= Date: Wed, 28 Aug 2024 20:32:10 -0300 Subject: [PATCH 52/73] =?UTF-8?q?=F0=9F=94=A5=20Remove=20unused=20function?= =?UTF-8?q?ality=20from=20`=5Ftyping.py`=20file=20(#805)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: svlandeg --- pyproject.toml | 3 - typer/_typing.py | 477 +---------------------------------------------- typer/main.py | 3 +- typer/utils.py | 5 +- 4 files changed, 7 insertions(+), 481 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b64b18d911..ce9d61afa3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -194,9 +194,6 @@ ignore = [ # Loop control variable `x` not used within loop body "docs_src/using_click/tutorial001.py" = ["B007"] -# TODO: refactor _typing.py, remove unnecessary code -"typer/_typing.py" = ["UP036", "F822"] - [tool.ruff.lint.isort] known-third-party = ["typer", "click"] # For docs_src/subcommands/tutorial003/ diff --git a/typer/_typing.py b/typer/_typing.py index 30cd612de9..147a50540b 100644 --- a/typer/_typing.py +++ b/typer/_typing.py @@ -1,304 +1,39 @@ # Copied from pydantic 1.9.2 (the latest version to support python 3.6.) # https://github.com/pydantic/pydantic/blob/v1.9.2/pydantic/typing.py +# Reduced drastically to only include Typer-specific 3.7+ functionality # mypy: ignore-errors import sys -from collections.abc import Callable as Callable -from os import PathLike from typing import ( - TYPE_CHECKING, - AbstractSet, Any, - ClassVar, - Dict, - ForwardRef, - Generator, - List, - Mapping, - NewType, + Callable, Optional, - Sequence, - Set, Tuple, Type, Union, - _eval_type, - cast, - get_type_hints, ) -from typing import Callable as TypingCallable - -from typing_extensions import Annotated, Literal - -AnyCallable = TypingCallable[..., Any] -NoArgAnyCallable = TypingCallable[[], Any] - -try: - from typing import _TypingBase as typing_base # type: ignore -except ImportError: - from typing import _Final as typing_base # type: ignore - -try: - from typing import GenericAlias as TypingGenericAlias # type: ignore -except ImportError: - # python < 3.9 does not have GenericAlias (list[int], tuple[str, ...] and so on) - TypingGenericAlias = () - -try: - from types import UnionType as TypesUnionType # type: ignore -except ImportError: - # python < 3.10 does not have UnionType (str | int, byte | bool and so on) - TypesUnionType = () - - -if sys.version_info < (3, 9): - - def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Any: - return type_._evaluate(globalns, localns) - -else: - - def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Any: - # Even though it is the right signature for python 3.9, mypy complains with - # `error: Too many arguments for "_evaluate" of "ForwardRef"` hence the cast... - return cast(Any, type_)._evaluate(globalns, localns, set()) - - -if sys.version_info < (3, 9): - # Ensure we always get all the whole `Annotated` hint, not just the annotated type. - # For 3.7 to 3.8, `get_type_hints` doesn't recognize `typing_extensions.Annotated`, - # so it already returns the full annotation - get_all_type_hints = get_type_hints - -else: - - def get_all_type_hints(obj: Any, globalns: Any = None, localns: Any = None) -> Any: - return get_type_hints(obj, globalns, localns, include_extras=True) - - -# Annotated[...] is implemented by returning an instance of one of these classes, depending on -# python/typing_extensions version. -AnnotatedTypeNames = {"AnnotatedMeta", "_AnnotatedAlias"} - - -if sys.version_info < (3, 8): - - def get_origin(t: Type[Any]) -> Optional[Type[Any]]: - if type(t).__name__ in AnnotatedTypeNames: - return cast( - Type[Any], Annotated - ) # mypy complains about _SpecialForm in py3.6 - return getattr(t, "__origin__", None) - -else: - from typing import get_origin as _typing_get_origin - - def get_origin(tp: Type[Any]) -> Optional[Type[Any]]: - """ - We can't directly use `typing.get_origin` since we need a fallback to support - custom generic classes like `ConstrainedList` - It should be useless once https://github.com/cython/cython/issues/3537 is - solved and https://github.com/samuelcolvin/pydantic/pull/1753 is merged. - """ - if type(tp).__name__ in AnnotatedTypeNames: - return cast(Type[Any], Annotated) # mypy complains about _SpecialForm - return _typing_get_origin(tp) or getattr(tp, "__origin__", None) - - -if sys.version_info < (3, 8): # noqa: C901 - from typing import _GenericAlias - - def get_args(t: Type[Any]) -> Tuple[Any, ...]: - """Compatibility version of get_args for python 3.7. - - Mostly compatible with the python 3.8 `typing` module version - and able to handle almost all use cases. - """ - if type(t).__name__ in AnnotatedTypeNames: - return t.__args__ + t.__metadata__ - if isinstance(t, _GenericAlias): - res = t.__args__ - if t.__origin__ is Callable and res and res[0] is not Ellipsis: - res = (list(res[:-1]), res[-1]) - return res - return getattr(t, "__args__", ()) - -else: - from typing import get_args as _typing_get_args - - def _generic_get_args(tp: Type[Any]) -> Tuple[Any, ...]: - """ - In python 3.9, `typing.Dict`, `typing.List`, ... - do have an empty `__args__` by default (instead of the generic ~T for example). - In order to still support `Dict` for example and consider it as `Dict[Any, Any]`, - we retrieve the `_nparams` value that tells us how many parameters it needs. - """ - if hasattr(tp, "_nparams"): - return (Any,) * tp._nparams - return () - - def get_args(tp: Type[Any]) -> Tuple[Any, ...]: - """Get type arguments with all substitutions performed. - - For unions, basic simplifications used by Union constructor are performed. - Examples:: - get_args(Dict[str, int]) == (str, int) - get_args(int) == () - get_args(Union[int, Union[T, int], str][int]) == (int, str) - get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int]) - get_args(Callable[[], T][int]) == ([], int) - """ - if type(tp).__name__ in AnnotatedTypeNames: - return tp.__args__ + tp.__metadata__ - # the fallback is needed for the same reasons as `get_origin` (see above) - return ( - _typing_get_args(tp) or getattr(tp, "__args__", ()) or _generic_get_args(tp) - ) - - -if sys.version_info < (3, 9): - - def convert_generics(tp: Type[Any]) -> Type[Any]: - """Python 3.9 and older only supports generics from `typing` module. - They convert strings to ForwardRef automatically. - - Examples:: - typing.List['Hero'] == typing.List[ForwardRef('Hero')] - """ - return tp - -else: - from typing import _UnionGenericAlias # type: ignore - - from typing_extensions import _AnnotatedAlias - - def convert_generics(tp: Type[Any]) -> Type[Any]: - """ - Recursively searches for `str` type hints and replaces them with ForwardRef. - - Examples:: - convert_generics(list['Hero']) == list[ForwardRef('Hero')] - convert_generics(dict['Hero', 'Team']) == dict[ForwardRef('Hero'), ForwardRef('Team')] - convert_generics(typing.Dict['Hero', 'Team']) == typing.Dict[ForwardRef('Hero'), ForwardRef('Team')] - convert_generics(list[str | 'Hero'] | int) == list[str | ForwardRef('Hero')] | int - """ - origin = get_origin(tp) - if not origin or not hasattr(tp, "__args__"): - return tp - - args = get_args(tp) - - # typing.Annotated needs special treatment - if origin is Annotated: - return _AnnotatedAlias(convert_generics(args[0]), args[1:]) - - # recursively replace `str` instances inside of `GenericAlias` with `ForwardRef(arg)` - converted = tuple( - ForwardRef(arg) - if isinstance(arg, str) and isinstance(tp, TypingGenericAlias) - else convert_generics(arg) - for arg in args - ) - - if converted == args: - return tp - elif isinstance(tp, TypingGenericAlias): - return TypingGenericAlias(origin, converted) - elif isinstance(tp, TypesUnionType): - # recreate types.UnionType (PEP604, Python >= 3.10) - return _UnionGenericAlias(origin, converted) - else: - try: - setattr(tp, "__args__", converted) # noqa: B010 - except AttributeError: - pass - return tp +from typing_extensions import Literal, get_args, get_origin if sys.version_info < (3, 10): def is_union(tp: Optional[Type[Any]]) -> bool: return tp is Union - WithArgsTypes = (TypingGenericAlias,) - else: import types - import typing def is_union(tp: Optional[Type[Any]]) -> bool: return tp is Union or tp is types.UnionType # noqa: E721 - WithArgsTypes = (typing._GenericAlias, types.GenericAlias, types.UnionType) - - -if sys.version_info < (3, 9): - StrPath = Union[str, PathLike] -else: - StrPath = Union[str, PathLike] - # TODO: Once we switch to Cython 3 to handle generics properly - # (https://github.com/cython/cython/issues/2753), use following lines instead - # of the one above - # # os.PathLike only becomes subscriptable from Python 3.9 onwards - # StrPath = Union[str, PathLike[str]] - - -if TYPE_CHECKING: - # Only in Pydantic - # from .fields import ModelField - - TupleGenerator = Generator[Tuple[str, Any], None, None] - DictStrAny = Dict[str, Any] - DictAny = Dict[Any, Any] - SetStr = Set[str] - ListStr = List[str] - IntStr = Union[int, str] - AbstractSetIntStr = AbstractSet[IntStr] - DictIntStrAny = Dict[IntStr, Any] - MappingIntStrAny = Mapping[IntStr, Any] - CallableGenerator = Generator[AnyCallable, None, None] - ReprArgs = Sequence[Tuple[Optional[str], Any]] - AnyClassMethod = classmethod[Any] __all__ = ( - "ForwardRef", - "Callable", - "AnyCallable", - "NoArgAnyCallable", "NoneType", "is_none_type", - "display_as_type", - "resolve_annotations", "is_callable_type", "is_literal_type", "all_literal_values", - "is_namedtuple", - "is_typeddict", - "is_new_type", - "new_type_supertype", - "is_classvar", - "update_field_forward_refs", - "update_model_forward_refs", - "TupleGenerator", - "DictStrAny", - "DictAny", - "SetStr", - "ListStr", - "IntStr", - "AbstractSetIntStr", - "DictIntStrAny", - "CallableGenerator", - "ReprArgs", - "AnyClassMethod", - "CallableGenerator", - "WithArgsTypes", - "get_args", - "get_origin", - "get_sub_types", - "typing_base", - "get_all_type_hints", "is_union", - "StrPath", ) @@ -339,66 +74,6 @@ def is_none_type(type_: Any) -> bool: return False -def display_as_type(v: Type[Any]) -> str: - if ( - not isinstance(v, typing_base) - and not isinstance(v, WithArgsTypes) - and not isinstance(v, type) - ): - v = v.__class__ - - if is_union(get_origin(v)): - return f'Union[{", ".join(map(display_as_type, get_args(v)))}]' - - if isinstance(v, WithArgsTypes): - # Generic alias are constructs like `list[int]` - return str(v).replace("typing.", "") - - try: - return v.__name__ - except AttributeError: - # happens with typing objects - return str(v).replace("typing.", "") - - -def resolve_annotations( - raw_annotations: Dict[str, Type[Any]], module_name: Optional[str] -) -> Dict[str, Type[Any]]: - """ - Partially taken from typing.get_type_hints. - - Resolve string or ForwardRef annotations into type objects if possible. - """ - base_globals: Optional[Dict[str, Any]] = None - if module_name: - try: - module = sys.modules[module_name] - except KeyError: - # happens occasionally, see https://github.com/samuelcolvin/pydantic/issues/2363 - pass - else: - base_globals = module.__dict__ - - annotations = {} - for name, value in raw_annotations.items(): - if isinstance(value, str): - if (3, 10) > sys.version_info >= (3, 9, 8) or sys.version_info >= ( - 3, - 10, - 1, - ): - value = ForwardRef(value, is_argument=False, is_class=True) - else: - value = ForwardRef(value, is_argument=False) - try: - value = _eval_type(value, base_globals, None) - except NameError: - # this is ok, it can be fixed with update_forward_refs - pass - annotations[name] = value - return annotations - - def is_callable_type(type_: Type[Any]) -> bool: return type_ is Callable or get_origin(type_) is Callable @@ -422,149 +97,3 @@ def all_literal_values(type_: Type[Any]) -> Tuple[Any, ...]: values = literal_values(type_) return tuple(x for value in values for x in all_literal_values(value)) - - -def is_namedtuple(type_: Type[Any]) -> bool: - """ - Check if a given class is a named tuple. - It can be either a `typing.NamedTuple` or `collections.namedtuple` - """ - from .utils import lenient_issubclass - - return lenient_issubclass(type_, tuple) and hasattr(type_, "_fields") - - -def is_typeddict(type_: Type[Any]) -> bool: - """ - Check if a given class is a typed dict (from `typing` or `typing_extensions`) - In 3.10, there will be a public method (https://docs.python.org/3.10/library/typing.html#typing.is_typeddict) - """ - from .utils import lenient_issubclass - - return lenient_issubclass(type_, dict) and hasattr(type_, "__total__") - - -test_type = NewType("test_type", str) - - -def is_new_type(type_: Type[Any]) -> bool: - """ - Check whether type_ was created using typing.NewType - """ - return isinstance(type_, test_type.__class__) and hasattr(type_, "__supertype__") # type: ignore - - -def new_type_supertype(type_: Type[Any]) -> Type[Any]: - while hasattr(type_, "__supertype__"): - type_ = type_.__supertype__ - return type_ - - -def _check_classvar(v: Optional[Type[Any]]) -> bool: - if v is None: - return False - - return v.__class__ == ClassVar.__class__ and getattr(v, "_name", None) == "ClassVar" - - -def is_classvar(ann_type: Type[Any]) -> bool: - if _check_classvar(ann_type) or _check_classvar(get_origin(ann_type)): - return True - - # this is an ugly workaround for class vars that contain forward references and are therefore themselves - # forward references, see #3679 - if ann_type.__class__ == ForwardRef and ann_type.__forward_arg__.startswith( - "ClassVar[" - ): - return True - - return False - - -# Only in Pydantic -# def update_field_forward_refs(field: "ModelField", globalns: Any, localns: Any) -> None: -# """ -# Try to update ForwardRefs on fields based on this ModelField, globalns and localns. -# """ -# if field.type_.__class__ == ForwardRef: -# field.type_ = evaluate_forwardref(field.type_, globalns, localns or None) -# field.prepare() - -# if field.sub_fields: -# for sub_f in field.sub_fields: -# update_field_forward_refs(sub_f, globalns=globalns, localns=localns) - -# if field.discriminator_key is not None: -# field.prepare_discriminated_union_sub_fields() - - -# Only in Pydantic -# def update_model_forward_refs( -# model: Type[Any], -# fields: Iterable["ModelField"], -# json_encoders: Dict[Union[Type[Any], str], AnyCallable], -# localns: "DictStrAny", -# exc_to_suppress: Tuple[Type[BaseException], ...] = (), -# ) -> None: -# """ -# Try to update model fields ForwardRefs based on model and localns. -# """ -# if model.__module__ in sys.modules: -# globalns = sys.modules[model.__module__].__dict__.copy() -# else: -# globalns = {} - -# globalns.setdefault(model.__name__, model) - -# for f in fields: -# try: -# update_field_forward_refs(f, globalns=globalns, localns=localns) -# except exc_to_suppress: -# pass - -# for key in set(json_encoders.keys()): -# if isinstance(key, str): -# fr: ForwardRef = ForwardRef(key) -# elif isinstance(key, ForwardRef): -# fr = key -# else: -# continue - -# try: -# new_key = evaluate_forwardref(fr, globalns, localns or None) -# except exc_to_suppress: # pragma: no cover -# continue - -# json_encoders[new_key] = json_encoders.pop(key) - - -def get_class(type_: Type[Any]) -> Union[None, bool, Type[Any]]: - """ - Tries to get the class of a Type[T] annotation. Returns True if Type is used - without brackets. Otherwise returns None. - """ - try: - origin = get_origin(type_) - if origin is None: # Python 3.6 - origin = type_ - if issubclass(origin, Type): # type: ignore - if not get_args(type_) or not isinstance(get_args(type_)[0], type): - return True - return get_args(type_)[0] - except (AttributeError, TypeError): - pass - return None - - -def get_sub_types(tp: Any) -> List[Any]: - """ - Return all the types that are allowed by type `tp` - `tp` can be a `Union` of allowed types or an `Annotated` type - """ - origin = get_origin(tp) - if origin is Annotated: - return get_sub_types(get_args(tp)[0]) - elif is_union(origin): - return [x for t in get_args(tp) for x in get_sub_types(t)] - else: - return [tp] diff --git a/typer/main.py b/typer/main.py index c385c48c5f..a621bda6ad 100644 --- a/typer/main.py +++ b/typer/main.py @@ -12,8 +12,9 @@ from uuid import UUID import click +from typing_extensions import get_args, get_origin -from ._typing import get_args, get_origin, is_union +from ._typing import is_union from .completion import get_completion_inspect_parameters from .core import ( DEFAULT_MARKUP_MODE, diff --git a/typer/utils.py b/typer/utils.py index 5c0e967a64..93c407447e 100644 --- a/typer/utils.py +++ b/typer/utils.py @@ -3,9 +3,8 @@ from copy import copy from typing import Any, Callable, Dict, List, Tuple, Type, cast -from typing_extensions import Annotated, get_type_hints +from typing_extensions import Annotated, get_args, get_origin, get_type_hints -from ._typing import get_args, get_origin from .models import ArgumentInfo, OptionInfo, ParameterInfo, ParamMeta @@ -96,7 +95,7 @@ def __str__(self) -> str: def _split_annotation_from_typer_annotations( base_annotation: Type[Any], ) -> Tuple[Type[Any], List[ParameterInfo]]: - if get_origin(base_annotation) is not Annotated: # type: ignore + if get_origin(base_annotation) is not Annotated: return base_annotation, [] base_annotation, *maybe_typer_annotations = get_args(base_annotation) return base_annotation, [ From 5889cf82f4ed925f92e6b0750bf1b1ed9ee672f3 Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 28 Aug 2024 23:32:31 +0000 Subject: [PATCH 53/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index d0a1d428a7..4c9c83d2bf 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -8,6 +8,7 @@ ### Refactors +* 🔥 Remove unused functionality from `_typing.py` file. PR [#805](https://github.com/fastapi/typer/pull/805) by [@ivantodorovich](https://github.com/ivantodorovich). * ✏️ Fix typo in function name `_make_rich_text`. PR [#959](https://github.com/fastapi/typer/pull/959) by [@svlandeg](https://github.com/svlandeg). ### Internal From 221bfba2b41c3bdc3195f9317d1b1290348d0d76 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2024 18:23:46 +0200 Subject: [PATCH 54/73] =?UTF-8?q?=E2=AC=86=20[pre-commit.ci]=20pre-commit?= =?UTF-8?q?=20autoupdate=20(#689)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ⬆ [pre-commit.ci] pre-commit autoupdate updates: - [github.com/pre-commit/pre-commit-hooks: v4.5.0 → v4.6.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.5.0...v4.6.0) - [github.com/astral-sh/ruff-pre-commit: v0.2.0 → v0.6.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.2.0...v0.6.2) * 🎨 [pre-commit.ci] Auto format from pre-commit.com hooks --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: svlandeg --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4ea5b873a6..3175140622 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ default_language_version: python: python3.10 repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: - id: check-added-large-files - id: check-toml @@ -14,7 +14,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.1 + rev: v0.6.2 hooks: - id: ruff args: From 1da02c3e6f7769ae58813d892d00a6e5731ccc01 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2024 18:24:00 +0200 Subject: [PATCH 55/73] =?UTF-8?q?=E2=AC=86=20Bump=20ruff=20from=200.6.1=20?= =?UTF-8?q?to=200.6.3=20(#961)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [ruff](https://github.com/astral-sh/ruff) from 0.6.1 to 0.6.3. - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/0.6.1...0.6.3) --- updated-dependencies: - dependency-name: ruff dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements-tests.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-tests.txt b/requirements-tests.txt index 0cacd0ecbe..d58de0d9b4 100644 --- a/requirements-tests.txt +++ b/requirements-tests.txt @@ -6,7 +6,7 @@ coverage[toml] >=6.2,<8.0 pytest-xdist >=1.32.0,<4.0.0 pytest-sugar >=0.9.4,<1.1.0 mypy ==1.4.1 -ruff ==0.6.1 +ruff ==0.6.3 # Needed explicitly by typer-slim rich >=10.11.0 shellingham >=1.3.0 From eb216559a72fddb881661781f87c776d65a9491c Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 30 Aug 2024 16:24:21 +0000 Subject: [PATCH 56/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 4c9c83d2bf..e99caaa588 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -13,6 +13,7 @@ ### Internal +* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#689](https://github.com/fastapi/typer/pull/689) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci). * ⬆ Bump ruff from 0.2.0 to 0.6.1. PR [#938](https://github.com/fastapi/typer/pull/938) by [@dependabot[bot]](https://github.com/apps/dependabot). * 👷 Update `latest-changes` GitHub Action. PR [#955](https://github.com/fastapi/typer/pull/955) by [@tiangolo](https://github.com/tiangolo). From f35ee838a8681d0d68325c3c5e436d11313137fe Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 30 Aug 2024 16:25:52 +0000 Subject: [PATCH 57/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index e99caaa588..8374952153 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -13,6 +13,7 @@ ### Internal +* ⬆ Bump ruff from 0.6.1 to 0.6.3. PR [#961](https://github.com/fastapi/typer/pull/961) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#689](https://github.com/fastapi/typer/pull/689) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci). * ⬆ Bump ruff from 0.2.0 to 0.6.1. PR [#938](https://github.com/fastapi/typer/pull/938) by [@dependabot[bot]](https://github.com/apps/dependabot). * 👷 Update `latest-changes` GitHub Action. PR [#955](https://github.com/fastapi/typer/pull/955) by [@tiangolo](https://github.com/tiangolo). From 5356ce72d284bf8a0e63d468ab38dc640c830701 Mon Sep 17 00:00:00 2001 From: Sofie Van Landeghem Date: Mon, 2 Sep 2024 22:04:05 +0200 Subject: [PATCH 58/73] =?UTF-8?q?=F0=9F=92=9A=20Set=20`include-hidden-file?= =?UTF-8?q?s`=20to=20`True`=20when=20using=20the=20`upload-artifact`=20GH?= =?UTF-8?q?=20action=20(#967)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit set include-hidden-files to true for actions/upload-artifact@v4 --- .github/workflows/build-docs.yml | 1 + .github/workflows/test.yml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index a38691714e..3c8515e625 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -77,6 +77,7 @@ jobs: with: name: docs-site path: ./site/** + include-hidden-files: true # https://github.com/marketplace/actions/alls-green#why docs-all-green: # This job does nothing and is only used for the branch protection diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5030d0bfdd..b8837ec1d3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -67,6 +67,7 @@ jobs: with: name: coverage-${{ runner.os }}-${{ matrix.python-version }} path: coverage + include-hidden-files: true coverage-combine: needs: [test] @@ -99,6 +100,7 @@ jobs: with: name: coverage-html path: htmlcov + include-hidden-files: true # https://github.com/marketplace/actions/alls-green#why check: # This job does nothing and is only used for the branch protection From 0331006cc70ed6894c521d63ea71d0a093056c22 Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 2 Sep 2024 20:04:23 +0000 Subject: [PATCH 59/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 8374952153..1981ab149c 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -13,6 +13,7 @@ ### Internal +* 💚 Set `include-hidden-files` to `True` when using the `upload-artifact` GH action. PR [#967](https://github.com/fastapi/typer/pull/967) by [@svlandeg](https://github.com/svlandeg). * ⬆ Bump ruff from 0.6.1 to 0.6.3. PR [#961](https://github.com/fastapi/typer/pull/961) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#689](https://github.com/fastapi/typer/pull/689) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci). * ⬆ Bump ruff from 0.2.0 to 0.6.1. PR [#938](https://github.com/fastapi/typer/pull/938) by [@dependabot[bot]](https://github.com/apps/dependabot). From 3c0f4029ef91d5e944df3651b4339260117b6b32 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 14:49:28 +0200 Subject: [PATCH 60/73] =?UTF-8?q?=E2=AC=86=20[pre-commit.ci]=20pre-commit?= =?UTF-8?q?=20autoupdate=20(#966)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.2 → v0.6.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.2...v0.6.3) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Sofie Van Landeghem --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3175140622..7e58afd4b9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,7 +14,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.2 + rev: v0.6.3 hooks: - id: ruff args: From 97cf5ae0d5552f44ef9124b9e69588cf5da01266 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 3 Sep 2024 12:49:48 +0000 Subject: [PATCH 61/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 1981ab149c..050458039c 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -13,6 +13,7 @@ ### Internal +* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#966](https://github.com/fastapi/typer/pull/966) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci). * 💚 Set `include-hidden-files` to `True` when using the `upload-artifact` GH action. PR [#967](https://github.com/fastapi/typer/pull/967) by [@svlandeg](https://github.com/svlandeg). * ⬆ Bump ruff from 0.6.1 to 0.6.3. PR [#961](https://github.com/fastapi/typer/pull/961) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#689](https://github.com/fastapi/typer/pull/689) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci). From b084cbd5a9931becbc0391a53a12641d1cb13ac6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 14:51:51 +0200 Subject: [PATCH 62/73] =?UTF-8?q?=E2=AC=86=20Bump=20pypa/gh-action-pypi-pu?= =?UTF-8?q?blish=20from=201.9.0=20to=201.10.1=20(#973)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.9.0 to 1.10.1. - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/v1.9.0...v1.10.1) --- updated-dependencies: - dependency-name: pypa/gh-action-pypi-publish dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 60e6c43836..a2dd05a810 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -36,4 +36,4 @@ jobs: TIANGOLO_BUILD_PACKAGE: ${{ matrix.package }} run: python -m build - name: Publish - uses: pypa/gh-action-pypi-publish@v1.9.0 + uses: pypa/gh-action-pypi-publish@v1.10.1 From aa8167dd0263b0bd88adc1aa21c5a1a40b793041 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 3 Sep 2024 12:52:22 +0000 Subject: [PATCH 63/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 050458039c..f7525b2347 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -13,6 +13,7 @@ ### Internal +* ⬆ Bump pypa/gh-action-pypi-publish from 1.9.0 to 1.10.1. PR [#973](https://github.com/fastapi/typer/pull/973) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#966](https://github.com/fastapi/typer/pull/966) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci). * 💚 Set `include-hidden-files` to `True` when using the `upload-artifact` GH action. PR [#967](https://github.com/fastapi/typer/pull/967) by [@svlandeg](https://github.com/svlandeg). * ⬆ Bump ruff from 0.6.1 to 0.6.3. PR [#961](https://github.com/fastapi/typer/pull/961) by [@dependabot[bot]](https://github.com/apps/dependabot). From 692dddce4b077548ce3c1452fc60291b3a39c883 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 15:15:06 +0200 Subject: [PATCH 64/73] =?UTF-8?q?=E2=AC=86=20Bump=20mkdocs-material=20from?= =?UTF-8?q?=209.5.33=20to=209.5.34=20(#963)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.33 to 9.5.34. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.33...9.5.34) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-docs.txt b/requirements-docs.txt index 5635b9558e..ddee32cb92 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -1,6 +1,6 @@ -e . -mkdocs-material==9.5.33 +mkdocs-material==9.5.34 mdx-include >=1.4.1,<2.0.0 mkdocs-redirects>=1.2.1,<1.3.0 pyyaml >=5.3.1,<7.0.0 From 2dac9b331944ac30173725f54614a4cb41032863 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 3 Sep 2024 13:15:29 +0000 Subject: [PATCH 65/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index f7525b2347..239bce1c14 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -13,6 +13,7 @@ ### Internal +* ⬆ Bump mkdocs-material from 9.5.33 to 9.5.34. PR [#963](https://github.com/fastapi/typer/pull/963) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ Bump pypa/gh-action-pypi-publish from 1.9.0 to 1.10.1. PR [#973](https://github.com/fastapi/typer/pull/973) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#966](https://github.com/fastapi/typer/pull/966) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci). * 💚 Set `include-hidden-files` to `True` when using the `upload-artifact` GH action. PR [#967](https://github.com/fastapi/typer/pull/967) by [@svlandeg](https://github.com/svlandeg). From c7ae8135ce4e3369088a6ed2c026274967344f58 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 14:08:00 +0200 Subject: [PATCH 66/73] =?UTF-8?q?=E2=AC=86=20Bump=20ruff=20from=200.6.3=20?= =?UTF-8?q?to=200.6.4=20(#975)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [ruff](https://github.com/astral-sh/ruff) from 0.6.3 to 0.6.4. - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/0.6.3...0.6.4) --- updated-dependencies: - dependency-name: ruff dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements-tests.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-tests.txt b/requirements-tests.txt index d58de0d9b4..df1aeb1f61 100644 --- a/requirements-tests.txt +++ b/requirements-tests.txt @@ -6,7 +6,7 @@ coverage[toml] >=6.2,<8.0 pytest-xdist >=1.32.0,<4.0.0 pytest-sugar >=0.9.4,<1.1.0 mypy ==1.4.1 -ruff ==0.6.3 +ruff ==0.6.4 # Needed explicitly by typer-slim rich >=10.11.0 shellingham >=1.3.0 From 63c0cf348e8997436faa151534edbdebe2a9f9b9 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 6 Sep 2024 12:08:22 +0000 Subject: [PATCH 67/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 239bce1c14..3637fa0748 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -13,6 +13,7 @@ ### Internal +* ⬆ Bump ruff from 0.6.3 to 0.6.4. PR [#975](https://github.com/fastapi/typer/pull/975) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ Bump mkdocs-material from 9.5.33 to 9.5.34. PR [#963](https://github.com/fastapi/typer/pull/963) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ Bump pypa/gh-action-pypi-publish from 1.9.0 to 1.10.1. PR [#973](https://github.com/fastapi/typer/pull/973) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#966](https://github.com/fastapi/typer/pull/966) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci). From 59702f003e48c93315ebb7298352091ead7c977c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sat, 7 Sep 2024 17:21:06 +0200 Subject: [PATCH 68/73] =?UTF-8?q?=F0=9F=91=B7=20Update=20`issue-manager.ym?= =?UTF-8?q?l`=20(#978)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/issue-manager.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/issue-manager.yml b/.github/workflows/issue-manager.yml index ec2ed2bbc2..e12d1fb95f 100644 --- a/.github/workflows/issue-manager.yml +++ b/.github/workflows/issue-manager.yml @@ -2,7 +2,7 @@ name: Issue Manager on: schedule: - - cron: "10 4 * * *" + - cron: "13 21 * * *" issue_comment: types: - created @@ -16,6 +16,7 @@ on: permissions: issues: write + pull-requests: write jobs: issue-manager: @@ -35,8 +36,8 @@ jobs: "delay": 864000, "message": "Assuming the original need was handled, this will be automatically closed now. But feel free to add more comments or create new issues or PRs." }, - "changes-requested": { + "waiting": { "delay": 2628000, - "message": "As this PR had requested changes to be applied but has been inactive for a while, it's now going to be closed. But if there's anyone interested, feel free to create a new PR." + "message": "As this PR has been waiting for the original user for a while but seems to be inactive, it's now going to be closed. But if there's anyone interested, feel free to create a new PR." } } From 29d3aaf10b417937005509b60f12778e07396e4b Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 7 Sep 2024 15:21:25 +0000 Subject: [PATCH 69/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 3637fa0748..b775292e4a 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -13,6 +13,7 @@ ### Internal +* 👷 Update `issue-manager.yml`. PR [#978](https://github.com/fastapi/typer/pull/978) by [@tiangolo](https://github.com/tiangolo). * ⬆ Bump ruff from 0.6.3 to 0.6.4. PR [#975](https://github.com/fastapi/typer/pull/975) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ Bump mkdocs-material from 9.5.33 to 9.5.34. PR [#963](https://github.com/fastapi/typer/pull/963) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ Bump pypa/gh-action-pypi-publish from 1.9.0 to 1.10.1. PR [#973](https://github.com/fastapi/typer/pull/973) by [@dependabot[bot]](https://github.com/apps/dependabot). From 4d7fb3e1d904345d31d9a76bb0305fedea750cca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 13:42:37 +0200 Subject: [PATCH 70/73] =?UTF-8?q?=E2=AC=86=20Bump=20tiangolo/issue-manager?= =?UTF-8?q?=20from=200.5.0=20to=200.5.1=20(#980)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [tiangolo/issue-manager](https://github.com/tiangolo/issue-manager) from 0.5.0 to 0.5.1. - [Release notes](https://github.com/tiangolo/issue-manager/releases) - [Commits](https://github.com/tiangolo/issue-manager/compare/0.5.0...0.5.1) --- updated-dependencies: - dependency-name: tiangolo/issue-manager dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/issue-manager.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/issue-manager.yml b/.github/workflows/issue-manager.yml index e12d1fb95f..ff71ba5ec3 100644 --- a/.github/workflows/issue-manager.yml +++ b/.github/workflows/issue-manager.yml @@ -27,7 +27,7 @@ jobs: env: GITHUB_CONTEXT: ${{ toJson(github) }} run: echo "$GITHUB_CONTEXT" - - uses: tiangolo/issue-manager@0.5.0 + - uses: tiangolo/issue-manager@0.5.1 with: token: ${{ secrets.GITHUB_TOKEN }} config: > From c77657b56c7756d1c3dd7d3b1031c037419e2ec8 Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 9 Sep 2024 11:42:59 +0000 Subject: [PATCH 71/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index b775292e4a..900540de9e 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -13,6 +13,7 @@ ### Internal +* ⬆ Bump tiangolo/issue-manager from 0.5.0 to 0.5.1. PR [#980](https://github.com/fastapi/typer/pull/980) by [@dependabot[bot]](https://github.com/apps/dependabot). * 👷 Update `issue-manager.yml`. PR [#978](https://github.com/fastapi/typer/pull/978) by [@tiangolo](https://github.com/tiangolo). * ⬆ Bump ruff from 0.6.3 to 0.6.4. PR [#975](https://github.com/fastapi/typer/pull/975) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ Bump mkdocs-material from 9.5.33 to 9.5.34. PR [#963](https://github.com/fastapi/typer/pull/963) by [@dependabot[bot]](https://github.com/apps/dependabot). From ceb16f359abf353fed670a672c7273ac7fda9f99 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 20:20:36 +0200 Subject: [PATCH 72/73] =?UTF-8?q?=E2=AC=86=20[pre-commit.ci]=20pre-commit?= =?UTF-8?q?=20autoupdate=20(#982)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.3 → v0.6.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.3...v0.6.4) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7e58afd4b9..f74816f123 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,7 +14,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.3 + rev: v0.6.4 hooks: - id: ruff args: From 1823714798c2cdaf4cbda0a639e7cf0aa85bd88a Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 9 Sep 2024 18:20:59 +0000 Subject: [PATCH 73/73] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 900540de9e..f24cb7c721 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -13,6 +13,7 @@ ### Internal +* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#982](https://github.com/fastapi/typer/pull/982) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci). * ⬆ Bump tiangolo/issue-manager from 0.5.0 to 0.5.1. PR [#980](https://github.com/fastapi/typer/pull/980) by [@dependabot[bot]](https://github.com/apps/dependabot). * 👷 Update `issue-manager.yml`. PR [#978](https://github.com/fastapi/typer/pull/978) by [@tiangolo](https://github.com/tiangolo). * ⬆ Bump ruff from 0.6.3 to 0.6.4. PR [#975](https://github.com/fastapi/typer/pull/975) by [@dependabot[bot]](https://github.com/apps/dependabot).