From 67da86335c58f44968d9b6959398b08b309cb722 Mon Sep 17 00:00:00 2001 From: Danipulok Date: Mon, 17 Nov 2025 20:52:37 +0200 Subject: [PATCH 1/5] Fix: fix reading emojis for Windows --- pytest_examples/config.py | 2 ++ pytest_examples/eval_example.py | 5 ++++- tests/test_run_examples.py | 20 ++++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/pytest_examples/config.py b/pytest_examples/config.py index ced2099..021fbf7 100644 --- a/pytest_examples/config.py +++ b/pytest_examples/config.py @@ -29,6 +29,8 @@ class ExamplesConfig: ruff_ignore: list[str] | None = None white_space_dot: bool = False """If True, replace spaces with `ยท` in example diffs.""" + encoding: str | None = 'utf-8' + """File encoding to use for write file operations. Default is 'utf-8'.""" def black_mode(self): return BlackMode( diff --git a/pytest_examples/eval_example.py b/pytest_examples/eval_example.py index e2f19e7..af7e399 100644 --- a/pytest_examples/eval_example.py +++ b/pytest_examples/eval_example.py @@ -43,6 +43,7 @@ def set_config( ruff_line_length: int | None = None, ruff_select: list[str] | None = None, ruff_ignore: list[str] | None = None, + encoding: str | None = 'utf-8', ): """Set the config for lints. @@ -56,6 +57,7 @@ def set_config( ruff_line_length: In general, we disable line-length checks in ruff, to let black take care of them. ruff_select: Ruff rules to select ruff_ignore: Ruff rules to ignore + encoding: File encoding to use for write file operations, defaults to 'utf-8'. """ self.config = ExamplesConfig( line_length=line_length, @@ -67,6 +69,7 @@ def set_config( ruff_line_length=ruff_line_length, ruff_select=ruff_select, ruff_ignore=ruff_ignore, + encoding=encoding, ) @property @@ -269,5 +272,5 @@ def _mark_for_update(self, example: CodeExample) -> None: def _write_file(self, example: CodeExample) -> Path: python_file = self.tmp_path / f'{example.module_name}.py' - python_file.write_text(example.source) + python_file.write_text(example.source, encoding=self.config.encoding) return python_file diff --git a/tests/test_run_examples.py b/tests/test_run_examples.py index d9df4b2..2313092 100644 --- a/tests/test_run_examples.py +++ b/tests/test_run_examples.py @@ -370,3 +370,23 @@ def test_find_run_examples(example: CodeExample, eval_example: EvalExample): result = pytester.runpytest('-p', 'no:pretty', '-v') result.assert_outcomes(passed=1) + + +def test_unicode_emoji(pytester: pytest.Pytester): + pytester.makefile( + '.md', + my_file="```py\nprint('๐Ÿš€ โœ“')\n#> ๐Ÿš€ โœ“\n```", + ) + pytester.makepyfile( + """ +from pytest_examples import find_examples, CodeExample, EvalExample +import pytest + +@pytest.mark.parametrize('example', find_examples('.'), ids=str) +def test_find_run_examples(example: CodeExample, eval_example: EvalExample): + eval_example.run_print_check(example) +""" + ) + + result = pytester.runpytest('-p', 'no:pretty', '-v') + result.assert_outcomes(passed=1) From e0730bdf253715cd7c7a5ac0274ceef2d27c089f Mon Sep 17 00:00:00 2001 From: Danipulok Date: Tue, 18 Nov 2025 20:36:30 +0200 Subject: [PATCH 2/5] Fix: read subprocess result with `config.encoding` --- pytest_examples/lint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytest_examples/lint.py b/pytest_examples/lint.py index 67133d4..bcf4b6b 100644 --- a/pytest_examples/lint.py +++ b/pytest_examples/lint.py @@ -51,7 +51,7 @@ def ruff_check( ruff = find_ruff_bin() args = ruff, 'check', '-', *config.ruff_config(), *extra_ruff_args - p = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines=True) + p = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE, encoding=config.encoding) stdout, stderr = p.communicate(example.source, timeout=10) if p.returncode == 1 and stdout: From 18660434c0211f0eb44622cfb1d436391ac65956 Mon Sep 17 00:00:00 2001 From: Danipulok Date: Wed, 19 Nov 2025 00:26:32 +0200 Subject: [PATCH 3/5] Fix: fix updating files on Windows --- pytest_examples/modify_files.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytest_examples/modify_files.py b/pytest_examples/modify_files.py index dc94387..76455d4 100644 --- a/pytest_examples/modify_files.py +++ b/pytest_examples/modify_files.py @@ -45,6 +45,6 @@ def _modify_files(examples: list[CodeExample]) -> str: count += 1 msg.append(f' {path} {count} examples updated') - path.write_text(content) + path.write_text(content, encoding='utf-8') return '\n'.join(msg) From 29a6e703e95e53bb581f1b08f43dceaba61a215f Mon Sep 17 00:00:00 2001 From: Danipulok Date: Wed, 19 Nov 2025 00:30:04 +0200 Subject: [PATCH 4/5] Fix: fix reading file with proper encoding --- pytest_examples/modify_files.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pytest_examples/modify_files.py b/pytest_examples/modify_files.py index 76455d4..311c95b 100644 --- a/pytest_examples/modify_files.py +++ b/pytest_examples/modify_files.py @@ -9,7 +9,7 @@ from .find_examples import CodeExample -def _modify_files(examples: list[CodeExample]) -> str: +def _modify_files(examples: list[CodeExample], encoding: str | None = 'utf-8') -> str: """Internal use only, update examples in place.""" # The same example shouldn't appear more than once unique_examples: set[str] = set() @@ -34,7 +34,7 @@ def _modify_files(examples: list[CodeExample]) -> str: msg = [f'pytest-examples: {len(examples)} examples to update in {len(files)} file(s)...'] for path, g in groupby(examples, key=lambda ex: ex.path): - content = path.read_text() + content = path.read_text(encoding=encoding) count = 0 for ex in g: example: CodeExample = ex @@ -45,6 +45,6 @@ def _modify_files(examples: list[CodeExample]) -> str: count += 1 msg.append(f' {path} {count} examples updated') - path.write_text(content, encoding='utf-8') + path.write_text(content, encoding=encoding) return '\n'.join(msg) From 50b2c5f708721150172df9c551632cb6d5d8be0f Mon Sep 17 00:00:00 2001 From: Danipulok Date: Fri, 10 Jul 2026 18:12:19 +0000 Subject: [PATCH 5/5] refactor: hardcode `utf-8` encoding instead of configurable `encoding` option --- pytest_examples/config.py | 2 -- pytest_examples/eval_example.py | 5 +---- pytest_examples/lint.py | 2 +- pytest_examples/modify_files.py | 6 +++--- 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/pytest_examples/config.py b/pytest_examples/config.py index 021fbf7..ced2099 100644 --- a/pytest_examples/config.py +++ b/pytest_examples/config.py @@ -29,8 +29,6 @@ class ExamplesConfig: ruff_ignore: list[str] | None = None white_space_dot: bool = False """If True, replace spaces with `ยท` in example diffs.""" - encoding: str | None = 'utf-8' - """File encoding to use for write file operations. Default is 'utf-8'.""" def black_mode(self): return BlackMode( diff --git a/pytest_examples/eval_example.py b/pytest_examples/eval_example.py index af7e399..a27ad03 100644 --- a/pytest_examples/eval_example.py +++ b/pytest_examples/eval_example.py @@ -43,7 +43,6 @@ def set_config( ruff_line_length: int | None = None, ruff_select: list[str] | None = None, ruff_ignore: list[str] | None = None, - encoding: str | None = 'utf-8', ): """Set the config for lints. @@ -57,7 +56,6 @@ def set_config( ruff_line_length: In general, we disable line-length checks in ruff, to let black take care of them. ruff_select: Ruff rules to select ruff_ignore: Ruff rules to ignore - encoding: File encoding to use for write file operations, defaults to 'utf-8'. """ self.config = ExamplesConfig( line_length=line_length, @@ -69,7 +67,6 @@ def set_config( ruff_line_length=ruff_line_length, ruff_select=ruff_select, ruff_ignore=ruff_ignore, - encoding=encoding, ) @property @@ -272,5 +269,5 @@ def _mark_for_update(self, example: CodeExample) -> None: def _write_file(self, example: CodeExample) -> Path: python_file = self.tmp_path / f'{example.module_name}.py' - python_file.write_text(example.source, encoding=self.config.encoding) + python_file.write_text(example.source, encoding='utf-8') return python_file diff --git a/pytest_examples/lint.py b/pytest_examples/lint.py index bcf4b6b..07d22b0 100644 --- a/pytest_examples/lint.py +++ b/pytest_examples/lint.py @@ -51,7 +51,7 @@ def ruff_check( ruff = find_ruff_bin() args = ruff, 'check', '-', *config.ruff_config(), *extra_ruff_args - p = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE, encoding=config.encoding) + p = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE, encoding='utf-8') stdout, stderr = p.communicate(example.source, timeout=10) if p.returncode == 1 and stdout: diff --git a/pytest_examples/modify_files.py b/pytest_examples/modify_files.py index 311c95b..7df4a07 100644 --- a/pytest_examples/modify_files.py +++ b/pytest_examples/modify_files.py @@ -9,7 +9,7 @@ from .find_examples import CodeExample -def _modify_files(examples: list[CodeExample], encoding: str | None = 'utf-8') -> str: +def _modify_files(examples: list[CodeExample]) -> str: """Internal use only, update examples in place.""" # The same example shouldn't appear more than once unique_examples: set[str] = set() @@ -34,7 +34,7 @@ def _modify_files(examples: list[CodeExample], encoding: str | None = 'utf-8') - msg = [f'pytest-examples: {len(examples)} examples to update in {len(files)} file(s)...'] for path, g in groupby(examples, key=lambda ex: ex.path): - content = path.read_text(encoding=encoding) + content = path.read_text(encoding='utf-8') count = 0 for ex in g: example: CodeExample = ex @@ -45,6 +45,6 @@ def _modify_files(examples: list[CodeExample], encoding: str | None = 'utf-8') - count += 1 msg.append(f' {path} {count} examples updated') - path.write_text(content, encoding=encoding) + path.write_text(content, encoding='utf-8') return '\n'.join(msg)