diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c31b846..eadf840 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,7 +8,6 @@ repos: - hooks: - id: check-yaml - id: check-ast - - id: check-docstring-first - id: check-merge-conflict - id: trailing-whitespace - id: mixed-line-ending diff --git a/docs/extras.md b/docs/extras.md new file mode 100644 index 0000000..1d708fa --- /dev/null +++ b/docs/extras.md @@ -0,0 +1,9 @@ + + +::: inline_snapshot.extras + options: + heading_level: 1 + show_root_heading: true + show_source: true + separate_signature: false + show_signature_annotations: true diff --git a/docs/testing.md b/docs/testing.md index 5a1b737..683043d 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -23,12 +23,12 @@ The following example shows how you can use the `Example` class to test what inl assert 1+1 == snapshot() """ } - ).run_inline( + ).run_inline( # run without flags reported_flags=snapshot(), - ).run_pytest( + ).run_pytest( # run without flags and check the pytest report changed_files=snapshot(), report=snapshot(), - ).run_pytest( + ).run_pytest( # run with create flag and check the changed files ["--inline-snapshot=create"], changed_files=snapshot(), ) @@ -52,9 +52,9 @@ The following example shows how you can use the `Example` class to test what inl assert 1+1 == snapshot() """ } - ).run_inline( + ).run_inline( # run without flags reported_flags=snapshot(["create"]), - ).run_pytest( + ).run_pytest( # run without flags and check the pytest report changed_files=snapshot({}), report=snapshot( """\ @@ -62,7 +62,7 @@ The following example shows how you can use the `Example` class to test what inl You can also use --inline-snapshot=review to approve the changes interactiv\ """ ), - ).run_pytest( + ).run_pytest( # run with create flag and check the changed files ["--inline-snapshot=create"], changed_files=snapshot( { @@ -80,53 +80,9 @@ The following example shows how you can use the `Example` class to test what inl ## API ::: inline_snapshot.testing.Example options: - separate_signature: true + heading_level: 3 + show_root_heading: true + show_root_full_path: false show_signature_annotations: true - - -## Types - -The following types are for type checking. - -::: inline_snapshot.Category - -see [categories](categories.md) - -::: inline_snapshot.Snapshot - -Can be used to annotate where snapshots can be passed as function arguments. - -??? note "Example" - - ```python - from typing import Optional - from inline_snapshot import snapshot, Snapshot - - - def check_in_bounds(value, lower: Snapshot[int], upper: Snapshot[int]): - assert lower <= value <= upper - - - def test_numbers(): - for c in "hello world": - check_in_bounds(ord(c), snapshot(32), snapshot(119)) - - - def check_container( - value, - *, - value_repr: Optional[Snapshot[str]] = None, - length: Optional[Snapshot[int]] = None - ): - if value_repr is not None: - assert repr(value) == value_repr - - if length is not None: - assert len(value) == length - - - def test_container(): - check_container([1, 2], value_repr=snapshot("[1, 2]"), length=snapshot(2)) - - check_container({1, 1}, length=snapshot(1)) - ``` + show_source: false + annotations_path: brief diff --git a/docs/types.md b/docs/types.md new file mode 100644 index 0000000..9721009 --- /dev/null +++ b/docs/types.md @@ -0,0 +1,8 @@ + + +::: inline_snapshot + options: + heading_level: 1 + members: [Snapshot,Category] + show_root_heading: true + show_bases: false diff --git a/mkdocs.yml b/mkdocs.yml index 78f371a..aa6e639 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -37,6 +37,8 @@ nav: - snapshot()[key]: getitem_snapshot.md - outsource(data): outsource.md - '@customize_repr': customize_repr.md + - extras: extras.md + - types: types.md - pytest integration: pytest.md - Categories: categories.md - Configuration: configuration.md @@ -64,15 +66,7 @@ markdown_extensions: alternate_style: true plugins: -- mkdocstrings: - handlers: - python: - options: - show_root_heading: true - show_source: false - show_root_full_path: false - merge_init_into_class: true - heading_level: 3 +- mkdocstrings - social - search - markdown-exec: diff --git a/pyproject.toml b/pyproject.toml index 41994ad..8755798 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -89,9 +89,10 @@ dependencies = [ "markdown-exec[ansi]>=1.8.0", "mkdocs>=1.4.2", "mkdocs-material[imaging]>=9.5.17", - "mkdocstrings[python-legacy]>=0.19.0", + "mkdocstrings[python]>=0.19.0", "replace-url @ {root:uri}/docs/plugins", - "pytest" + "pytest", + "black" ] [tool.hatch.envs.docs.scripts] @@ -125,6 +126,9 @@ python = ["3.8", "3.9", "3.10", "3.11", "3.12"] [tool.hatch.envs.types.scripts] check = "mypy --install-types --non-interactive {args:src/inline_snapshot tests}" +[tool.hatch.venv.typing] +inherit = "types" + [tool.mypy] exclude = "tests/.*_samples" diff --git a/src/inline_snapshot/_types.py b/src/inline_snapshot/_types.py index a518025..b87d6f0 100644 --- a/src/inline_snapshot/_types.py +++ b/src/inline_snapshot/_types.py @@ -1,11 +1,75 @@ +"""The following types are for type checking only.""" + +... # prevent lint error with black and reorder-python-imports + +from typing import Generic from typing import Literal +from typing import TYPE_CHECKING from typing import TypeVar -from typing_extensions import Annotated +from typing_extensions import TypeAlias + +if TYPE_CHECKING: + + T = TypeVar("T") + + Snapshot: TypeAlias = T + +else: + T = TypeVar("T") + + class Snapshot(Generic[T]): + """Can be used to annotate function arguments which accept snapshot + values. + + You can annotate function arguments with `Snapshot[T]` to declare that a snapshot-value can be passed as function argument. + `Snapshot[T]` is a type alias for `T`, which allows you to pass `int` values instead of `int` snapshots. + + + Example: + + ```python + from typing import Optional + from inline_snapshot import snapshot, Snapshot + + # required snapshots + + + def check_in_bounds(value, lower: Snapshot[int], upper: Snapshot[int]): + assert lower <= value <= upper + + + def test_numbers(): + for c in "hello world": + check_in_bounds(ord(c), snapshot(32), snapshot(119)) + + # use with normal values + check_in_bounds(5, 0, 10) + + + # optional snapshots + + + def check_container( + value, + *, + value_repr: Optional[Snapshot[str]] = None, + length: Optional[Snapshot[int]] = None + ): + if value_repr is not None: + assert repr(value) == value_repr + + if length is not None: + assert len(value) == length + -T = TypeVar("T") + def test_container(): + check_container([1, 2], value_repr=snapshot("[1, 2]"), length=snapshot(2)) -Snapshot = Annotated[T, "just an alias"] + check_container({1, 1}, length=snapshot(1)) + ``` + """ Category = Literal["update", "fix", "create", "trim"] +"""See [categories](categories.md)""" diff --git a/src/inline_snapshot/extras.py b/src/inline_snapshot/extras.py new file mode 100644 index 0000000..1839d89 --- /dev/null +++ b/src/inline_snapshot/extras.py @@ -0,0 +1,56 @@ +"""The following functions are build on top of inline-snapshot and could also +be implemented in an extra library. + +They are part of inline-snapshot because they are general useful and do +not depend on other libraries. +""" + +... # prevent lint error with black and reorder-python-imports +import contextlib +from inline_snapshot import Snapshot + + +@contextlib.contextmanager +def raises(message: Snapshot[str]): + """Check that an exception is raised. + + Parameters: + message: snapshot which is compared with `#!python f"{type}: {message}"` if an exception occured or `#!python ""` if no exception was raised. + + === "original" + + + ```python + from inline_snapshot import snapshot + from inline_snapshot.extras import raises + + + def test_raises(): + with raises(snapshot()): + 1 / 0 + ``` + + === "--inline-snapshot=create" + + + ```python + from inline_snapshot import snapshot + from inline_snapshot.extras import raises + + + def test_raises(): + with raises(snapshot("ZeroDivisionError: division by zero")): + 1 / 0 + ``` + """ + + try: + yield + except Exception as exception: + msg = str(exception) + if "\n" in msg: + assert f"{type(exception).__name__}:\n{exception}" == message + else: + assert f"{type(exception).__name__}: {exception}" == message + else: + assert "" == message diff --git a/src/inline_snapshot/testing/_example.py b/src/inline_snapshot/testing/_example.py index 70df563..c160567 100644 --- a/src/inline_snapshot/testing/_example.py +++ b/src/inline_snapshot/testing/_example.py @@ -71,7 +71,8 @@ class Example: def __init__(self, files: str | dict[str, str]): """ Parameters: - files: a collecton of files where inline-snapshot opperates on. + files: a collecton of files where inline-snapshot opperates on, + or just a string which will be saved as *test_something.py*. """ if isinstance(files, str): files = {"test_something.py": files} diff --git a/tests/test_docs.py b/tests/test_docs.py index 1898a8a..7298370 100644 --- a/tests/test_docs.py +++ b/tests/test_docs.py @@ -9,7 +9,7 @@ @pytest.mark.skipif( platform.system() == "Windows", - reason="\r in stdout can cause problems in snapshot strings", + reason="\\r in stdout can cause problems in snapshot strings", ) @pytest.mark.parametrize( "file", @@ -18,6 +18,7 @@ for file in [ *(Path(__file__).parent.parent / "docs").rglob("*.md"), *(Path(__file__).parent.parent).glob("*.md"), + *(Path(__file__).parent.parent / "inline_snapshot").rglob("*.py"), ] ], ) diff --git a/tests/test_external.py b/tests/test_external.py index d3f07f4..0a7331d 100644 --- a/tests/test_external.py +++ b/tests/test_external.py @@ -3,8 +3,8 @@ from inline_snapshot import external from inline_snapshot import outsource from inline_snapshot import snapshot +from inline_snapshot.extras import raises -from .utils import raises from tests.utils import config diff --git a/tests/test_raises.py b/tests/test_raises.py new file mode 100644 index 0000000..4ec7b80 --- /dev/null +++ b/tests/test_raises.py @@ -0,0 +1,21 @@ +from inline_snapshot import snapshot +from inline_snapshot.extras import raises + + +def test_raises(): + with raises(snapshot("ZeroDivisionError: division by zero")): + 0 / 0 + + with raises(snapshot("")): + pass + + with raises( + snapshot( + """\ +ValueError: +with +two lines\ +""" + ) + ): + raise ValueError("with\ntwo lines") diff --git a/tests/utils.py b/tests/utils.py index e492b6c..3537f22 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -20,14 +20,6 @@ def config(**args): _config.config = current_config -@contextlib.contextmanager -def raises(snapshot): - with pytest.raises(Exception) as excinfo: - yield - - assert f"{type(excinfo.value).__name__}: {excinfo.value}" == snapshot - - @contextlib.contextmanager def apply_changes(): with ChangeRecorder().activate() as recorder: