diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 51340beb..da22c03c 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -36,7 +36,7 @@ jobs:
python-version: ${{matrix.python-version}}
- run: |
- uv run ${{matrix.extra_deps}} -m ${{ matrix.os == 'ubuntu-latest' && 'coverage run -m' || '' }} pytest -n=auto
+ uv run ${{matrix.extra_deps}} --extra black -m ${{ matrix.os == 'ubuntu-latest' && 'coverage run -m' || '' }} pytest -n=auto -vv
- run: |
uv run -m coverage combine
mv .coverage .coverage.${{ matrix.python-version }}-${{matrix.os}}-${{strategy.job-index}}
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 9a05e291..de72e6fe 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -27,6 +27,14 @@ repos:
repo: https://github.com/myint/autoflake
rev: v2.3.1
+- repo: local
+ hooks:
+ - id: replace-words
+ name: Replace Words
+ entry: python3 scripts/replace_words.py
+ language: system
+ files: \.(md|py)$
+
- repo: https://github.com/asottile/setup-cfg-fmt
rev: v2.5.0
hooks:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7bf28927..5eb44e99 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,7 +8,7 @@
## Fixed
-- use '.model_fields' on pydantic model class and not instance. This fixes a deprecation warning in the upcomming pydantic v2.11 (#169)
+- use '.model_fields' on pydantic model class and not instance. This fixes a deprecation warning in the upcoming pydantic v2.11 (#169)
# 0.18.1 — 2024-12-22
@@ -346,7 +346,7 @@
### Fix
-- remove upper bound from dependencies in pyproject.toml
+- remove upper bound from dependency in pyproject.toml
## v0.5.1 (2023-10-20)
diff --git a/changelog.d/20250106_220025_15r10nk-git_format_cmd.md b/changelog.d/20250106_220025_15r10nk-git_format_cmd.md
new file mode 100644
index 00000000..fa816755
--- /dev/null
+++ b/changelog.d/20250106_220025_15r10nk-git_format_cmd.md
@@ -0,0 +1,7 @@
+### Added
+
+- You can now specify which tool you want to use to format your code by setting a `format-command` in your [configuration](https://15r10nk.github.io/inline-snapshot/latest/configuration/#format-command).
+
+### Changed
+
+- **BREAKING-CHANGE** you have to install `inline-snapshot[black]` now if you want to format your code like in the previous versions. This option is not required if you use a `format-command`.
diff --git a/changelog.d/20250109_071706_15r10nk-git_format_cmd.md b/changelog.d/20250109_071706_15r10nk-git_format_cmd.md
new file mode 100644
index 00000000..95548c00
--- /dev/null
+++ b/changelog.d/20250109_071706_15r10nk-git_format_cmd.md
@@ -0,0 +1,4 @@
+### Fixed
+
+- Load default config values even if `[tool.inline-snapshot]` is missing.
+ This makes the documented default shortcuts `--review` and `--fix` work.
diff --git a/docs/changelog.md b/docs/changelog.md
index 786b75d5..3e1b4202 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -1 +1,16 @@
+
+
+``` python exec="1"
+from pathlib import Path
+
+new_changes = list(Path.cwd().glob("changelog.d/*.md"))
+
+if new_changes:
+ print("# upcomming changes")
+
+for file in new_changes:
+ print(file.read_text().replace("###", "##"))
+```
+
+
--8<-- "CHANGELOG.md"
diff --git a/docs/code_generation.md b/docs/code_generation.md
index 42ccbcfd..fd76d618 100644
--- a/docs/code_generation.md
+++ b/docs/code_generation.md
@@ -97,15 +97,25 @@ The code is generated in the following way:
```
-4. The code is formatted with black.
+4. The new code fragments are formatted with black if it is installed.
+ !!! note
+ Black is an optional dependency since inline-snapshot v0.19.0.
+ You can install it with:
+ ``` sh
+ pip install inline-snapshot[black]
+ ```
-5. The whole file is formatted with black if it was formatted before.
+5. The whole file is formatted
+ * with black if it was formatted with black before.
- !!! note
- The black formatting of the whole file could not work for the following reasons:
+ !!! note
+ The black formatting of the whole file could not work for the following reasons:
+
+ 1. black is configured with cli arguments and not in a configuration file.
+ **Solution:** configure black in a [configuration file](https://black.readthedocs.io/en/stable/usage_and_configuration/the_basics.html#configuration-via-a-file)
+ 2. inline-snapshot uses a different black version.
+ **Solution:** specify which black version inline-snapshot should use by adding black with a specific version to your dependencies.
+ 3. black is not installed. Black is an optional dependency since inline-snapshot v0.19.0
- 1. black is configured with cli arguments and not in a configuration file.
- **Solution:** configure black in a [configuration file](https://black.readthedocs.io/en/stable/usage_and_configuration/the_basics.html#configuration-via-a-file)
- 2. inline-snapshot uses a different black version.
- **Solution:** specify which black version inline-snapshot should use by adding black with a specific version to your dependencies.
+ * or with the [format-command][format-command] if you defined one.
diff --git a/docs/configuration.md b/docs/configuration.md
index 851c99cc..0a88cf84 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -4,6 +4,7 @@ Default configuration:
[tool.inline-snapshot]
hash-length=15
default-flags=["short-report"]
+format-command=""
[tool.inline-snapshot.shortcuts]
review=["review"]
@@ -23,3 +24,12 @@ fix=["create","fix"]
By default, it will be `/.inline-snapshot`,
where `` is replaced by the directory containing the Pytest configuration file, if any.
External snapshots will be stored in the `external` subfolder of the storage directory.
+* **format-command:[](){#format-command}** allows you to specify a custom command which is used to format the python code after code is changed.
+ ``` toml
+ [tool.inline-snapshot]
+ format-command="ruff format --stdin-filename {filename}"
+ ```
+ The placeholder `{filename}` can be used to specify the filename if it is needed to find the correct formatting options for this file.
+
+ !!! important
+ The command should **not** format the file on disk. The current file content (with the new code changes) is passed to *stdin* and the formatted content should be written to *stdout*.
diff --git a/mkdocs.yml b/mkdocs.yml
index 04fe6417..87cf55d9 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -72,6 +72,7 @@ markdown_extensions:
- pymdownx.superfences
- pymdownx.tabbed:
alternate_style: true
+- attr_list
plugins:
- mkdocstrings:
@@ -85,6 +86,7 @@ plugins:
- markdown-exec:
ansi: required
- replace-url
+- autorefs
extra:
diff --git a/pyproject.toml b/pyproject.toml
index 1801ebcf..45f718ca 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -25,12 +25,9 @@ classifiers = [
]
dependencies = [
"asttokens>=2.0.5",
- "black>=23.3.0",
- "click>=8.1.4",
"executing>=2.1.0",
"rich>=13.7.1",
- "tomli>=2.0.0; python_version < '3.11'",
- "typing-extensions"
+ "tomli>=2.0.0; python_version < '3.11'"
]
description = "golden master/snapshot/approval testing library which puts the values right into your source code"
keywords = []
@@ -39,6 +36,12 @@ readme = "README.md"
requires-python = ">=3.8"
version = "0.18.2"
+[project.optional-dependencies]
+black = [
+ "black>=23.3.0",
+ "click>=8.1.4"
+]
+
[dependency-groups]
dev = [
"hypothesis>=6.75.5",
@@ -99,6 +102,7 @@ dependencies = [
"mkdocs-material[imaging]>=9.5.17",
"mike",
"mkdocstrings[python]>=0.19.0",
+ "mkdocs-autorefs",
"replace-url @ {root:uri}/docs/plugins",
"pytest",
"black"
@@ -130,6 +134,7 @@ matrix.extra-deps.dependencies = [
[tool.hatch.envs.hatch-test]
extra-dependencies = [
+ "inline-snapshot[black]",
"dirty-equals>=0.7.0",
"hypothesis>=6.75.5",
"mypy>=1.2.0",
@@ -141,18 +146,20 @@ extra-dependencies = [
env-vars.TOP = "{root}"
[tool.hatch.envs.hatch-test.scripts]
-run = "pytest{env:HATCH_TEST_ARGS:} --use-uv {args}"
-run-cov = "coverage run -m pytest{env:HATCH_TEST_ARGS:} --use-uv {args}"
+run = "pytest{env:HATCH_TEST_ARGS:} {args}"
+run-cov = "coverage run -m pytest{env:HATCH_TEST_ARGS:} {args}"
cov-combine = "coverage combine"
cov-report=["coverage report","coverage html"]
[tool.hatch.envs.types]
extra-dependencies = [
+ "inline-snapshot[black]",
"mypy>=1.0.0",
"pytest",
"hypothesis>=6.75.5",
"pydantic",
- "attrs"
+ "attrs",
+ "typing-extensions"
]
[[tool.hatch.envs.types.matrix]]
diff --git a/scripts/replace_words.py b/scripts/replace_words.py
new file mode 100644
index 00000000..fa346d38
--- /dev/null
+++ b/scripts/replace_words.py
@@ -0,0 +1,24 @@
+import re
+import sys
+
+
+def replace_words(file_path, replacements):
+ with open(file_path) as file:
+ content = file.read()
+
+ for old_word, new_word in replacements.items():
+ content = re.sub(rf"\b{re.escape(old_word)}\b", new_word, content)
+
+ with open(file_path, "w") as file:
+ file.write(content)
+
+
+if __name__ == "__main__":
+
+ replacements = {
+ "http://localhost:8000/inline-snapshot/": "https://15r10nk.github.io/inline-snapshot/latest/",
+ }
+
+ for file_path in sys.argv[1:]:
+ print(file_path)
+ replace_words(file_path, replacements)
diff --git a/src/inline_snapshot/_config.py b/src/inline_snapshot/_config.py
index 99624c6d..aad23d1f 100644
--- a/src/inline_snapshot/_config.py
+++ b/src/inline_snapshot/_config.py
@@ -19,6 +19,7 @@ class Config:
hash_length: int = 12
default_flags: List[str] = field(default_factory=lambda: ["short-report"])
shortcuts: Dict[str, List[str]] = field(default_factory=dict)
+ format_command: Optional[str] = None
storage_dir: Optional[Path] = None
@@ -27,35 +28,37 @@ class Config:
def read_config(path: Path) -> Config:
result = Config()
+ config = {}
if path.exists():
-
data = loads(path.read_text("utf-8"))
try:
config = data["tool"]["inline-snapshot"]
except KeyError:
pass
- else:
- try:
- result.hash_length = config["hash-length"]
- except KeyError:
- pass
-
- try:
- result.default_flags = config["default-flags"]
- except KeyError:
- pass
-
- result.shortcuts = config.get(
- "shortcuts", {"fix": ["create", "fix"], "review": ["review"]}
- )
-
- if storage_dir := config.get("storage-dir"):
- storage_dir = Path(storage_dir)
- if not storage_dir.is_absolute():
- # Make it relative to pyproject.toml, and absolute.
- storage_dir = path.parent.joinpath(storage_dir).absolute()
- result.storage_dir = storage_dir
+
+ try:
+ result.hash_length = config["hash-length"]
+ except KeyError:
+ pass
+
+ try:
+ result.default_flags = config["default-flags"]
+ except KeyError:
+ pass
+
+ result.shortcuts = config.get(
+ "shortcuts", {"fix": ["create", "fix"], "review": ["review"]}
+ )
+
+ if storage_dir := config.get("storage-dir"):
+ storage_dir = Path(storage_dir)
+ if not storage_dir.is_absolute():
+ # Make it relative to pyproject.toml, and absolute.
+ storage_dir = path.parent.joinpath(storage_dir).absolute()
+ result.storage_dir = storage_dir
+
+ result.format_command = config.get("format-command", None)
env_var = "INLINE_SNAPSHOT_DEFAULT_FLAGS"
if env_var in os.environ:
diff --git a/src/inline_snapshot/_format.py b/src/inline_snapshot/_format.py
index a0b8aa17..b3d5b6c8 100644
--- a/src/inline_snapshot/_format.py
+++ b/src/inline_snapshot/_format.py
@@ -1,11 +1,47 @@
+import subprocess as sp
import warnings
-from black import main
-from click.testing import CliRunner
-from inline_snapshot._problems import raise_problem
+from rich.markup import escape
+
+from . import _config
+from ._problems import raise_problem
+
+
+def enforce_formatting():
+ return _config.config.format_command is not None
def format_code(text, filename):
+ if _config.config.format_command is not None:
+ format_command = _config.config.format_command.format(filename=filename)
+ result = sp.run(
+ format_command, shell=True, input=text.encode("utf-8"), capture_output=True
+ )
+ if result.returncode != 0:
+ raise_problem(
+ f"""\
+[b]The format_command '{escape(format_command)}' caused the following error:[/b]
+"""
+ + result.stdout.decode("utf-8")
+ + result.stderr.decode("utf-8")
+ )
+ return text
+ return result.stdout.decode("utf-8")
+
+ try:
+ from black import main
+ from click.testing import CliRunner
+ except ImportError:
+ raise_problem(
+ f"""\
+[b]inline-snapshot is not able to format your code.[/b]
+This issue can be solved by:
+ * installing {escape('inline-snapshot[black]')} which gives you the same formatting like in older versions
+ * adding a `format-command` to your pyproject.toml (see [link=https://15r10nk.github.io/inline-snapshot/latest/configuration/#format-command]https://15r10nk.github.io/inline-snapshot/latest/configuration/#format-command[/link] for more information).
+"""
+ )
+ return text
+
with warnings.catch_warnings():
warnings.simplefilter("ignore")
@@ -17,7 +53,7 @@ def format_code(text, filename):
if result.exit_code != 0:
raise_problem(
"""\
-black could not format your code, which might be caused by this issue:
+[b]black could not format your code, which might be caused by this issue:[/b]
[link=https://github.com/15r10nk/inline-snapshot/issues/138]https://github.com/15r10nk/inline-snapshot/issues/138[/link]\
"""
)
diff --git a/src/inline_snapshot/_problems.py b/src/inline_snapshot/_problems.py
index c8272ad8..d6a9641c 100644
--- a/src/inline_snapshot/_problems.py
+++ b/src/inline_snapshot/_problems.py
@@ -14,6 +14,7 @@ def report_problems(console: Console):
return
console.rule("[red]Problems")
for problem in all_problems:
- console.print(f"[b]{problem}")
+ console.print(f"{problem}")
+ console.print()
all_problems = set()
diff --git a/src/inline_snapshot/_rewrite_code.py b/src/inline_snapshot/_rewrite_code.py
index 0cab4c56..109e4b90 100644
--- a/src/inline_snapshot/_rewrite_code.py
+++ b/src/inline_snapshot/_rewrite_code.py
@@ -13,8 +13,10 @@
import asttokens.util
from asttokens import LineNumbers
+from ._format import enforce_formatting
from ._format import format_code
+
if sys.version_info >= (3, 10):
from itertools import pairwise
else:
@@ -158,9 +160,11 @@ def new_code(self) -> str:
code = self.filename.read_text("utf-8")
- is_formatted = code == format_code(code, self.filename)
+ format_whole_file = enforce_formatting() or code == format_code(
+ code, self.filename
+ )
- if not is_formatted:
+ if not format_whole_file:
logging.info(f"file is not formatted with black: {self.filename}")
import black
@@ -180,7 +184,7 @@ def new_code(self) -> str:
],
)
- if is_formatted:
+ if format_whole_file:
new_code = format_code(new_code, self.filename)
return new_code
diff --git a/src/inline_snapshot/_source_file.py b/src/inline_snapshot/_source_file.py
index ba8a94bc..d3f55eca 100644
--- a/src/inline_snapshot/_source_file.py
+++ b/src/inline_snapshot/_source_file.py
@@ -2,6 +2,7 @@
from pathlib import Path
from executing import Source
+from inline_snapshot._format import enforce_formatting
from inline_snapshot._format import format_code
from inline_snapshot._utils import normalize
from inline_snapshot._utils import simple_token
@@ -24,7 +25,7 @@ def filename(self):
return self._source.filename
def _format(self, text):
- if self._source is None:
+ if self._source is None or enforce_formatting():
return text
else:
return format_code(text, Path(self._source.filename))
diff --git a/src/inline_snapshot/_types.py b/src/inline_snapshot/_types.py
index d6501894..50807a76 100644
--- a/src/inline_snapshot/_types.py
+++ b/src/inline_snapshot/_types.py
@@ -7,10 +7,10 @@
from typing import TYPE_CHECKING
from typing import TypeVar
-from typing_extensions import TypeAlias
-
if TYPE_CHECKING:
+ from typing_extensions import TypeAlias
+
T = TypeVar("T")
Snapshot: TypeAlias = T
diff --git a/src/inline_snapshot/_unmanaged.py b/src/inline_snapshot/_unmanaged.py
index 5e46b9b5..111d86aa 100644
--- a/src/inline_snapshot/_unmanaged.py
+++ b/src/inline_snapshot/_unmanaged.py
@@ -17,13 +17,23 @@ def is_dirty_equal(value):
def update_allowed(value):
- return not (is_dirty_equal(value) or isinstance(value, (Is, Snapshot))) # type: ignore
+ global unmanaged_types
+ return not (is_dirty_equal(value) or isinstance(value, tuple(unmanaged_types))) # type: ignore
+
+
+unmanaged_types = [Is, Snapshot]
def is_unmanaged(value):
return not update_allowed(value)
+def declare_unmanaged(typ):
+ global unmanaged_types
+ unmanaged_types.append(typ)
+ return typ
+
+
class Unmanaged:
def __init__(self, value):
self.value = value
diff --git a/src/inline_snapshot/pytest_plugin.py b/src/inline_snapshot/pytest_plugin.py
index 0c540b4e..afda98ab 100644
--- a/src/inline_snapshot/pytest_plugin.py
+++ b/src/inline_snapshot/pytest_plugin.py
@@ -46,7 +46,7 @@ def pytest_addoption(parser, pluginmanager):
config_path = Path("pyproject.toml")
if config_path.exists():
- config = _config.read_config(Path("pyproject.toml"))
+ config = _config.read_config(config_path)
for name, value in config.shortcuts.items():
value = ",".join(value)
group.addoption(
diff --git a/src/inline_snapshot/testing/_example.py b/src/inline_snapshot/testing/_example.py
index 62f0e82f..3badf584 100644
--- a/src/inline_snapshot/testing/_example.py
+++ b/src/inline_snapshot/testing/_example.py
@@ -78,6 +78,16 @@ def snapshot_env():
)
+def normalize(text):
+ text = ansi_escape.sub("", text)
+
+ # fix windows problems
+ text = text.replace("\u2500", "-")
+ text = text.replace("\r", "")
+ text = text.replace(" \n", " ⏎\n")
+ return text
+
+
class Example:
def __init__(self, files: str | dict[str, str]):
"""
@@ -228,7 +238,7 @@ def run_inline(
assert changed_files == current_files
if report is not None:
- assert report == report_output.getvalue()
+ assert report == normalize(report_output.getvalue())
return Example(self._read_files(tmp_path))
@@ -236,6 +246,7 @@ def run_pytest(
self,
args: list[str] = [],
*,
+ term_columns=80,
env: dict[str, str] = {},
changed_files: Snapshot[dict[str, str]] | None = None,
report: Snapshot[str] | None = None,
@@ -265,8 +276,6 @@ def run_pytest(
cmd = [sys.executable, "-m", "pytest", *args]
- term_columns = 80
-
command_env = dict(os.environ)
command_env["TERM"] = "unknown"
command_env["COLUMNS"] = str(
@@ -307,14 +316,7 @@ def run_pytest(
report_str = "\n".join(report_list)
- report_str = ansi_escape.sub("", report_str)
-
- # fix windows problems
- report_str = report_str.replace("\u2500", "-")
- report_str = report_str.replace("\r", "")
- report_str = report_str.replace(" \n", " ⏎\n")
-
- assert report_str == report, repr(report_str)
+ assert normalize(report_str) == report, repr(report_str)
if changed_files is not None:
current_files = {}
diff --git a/tests/_is_normalized.py b/tests/_is_normalized.py
new file mode 100644
index 00000000..fa8bd8d4
--- /dev/null
+++ b/tests/_is_normalized.py
@@ -0,0 +1,23 @@
+from inline_snapshot._unmanaged import declare_unmanaged
+
+
+@declare_unmanaged
+class IsNormalized:
+ def __init__(self, func, value) -> None:
+ self._func = func
+ self._value = value
+ self._last_value = None
+
+ def __eq__(self, other) -> bool:
+ self._last_value = self._func(other)
+ return self._last_value == self._value
+
+ def __repr__(self):
+ return f"IsNormalized({self._value}, should_be={self._last_value!r})"
+
+
+def normalization(func):
+ def f(value):
+ return IsNormalized(func, value)
+
+ return f
diff --git a/tests/conftest.py b/tests/conftest.py
index 2159aa85..08993d16 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -43,15 +43,6 @@ def check_pypy(request):
yield
-def pytest_addoption(parser):
- parser.addoption(
- "--use-uv",
- action="store_true",
- default=False,
- help="install different package versions at test time",
- )
-
-
@pytest.fixture()
def check_update(source):
def w(source_code, *, flags="", reported_flags=None, number=1):
diff --git a/tests/test_config.py b/tests/test_config.py
index 98fcb518..8e11ccf8 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -54,3 +54,27 @@ def test_shortcuts():
""",
}
).run_pytest(["--strim"], changed_files=trimmed_files)
+
+
+def test_default_shortcuts():
+
+ Example(
+ {
+ **file_to_trim,
+ "pyproject.toml": """
+ """,
+ }
+ ).run_pytest(
+ ["--fix"],
+ changed_files=snapshot(
+ {
+ "test_a.py": """\
+from inline_snapshot import snapshot
+
+def test_a():
+ assert 1 <= snapshot(5)
+ assert 1 == snapshot(1)
+"""
+ }
+ ),
+ )
diff --git a/tests/test_formating.py b/tests/test_formating.py
index c8f4c6d6..9b347a7c 100644
--- a/tests/test_formating.py
+++ b/tests/test_formating.py
@@ -1,9 +1,13 @@
+import re
+import sys
from types import SimpleNamespace
from click.testing import CliRunner
from inline_snapshot import snapshot
from inline_snapshot.testing import Example
+from tests._is_normalized import normalization
+
def test_black_formatting_error(mocker):
mocker.patch.object(CliRunner, "invoke", return_value=SimpleNamespace(exit_code=1))
@@ -33,9 +37,10 @@ def test_something():
),
report=snapshot(
"""\
-─────────────────────────────────── Problems ───────────────────────────────────
+----------------------------------- Problems -----------------------------------
black could not format your code, which might be caused by this issue:
https://github.com/15r10nk/inline-snapshot/issues/138
+
"""
),
)
@@ -57,3 +62,152 @@ def test_a():
return None
"""
).run_pytest(returncode=0)
+
+
+def test_format_command():
+ Example(
+ {
+ "fmt_cmd.py": """\
+from sys import stdin
+import re
+
+text=stdin.read()
+text=re.sub("#.*","",text)
+print(text)
+""",
+ "pyproject.toml": f"""\
+[tool.inline-snapshot]
+format-command="{sys.executable} fmt_cmd.py {{filename}}"
+""",
+ "test_a.py": """\
+from inline_snapshot import snapshot
+# some comment
+def test_a():
+ assert "5" == snapshot('''3''')# abc
+""",
+ }
+ ).run_pytest(
+ ["--inline-snapshot=fix"],
+ changed_files=snapshot(
+ {
+ "test_a.py": """\
+from inline_snapshot import snapshot
+
+def test_a():
+ assert "5" == snapshot('5')
+
+"""
+ }
+ ),
+ )
+
+
+def test_format_command_fail():
+
+ @normalization
+ def NoPaths(text):
+ text = re.sub(
+ "The format_command.*following error:",
+ lambda m: m[0].replace("\n", ""),
+ text,
+ flags=re.MULTILINE | re.DOTALL,
+ )
+ text = re.sub("/[^ ]*/", "/.../", text, flags=re.MULTILINE)
+ return text
+
+ Example(
+ {
+ "fmt_cmd.py": """
+import sys
+print("some problem")
+sys.exit(1)
+""",
+ "pyproject.toml": f"""\
+[tool.inline-snapshot]
+format-command="{sys.executable} fmt_cmd.py {{filename}}"
+""",
+ "test_a.py": """
+from inline_snapshot import snapshot
+
+def test_a():
+ assert "5" == snapshot('''3''')
+""",
+ }
+ ).run_pytest(
+ ["--inline-snapshot=fix"],
+ term_columns=200,
+ changed_files=snapshot(
+ {
+ "test_a.py": """\
+
+from inline_snapshot import snapshot
+
+def test_a():
+ assert "5" == snapshot('5')
+"""
+ }
+ ),
+ report=NoPaths(
+ snapshot(
+ """\
+-------------------------------------------------------------------------------------------- Fix snapshots ---------------------------------------------------------------------------------------------
++--------------------------------------------------------------------------------------------- test_a.py ----------------------------------------------------------------------------------------------+
+| @@ -2,4 +2,4 @@ |
+| |
+| from inline_snapshot import snapshot |
+| |
+| def test_a(): |
+| - assert "5" == snapshot('''3''') |
+| + assert "5" == snapshot('5') |
++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+These changes will be applied, because you used --inline-snapshot=fix
+----------------------------------------------------------------------------------------------- Problems -----------------------------------------------------------------------------------------------
+The format_command '/.../python3 fmt_cmd.py /.../test_a.py' caused the following error:
+some problem\
+"""
+ )
+ ),
+ )
+
+
+def test_no_black(mocker):
+
+ mocker.patch.dict(sys.modules, {"black": None})
+
+ Example(
+ {
+ "test_a.py": """
+from inline_snapshot import snapshot
+
+def test_a():
+ assert "5" == snapshot('''3''')
+""",
+ }
+ ).run_inline(
+ ["--inline-snapshot=fix"],
+ changed_files=snapshot(
+ {
+ "test_a.py": """\
+
+from inline_snapshot import snapshot
+
+def test_a():
+ assert "5" == snapshot('5')
+"""
+ }
+ ),
+ report=snapshot(
+ """\
+----------------------------------- Problems -----------------------------------
+inline-snapshot is not able to format your code.
+This issue can be solved by:
+ * installing inline-snapshot[black] which gives you the same formatting like in
+older versions
+ * adding a `format-command` to your pyproject.toml (see ⏎
+https://15r10nk.github.io/inline-snapshot/latest/configuration/#format-command ⏎
+for more information).
+
+
+"""
+ ),
+ )
diff --git a/tests/test_is_normalized.py b/tests/test_is_normalized.py
new file mode 100644
index 00000000..dea01ace
--- /dev/null
+++ b/tests/test_is_normalized.py
@@ -0,0 +1,31 @@
+from inline_snapshot import snapshot
+from inline_snapshot.testing._example import Example
+
+
+def test_repr():
+ Example(
+ """\
+from inline_snapshot import snapshot
+from tests._is_normalized import IsNormalized
+
+def test_a():
+ n=IsNormalized(sorted,snapshot())
+ assert [3,5,2] == n
+ assert repr(n)==snapshot()
+"""
+ ).run_inline(
+ ["--inline-snapshot=create"],
+ changed_files=snapshot(
+ {
+ "test_something.py": """\
+from inline_snapshot import snapshot
+from tests._is_normalized import IsNormalized
+
+def test_a():
+ n=IsNormalized(sorted,snapshot([2, 3, 5]))
+ assert [3,5,2] == n
+ assert repr(n)==snapshot("IsNormalized([2, 3, 5], should_be=[2, 3, 5])")
+"""
+ }
+ ),
+ )