File: register_format.md

package info (click to toggle)
python-inline-snapshot 0.31.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,708 kB
  • sloc: python: 9,261; makefile: 39; sh: 32
file content (149 lines) | stat: -rw-r--r-- 4,501 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
## General

`register_format()` allows you to add support for your custom file formats, such as the following `Array` type.

<!-- inline-snapshot-lib: my_array.py -->
``` python title="my_array.py"
from dataclasses import dataclass
from typing import List


@dataclass
class Array:
    numbers: List[int]
```
[](){#array-example}
<!-- inline-snapshot-lib: conftest.py -->
``` python title="conftest.py"
from pathlib import Path
from inline_snapshot import register_format, TextDiff, Format

from my_array import Array


@register_format
class ArrayFormat(TextDiff, Format[Array]):
    suffix = ".arr"

    @staticmethod
    def is_format_for(data: object):
        return isinstance(data, Array)

    @staticmethod
    def encode(value: Array, path: Path):
        with path.open("w", encoding="utf-8", newline="\n") as f:
            f.write("\n".join(map(str, value.numbers)))

    @staticmethod
    def decode(path: Path) -> Array:
        with path.open("r", encoding="utf-8", newline="\n") as f:
            return Array(list(map(int, f.read().splitlines())))
```

You can then use `external()` to save this type into an external file.

<!-- inline-snapshot: first_block outcome-failed=1 outcome-errors=1 -->
``` python
from my_array import Array
from inline_snapshot import external


def test_array():
    assert Array([1, 2, 3]) == external()
```

inline-snapshot will check if the type matches by using `is_format_for()` and create a file with the given suffix.

<!-- inline-snapshot: create outcome-passed=1 outcome-errors=1 -->
``` python hl_lines="6 7 8"
from my_array import Array
from inline_snapshot import external


def test_array():
    assert Array([1, 2, 3]) == external(
        "uuid:e3e70682-c209-4cac-a29f-6fbed82c07cd.arr"
    )
```


### Report

inline-snapshot needs to know how changes in your external files should be displayed. `TextDiff` and `BinaryDiff` can be used as mixin classes (see the `Array` example (above)[#array-example]) to provide generic representations for text or binary formats, but you can also define custom functions for your files.

* `rich_diff()` is used every time the external snapshot value is changed and should show the difference between the original and new versions in a human-readable form.
* `rich_show()` is used every time an external snapshot is created and should show a human-readable form of the initial value.

<!-- inline-snapshot-lib-set: conftest.py -->
``` python title="conftest.py"
from pathlib import Path
from inline_snapshot import register_format, Format, external
from number_set import NumberSet


@register_format
class NumberSetFormat(Format[NumberSet]):
    suffix = ".numberset"

    def rich_diff(self, original: Path, new: Path):
        original_numbers = set(self.decode(original).numbers)
        new_numbers = set(self.decode(new).numbers)

        return (
            f"new numbers: [green]{new_numbers-original_numbers}[/]\n"
            f"removed numbers: [red]{original_numbers-new_numbers}[/]"
        )

    def rich_show(self, path: Path):
        return " ".join(f"[blue]{n}[/]" for n in self.decode(path).numbers)

    def is_format_for(self, data: object):
        return isinstance(data, NumberSet)

    def encode(self, value: NumberSet, path: Path):
        with path.open("w", encoding="utf-8", newline="\n") as f:
            f.write("\n".join(sorted(map(str, value.numbers))))

    def decode(self, path: Path) -> NumberSet:
        with path.open("r", encoding="utf-8", newline="\n") as f:
            return NumberSet(set(map(int, f.read().splitlines())))
```

<!-- inline-snapshot-lib: number_set.py -->
``` python title="number_set.py"
from dataclasses import dataclass
from typing import Set


@dataclass
class NumberSet:
    numbers: Set[int]
```

The custom format is then used every time a `NumberSet` is compared with an empty external.

=== "example pytest output"

    ![](/assets/number_set_output.png)

=== "example"
    <!-- inline-snapshot: create first_block outcome-failed=1 -->
    ``` python
    from inline_snapshot import external
    from number_set import NumberSet


    def test():
        assert NumberSet([1, 2, 5]) == external("hash:b85198032326*.numberset")
        assert NumberSet([1, 2, 8]) == external("hash:f8a68eb0c510*.numberset")
    ```

## Reference

::: inline_snapshot
    options:
      heading_level: 3
      members: [register_format, register_format_alias, Format]
      show_root_heading: false
      show_bases: false
      show_source: false