From 895e7d0325723e9fa4e312974d308f8de03ef72d Mon Sep 17 00:00:00 2001 From: Noah Negin-Ulster Date: Tue, 4 Apr 2023 18:03:18 -0400 Subject: [PATCH 01/11] test: add coverage for bug #719 --- ...st_snapshot_option_extension_pythonpath.py | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 tests/integration/test_snapshot_option_extension_pythonpath.py diff --git a/tests/integration/test_snapshot_option_extension_pythonpath.py b/tests/integration/test_snapshot_option_extension_pythonpath.py new file mode 100644 index 00000000..cf82c9f6 --- /dev/null +++ b/tests/integration/test_snapshot_option_extension_pythonpath.py @@ -0,0 +1,84 @@ +from pathlib import Path + +import pytest + + +@pytest.fixture +def testfile(testdir): + testdir.makepyfile( + extension_file=( + """ + import syrupy + class MySingleFileExtension( + syrupy.extensions.single_file.SingleFileSnapshotExtension + ): + pass + """ + ), + test_file=( + """ + def test_default(snapshot): + assert b"default extension serializer" == snapshot + """ + ), + ) + return testdir + + +def test_snapshot_default_extension_option_success(testfile): + result = testfile.runpytest( + "-v", + "--pythonpath", + testfile.tmpdir, + "--snapshot-update", + "--snapshot-default-extension", + "extension_file.MySingleFileExtension", + ) + assert not result.errlines + result.stdout.re_match_lines((r"1 snapshot generated\.")) + assert Path( + testfile.tmpdir, "__snapshots__", "test_file", "test_default.raw" + ).exists() + assert not result.ret + + +def test_snapshot_default_extension_option_module_not_found(testfile): + result = testfile.runpytest( + "-v", + "--snapshot-update", + "--snapshot-default-extension", + "extension_file.MySingleFileExtensions", + ) + assert not result.outlines + result.stderr.re_match_lines( + ( + r".*error: argument --snapshot-default-extension" + r": Module 'extension_file' does not exist.*", + ) + ) + assert not Path( + testfile.tmpdir, "__snapshots__", "test_file", "test_default.raw" + ).exists() + assert result.ret + + +def test_snapshot_default_extension_option_failure(testfile): + result = testfile.runpytest( + "-v", + "--pythonpath", + testfile.tmpdir, + "--snapshot-update", + "--snapshot-default-extension", + "extension_file.DoesNotExistExtension", + ) + assert not result.outlines + result.stderr.re_match_lines( + ( + r".*error: argument --snapshot-default-extension" + r": Member 'DoesNotExistExtension' not found.*", + ) + ) + assert not Path( + testfile.tmpdir, "__snapshots__", "test_file", "test_default.raw" + ).exists() + assert result.ret From ea661146b6f635f31e40984ac7c27cde7d35aec4 Mon Sep 17 00:00:00 2001 From: John Kurkowski Date: Thu, 6 Apr 2023 15:34:27 -0700 Subject: [PATCH 02/11] test: fix pytest CLI option that is INI-only * Fix missing module in test --- ...st_snapshot_option_extension_pythonpath.py | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/tests/integration/test_snapshot_option_extension_pythonpath.py b/tests/integration/test_snapshot_option_extension_pythonpath.py index cf82c9f6..130414cc 100644 --- a/tests/integration/test_snapshot_option_extension_pythonpath.py +++ b/tests/integration/test_snapshot_option_extension_pythonpath.py @@ -6,6 +6,7 @@ @pytest.fixture def testfile(testdir): testdir.makepyfile( + __init__="", extension_file=( """ import syrupy @@ -26,10 +27,16 @@ def test_default(snapshot): def test_snapshot_default_extension_option_success(testfile): + testfile.makeini( + f""" + [pytest] + pythonpath = + {testfile.tmpdir} + """ + ) + result = testfile.runpytest( "-v", - "--pythonpath", - testfile.tmpdir, "--snapshot-update", "--snapshot-default-extension", "extension_file.MySingleFileExtension", @@ -63,10 +70,16 @@ def test_snapshot_default_extension_option_module_not_found(testfile): def test_snapshot_default_extension_option_failure(testfile): + testfile.makeini( + f""" + [pytest] + pythonpath = + {testfile.tmpdir} + """ + ) + result = testfile.runpytest( "-v", - "--pythonpath", - testfile.tmpdir, "--snapshot-update", "--snapshot-default-extension", "extension_file.DoesNotExistExtension", From 2e0400e79def019f69a5e3a9119cdbf596545bea Mon Sep 17 00:00:00 2001 From: John Kurkowski Date: Thu, 6 Apr 2023 16:35:42 -0700 Subject: [PATCH 03/11] fix: shadowing `AttributeError` during import --- src/syrupy/utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/syrupy/utils.py b/src/syrupy/utils.py index c0861734..4dd08cdb 100644 --- a/src/syrupy/utils.py +++ b/src/syrupy/utils.py @@ -48,11 +48,14 @@ def import_module_member(path: str) -> Any: ) ) try: - return getattr(import_module(module_name), module_member_name) + module = import_module(module_name) except ModuleNotFoundError: raise FailedToLoadModuleMember( gettext("Module '{}' does not exist.").format(module_name) ) + + try: + return getattr(module, module_member_name) except AttributeError: raise FailedToLoadModuleMember( gettext("Member '{}' not found in module '{}'.").format( From 06607c1547d596c3c596539b791212812905aca0 Mon Sep 17 00:00:00 2001 From: John Kurkowski Date: Wed, 5 Apr 2023 13:23:18 -0700 Subject: [PATCH 04/11] fix: defer snapshot default extension import Fixes unable to use pytest's `pythonpath` option with this project's `--snapshot-default-extension` option. Does cause extension import errors to raise later than CLI argument parsing, and therefore emit on stdout, instead of stderr. --- src/syrupy/__init__.py | 6 ------ .../test_snapshot_option_extension.py | 7 +------ ...test_snapshot_option_extension_pythonpath.py | 17 ++--------------- 3 files changed, 3 insertions(+), 27 deletions(-) diff --git a/src/syrupy/__init__.py b/src/syrupy/__init__.py index 5f1a646c..560ddb0f 100644 --- a/src/syrupy/__init__.py +++ b/src/syrupy/__init__.py @@ -41,11 +41,6 @@ def __import_extension(value: Optional[str]) -> Any: raise argparse.ArgumentTypeError(e) -def __default_extension_option(value: Optional[str]) -> Any: - __import_extension(value) - return value - - def pytest_addoption(parser: Any) -> None: """ Exposes snapshot plugin configuration to pytest. @@ -78,7 +73,6 @@ def pytest_addoption(parser: Any) -> None: # all pytest options to be serializable. group.addoption( "--snapshot-default-extension", - type=__default_extension_option, default=None, dest="default_extension", help="Specify the default snapshot extension", diff --git a/tests/integration/test_snapshot_option_extension.py b/tests/integration/test_snapshot_option_extension.py index 546da3ff..884f92a6 100644 --- a/tests/integration/test_snapshot_option_extension.py +++ b/tests/integration/test_snapshot_option_extension.py @@ -37,12 +37,7 @@ def test_snapshot_default_extension_option_failure(testfile): "--snapshot-default-extension", "syrupy.extensions.amber.DoesNotExistExtension", ) - result.stderr.re_match_lines( - ( - r".*error: argument --snapshot-default-extension" - r": Member 'DoesNotExistExtension' not found.*", - ) - ) + result.stdout.re_match_lines((r".*: Member 'DoesNotExistExtension' not found.*",)) assert not Path( testfile.tmpdir, "__snapshots__", "test_file", "test_default.raw" ).exists() diff --git a/tests/integration/test_snapshot_option_extension_pythonpath.py b/tests/integration/test_snapshot_option_extension_pythonpath.py index 130414cc..4324d77c 100644 --- a/tests/integration/test_snapshot_option_extension_pythonpath.py +++ b/tests/integration/test_snapshot_option_extension_pythonpath.py @@ -41,7 +41,6 @@ def test_snapshot_default_extension_option_success(testfile): "--snapshot-default-extension", "extension_file.MySingleFileExtension", ) - assert not result.errlines result.stdout.re_match_lines((r"1 snapshot generated\.")) assert Path( testfile.tmpdir, "__snapshots__", "test_file", "test_default.raw" @@ -56,13 +55,7 @@ def test_snapshot_default_extension_option_module_not_found(testfile): "--snapshot-default-extension", "extension_file.MySingleFileExtensions", ) - assert not result.outlines - result.stderr.re_match_lines( - ( - r".*error: argument --snapshot-default-extension" - r": Module 'extension_file' does not exist.*", - ) - ) + result.stdout.re_match_lines((r".*: Module 'extension_file' does not exist.*",)) assert not Path( testfile.tmpdir, "__snapshots__", "test_file", "test_default.raw" ).exists() @@ -84,13 +77,7 @@ def test_snapshot_default_extension_option_failure(testfile): "--snapshot-default-extension", "extension_file.DoesNotExistExtension", ) - assert not result.outlines - result.stderr.re_match_lines( - ( - r".*error: argument --snapshot-default-extension" - r": Member 'DoesNotExistExtension' not found.*", - ) - ) + result.stdout.re_match_lines((r".*: Member 'DoesNotExistExtension' not found.*",)) assert not Path( testfile.tmpdir, "__snapshots__", "test_file", "test_default.raw" ).exists() From 9431dc2f672de0178bd8df6b2d4d55e6d9b117d5 Mon Sep 17 00:00:00 2001 From: John Kurkowski Date: Fri, 7 Apr 2023 14:32:04 -0700 Subject: [PATCH 05/11] squash! test: fix pytest CLI option that is INI-only --- tests/integration/test_snapshot_option_extension_pythonpath.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/integration/test_snapshot_option_extension_pythonpath.py b/tests/integration/test_snapshot_option_extension_pythonpath.py index 4324d77c..1370bce2 100644 --- a/tests/integration/test_snapshot_option_extension_pythonpath.py +++ b/tests/integration/test_snapshot_option_extension_pythonpath.py @@ -6,7 +6,6 @@ @pytest.fixture def testfile(testdir): testdir.makepyfile( - __init__="", extension_file=( """ import syrupy From 72f97027853818fd5720d9f4c9bce91aa2d893ef Mon Sep 17 00:00:00 2001 From: John Kurkowski Date: Fri, 7 Apr 2023 15:39:13 -0700 Subject: [PATCH 06/11] test: fix typo --- tests/integration/test_snapshot_option_extension_pythonpath.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/test_snapshot_option_extension_pythonpath.py b/tests/integration/test_snapshot_option_extension_pythonpath.py index 1370bce2..3ae71fe9 100644 --- a/tests/integration/test_snapshot_option_extension_pythonpath.py +++ b/tests/integration/test_snapshot_option_extension_pythonpath.py @@ -52,7 +52,7 @@ def test_snapshot_default_extension_option_module_not_found(testfile): "-v", "--snapshot-update", "--snapshot-default-extension", - "extension_file.MySingleFileExtensions", + "extension_file.MySingleFileExtension", ) result.stdout.re_match_lines((r".*: Module 'extension_file' does not exist.*",)) assert not Path( From b9bb2634f3535d1fd6d8f57276fe880fea44c986 Mon Sep 17 00:00:00 2001 From: John Kurkowski Date: Fri, 7 Apr 2023 15:39:42 -0700 Subject: [PATCH 07/11] test: move extension to subdirectory not on default path --- ...st_snapshot_option_extension_pythonpath.py | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/tests/integration/test_snapshot_option_extension_pythonpath.py b/tests/integration/test_snapshot_option_extension_pythonpath.py index 3ae71fe9..59ee6754 100644 --- a/tests/integration/test_snapshot_option_extension_pythonpath.py +++ b/tests/integration/test_snapshot_option_extension_pythonpath.py @@ -1,27 +1,37 @@ +import textwrap from pathlib import Path import pytest +SUBDIR = "subdir_not_on_default_path" + @pytest.fixture def testfile(testdir): - testdir.makepyfile( - extension_file=( + subdir = testdir.mkdir(SUBDIR) + + Path( + subdir, + "extension_file.py", + ).write_text( + textwrap.dedent( """ - import syrupy - class MySingleFileExtension( - syrupy.extensions.single_file.SingleFileSnapshotExtension - ): + from syrupy.extensions.single_file import SingleFileSnapshotExtension + class MySingleFileExtension(SingleFileSnapshotExtension): pass """ - ), + ) + ) + + testdir.makepyfile( test_file=( """ def test_default(snapshot): assert b"default extension serializer" == snapshot """ - ), + ) ) + return testdir @@ -30,7 +40,7 @@ def test_snapshot_default_extension_option_success(testfile): f""" [pytest] pythonpath = - {testfile.tmpdir} + {Path(testfile.tmpdir, SUBDIR)} """ ) @@ -66,7 +76,7 @@ def test_snapshot_default_extension_option_failure(testfile): f""" [pytest] pythonpath = - {testfile.tmpdir} + {Path(testfile.tmpdir, SUBDIR)} """ ) From 1ec5886e4571bb6adc11f9b4b86c17f5e7132d66 Mon Sep 17 00:00:00 2001 From: John Kurkowski Date: Fri, 7 Apr 2023 15:40:29 -0700 Subject: [PATCH 08/11] test: work around cached extension import --- .../test_snapshot_option_extension_pythonpath.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/integration/test_snapshot_option_extension_pythonpath.py b/tests/integration/test_snapshot_option_extension_pythonpath.py index 59ee6754..6d6ba2ea 100644 --- a/tests/integration/test_snapshot_option_extension_pythonpath.py +++ b/tests/integration/test_snapshot_option_extension_pythonpath.py @@ -3,9 +3,16 @@ import pytest +import syrupy + SUBDIR = "subdir_not_on_default_path" +@pytest.fixture(autouse=True) +def cache_clear(): + syrupy.__import_extension.cache_clear() + + @pytest.fixture def testfile(testdir): subdir = testdir.mkdir(SUBDIR) From 5ffbe6d6f1aca5d336233655ef4b713b9236b5ce Mon Sep 17 00:00:00 2001 From: Noah Negin-Ulster Date: Fri, 14 Apr 2023 18:14:11 -0400 Subject: [PATCH 09/11] wip: use mkpydir --- .../test_snapshot_option_extension_pythonpath.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/integration/test_snapshot_option_extension_pythonpath.py b/tests/integration/test_snapshot_option_extension_pythonpath.py index 6d6ba2ea..8f741824 100644 --- a/tests/integration/test_snapshot_option_extension_pythonpath.py +++ b/tests/integration/test_snapshot_option_extension_pythonpath.py @@ -15,19 +15,20 @@ def cache_clear(): @pytest.fixture def testfile(testdir): - subdir = testdir.mkdir(SUBDIR) + subdir = testdir.mkpydir(SUBDIR) Path( subdir, "extension_file.py", ).write_text( - textwrap.dedent( + data=textwrap.dedent( """ from syrupy.extensions.single_file import SingleFileSnapshotExtension class MySingleFileExtension(SingleFileSnapshotExtension): pass """ - ) + ), + encoding="utf-8", ) testdir.makepyfile( From aa8370ab80232d65c1a0d9f374a25c8cfbb2d45f Mon Sep 17 00:00:00 2001 From: John Kurkowski Date: Fri, 14 Apr 2023 15:39:24 -0700 Subject: [PATCH 10/11] refactor: switch from legacy testdir fixture to pytester fixture --- ...est_snapshot_option_extension_pythonpath.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/integration/test_snapshot_option_extension_pythonpath.py b/tests/integration/test_snapshot_option_extension_pythonpath.py index 8f741824..258c29f2 100644 --- a/tests/integration/test_snapshot_option_extension_pythonpath.py +++ b/tests/integration/test_snapshot_option_extension_pythonpath.py @@ -14,8 +14,8 @@ def cache_clear(): @pytest.fixture -def testfile(testdir): - subdir = testdir.mkpydir(SUBDIR) +def testfile(pytester): + subdir = pytester.mkpydir(SUBDIR) Path( subdir, @@ -31,7 +31,7 @@ class MySingleFileExtension(SingleFileSnapshotExtension): encoding="utf-8", ) - testdir.makepyfile( + pytester.makepyfile( test_file=( """ def test_default(snapshot): @@ -40,7 +40,7 @@ def test_default(snapshot): ) ) - return testdir + return pytester def test_snapshot_default_extension_option_success(testfile): @@ -48,7 +48,7 @@ def test_snapshot_default_extension_option_success(testfile): f""" [pytest] pythonpath = - {Path(testfile.tmpdir, SUBDIR)} + {Path(testfile.path, SUBDIR)} """ ) @@ -60,7 +60,7 @@ def test_snapshot_default_extension_option_success(testfile): ) result.stdout.re_match_lines((r"1 snapshot generated\.")) assert Path( - testfile.tmpdir, "__snapshots__", "test_file", "test_default.raw" + testfile.path, "__snapshots__", "test_file", "test_default.raw" ).exists() assert not result.ret @@ -74,7 +74,7 @@ def test_snapshot_default_extension_option_module_not_found(testfile): ) result.stdout.re_match_lines((r".*: Module 'extension_file' does not exist.*",)) assert not Path( - testfile.tmpdir, "__snapshots__", "test_file", "test_default.raw" + testfile.path, "__snapshots__", "test_file", "test_default.raw" ).exists() assert result.ret @@ -84,7 +84,7 @@ def test_snapshot_default_extension_option_failure(testfile): f""" [pytest] pythonpath = - {Path(testfile.tmpdir, SUBDIR)} + {Path(testfile.path, SUBDIR)} """ ) @@ -96,6 +96,6 @@ def test_snapshot_default_extension_option_failure(testfile): ) result.stdout.re_match_lines((r".*: Member 'DoesNotExistExtension' not found.*",)) assert not Path( - testfile.tmpdir, "__snapshots__", "test_file", "test_default.raw" + testfile.path, "__snapshots__", "test_file", "test_default.raw" ).exists() assert result.ret From 7283e2ba6f2656f08c05014763039bab5da76a6b Mon Sep 17 00:00:00 2001 From: Noah Negin-Ulster Date: Tue, 25 Apr 2023 10:42:27 -0400 Subject: [PATCH 11/11] wip: windows support --- .../integration/test_snapshot_option_extension_pythonpath.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/test_snapshot_option_extension_pythonpath.py b/tests/integration/test_snapshot_option_extension_pythonpath.py index 258c29f2..35701540 100644 --- a/tests/integration/test_snapshot_option_extension_pythonpath.py +++ b/tests/integration/test_snapshot_option_extension_pythonpath.py @@ -48,7 +48,7 @@ def test_snapshot_default_extension_option_success(testfile): f""" [pytest] pythonpath = - {Path(testfile.path, SUBDIR)} + {Path(testfile.path, SUBDIR).as_posix()} """ ) @@ -84,7 +84,7 @@ def test_snapshot_default_extension_option_failure(testfile): f""" [pytest] pythonpath = - {Path(testfile.path, SUBDIR)} + {Path(testfile.path, SUBDIR).as_posix()} """ )