Skip to content

Commit

Permalink
✨ Refactor and document handling pretty exceptions (#422)
Browse files Browse the repository at this point in the history
* ♻️ Rename pretty_errors_ to pretty_exceptions_ to avoid confusion with validation errors

* ✨ Add source examples for pretty_exceptions

* 📝 Add docs for pretty exceptions

* 🔧 Add Exceptions docs to MkDocs

* ✅ Add tests for tutorial about exceptions

* ✅ Update trackeback tests, remove tests covered by tutorial

* ✅ Fix test for module
  • Loading branch information
tiangolo committed Jul 12, 2022
1 parent d4dff65 commit c750f82
Show file tree
Hide file tree
Showing 15 changed files with 531 additions and 117 deletions.
281 changes: 281 additions & 0 deletions docs/tutorial/exceptions.md

Large diffs are not rendered by default.

File renamed without changes.
12 changes: 12 additions & 0 deletions docs_src/exceptions/tutorial002.py
@@ -0,0 +1,12 @@
import typer

app = typer.Typer(pretty_exceptions_show_locals=False)


@app.command()
def main(password: str):
print(password + 3)


if __name__ == "__main__":
app()
@@ -1,6 +1,6 @@
import typer

app = typer.Typer(pretty_errors_enable=False)
app = typer.Typer(pretty_exceptions_short=False)


@app.command()
Expand Down
@@ -1,6 +1,6 @@
import typer

app = typer.Typer(pretty_errors_short=False)
app = typer.Typer(pretty_exceptions_enable=False)


@app.command()
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Expand Up @@ -79,6 +79,7 @@ nav:
- Testing: tutorial/testing.md
- Using Click: tutorial/using-click.md
- Building a Package: tutorial/package.md
- tutorial/exceptions.md
- Typer CLI - completion for small scripts: typer-cli.md
- Alternatives, Inspiration and Comparisons: alternatives.md
- Help Typer - Get Help: help-typer.md
Expand Down
2 changes: 1 addition & 1 deletion tests/assets/type_error_no_rich_short_disable.py
Expand Up @@ -4,7 +4,7 @@
typer.main.rich = None


app = typer.Typer(pretty_errors_short=False)
app = typer.Typer(pretty_exceptions_short=False)


@app.command()
Expand Down
86 changes: 0 additions & 86 deletions tests/test_tracebacks.py
Expand Up @@ -3,72 +3,6 @@
from pathlib import Path


def test_traceback_rich():
file_path = Path(__file__).parent / "assets/type_error_rich.py"
result = subprocess.run(
["coverage", "run", str(file_path)],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={**os.environ, "_TYPER_STANDARD_TRACEBACK": ""},
)
assert "return get_command(self)(*args, **kwargs)" not in result.stderr

assert "typer.run(main)" not in result.stderr
assert "print(name + 3)" in result.stderr

# TODO: when deprecating Python 3.6, remove second option
assert (
'TypeError: can only concatenate str (not "int") to str' in result.stderr
or "TypeError: must be str, not int" in result.stderr
)
assert "name = 'morty'" in result.stderr


def test_standard_traceback_env_var():
file_path = Path(__file__).parent / "assets/type_error_rich.py"
result = subprocess.run(
["coverage", "run", str(file_path)],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={**os.environ, "_TYPER_STANDARD_TRACEBACK": "1"},
)
assert "return get_command(self)(*args, **kwargs)" in result.stderr

assert "typer.run(main)" in result.stderr
assert "print(name + 3)" in result.stderr

# TODO: when deprecating Python 3.6, remove second option
assert (
'TypeError: can only concatenate str (not "int") to str' in result.stderr
or "TypeError: must be str, not int" in result.stderr
)
assert "name = 'morty'" not in result.stderr


def test_traceback_rich_pretty_short_disable():
file_path = Path(__file__).parent / "assets/type_error_rich_short_disable.py"
result = subprocess.run(
["coverage", "run", str(file_path)],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={**os.environ, "_TYPER_STANDARD_TRACEBACK": ""},
)
assert "return get_command(self)(*args, **kwargs)" not in result.stderr

assert "app()" in result.stderr
assert "print(name + 3)" in result.stderr

# TODO: when deprecating Python 3.6, remove second option
assert (
'TypeError: can only concatenate str (not "int") to str' in result.stderr
or "TypeError: must be str, not int" in result.stderr
)
assert "name = 'morty'" in result.stderr


def test_traceback_no_rich():
file_path = Path(__file__).parent / "assets/type_error_no_rich.py"
result = subprocess.run(
Expand Down Expand Up @@ -130,23 +64,3 @@ def test_unmodified_traceback():
'TypeError: can only concatenate str (not "int") to str' in result.stderr
or "TypeError: must be str, not int" in result.stderr
)


def test_rich_pretty_errors_disable():
file_path = Path(__file__).parent / "assets/type_error_rich_pretty_disable.py"
result = subprocess.run(
["coverage", "run", str(file_path)],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={**os.environ, "_TYPER_STANDARD_TRACEBACK": ""},
)
assert "return get_command(self)(*args, **kwargs)" in result.stderr

assert "app()" in result.stderr
assert "print(name + 3)" in result.stderr
# TODO: when deprecating Python 3.6, remove second option
assert (
'TypeError: can only concatenate str (not "int") to str' in result.stderr
or "TypeError: must be str, not int" in result.stderr
)
Empty file.
63 changes: 63 additions & 0 deletions tests/test_tutorial/test_exceptions/test_tutorial001.py
@@ -0,0 +1,63 @@
import os
import subprocess
from pathlib import Path

from typer.testing import CliRunner

from docs_src.exceptions import tutorial001 as mod

runner = CliRunner()


def test_traceback_rich():
file_path = Path(mod.__file__)
result = subprocess.run(
["coverage", "run", str(file_path)],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={**os.environ, "_TYPER_STANDARD_TRACEBACK": ""},
)
assert "return get_command(self)(*args, **kwargs)" not in result.stderr

assert "typer.run(main)" not in result.stderr
assert "print(name + 3)" in result.stderr

# TODO: when deprecating Python 3.6, remove second option
assert (
'TypeError: can only concatenate str (not "int") to str' in result.stderr
or "TypeError: must be str, not int" in result.stderr
)
assert "name = 'morty'" in result.stderr


def test_standard_traceback_env_var():
file_path = Path(mod.__file__)
result = subprocess.run(
["coverage", "run", str(file_path)],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={**os.environ, "_TYPER_STANDARD_TRACEBACK": "1"},
)
assert "return get_command(self)(*args, **kwargs)" in result.stderr

assert "typer.run(main)" in result.stderr
assert "print(name + 3)" in result.stderr

# TODO: when deprecating Python 3.6, remove second option
assert (
'TypeError: can only concatenate str (not "int") to str' in result.stderr
or "TypeError: must be str, not int" in result.stderr
)
assert "name = 'morty'" not in result.stderr


def test_script():
result = subprocess.run(
["coverage", "run", mod.__file__, "--help"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
)
assert "Usage" in result.stdout
63 changes: 63 additions & 0 deletions tests/test_tutorial/test_exceptions/test_tutorial002.py
@@ -0,0 +1,63 @@
import os
import subprocess
from pathlib import Path

from typer.testing import CliRunner

from docs_src.exceptions import tutorial002 as mod

runner = CliRunner()


def test_traceback_rich():
file_path = Path(mod.__file__)
result = subprocess.run(
["coverage", "run", str(file_path), "secret"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={**os.environ, "_TYPER_STANDARD_TRACEBACK": ""},
)
assert "return get_command(self)(*args, **kwargs)" not in result.stderr

assert "app()" not in result.stderr
assert "print(password + 3)" in result.stderr

# TODO: when deprecating Python 3.6, remove second option
assert (
'TypeError: can only concatenate str (not "int") to str' in result.stderr
or "TypeError: must be str, not int" in result.stderr
)
assert "name = 'morty'" not in result.stderr


def test_standard_traceback_env_var():
file_path = Path(mod.__file__)
result = subprocess.run(
["coverage", "run", str(file_path), "secret"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={**os.environ, "_TYPER_STANDARD_TRACEBACK": "1"},
)
assert "return get_command(self)(*args, **kwargs)" in result.stderr

assert "app()" in result.stderr
assert "print(password + 3)" in result.stderr

# TODO: when deprecating Python 3.6, remove second option
assert (
'TypeError: can only concatenate str (not "int") to str' in result.stderr
or "TypeError: must be str, not int" in result.stderr
)
assert "name = 'morty'" not in result.stderr


def test_script():
result = subprocess.run(
["coverage", "run", mod.__file__, "--help"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
)
assert "Usage" in result.stdout
41 changes: 41 additions & 0 deletions tests/test_tutorial/test_exceptions/test_tutorial003.py
@@ -0,0 +1,41 @@
import os
import subprocess
from pathlib import Path

from typer.testing import CliRunner

from docs_src.exceptions import tutorial003 as mod

runner = CliRunner()


def test_traceback_rich_pretty_short_disable():
file_path = Path(mod.__file__)
result = subprocess.run(
["coverage", "run", str(file_path)],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={**os.environ, "_TYPER_STANDARD_TRACEBACK": ""},
)
assert "return get_command(self)(*args, **kwargs)" not in result.stderr

assert "app()" in result.stderr
assert "print(name + 3)" in result.stderr

# TODO: when deprecating Python 3.6, remove second option
assert (
'TypeError: can only concatenate str (not "int") to str' in result.stderr
or "TypeError: must be str, not int" in result.stderr
)
assert "name = 'morty'" in result.stderr


def test_script():
result = subprocess.run(
["coverage", "run", mod.__file__, "--help"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
)
assert "Usage" in result.stdout
39 changes: 39 additions & 0 deletions tests/test_tutorial/test_exceptions/test_tutorial004.py
@@ -0,0 +1,39 @@
import os
import subprocess
from pathlib import Path

from typer.testing import CliRunner

from docs_src.exceptions import tutorial004 as mod

runner = CliRunner()


def test_rich_pretty_exceptions_disable():
file_path = Path(mod.__file__)
result = subprocess.run(
["coverage", "run", str(file_path)],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env={**os.environ, "_TYPER_STANDARD_TRACEBACK": ""},
)
assert "return get_command(self)(*args, **kwargs)" in result.stderr

assert "app()" in result.stderr
assert "print(name + 3)" in result.stderr
# TODO: when deprecating Python 3.6, remove second option
assert (
'TypeError: can only concatenate str (not "int") to str' in result.stderr
or "TypeError: must be str, not int" in result.stderr
)


def test_script():
result = subprocess.run(
["coverage", "run", mod.__file__, "--help"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
)
assert "Usage" in result.stdout

0 comments on commit c750f82

Please sign in to comment.