Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-typer-slim for openSUSE:Factory checked in at 2025-09-22 16:38:28 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-typer-slim (Old) and /work/SRC/openSUSE:Factory/.python-typer-slim.new.27445 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-typer-slim" Mon Sep 22 16:38:28 2025 rev:17 rq:1306167 version:0.19.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-typer-slim/python-typer-slim.changes 2025-09-08 09:56:31.615906190 +0200 +++ /work/SRC/openSUSE:Factory/.python-typer-slim.new.27445/python-typer-slim.changes 2025-09-22 16:38:42.910976127 +0200 @@ -1,0 +2,15 @@ +Sat Sep 20 18:24:01 UTC 2025 - Matthias Bach <ma...@marix.org> - 0.19.1 + +- Update to 0.19.1 + * Ensure that Optional[list] values work correctly with callbacks. + * Support typing.Literal to define a set of predefined choices. + * Allow setting an environment variable to None in CliRunner.invoke. + +------------------------------------------------------------------- +Fri Sep 19 22:51:43 UTC 2025 - Matthias Bach <ma...@marix.org> - 0.18.0 + +- Update to 0.18.0 + * Ensure compatibility with Click 8.3.0 by restoring the original + value_is_missing function. + +------------------------------------------------------------------- Old: ---- typer_slim-0.17.4.tar.gz New: ---- typer_slim-0.19.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-typer-slim.spec ++++++ --- /var/tmp/diff_new_pack.OFq0st/_old 2025-09-22 16:38:43.643006928 +0200 +++ /var/tmp/diff_new_pack.OFq0st/_new 2025-09-22 16:38:43.643006928 +0200 @@ -20,7 +20,7 @@ %{?sle15_python_module_pythons} Name: python-typer-slim -Version: 0.17.4 +Version: 0.19.1 Release: 0 Summary: Typer, build great CLIs. Easy to code. Based on Python type hints License: MIT ++++++ typer_slim-0.17.4.tar.gz -> typer_slim-0.19.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/PKG-INFO new/typer_slim-0.19.1/PKG-INFO --- old/typer_slim-0.17.4/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 +++ new/typer_slim-0.19.1/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: typer-slim -Version: 0.17.4 +Version: 0.19.1 Summary: Typer, build great CLIs. Easy to code. Based on Python type hints. Author-Email: =?utf-8?q?Sebasti=C3=A1n_Ram=C3=ADrez?= <tiang...@gmail.com> Classifier: Intended Audience :: Information Technology @@ -357,6 +357,8 @@ </div> +**Note**: If your app only has one command, by default the command name is **omitted** in usage: `python main.py Camila`. However, when there are multiple commands, you must **explicitly include the command name**: `python main.py hello Camila`. See [One or Multiple Commands](https://typer.tiangolo.com/tutorial/commands/one-or-multiple/) for more details. + ### Recap In summary, you declare **once** the types of parameters (*CLI arguments* and *CLI options*) as function parameters. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/README.md new/typer_slim-0.19.1/README.md --- old/typer_slim-0.17.4/README.md 2025-09-05 20:14:23.142923600 +0200 +++ new/typer_slim-0.19.1/README.md 2025-09-20 10:59:07.667539600 +0200 @@ -318,6 +318,8 @@ </div> +**Note**: If your app only has one command, by default the command name is **omitted** in usage: `python main.py Camila`. However, when there are multiple commands, you must **explicitly include the command name**: `python main.py hello Camila`. See [One or Multiple Commands](https://typer.tiangolo.com/tutorial/commands/one-or-multiple/) for more details. + ### Recap In summary, you declare **once** the types of parameters (*CLI arguments* and *CLI options*) as function parameters. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/docs_src/parameter_types/enum/tutorial004.py new/typer_slim-0.19.1/docs_src/parameter_types/enum/tutorial004.py --- old/typer_slim-0.17.4/docs_src/parameter_types/enum/tutorial004.py 1970-01-01 01:00:00.000000000 +0100 +++ new/typer_slim-0.19.1/docs_src/parameter_types/enum/tutorial004.py 2025-09-20 10:59:07.678539800 +0200 @@ -0,0 +1,10 @@ +import typer +from typing_extensions import Literal + + +def main(network: Literal["simple", "conv", "lstm"] = typer.Option("simple")): + print(f"Training neural network of type: {network}") + + +if __name__ == "__main__": + typer.run(main) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/docs_src/parameter_types/enum/tutorial004_an.py new/typer_slim-0.19.1/docs_src/parameter_types/enum/tutorial004_an.py --- old/typer_slim-0.17.4/docs_src/parameter_types/enum/tutorial004_an.py 1970-01-01 01:00:00.000000000 +0100 +++ new/typer_slim-0.19.1/docs_src/parameter_types/enum/tutorial004_an.py 2025-09-20 10:59:07.678539800 +0200 @@ -0,0 +1,12 @@ +import typer +from typing_extensions import Annotated, Literal + + +def main( + network: Annotated[Literal["simple", "conv", "lstm"], typer.Option()] = "simple", +): + print(f"Training neural network of type: {network}") + + +if __name__ == "__main__": + typer.run(main) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/pyproject.toml new/typer_slim-0.19.1/pyproject.toml --- old/typer_slim-0.17.4/pyproject.toml 2025-09-05 20:14:25.710039400 +0200 +++ new/typer_slim-0.19.1/pyproject.toml 2025-09-20 10:59:14.761332300 +0200 @@ -40,7 +40,7 @@ "typing-extensions >= 3.7.4.3", ] readme = "README.md" -version = "0.17.4" +version = "0.19.1" [project.urls] Homepage = "https://github.com/fastapi/typer" @@ -165,15 +165,20 @@ "B", "C4", "UP", + "TID", ] ignore = [ "E501", "B008", "C901", "W191", + "TID252", ] [tool.ruff.lint.per-file-ignores] +"typer/rich_utils.py" = [ + "TID251", +] "docs_src/progressbar/tutorial004.py" = [ "UP028", "B007", @@ -214,6 +219,9 @@ "docs_src/using_click/tutorial001.py" = [ "B007", ] +"docs_src/*" = [ + "TID", +] [tool.ruff.lint.isort] known-third-party = [ @@ -230,3 +238,11 @@ [tool.ruff.lint.pyupgrade] keep-runtime-typing = true + +[tool.ruff.lint.flake8-tidy-imports] +banned-module-level-imports = [ + "typer.rich_utils", +] + +[tool.ruff.lint.flake8-tidy-imports.banned-api.rich] +msg = "Use 'typer.rich_utils' instead of importing from 'rich' directly." diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/requirements-docs.txt new/typer_slim-0.19.1/requirements-docs.txt --- old/typer_slim-0.17.4/requirements-docs.txt 2025-09-05 20:14:23.157924200 +0200 +++ new/typer_slim-0.19.1/requirements-docs.txt 2025-09-20 10:59:07.681539800 +0200 @@ -1,6 +1,6 @@ -e . -mkdocs-material==9.5.50 +mkdocs-material==9.6.20 mdx-include >=1.4.1,<2.0.0 mkdocs-redirects>=1.2.1,<1.3.0 pyyaml >=5.3.1,<7.0.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/requirements-tests.txt new/typer_slim-0.19.1/requirements-tests.txt --- old/typer_slim-0.17.4/requirements-tests.txt 2025-09-05 20:14:23.157924200 +0200 +++ new/typer_slim-0.19.1/requirements-tests.txt 2025-09-20 10:59:07.682539700 +0200 @@ -1,12 +1,12 @@ -e . pytest >=4.4.0,<9.0.0 -pytest-cov >=2.10.0,<7.0.0 +pytest-cov >=2.10.0,<8.0.0 coverage[toml] >=6.2,<8.0 pytest-xdist >=1.32.0,<4.0.0 pytest-sugar >=0.9.4,<1.2.0 mypy ==1.4.1 -ruff ==0.12.11 +ruff ==0.13.0 # Needed explicitly by typer-slim rich >=10.11.0 shellingham >=1.3.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/tests/assets/type_error_no_rich.py new/typer_slim-0.19.1/tests/assets/type_error_no_rich.py --- old/typer_slim-0.17.4/tests/assets/type_error_no_rich.py 2025-09-05 20:14:23.158924300 +0200 +++ new/typer_slim-0.19.1/tests/assets/type_error_no_rich.py 2025-09-20 10:59:07.683539600 +0200 @@ -1,7 +1,7 @@ import typer import typer.main -typer.main.rich = None +typer.main.HAS_RICH = False def main(name: str = "morty"): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/tests/assets/type_error_no_rich_short_disable.py new/typer_slim-0.19.1/tests/assets/type_error_no_rich_short_disable.py --- old/typer_slim-0.17.4/tests/assets/type_error_no_rich_short_disable.py 2025-09-05 20:14:23.158924300 +0200 +++ new/typer_slim-0.19.1/tests/assets/type_error_no_rich_short_disable.py 2025-09-20 10:59:07.683539600 +0200 @@ -1,7 +1,7 @@ import typer import typer.main -typer.main.rich = None +typer.main.HAS_RICH = False app = typer.Typer(pretty_exceptions_short=False) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/tests/test_corner_cases.py new/typer_slim-0.19.1/tests/test_corner_cases.py --- old/typer_slim-0.17.4/tests/test_corner_cases.py 2025-09-05 20:14:23.159924300 +0200 +++ new/typer_slim-0.19.1/tests/test_corner_cases.py 2025-09-20 10:59:07.684539800 +0200 @@ -1,3 +1,4 @@ +import pytest import typer.core from typer.testing import CliRunner @@ -16,9 +17,9 @@ assert "(dynamic)" in result.output -def test_hidden_option_no_rich(): - rich = typer.core.rich - typer.core.rich = None +def test_hidden_option_no_rich(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setattr(typer.core, "HAS_RICH", False) + result = runner.invoke(mod.app, ["--help"]) assert result.exit_code == 0 assert "Say hello" in result.output @@ -26,7 +27,6 @@ assert "/lastname" in result.output assert "TEST_LASTNAME" in result.output assert "(dynamic)" in result.output - typer.core.rich = rich def test_coverage_call(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/tests/test_others.py new/typer_slim-0.19.1/tests/test_others.py --- old/typer_slim-0.17.4/tests/test_others.py 2025-09-05 20:14:23.159924300 +0200 +++ new/typer_slim-0.19.1/tests/test_others.py 2025-09-20 10:59:07.684539800 +0200 @@ -144,6 +144,32 @@ assert "value is: Camila" in result.stdout +def test_callback_4_list_none(): + app = typer.Typer() + + def names_callback(ctx, param, values: typing.Optional[typing.List[str]]): + if values is None: + return values + return [value.upper() for value in values] + + @app.command() + def main( + names: typing.Optional[typing.List[str]] = typer.Option( + None, "--name", callback=names_callback + ), + ): + if names is None: + print("Hello World") + else: + print(f"Hello {', '.join(names)}") + + result = runner.invoke(app, ["--name", "Sideshow", "--name", "Bob"]) + assert "Hello SIDESHOW, BOB" in result.stdout + + result = runner.invoke(app, []) + assert "Hello World" in result.stdout + + def test_completion_argument(): file_path = Path(__file__).parent / "assets/completion_argument.py" result = subprocess.run( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/tests/test_tutorial/test_arguments/test_envvar/test_tutorial001.py new/typer_slim-0.19.1/tests/test_tutorial/test_arguments/test_envvar/test_tutorial001.py --- old/typer_slim-0.17.4/tests/test_tutorial/test_arguments/test_envvar/test_tutorial001.py 2025-09-05 20:14:23.160924400 +0200 +++ new/typer_slim-0.19.1/tests/test_tutorial/test_arguments/test_envvar/test_tutorial001.py 2025-09-20 10:59:07.685539700 +0200 @@ -1,6 +1,7 @@ import subprocess import sys +import pytest import typer import typer.core from typer.testing import CliRunner @@ -22,16 +23,14 @@ assert "default: World" in result.output -def test_help_no_rich(): - rich = typer.core.rich - typer.core.rich = None +def test_help_no_rich(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setattr(typer.core, "HAS_RICH", False) result = runner.invoke(app, ["--help"]) assert result.exit_code == 0 assert "[OPTIONS] [NAME]" in result.output assert "Arguments" in result.output assert "env var: AWESOME_NAME" in result.output assert "default: World" in result.output - typer.core.rich = rich def test_call_arg(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/tests/test_tutorial/test_arguments/test_envvar/test_tutorial001_an.py new/typer_slim-0.19.1/tests/test_tutorial/test_arguments/test_envvar/test_tutorial001_an.py --- old/typer_slim-0.17.4/tests/test_tutorial/test_arguments/test_envvar/test_tutorial001_an.py 2025-09-05 20:14:23.160924400 +0200 +++ new/typer_slim-0.19.1/tests/test_tutorial/test_arguments/test_envvar/test_tutorial001_an.py 2025-09-20 10:59:07.685539700 +0200 @@ -1,6 +1,7 @@ import subprocess import sys +import pytest import typer import typer.core from typer.testing import CliRunner @@ -22,16 +23,15 @@ assert "default: World" in result.output -def test_help_no_rich(): - rich = typer.core.rich - typer.core.rich = None +def test_help_no_rich(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setattr(typer.core, "HAS_RICH", False) + result = runner.invoke(app, ["--help"]) assert result.exit_code == 0 assert "[OPTIONS] [NAME]" in result.output assert "Arguments" in result.output assert "env var: AWESOME_NAME" in result.output assert "default: World" in result.output - typer.core.rich = rich def test_call_arg(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/tests/test_tutorial/test_arguments/test_help/test_tutorial001.py new/typer_slim-0.19.1/tests/test_tutorial/test_arguments/test_help/test_tutorial001.py --- old/typer_slim-0.17.4/tests/test_tutorial/test_arguments/test_help/test_tutorial001.py 2025-09-05 20:14:23.160924400 +0200 +++ new/typer_slim-0.19.1/tests/test_tutorial/test_arguments/test_help/test_tutorial001.py 2025-09-20 10:59:07.685539700 +0200 @@ -1,6 +1,7 @@ import subprocess import sys +import pytest import typer import typer.core from typer.testing import CliRunner @@ -23,9 +24,8 @@ assert "[required]" in result.output -def test_help_no_rich(): - rich = typer.core.rich - typer.core.rich = None +def test_help_no_rich(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setattr(typer.core, "HAS_RICH", False) result = runner.invoke(app, ["--help"]) assert result.exit_code == 0 assert "[OPTIONS] NAME" in result.output @@ -33,7 +33,6 @@ assert "NAME" in result.output assert "The name of the user to greet" in result.output assert "[required]" in result.output - typer.core.rich = rich def test_call_arg(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/tests/test_tutorial/test_arguments/test_help/test_tutorial001_an.py new/typer_slim-0.19.1/tests/test_tutorial/test_arguments/test_help/test_tutorial001_an.py --- old/typer_slim-0.17.4/tests/test_tutorial/test_arguments/test_help/test_tutorial001_an.py 2025-09-05 20:14:23.160924400 +0200 +++ new/typer_slim-0.19.1/tests/test_tutorial/test_arguments/test_help/test_tutorial001_an.py 2025-09-20 10:59:07.685539700 +0200 @@ -1,6 +1,7 @@ import subprocess import sys +import pytest import typer import typer.core from typer.testing import CliRunner @@ -23,9 +24,8 @@ assert "[required]" in result.output -def test_help_no_rich(): - rich = typer.core.rich - typer.core.rich = None +def test_help_no_rich(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setattr(typer.core, "HAS_RICH", False) result = runner.invoke(app, ["--help"]) assert result.exit_code == 0 assert "[OPTIONS] NAME" in result.output @@ -33,7 +33,6 @@ assert "NAME" in result.output assert "The name of the user to greet" in result.output assert "[required]" in result.output - typer.core.rich = rich def test_call_arg(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/tests/test_tutorial/test_arguments/test_help/test_tutorial008.py new/typer_slim-0.19.1/tests/test_tutorial/test_arguments/test_help/test_tutorial008.py --- old/typer_slim-0.17.4/tests/test_tutorial/test_arguments/test_help/test_tutorial008.py 2025-09-05 20:14:23.161924400 +0200 +++ new/typer_slim-0.19.1/tests/test_tutorial/test_arguments/test_help/test_tutorial008.py 2025-09-20 10:59:07.685539700 +0200 @@ -1,6 +1,7 @@ import subprocess import sys +import pytest import typer import typer.core from typer.testing import CliRunner @@ -22,16 +23,14 @@ assert "[default: World]" not in result.output -def test_help_no_rich(): - rich = typer.core.rich - typer.core.rich = None +def test_help_no_rich(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setattr(typer.core, "HAS_RICH", False) result = runner.invoke(app, ["--help"]) assert result.exit_code == 0 assert "[OPTIONS] [NAME]" in result.output assert "Say hi to NAME very gently, like Dirk." in result.output assert "Arguments" not in result.output assert "[default: World]" not in result.output - typer.core.rich = rich def test_call_arg(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/tests/test_tutorial/test_arguments/test_help/test_tutorial008_an.py new/typer_slim-0.19.1/tests/test_tutorial/test_arguments/test_help/test_tutorial008_an.py --- old/typer_slim-0.17.4/tests/test_tutorial/test_arguments/test_help/test_tutorial008_an.py 2025-09-05 20:14:23.161924400 +0200 +++ new/typer_slim-0.19.1/tests/test_tutorial/test_arguments/test_help/test_tutorial008_an.py 2025-09-20 10:59:07.685539700 +0200 @@ -1,6 +1,7 @@ import subprocess import sys +import pytest import typer import typer.core from typer.testing import CliRunner @@ -22,16 +23,14 @@ assert "[default: World]" not in result.output -def test_help_no_rich(): - rich = typer.core.rich - typer.core.rich = None +def test_help_no_rich(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setattr(typer.core, "HAS_RICH", False) result = runner.invoke(app, ["--help"]) assert result.exit_code == 0 assert "[OPTIONS] [NAME]" in result.output assert "Say hi to NAME very gently, like Dirk." in result.output assert "Arguments" not in result.output assert "[default: World]" not in result.output - typer.core.rich = rich def test_call_arg(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/tests/test_tutorial/test_arguments/test_optional/test_tutorial001.py new/typer_slim-0.19.1/tests/test_tutorial/test_arguments/test_optional/test_tutorial001.py --- old/typer_slim-0.17.4/tests/test_tutorial/test_arguments/test_optional/test_tutorial001.py 2025-09-05 20:14:23.161924400 +0200 +++ new/typer_slim-0.19.1/tests/test_tutorial/test_arguments/test_optional/test_tutorial001.py 2025-09-20 10:59:07.685539700 +0200 @@ -1,6 +1,7 @@ import subprocess import sys +import pytest import typer import typer.core from typer.testing import CliRunner @@ -25,14 +26,12 @@ assert result.exit_code != 0 -def test_call_no_arg_no_rich(): +def test_call_no_arg_no_rich(monkeypatch: pytest.MonkeyPatch): # Mainly for coverage - rich = typer.core.rich - typer.core.rich = None + monkeypatch.setattr(typer.core, "HAS_RICH", False) result = runner.invoke(app) assert result.exit_code != 0 assert "Error: Missing argument 'NAME'" in result.output - typer.core.rich = rich def test_call_arg(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/tests/test_tutorial/test_arguments/test_optional/test_tutorial001_an.py new/typer_slim-0.19.1/tests/test_tutorial/test_arguments/test_optional/test_tutorial001_an.py --- old/typer_slim-0.17.4/tests/test_tutorial/test_arguments/test_optional/test_tutorial001_an.py 2025-09-05 20:14:23.161924400 +0200 +++ new/typer_slim-0.19.1/tests/test_tutorial/test_arguments/test_optional/test_tutorial001_an.py 2025-09-20 10:59:07.685539700 +0200 @@ -1,6 +1,7 @@ import subprocess import sys +import pytest import typer import typer.core from typer.testing import CliRunner @@ -25,14 +26,12 @@ assert result.exit_code != 0 -def test_call_no_arg_no_rich(): +def test_call_no_arg_no_rich(monkeypatch: pytest.MonkeyPatch): # Mainly for coverage - rich = typer.core.rich - typer.core.rich = None + monkeypatch.setattr(typer.core, "HAS_RICH", False) result = runner.invoke(app) assert result.exit_code != 0 assert "Error: Missing argument 'NAME'" in result.output - typer.core.rich = rich def test_call_arg(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/tests/test_tutorial/test_arguments/test_optional/test_tutorial003.py new/typer_slim-0.19.1/tests/test_tutorial/test_arguments/test_optional/test_tutorial003.py --- old/typer_slim-0.17.4/tests/test_tutorial/test_arguments/test_optional/test_tutorial003.py 2025-09-05 20:14:23.161924400 +0200 +++ new/typer_slim-0.19.1/tests/test_tutorial/test_arguments/test_optional/test_tutorial003.py 2025-09-20 10:59:07.686539600 +0200 @@ -1,6 +1,7 @@ import subprocess import sys +import pytest import typer import typer.core from typer.testing import CliRunner @@ -25,14 +26,12 @@ assert result.exit_code != 0 -def test_call_no_arg_no_rich(): +def test_call_no_arg_no_rich(monkeypatch: pytest.MonkeyPatch): # Mainly for coverage - rich = typer.core.rich - typer.core.rich = None + monkeypatch.setattr(typer.core, "HAS_RICH", False) result = runner.invoke(app) assert result.exit_code != 0 assert "Error: Missing argument 'NAME'" in result.output - typer.core.rich = rich def test_call_arg(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/tests/test_tutorial/test_commands/test_callback/test_tutorial001.py new/typer_slim-0.19.1/tests/test_tutorial/test_commands/test_callback/test_tutorial001.py --- old/typer_slim-0.17.4/tests/test_tutorial/test_commands/test_callback/test_tutorial001.py 2025-09-05 20:14:23.161924400 +0200 +++ new/typer_slim-0.19.1/tests/test_tutorial/test_commands/test_callback/test_tutorial001.py 2025-09-20 10:59:07.686539600 +0200 @@ -1,6 +1,7 @@ import subprocess import sys +import pytest import typer.core from typer.testing import CliRunner @@ -19,15 +20,13 @@ assert "--no-verbose" in result.output -def test_help_no_rich(): - rich = typer.core.rich - typer.core.rich = None +def test_help_no_rich(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setattr(typer.core, "HAS_RICH", False) result = runner.invoke(app, ["--help"]) assert result.exit_code == 0 assert "Manage users in the awesome CLI app." in result.output assert "--verbose" in result.output assert "--no-verbose" in result.output - typer.core.rich = rich def test_create(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/tests/test_tutorial/test_options/test_required/test_tutorial001.py new/typer_slim-0.19.1/tests/test_tutorial/test_options/test_required/test_tutorial001.py --- old/typer_slim-0.17.4/tests/test_tutorial/test_options/test_required/test_tutorial001.py 2025-09-05 20:14:23.164924600 +0200 +++ new/typer_slim-0.19.1/tests/test_tutorial/test_options/test_required/test_tutorial001.py 2025-09-20 10:59:07.689540000 +0200 @@ -1,6 +1,7 @@ import subprocess import sys +import pytest import typer import typer.core from typer.testing import CliRunner @@ -33,15 +34,13 @@ assert "[required]" in result.output -def test_help_no_rich(): - rich = typer.core.rich - typer.core.rich = None +def test_help_no_rich(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setattr(typer.core, "HAS_RICH", False) result = runner.invoke(app, ["--help"]) assert result.exit_code == 0 assert "--lastname" in result.output assert "TEXT" in result.output assert "[required]" in result.output - typer.core.rich = rich def test_script(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/tests/test_tutorial/test_options/test_required/test_tutorial001_an.py new/typer_slim-0.19.1/tests/test_tutorial/test_options/test_required/test_tutorial001_an.py --- old/typer_slim-0.17.4/tests/test_tutorial/test_options/test_required/test_tutorial001_an.py 2025-09-05 20:14:23.164924600 +0200 +++ new/typer_slim-0.19.1/tests/test_tutorial/test_options/test_required/test_tutorial001_an.py 2025-09-20 10:59:07.689540000 +0200 @@ -1,6 +1,7 @@ import subprocess import sys +import pytest import typer import typer.core from typer.testing import CliRunner @@ -33,15 +34,13 @@ assert "[required]" in result.output -def test_help_no_rich(): - rich = typer.core.rich - typer.core.rich = None +def test_help_no_rich(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setattr(typer.core, "HAS_RICH", False) result = runner.invoke(app, ["--help"]) assert result.exit_code == 0 assert "--lastname" in result.output assert "TEXT" in result.output assert "[required]" in result.output - typer.core.rich = rich def test_script(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/tests/test_tutorial/test_parameter_types/test_bool/test_tutorial002.py new/typer_slim-0.19.1/tests/test_tutorial/test_parameter_types/test_bool/test_tutorial002.py --- old/typer_slim-0.17.4/tests/test_tutorial/test_parameter_types/test_bool/test_tutorial002.py 2025-09-05 20:14:23.165924500 +0200 +++ new/typer_slim-0.19.1/tests/test_tutorial/test_parameter_types/test_bool/test_tutorial002.py 2025-09-20 10:59:07.690539800 +0200 @@ -1,6 +1,7 @@ import subprocess import sys +import pytest import typer import typer.core from typer.testing import CliRunner @@ -21,15 +22,13 @@ assert "--no-accept" not in result.output -def test_help_no_rich(): - rich = typer.core.rich - typer.core.rich = None +def test_help_no_rich(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setattr(typer.core, "HAS_RICH", False) result = runner.invoke(app, ["--help"]) assert result.exit_code == 0 assert "--accept" in result.output assert "--reject" in result.output assert "--no-accept" not in result.output - typer.core.rich = rich def test_main(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/tests/test_tutorial/test_parameter_types/test_bool/test_tutorial002_an.py new/typer_slim-0.19.1/tests/test_tutorial/test_parameter_types/test_bool/test_tutorial002_an.py --- old/typer_slim-0.17.4/tests/test_tutorial/test_parameter_types/test_bool/test_tutorial002_an.py 2025-09-05 20:14:23.165924500 +0200 +++ new/typer_slim-0.19.1/tests/test_tutorial/test_parameter_types/test_bool/test_tutorial002_an.py 2025-09-20 10:59:07.690539800 +0200 @@ -1,6 +1,7 @@ import subprocess import sys +import pytest import typer import typer.core from typer.testing import CliRunner @@ -21,15 +22,13 @@ assert "--no-accept" not in result.output -def test_help_no_rich(): - rich = typer.core.rich - typer.core.rich = None +def test_help_no_rich(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setattr(typer.core, "HAS_RICH", False) result = runner.invoke(app, ["--help"]) assert result.exit_code == 0 assert "--accept" in result.output assert "--reject" in result.output assert "--no-accept" not in result.output - typer.core.rich = rich def test_main(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial004.py new/typer_slim-0.19.1/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial004.py --- old/typer_slim-0.17.4/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial004.py 1970-01-01 01:00:00.000000000 +0100 +++ new/typer_slim-0.19.1/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial004.py 2025-09-20 10:59:07.691539800 +0200 @@ -0,0 +1,46 @@ +import subprocess +import sys + +import typer +from typer.testing import CliRunner + +from docs_src.parameter_types.enum import tutorial004 as mod + +runner = CliRunner() + +app = typer.Typer() +app.command()(mod.main) + + +def test_help(): + result = runner.invoke(app, ["--help"]) + assert result.exit_code == 0 + assert "--network [simple|conv|lstm]" in result.output.replace(" ", "") + + +def test_main(): + result = runner.invoke(app, ["--network", "conv"]) + assert result.exit_code == 0 + assert "Training neural network of type: conv" in result.output + + +def test_invalid(): + result = runner.invoke(app, ["--network", "capsule"]) + assert result.exit_code != 0 + assert "Invalid value for '--network'" in result.output + assert ( + "invalid choice: capsule. (choose from" in result.output + or "'capsule' is not one of" in result.output + ) + assert "simple" in result.output + assert "conv" in result.output + assert "lstm" 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial004_an.py new/typer_slim-0.19.1/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial004_an.py --- old/typer_slim-0.17.4/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial004_an.py 1970-01-01 01:00:00.000000000 +0100 +++ new/typer_slim-0.19.1/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial004_an.py 2025-09-20 10:59:07.691539800 +0200 @@ -0,0 +1,46 @@ +import subprocess +import sys + +import typer +from typer.testing import CliRunner + +from docs_src.parameter_types.enum import tutorial004_an as mod + +runner = CliRunner() + +app = typer.Typer() +app.command()(mod.main) + + +def test_help(): + result = runner.invoke(app, ["--help"]) + assert result.exit_code == 0 + assert "--network [simple|conv|lstm]" in result.output.replace(" ", "") + + +def test_main(): + result = runner.invoke(app, ["--network", "conv"]) + assert result.exit_code == 0 + assert "Training neural network of type: conv" in result.output + + +def test_invalid(): + result = runner.invoke(app, ["--network", "capsule"]) + assert result.exit_code != 0 + assert "Invalid value for '--network'" in result.output + assert ( + "invalid choice: capsule. (choose from" in result.output + or "'capsule' is not one of" in result.output + ) + assert "simple" in result.output + assert "conv" in result.output + assert "lstm" 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/tests/test_tutorial/test_parameter_types/test_number/test_tutorial001.py new/typer_slim-0.19.1/tests/test_tutorial/test_parameter_types/test_number/test_tutorial001.py --- old/typer_slim-0.17.4/tests/test_tutorial/test_parameter_types/test_number/test_tutorial001.py 2025-09-05 20:14:23.166924700 +0200 +++ new/typer_slim-0.19.1/tests/test_tutorial/test_parameter_types/test_number/test_tutorial001.py 2025-09-20 10:59:07.691539800 +0200 @@ -1,6 +1,7 @@ import subprocess import sys +import pytest import typer import typer.core from typer.testing import CliRunner @@ -9,7 +10,7 @@ runner = CliRunner() -app = typer.Typer() +app = typer.Typer(rich_markup_mode=None) app.command()(mod.main) @@ -22,16 +23,14 @@ assert "FLOAT RANGE" in result.output -def test_help_no_rich(): - rich = typer.core.rich - typer.core.rich = None +def test_help_no_rich(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setattr(typer.core, "HAS_RICH", False) result = runner.invoke(app, ["--help"]) assert result.exit_code == 0 assert "--age" in result.output assert "INTEGER RANGE" in result.output assert "--score" in result.output assert "FLOAT RANGE" in result.output - typer.core.rich = rich def test_params(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/tests/test_tutorial/test_parameter_types/test_number/test_tutorial001_an.py new/typer_slim-0.19.1/tests/test_tutorial/test_parameter_types/test_number/test_tutorial001_an.py --- old/typer_slim-0.17.4/tests/test_tutorial/test_parameter_types/test_number/test_tutorial001_an.py 2025-09-05 20:14:23.166924700 +0200 +++ new/typer_slim-0.19.1/tests/test_tutorial/test_parameter_types/test_number/test_tutorial001_an.py 2025-09-20 10:59:07.691539800 +0200 @@ -1,6 +1,7 @@ import subprocess import sys +import pytest import typer import typer.core from typer.testing import CliRunner @@ -9,7 +10,7 @@ runner = CliRunner() -app = typer.Typer() +app = typer.Typer(rich_markup_mode=None) app.command()(mod.main) @@ -22,16 +23,14 @@ assert "FLOAT RANGE" in result.output -def test_help_no_rich(): - rich = typer.core.rich - typer.core.rich = None +def test_help_no_rich(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setattr(typer.core, "HAS_RICH", False) result = runner.invoke(app, ["--help"]) assert result.exit_code == 0 assert "--age" in result.output assert "INTEGER RANGE" in result.output assert "--score" in result.output assert "FLOAT RANGE" in result.output - typer.core.rich = rich def test_params(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/tests/test_tutorial/test_terminating/test_tutorial003.py new/typer_slim-0.19.1/tests/test_tutorial/test_terminating/test_tutorial003.py --- old/typer_slim-0.17.4/tests/test_tutorial/test_terminating/test_tutorial003.py 2025-09-05 20:14:23.167924600 +0200 +++ new/typer_slim-0.19.1/tests/test_tutorial/test_terminating/test_tutorial003.py 2025-09-20 10:59:07.692540000 +0200 @@ -1,6 +1,7 @@ import subprocess import sys +import pytest import typer import typer.core from typer.testing import CliRunner @@ -32,15 +33,13 @@ assert result.exit_code == 1 -def test_root_no_rich(): +def test_root_no_rich(monkeypatch: pytest.MonkeyPatch): # Mainly for coverage - rich = typer.core.rich - typer.core.rich = None + monkeypatch.setattr(typer.core, "HAS_RICH", False) result = runner.invoke(app, ["root"]) assert result.exit_code == 1 assert "The root user is reserved" in result.output assert "Aborted!" in result.output - typer.core.rich = rich def test_script(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/typer/__init__.py new/typer_slim-0.19.1/typer/__init__.py --- old/typer_slim-0.17.4/typer/__init__.py 2025-09-05 20:14:23.168924800 +0200 +++ new/typer_slim-0.19.1/typer/__init__.py 2025-09-20 10:59:07.693539900 +0200 @@ -1,6 +1,6 @@ """Typer, build great CLIs. Easy to code. Based on Python type hints.""" -__version__ = "0.17.4" +__version__ = "0.19.1" from shutil import get_terminal_size as get_terminal_size diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/typer/_typing.py new/typer_slim-0.19.1/typer/_typing.py --- old/typer_slim-0.17.4/typer/_typing.py 2025-09-05 20:14:23.168924800 +0200 +++ new/typer_slim-0.19.1/typer/_typing.py 2025-09-20 10:59:07.693539900 +0200 @@ -93,7 +93,9 @@ def is_literal_type(type_: Type[Any]) -> bool: - return Literal is not None and get_origin(type_) is Literal + import typing_extensions + + return get_origin(type_) in (Literal, typing_extensions.Literal) def literal_values(type_: Type[Any]) -> Tuple[Any, ...]: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/typer/cli.py new/typer_slim-0.19.1/typer/cli.py --- old/typer_slim-0.17.4/typer/cli.py 2025-09-05 20:14:23.168924800 +0200 +++ new/typer_slim-0.19.1/typer/cli.py 2025-09-20 10:59:07.693539900 +0200 @@ -10,15 +10,7 @@ from click import Command, Group, Option from . import __version__ - -try: - import rich - - has_rich = True - -except ImportError: # pragma: no cover - has_rich = False - rich = None # type: ignore +from .core import HAS_RICH default_app_names = ("app", "cli", "main") default_func_names = ("main", "cli", "app") @@ -272,7 +264,7 @@ def _parse_html(input_text: str) -> str: - if not has_rich: # pragma: no cover + if not HAS_RICH: # pragma: no cover return input_text from . import rich_utils diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/typer/core.py new/typer_slim-0.19.1/typer/core.py --- old/typer_slim-0.17.4/typer/core.py 2025-09-05 20:14:23.168924800 +0200 +++ new/typer_slim-0.19.1/typer/core.py 2025-09-20 10:59:07.693539900 +0200 @@ -1,4 +1,5 @@ import errno +import importlib.util import inspect import os import sys @@ -30,13 +31,11 @@ MarkupMode = Literal["markdown", "rich", None] -try: - import rich +HAS_RICH = importlib.util.find_spec("rich") is not None +if HAS_RICH: DEFAULT_MARKUP_MODE: MarkupMode = "rich" - -except ImportError: # pragma: no cover - rich = None # type: ignore +else: # pragma: no cover DEFAULT_MARKUP_MODE = None @@ -210,7 +209,7 @@ if not standalone_mode: raise # Typer override - if rich and rich_markup_mode is not None: + if HAS_RICH and rich_markup_mode is not None: from . import rich_utils rich_utils.rich_format_error(e) @@ -242,7 +241,7 @@ if not standalone_mode: raise # Typer override - if rich and rich_markup_mode is not None: + if HAS_RICH and rich_markup_mode is not None: from . import rich_utils rich_utils.rich_abort_error() @@ -370,7 +369,7 @@ if extra: extra_str = "; ".join(extra) extra_str = f"[{extra_str}]" - if rich is not None: + if HAS_RICH: # This is needed for when we want to export to HTML from . import rich_utils @@ -403,6 +402,9 @@ var += "..." return var + def value_is_missing(self, value: Any) -> bool: + return _value_is_missing(self, value) + class TyperOption(click.core.Option): def __init__( @@ -583,7 +585,7 @@ if extra: extra_str = "; ".join(extra) extra_str = f"[{extra_str}]" - if rich is not None: + if HAS_RICH: # This is needed for when we want to export to HTML from . import rich_utils @@ -593,6 +595,23 @@ return ("; " if any_prefix_is_slash else " / ").join(rv), help + def value_is_missing(self, value: Any) -> bool: + return _value_is_missing(self, value) + + +def _value_is_missing(param: click.Parameter, value: Any) -> bool: + if value is None: + return True + + # Click 8.3 and beyond + # if value is UNSET: + # return True + + if (param.nargs != 1 or param.multiple) and value == (): + return True # pragma: no cover + + return False + def _typer_format_options( self: click.core.Command, *, ctx: click.Context, formatter: click.HelpFormatter @@ -709,7 +728,7 @@ ) def format_help(self, ctx: click.Context, formatter: click.HelpFormatter) -> None: - if not rich or self.rich_markup_mode is None: + if not HAS_RICH or self.rich_markup_mode is None: return super().format_help(ctx, formatter) from . import rich_utils @@ -774,7 +793,7 @@ ) def format_help(self, ctx: click.Context, formatter: click.HelpFormatter) -> None: - if not rich or self.rich_markup_mode is None: + if not HAS_RICH or self.rich_markup_mode is None: return super().format_help(ctx, formatter) from . import rich_utils diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/typer/main.py new/typer_slim-0.19.1/typer/main.py --- old/typer_slim-0.17.4/typer/main.py 2025-09-05 20:14:23.168924800 +0200 +++ new/typer_slim-0.19.1/typer/main.py 2025-09-20 10:59:07.694539800 +0200 @@ -17,10 +17,11 @@ import click from typer._types import TyperChoice -from ._typing import get_args, get_origin, is_union +from ._typing import get_args, get_origin, is_literal_type, is_union, literal_values from .completion import get_completion_inspect_parameters from .core import ( DEFAULT_MARKUP_MODE, + HAS_RICH, MarkupMode, TyperArgument, TyperCommand, @@ -49,12 +50,6 @@ ) from .utils import get_params_from_function -try: - import rich - -except ImportError: # pragma: no cover - rich = None # type: ignore - _original_except_hook = sys.excepthook _typer_developer_exception_attr_name = "__typer_developer_exception__" @@ -77,7 +72,7 @@ click_path = os.path.dirname(click.__file__) internal_dir_names = [typer_path, click_path] exc = exc_value - if rich: + if HAS_RICH: from . import rich_utils rich_tb = rich_utils.get_traceback(exc, exception_config, internal_dir_names) @@ -632,10 +627,10 @@ def generate_list_convertor( convertor: Optional[Callable[[Any], Any]], default_value: Optional[Any] -) -> Callable[[Sequence[Any]], Optional[List[Any]]]: - def internal_convertor(value: Sequence[Any]) -> Optional[List[Any]]: - if default_value is None and len(value) == 0: - return None +) -> Callable[[Optional[Sequence[Any]]], Optional[List[Any]]]: + def internal_convertor(value: Optional[Sequence[Any]]) -> Optional[List[Any]]: + if value is None or len(value) == 0: + return default_value return [convertor(v) if convertor else v for v in value] return internal_convertor @@ -788,6 +783,11 @@ [item.value for item in annotation], case_sensitive=parameter_info.case_sensitive, ) + elif is_literal_type(annotation): + return click.Choice( + literal_values(annotation), + case_sensitive=parameter_info.case_sensitive, + ) raise RuntimeError(f"Type not yet supported: {annotation}") # pragma: no cover diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/typer_slim-0.17.4/typer/testing.py new/typer_slim-0.19.1/typer/testing.py --- old/typer_slim-0.17.4/typer/testing.py 2025-09-05 20:14:23.169924700 +0200 +++ new/typer_slim-0.19.1/typer/testing.py 2025-09-20 10:59:07.694539800 +0200 @@ -12,7 +12,7 @@ app: Typer, args: Optional[Union[str, Sequence[str]]] = None, input: Optional[Union[bytes, str, IO[Any]]] = None, - env: Optional[Mapping[str, str]] = None, + env: Optional[Mapping[str, Optional[str]]] = None, catch_exceptions: bool = True, color: bool = False, **extra: Any,