From 1583c7d3094c0543cf8e560a08ee5cff55416f56 Mon Sep 17 00:00:00 2001 From: Alvaro Valdebenito Date: Tue, 21 Mar 2023 10:27:57 +0100 Subject: [PATCH 01/14] add test for #570 --- tests/assets/enum_case.py | 25 +++++++++++++++++++++++++ tests/test_enum_case.py | 26 ++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 tests/assets/enum_case.py create mode 100644 tests/test_enum_case.py diff --git a/tests/assets/enum_case.py b/tests/assets/enum_case.py new file mode 100644 index 0000000000..bce2b15d85 --- /dev/null +++ b/tests/assets/enum_case.py @@ -0,0 +1,25 @@ +from enum import Enum + +import typer +from typer.testing import CliRunner + +runner = CliRunner() +app = typer.Typer() + + +class Case(str, Enum): + UPPER = "CASE" + TITLE = "Case" + LOWER = "case" + + def __str__(self) -> str: + return self.value + + +@app.command() +def enum_case(case: Case): + print(case) + + +if __name__ == "__main__": + app() diff --git a/tests/test_enum_case.py b/tests/test_enum_case.py new file mode 100644 index 0000000000..54680947c9 --- /dev/null +++ b/tests/test_enum_case.py @@ -0,0 +1,26 @@ +import pytest +from typer.testing import CliRunner + +from tests.assets import enum_case as mod + +runner = CliRunner() + + +wrong_case = pytest.mark.xfail( + reason="Enum values that differ in case get conflated https://github.com/tiangolo/typer/discussions/570", + strict=True, +) + + +@pytest.mark.parametrize( + "case", + ( + pytest.param(mod.Case.UPPER, marks=wrong_case), + pytest.param(mod.Case.TITLE, marks=wrong_case), + pytest.param(mod.Case.LOWER), + ), +) +def test_enum_case(case: mod.Case): + result = runner.invoke(mod.app, [f"{case}"]) + assert result.exit_code == 0 + assert case in result.output From a933d9115ba5649d38629fe31f1750809d80a3c9 Mon Sep 17 00:00:00 2001 From: Alvaro Valdebenito Date: Tue, 21 Mar 2023 12:54:25 +0100 Subject: [PATCH 02/14] remove lower call from generate_enum_convertor --- tests/test_enum_case.py | 16 ++-------------- typer/main.py | 8 ++++---- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/tests/test_enum_case.py b/tests/test_enum_case.py index 54680947c9..f08a383c93 100644 --- a/tests/test_enum_case.py +++ b/tests/test_enum_case.py @@ -6,21 +6,9 @@ runner = CliRunner() -wrong_case = pytest.mark.xfail( - reason="Enum values that differ in case get conflated https://github.com/tiangolo/typer/discussions/570", - strict=True, -) - - -@pytest.mark.parametrize( - "case", - ( - pytest.param(mod.Case.UPPER, marks=wrong_case), - pytest.param(mod.Case.TITLE, marks=wrong_case), - pytest.param(mod.Case.LOWER), - ), -) +@pytest.mark.parametrize("case", mod.Case) def test_enum_case(case: mod.Case): + """regresion test for https://github.com/tiangolo/typer/discussions/570""" result = runner.invoke(mod.app, [f"{case}"]) assert result.exit_code == 0 assert case in result.output diff --git a/typer/main.py b/typer/main.py index f493e544e2..6c4e75a55f 100644 --- a/typer/main.py +++ b/typer/main.py @@ -618,13 +618,13 @@ def param_path_convertor(value: Optional[str] = None) -> Optional[Path]: def generate_enum_convertor(enum: Type[Enum]) -> Callable[[Any], Any]: - lower_val_map = {str(val.value).lower(): val for val in enum} + val_map = {str(val.value): val for val in enum} def convertor(value: Any) -> Any: if value is not None: - low = str(value).lower() - if low in lower_val_map: - key = lower_val_map[low] + val = str(value) + if val in val_map: + key = val_map[val] return enum(key) return convertor From 43a56b8c399e0eb3bf11425e9fdb84fea6a74578 Mon Sep 17 00:00:00 2001 From: Alvaro Valdebenito Date: Tue, 21 Mar 2023 13:24:50 +0100 Subject: [PATCH 03/14] consoliadte Enum case test, close #570 --- tests/assets/enum_case.py | 25 ------------------------- tests/test_enum_case.py | 32 ++++++++++++++++++++++++++------ 2 files changed, 26 insertions(+), 31 deletions(-) delete mode 100644 tests/assets/enum_case.py diff --git a/tests/assets/enum_case.py b/tests/assets/enum_case.py deleted file mode 100644 index bce2b15d85..0000000000 --- a/tests/assets/enum_case.py +++ /dev/null @@ -1,25 +0,0 @@ -from enum import Enum - -import typer -from typer.testing import CliRunner - -runner = CliRunner() -app = typer.Typer() - - -class Case(str, Enum): - UPPER = "CASE" - TITLE = "Case" - LOWER = "case" - - def __str__(self) -> str: - return self.value - - -@app.command() -def enum_case(case: Case): - print(case) - - -if __name__ == "__main__": - app() diff --git a/tests/test_enum_case.py b/tests/test_enum_case.py index f08a383c93..62b5ecb005 100644 --- a/tests/test_enum_case.py +++ b/tests/test_enum_case.py @@ -1,14 +1,34 @@ +""" +Regresion test for + Enum values that differ in case get conflated #570 + https://github.com/tiangolo/typer/discussions/570 +""" +from enum import Enum + import pytest +import typer from typer.testing import CliRunner -from tests.assets import enum_case as mod - runner = CliRunner() +app = typer.Typer() + + +class Case(str, Enum): + UPPER = "CASE" + TITLE = "Case" + LOWER = "case" + + def __str__(self) -> str: + return self.value + + +@app.command() +def enum_case(case: Case): + print(case) -@pytest.mark.parametrize("case", mod.Case) -def test_enum_case(case: mod.Case): - """regresion test for https://github.com/tiangolo/typer/discussions/570""" - result = runner.invoke(mod.app, [f"{case}"]) +@pytest.mark.parametrize("case", Case) +def test_enum_case(case: Case): + result = runner.invoke(app, [f"{case}"]) assert result.exit_code == 0 assert case in result.output From de46d1a879b9064467c8490ad11e41ba9c5ee893 Mon Sep 17 00:00:00 2001 From: svlandeg Date: Wed, 6 Mar 2024 18:06:15 +0100 Subject: [PATCH 04/14] Fix typo --- tests/test_enum_case.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_enum_case.py b/tests/test_enum_case.py index 62b5ecb005..492b0619ca 100644 --- a/tests/test_enum_case.py +++ b/tests/test_enum_case.py @@ -1,5 +1,5 @@ """ -Regresion test for +Regression test for Enum values that differ in case get conflated #570 https://github.com/tiangolo/typer/discussions/570 """ From 6e6ea7c22a4ea74fe6972621a9bfdae3fb8af727 Mon Sep 17 00:00:00 2001 From: svlandeg Date: Wed, 6 Mar 2024 18:10:22 +0100 Subject: [PATCH 05/14] Rewrite test to use Interval Enum --- tests/test_enum_case.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/tests/test_enum_case.py b/tests/test_enum_case.py index 492b0619ca..a0c865801c 100644 --- a/tests/test_enum_case.py +++ b/tests/test_enum_case.py @@ -13,22 +13,18 @@ app = typer.Typer() -class Case(str, Enum): - UPPER = "CASE" - TITLE = "Case" - LOWER = "case" - - def __str__(self) -> str: - return self.value +class Interval(Enum): + ONE_MINUTE = "1m" + ONE_MONTH = "1M" @app.command() -def enum_case(case: Case): - print(case) +def enum_case(interval: Interval): + print(interval.value) -@pytest.mark.parametrize("case", Case) -def test_enum_case(case: Case): - result = runner.invoke(app, [f"{case}"]) +@pytest.mark.parametrize("interval", ["1M", "1m"]) +def test_enum_case(interval: str): + result = runner.invoke(app, [interval]) assert result.exit_code == 0 - assert case in result.output + assert interval in result.output From db416e1acb643eabb70c7f5a5b416649af4aa36b Mon Sep 17 00:00:00 2001 From: svlandeg Date: Wed, 6 Mar 2024 18:22:31 +0100 Subject: [PATCH 06/14] Add version of the test as tutorial003 for parameter type enum --- docs_src/parameter_types/enum/tutorial003.py | 19 +++++++++++++++++++ .../test_enum/test_tutorial003.py | 17 +++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 docs_src/parameter_types/enum/tutorial003.py create mode 100644 tests/test_tutorial/test_parameter_types/test_enum/test_tutorial003.py diff --git a/docs_src/parameter_types/enum/tutorial003.py b/docs_src/parameter_types/enum/tutorial003.py new file mode 100644 index 0000000000..137caa3070 --- /dev/null +++ b/docs_src/parameter_types/enum/tutorial003.py @@ -0,0 +1,19 @@ +from enum import Enum + +import typer + + +class Interval(Enum): + ONE_MINUTE = "1m" + ONE_MONTH = "1M" + OTHER = "o" + + +def main( + interval: Interval = typer.Option(Interval.OTHER, case_sensitive=True) +): + print(f"Found interval: {interval.value}") + + +if __name__ == "__main__": + typer.run(main) diff --git a/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial003.py b/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial003.py new file mode 100644 index 0000000000..2998be5253 --- /dev/null +++ b/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial003.py @@ -0,0 +1,17 @@ +import pytest +import typer +from typer.testing import CliRunner + +from docs_src.parameter_types.enum import tutorial003 as mod + +runner = CliRunner() + +app = typer.Typer() +app.command()(mod.main) + + +@pytest.mark.parametrize("interval", ["1M", "1m"]) +def test_case(interval): + result = runner.invoke(app, ["--interval", interval]) + assert result.exit_code == 0 + assert f"Found interval: {interval}" in result.output From 40e2e924aea227e7e3b4d82f5b0457c44df9eee0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 6 Mar 2024 17:22:46 +0000 Subject: [PATCH 07/14] =?UTF-8?q?=F0=9F=8E=A8=20[pre-commit.ci]=20Auto=20f?= =?UTF-8?q?ormat=20from=20pre-commit.com=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_src/parameter_types/enum/tutorial003.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs_src/parameter_types/enum/tutorial003.py b/docs_src/parameter_types/enum/tutorial003.py index 137caa3070..c06512ddfc 100644 --- a/docs_src/parameter_types/enum/tutorial003.py +++ b/docs_src/parameter_types/enum/tutorial003.py @@ -9,9 +9,7 @@ class Interval(Enum): OTHER = "o" -def main( - interval: Interval = typer.Option(Interval.OTHER, case_sensitive=True) -): +def main(interval: Interval = typer.Option(Interval.OTHER, case_sensitive=True)): print(f"Found interval: {interval.value}") From f2e14aa0a22bbdaf0790a051d1cbe446100c617a Mon Sep 17 00:00:00 2001 From: svlandeg Date: Wed, 6 Mar 2024 18:37:32 +0100 Subject: [PATCH 08/14] Add test_script --- .../test_enum/test_tutorial003.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial003.py b/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial003.py index 2998be5253..b4791f12e3 100644 --- a/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial003.py +++ b/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial003.py @@ -1,3 +1,6 @@ +import subprocess +import sys + import pytest import typer from typer.testing import CliRunner @@ -15,3 +18,13 @@ def test_case(interval): result = runner.invoke(app, ["--interval", interval]) assert result.exit_code == 0 assert f"Found interval: {interval}" in result.output + + +def test_script(): + result = subprocess.run( + [sys.executable, "-m", "coverage", "run", mod.__file__, "--help"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding="utf-8", + ) + assert "Usage" in result.stdout From 161706a32994feba4b9e3df069768c4b0c35ad1c Mon Sep 17 00:00:00 2001 From: svlandeg Date: Mon, 11 Mar 2024 16:37:07 +0100 Subject: [PATCH 09/14] update enum tutorial003 to use the same example from the docs as the other tests --- docs_src/parameter_types/enum/tutorial003.py | 15 +++++++++------ .../test_enum/test_tutorial003.py | 13 +++++++++---- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/docs_src/parameter_types/enum/tutorial003.py b/docs_src/parameter_types/enum/tutorial003.py index c06512ddfc..854b34d403 100644 --- a/docs_src/parameter_types/enum/tutorial003.py +++ b/docs_src/parameter_types/enum/tutorial003.py @@ -3,14 +3,17 @@ import typer -class Interval(Enum): - ONE_MINUTE = "1m" - ONE_MONTH = "1M" - OTHER = "o" +class NeuralNetwork(str, Enum): + simple = "simple" + conv = "conv" + lstm_lower = "lstm" + lstm_upper = "LSTM" -def main(interval: Interval = typer.Option(Interval.OTHER, case_sensitive=True)): - print(f"Found interval: {interval.value}") +def main( + network: NeuralNetwork = typer.Option(NeuralNetwork.simple) +): + print(f"Training neural network of type: {network.value}") if __name__ == "__main__": diff --git a/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial003.py b/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial003.py index b4791f12e3..25f4a9778c 100644 --- a/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial003.py +++ b/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial003.py @@ -13,11 +13,16 @@ app.command()(mod.main) -@pytest.mark.parametrize("interval", ["1M", "1m"]) -def test_case(interval): - result = runner.invoke(app, ["--interval", interval]) +def test_upper(): + result = runner.invoke(app, ["--network", "LSTM"]) assert result.exit_code == 0 - assert f"Found interval: {interval}" in result.output + assert "Training neural network of type: LSTM" in result.output + + +def test_lower(): + result = runner.invoke(app, ["--network", "lstm"]) + assert result.exit_code == 0 + assert "Training neural network of type: lstm" in result.output def test_script(): From 0efe55c934354f697b28d85b9f4d5b55ec77d24f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 15:37:25 +0000 Subject: [PATCH 10/14] =?UTF-8?q?=F0=9F=8E=A8=20[pre-commit.ci]=20Auto=20f?= =?UTF-8?q?ormat=20from=20pre-commit.com=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_src/parameter_types/enum/tutorial003.py | 4 +--- .../test_parameter_types/test_enum/test_tutorial003.py | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/docs_src/parameter_types/enum/tutorial003.py b/docs_src/parameter_types/enum/tutorial003.py index 854b34d403..8a4ad5272a 100644 --- a/docs_src/parameter_types/enum/tutorial003.py +++ b/docs_src/parameter_types/enum/tutorial003.py @@ -10,9 +10,7 @@ class NeuralNetwork(str, Enum): lstm_upper = "LSTM" -def main( - network: NeuralNetwork = typer.Option(NeuralNetwork.simple) -): +def main(network: NeuralNetwork = typer.Option(NeuralNetwork.simple)): print(f"Training neural network of type: {network.value}") diff --git a/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial003.py b/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial003.py index 25f4a9778c..75f206e4e4 100644 --- a/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial003.py +++ b/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial003.py @@ -1,7 +1,6 @@ import subprocess import sys -import pytest import typer from typer.testing import CliRunner From 32459c79f5e5eca6d2355c49ba3bb92fb7f41b66 Mon Sep 17 00:00:00 2001 From: svlandeg Date: Mon, 11 Mar 2024 16:37:59 +0100 Subject: [PATCH 11/14] remove redundant test file --- tests/test_enum_case.py | 30 ------------------------------ 1 file changed, 30 deletions(-) delete mode 100644 tests/test_enum_case.py diff --git a/tests/test_enum_case.py b/tests/test_enum_case.py deleted file mode 100644 index a0c865801c..0000000000 --- a/tests/test_enum_case.py +++ /dev/null @@ -1,30 +0,0 @@ -""" -Regression test for - Enum values that differ in case get conflated #570 - https://github.com/tiangolo/typer/discussions/570 -""" -from enum import Enum - -import pytest -import typer -from typer.testing import CliRunner - -runner = CliRunner() -app = typer.Typer() - - -class Interval(Enum): - ONE_MINUTE = "1m" - ONE_MONTH = "1M" - - -@app.command() -def enum_case(interval: Interval): - print(interval.value) - - -@pytest.mark.parametrize("interval", ["1M", "1m"]) -def test_enum_case(interval: str): - result = runner.invoke(app, [interval]) - assert result.exit_code == 0 - assert interval in result.output From bb6d5a3e339eebea454a6aae6a942135c33b1bc7 Mon Sep 17 00:00:00 2001 From: svlandeg Date: Mon, 11 Mar 2024 16:51:55 +0100 Subject: [PATCH 12/14] Explain default case sensitiveness with tutorial001 in docs, add test --- docs/tutorial/parameter-types/enum.md | 8 ++++++++ .../test_enum/test_tutorial001.py | 17 ++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/docs/tutorial/parameter-types/enum.md b/docs/tutorial/parameter-types/enum.md index 767c329b27..61ed655ce3 100644 --- a/docs/tutorial/parameter-types/enum.md +++ b/docs/tutorial/parameter-types/enum.md @@ -28,6 +28,14 @@ $ python main.py --network conv Training neural network of type: conv +// Note that enums are case sensitive by default +$ python main.py --network CONV + +Usage: main.py [OPTIONS] +Try "main.py --help" for help. + +Error: Invalid value for '--network': invalid choice: CONV. (choose from simple, conv, lstm) + // Invalid value $ python main.py --network capsule diff --git a/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial001.py b/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial001.py index 584b71a0c7..4f2c246ea0 100644 --- a/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial001.py +++ b/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial001.py @@ -25,7 +25,22 @@ def test_main(): assert "Training neural network of type: conv" in result.output -def test_invalid(): +def test_invalid_case(): + result = runner.invoke(app, ["--network", "CONV"]) + assert result.exit_code != 0 + # TODO: when deprecating Click 7, remove second option + + assert ( + "Invalid value for '--network': 'CONV' is not one of" in result.output + or "Invalid value for '--network': invalid choice: CONV. (choose from" + in result.output + ) + assert "simple" in result.output + assert "conv" in result.output + assert "lstm" in result.output + + +def test_invalid_other(): result = runner.invoke(app, ["--network", "capsule"]) assert result.exit_code != 0 # TODO: when deprecating Click 7, remove second option From caecbd2660dba0ee1f25a328ef22fad301931277 Mon Sep 17 00:00:00 2001 From: svlandeg Date: Mon, 11 Mar 2024 16:53:05 +0100 Subject: [PATCH 13/14] Remove redundant enum tutorial003 --- docs_src/parameter_types/enum/tutorial003.py | 18 ---------- .../test_enum/test_tutorial003.py | 34 ------------------- 2 files changed, 52 deletions(-) delete mode 100644 docs_src/parameter_types/enum/tutorial003.py delete mode 100644 tests/test_tutorial/test_parameter_types/test_enum/test_tutorial003.py diff --git a/docs_src/parameter_types/enum/tutorial003.py b/docs_src/parameter_types/enum/tutorial003.py deleted file mode 100644 index 8a4ad5272a..0000000000 --- a/docs_src/parameter_types/enum/tutorial003.py +++ /dev/null @@ -1,18 +0,0 @@ -from enum import Enum - -import typer - - -class NeuralNetwork(str, Enum): - simple = "simple" - conv = "conv" - lstm_lower = "lstm" - lstm_upper = "LSTM" - - -def main(network: NeuralNetwork = typer.Option(NeuralNetwork.simple)): - print(f"Training neural network of type: {network.value}") - - -if __name__ == "__main__": - typer.run(main) diff --git a/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial003.py b/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial003.py deleted file mode 100644 index 75f206e4e4..0000000000 --- a/tests/test_tutorial/test_parameter_types/test_enum/test_tutorial003.py +++ /dev/null @@ -1,34 +0,0 @@ -import subprocess -import sys - -import typer -from typer.testing import CliRunner - -from docs_src.parameter_types.enum import tutorial003 as mod - -runner = CliRunner() - -app = typer.Typer() -app.command()(mod.main) - - -def test_upper(): - result = runner.invoke(app, ["--network", "LSTM"]) - assert result.exit_code == 0 - assert "Training neural network of type: LSTM" in result.output - - -def test_lower(): - result = runner.invoke(app, ["--network", "lstm"]) - assert result.exit_code == 0 - assert "Training neural network of type: lstm" in result.output - - -def test_script(): - result = subprocess.run( - [sys.executable, "-m", "coverage", "run", mod.__file__, "--help"], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - encoding="utf-8", - ) - assert "Usage" in result.stdout From 53a94e43df459917998a5b4540c15541532b43bc Mon Sep 17 00:00:00 2001 From: svlandeg Date: Mon, 11 Mar 2024 16:57:48 +0100 Subject: [PATCH 14/14] switch order of examples --- docs/tutorial/parameter-types/enum.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/tutorial/parameter-types/enum.md b/docs/tutorial/parameter-types/enum.md index 61ed655ce3..b208ed9aa5 100644 --- a/docs/tutorial/parameter-types/enum.md +++ b/docs/tutorial/parameter-types/enum.md @@ -28,21 +28,21 @@ $ python main.py --network conv Training neural network of type: conv -// Note that enums are case sensitive by default -$ python main.py --network CONV +// Invalid value +$ python main.py --network capsule Usage: main.py [OPTIONS] Try "main.py --help" for help. -Error: Invalid value for '--network': invalid choice: CONV. (choose from simple, conv, lstm) +Error: Invalid value for '--network': invalid choice: capsule. (choose from simple, conv, lstm) -// Invalid value -$ python main.py --network capsule +// Note that enums are case sensitive by default +$ python main.py --network CONV Usage: main.py [OPTIONS] Try "main.py --help" for help. -Error: Invalid value for '--network': invalid choice: capsule. (choose from simple, conv, lstm) +Error: Invalid value for '--network': invalid choice: CONV. (choose from simple, conv, lstm) ```