Skip to content

Commit

Permalink
feat: implement extras.raises
Browse files Browse the repository at this point in the history
  • Loading branch information
15r10nk committed Jul 17, 2024
1 parent 8c54538 commit 0443264
Show file tree
Hide file tree
Showing 13 changed files with 186 additions and 81 deletions.
1 change: 0 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions docs/extras.md
Original file line number Diff line number Diff line change
@@ -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
66 changes: 11 additions & 55 deletions docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
)
Expand All @@ -52,17 +52,17 @@ 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(
"""\
Error: one snapshot is missing a value (--inline-snapshot=create)
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(
{
Expand All @@ -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"
<!-- inline-snapshot: create fix trim this outcome-passed=2 -->
```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
8 changes: 8 additions & 0 deletions docs/types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@


::: inline_snapshot
options:
heading_level: 1
members: [Snapshot,Category]
show_root_heading: true
show_bases: false
12 changes: 3 additions & 9 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down
8 changes: 6 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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"

Expand Down
70 changes: 67 additions & 3 deletions src/inline_snapshot/_types.py
Original file line number Diff line number Diff line change
@@ -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:
<!-- inline-snapshot: create fix trim this outcome-passed=2 -->
```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)"""
56 changes: 56 additions & 0 deletions src/inline_snapshot/extras.py
Original file line number Diff line number Diff line change
@@ -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 "<no exception>"` if no exception was raised.
=== "original"
<!-- inline-snapshot: outcome-passed=1 outcome-errors=1 -->
```python
from inline_snapshot import snapshot
from inline_snapshot.extras import raises
def test_raises():
with raises(snapshot()):
1 / 0
```
=== "--inline-snapshot=create"
<!-- inline-snapshot: create outcome-passed=1 -->
```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 "<no exception>" == message
3 changes: 2 additions & 1 deletion src/inline_snapshot/testing/_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
3 changes: 2 additions & 1 deletion tests/test_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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"),
]
],
)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down
21 changes: 21 additions & 0 deletions tests/test_raises.py
Original file line number Diff line number Diff line change
@@ -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("<no exception>")):
pass

with raises(
snapshot(
"""\
ValueError:
with
two lines\
"""
)
):
raise ValueError("with\ntwo lines")
8 changes: 0 additions & 8 deletions tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down

0 comments on commit 0443264

Please sign in to comment.