Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🔥 Remove support for Click 7, require Click 8+ #760

Merged
merged 16 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 2 additions & 9 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ jobs:
strategy:
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
click-version:
- click-7
- click-8
fail-fast: false

steps:
Expand All @@ -28,18 +25,14 @@ jobs:
run: pip install flit
- name: Install Dependencies
run: python -m flit install --symlink
- name: Install Click 7
if: matrix.click-version == 'click-7'
run: pip install "click<8.0.0"
- name: Lint
if: ${{ matrix.click-version == 'click-8' }}
run: bash scripts/lint.sh
- run: mkdir coverage
- name: Test
run: bash scripts/test.sh
env:
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}-${{ matrix.click-version }}
CONTEXT: ${{ runner.os }}-py${{ matrix.python-version }}-${{ matrix.click-version }}
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}
CONTEXT: ${{ runner.os }}-py${{ matrix.python-version }}
- name: Store coverage files
uses: actions/upload-artifact@v3
with:
Expand Down
18 changes: 9 additions & 9 deletions docs/tutorial/options/callback-and-context.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ For example, you could do some validation before the rest of the code is execute

=== "Python 3.7+"

```Python hl_lines="5-8 11"
```Python hl_lines="7-10 13"
{!> ../docs_src/options/callback/tutorial001_an.py!}
```

Expand All @@ -17,7 +17,7 @@ For example, you could do some validation before the rest of the code is execute
!!! tip
Prefer to use the `Annotated` version if possible.

```Python hl_lines="4-7 10"
```Python hl_lines="6-9 12"
{!> ../docs_src/options/callback/tutorial001.py!}
```

Expand Down Expand Up @@ -107,7 +107,7 @@ Let's say that when the callback is running, we want to show a message saying th

=== "Python 3.7+"

```Python hl_lines="6"
```Python hl_lines="8"
{!> ../docs_src/options/callback/tutorial002_an.py!}
```

Expand All @@ -116,7 +116,7 @@ Let's say that when the callback is running, we want to show a message saying th
!!! tip
Prefer to use the `Annotated` version if possible.

```Python hl_lines="5"
```Python hl_lines="7"
{!> ../docs_src/options/callback/tutorial002.py!}
```

Expand Down Expand Up @@ -155,7 +155,7 @@ The "context" has some additional data about the current execution of your progr

=== "Python 3.7+"

```Python hl_lines="5-7"
```Python hl_lines="7-9"
{!> ../docs_src/options/callback/tutorial003_an.py!}
```

Expand All @@ -164,15 +164,15 @@ The "context" has some additional data about the current execution of your progr
!!! tip
Prefer to use the `Annotated` version if possible.

```Python hl_lines="4-6"
```Python hl_lines="6-8"
{!> ../docs_src/options/callback/tutorial003.py!}
```

The `ctx.resilient_parsing` will be `True` when handling completion, so you can just return without printing anything else.

But it will be `False` when calling the program normally. So you can continue the execution of your previous code.

That's all is needed to fix completion 🚀
That's all is needed to fix completion. 🚀

Check it:

Expand Down Expand Up @@ -200,7 +200,7 @@ The same way you can access the `typer.Context` by declaring a function paramete

=== "Python 3.7+"

```Python hl_lines="5 8"
```Python hl_lines="7 10"
{!> ../docs_src/options/callback/tutorial004_an.py!}
```

Expand All @@ -209,7 +209,7 @@ The same way you can access the `typer.Context` by declaring a function paramete
!!! tip
Prefer to use the `Annotated` version if possible.

```Python hl_lines="4 7"
```Python hl_lines="6 9"
{!> ../docs_src/options/callback/tutorial004.py!}
```

Expand Down
4 changes: 3 additions & 1 deletion docs_src/options/callback/tutorial001.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Optional

import typer


Expand All @@ -7,7 +9,7 @@ def name_callback(value: str):
return value


def main(name: str = typer.Option(..., callback=name_callback)):
def main(name: Optional[str] = typer.Option(default=None, callback=name_callback)):
print(f"Hello {name}")


Expand Down
4 changes: 3 additions & 1 deletion docs_src/options/callback/tutorial001_an.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Optional

import typer
from typing_extensions import Annotated

Expand All @@ -8,7 +10,7 @@ def name_callback(value: str):
return value


def main(name: Annotated[str, typer.Option(callback=name_callback)]):
def main(name: Annotated[Optional[str], typer.Option(callback=name_callback)] = None):
print(f"Hello {name}")


Expand Down
4 changes: 3 additions & 1 deletion docs_src/options/callback/tutorial002.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Optional

import typer


Expand All @@ -8,7 +10,7 @@ def name_callback(value: str):
return value


def main(name: str = typer.Option(..., callback=name_callback)):
def main(name: Optional[str] = typer.Option(default=None, callback=name_callback)):
print(f"Hello {name}")


Expand Down
4 changes: 3 additions & 1 deletion docs_src/options/callback/tutorial002_an.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Optional

import typer
from typing_extensions import Annotated

Expand All @@ -9,7 +11,7 @@ def name_callback(value: str):
return value


def main(name: Annotated[str, typer.Option(callback=name_callback)]):
def main(name: Annotated[Optional[str], typer.Option(callback=name_callback)] = None):
print(f"Hello {name}")


Expand Down
4 changes: 3 additions & 1 deletion docs_src/options/callback/tutorial003.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Optional

import typer


Expand All @@ -10,7 +12,7 @@ def name_callback(ctx: typer.Context, value: str):
return value


def main(name: str = typer.Option(..., callback=name_callback)):
def main(name: Optional[str] = typer.Option(default=None, callback=name_callback)):
print(f"Hello {name}")


Expand Down
4 changes: 3 additions & 1 deletion docs_src/options/callback/tutorial003_an.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Optional

import typer
from typing_extensions import Annotated

Expand All @@ -11,7 +13,7 @@ def name_callback(ctx: typer.Context, value: str):
return value


def main(name: Annotated[str, typer.Option(callback=name_callback)]):
def main(name: Annotated[Optional[str], typer.Option(callback=name_callback)] = None):
print(f"Hello {name}")


Expand Down
4 changes: 3 additions & 1 deletion docs_src/options/callback/tutorial004.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Optional

import typer


Expand All @@ -10,7 +12,7 @@ def name_callback(ctx: typer.Context, param: typer.CallbackParam, value: str):
return value


def main(name: str = typer.Option(..., callback=name_callback)):
def main(name: Optional[str] = typer.Option(default=None, callback=name_callback)):
print(f"Hello {name}")


Expand Down
4 changes: 3 additions & 1 deletion docs_src/options/callback/tutorial004_an.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Optional

import typer
from typing_extensions import Annotated

Expand All @@ -11,7 +13,7 @@ def name_callback(ctx: typer.Context, param: typer.CallbackParam, value: str):
return value


def main(name: Annotated[str, typer.Option(callback=name_callback)]):
def main(name: Annotated[Optional[str], typer.Option(callback=name_callback)] = None):
print(f"Hello {name}")


Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ classifiers = [
"License :: OSI Approved :: MIT License"
]
requires = [
"click >= 7.1.1, <9.0.0",
"click >= 8.0.0",
"typing-extensions >= 3.7.4.3",
]
description-file = "README.md"
Expand Down
3 changes: 1 addition & 2 deletions tests/test_compat/test_option_get_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,4 @@ def test_completion():
"_TYPER_COMPLETE_TESTING": "True",
},
)
# TODO: when deprecating Click 7, remove second option
assert "Jonny" in result.stdout or "_files" in result.stdout
assert "Jonny" in result.stdout
6 changes: 1 addition & 5 deletions tests/test_completion/test_completion_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,7 @@ def test_completion_install_no_shell():
"_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION": "True",
},
)
# TODO: when deprecating Click 7, remove second option
assert (
"Option '--install-completion' requires an argument" in result.stderr
or "--install-completion option requires an argument" in result.stderr
)
assert "Option '--install-completion' requires an argument" in result.stderr


def test_completion_install_bash():
Expand Down
23 changes: 18 additions & 5 deletions tests/test_completion/test_completion_show.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import os
import subprocess
import sys
from unittest import mock

import shellingham
import typer
from typer.testing import CliRunner

from docs_src.commands.index import tutorial001 as mod

runner = CliRunner()
app = typer.Typer()
app.command()(mod.main)


def test_completion_show_no_shell():
result = subprocess.run(
Expand All @@ -17,11 +26,7 @@ def test_completion_show_no_shell():
"_TYPER_COMPLETE_TEST_DISABLE_SHELL_DETECTION": "True",
},
)
# TODO: when deprecating Click 7, remove second option
assert (
"Option '--show-completion' requires an argument" in result.stderr
or "--show-completion option requires an argument" in result.stderr
)
assert "Option '--show-completion' requires an argument" in result.stderr


def test_completion_show_bash():
Expand Down Expand Up @@ -146,3 +151,11 @@ def test_completion_source_pwsh():
"Register-ArgumentCompleter -Native -CommandName tutorial001.py -ScriptBlock $scriptblock"
in result.stdout
)


def test_completion_show_invalid_shell():
with mock.patch.object(
shellingham, "detect_shell", return_value=("xshell", "/usr/bin/xshell")
):
result = runner.invoke(app, ["--show-completion"])
assert "Shell xshell not supported" in result.stdout
37 changes: 22 additions & 15 deletions tests/test_others.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import shellingham
import typer
import typer.completion
from typer.core import _split_opt
from typer.main import solve_typer_info_defaults, solve_typer_info_help
from typer.models import ParameterInfo, TyperInfo
from typer.testing import CliRunner
Expand Down Expand Up @@ -155,11 +156,7 @@ def test_completion_untyped_parameters():
},
)
assert "info name is: completion_no_types.py" in result.stderr
# TODO: when deprecating Click 7, remove second option
assert (
"args is: []" in result.stderr
or "args is: ['--name', 'Sebastian', '--name']" in result.stderr
)
assert "args is: []" in result.stderr
assert "incomplete is: Ca" in result.stderr
assert '"Camila":"The reader of books."' in result.stdout
assert '"Carlos":"The writer of scripts."' in result.stdout
Expand Down Expand Up @@ -188,11 +185,7 @@ def test_completion_untyped_parameters_different_order_correct_names():
},
)
assert "info name is: completion_no_types_order.py" in result.stderr
# TODO: when deprecating Click 7, remove second option
assert (
"args is: []" in result.stderr
or "args is: ['--name', 'Sebastian', '--name']" in result.stderr
)
assert "args is: []" in result.stderr
assert "incomplete is: Ca" in result.stderr
assert '"Camila":"The reader of books."' in result.stdout
assert '"Carlos":"The writer of scripts."' in result.stdout
Expand Down Expand Up @@ -233,12 +226,8 @@ def main(arg1, arg2: int, arg3: "int", arg4: bool = False, arg5: "bool" = False)
print(f"arg5: {type(arg5)} {arg5}")

result = runner.invoke(app, ["Hello", "2", "invalid"])
# TODO: when deprecating Click 7, remove second option

assert (
"Invalid value for 'ARG3': 'invalid' is not a valid integer" in result.stdout
or "Invalid value for 'ARG3': invalid is not a valid integer" in result.stdout
)
assert "Invalid value for 'ARG3': 'invalid' is not a valid integer" in result.stdout
result = runner.invoke(app, ["Hello", "2", "3", "--arg4", "--arg5"])
assert (
"arg1: <class 'str'> Hello\narg2: <class 'int'> 2\narg3: <class 'int'> 3\narg4: <class 'bool'> True\narg5: <class 'bool'> True\n"
Expand All @@ -255,3 +244,21 @@ def main(name: str):

result = runner.invoke(app, ["main", "-h"])
assert "Show this message and exit." in result.stdout


def test_split_opt():
prefix, opt = _split_opt("--verbose")
assert prefix == "--"
assert opt == "verbose"

prefix, opt = _split_opt("//verbose")
assert prefix == "//"
assert opt == "verbose"

prefix, opt = _split_opt("-verbose")
assert prefix == "-"
assert opt == "verbose"

prefix, opt = _split_opt("verbose")
assert prefix == ""
assert opt == "verbose"
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,7 @@ def test_delete_verbose():
def test_wrong_verbose():
result = runner.invoke(app, ["delete", "--verbose", "Camila"])
assert result.exit_code != 0
# TODO: when deprecating Click 7, remove second option
assert (
"No such option: --verbose" in result.output
or "no such option: --verbose" in result.output
)
assert "No such option: --verbose" in result.output


def test_script():
Expand Down
Loading
Loading