From c555b6f930088b15a1ac56eb60343c2dcc68e1b7 Mon Sep 17 00:00:00 2001 From: Michael Maeng Date: Thu, 20 Oct 2022 10:26:12 -0400 Subject: [PATCH 1/3] Work around to temporary_ini_file to work on Windows --- src/rpdk/core/test.py | 13 ++++++++++++- tests/test_test.py | 3 +++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/rpdk/core/test.py b/src/rpdk/core/test.py index b7e24993..ffdc5fe9 100644 --- a/src/rpdk/core/test.py +++ b/src/rpdk/core/test.py @@ -76,11 +76,20 @@ def empty_override(): def empty_hook_override(): return {"CREATE_PRE_PROVISION": {}} +# As per Python docs NamedTemporaryFile does NOT work the same in Windows. Setting delete=False as workaround. +# +# Temporary file must be explicitly cleaned up after temporary_ini_file() is called! +# +# "Whether the name can be used to open the file a second time, while the named temporary file is still open, +# varies across platforms (it can be so used on Unix; it cannot on Windows)." +# https://docs.python.org/3.9/library/tempfile.html#tempfile.NamedTemporaryFile +# +# Fix being tracked here https://github.com/python/cpython/issues/58451 @contextmanager def temporary_ini_file(): with NamedTemporaryFile( - mode="w", encoding="utf-8", prefix="pytest_", suffix=".ini" + mode="w", encoding="utf-8", prefix="pytest_", suffix=".ini", delete=False ) as temp: LOG.debug("temporary pytest.ini path: %s", temp.name) path = Path(temp.name).resolve(strict=True) @@ -394,6 +403,8 @@ def invoke_test(args, project, overrides, inputs): pytest_args.extend(args.passed_to_pytest) LOG.debug("pytest args: %s", pytest_args) ret = pytest.main(pytest_args, plugins=[plugin]) + # Manually clean up temporary file before exiting - issue with NamedTemporaryFile method on Windows + os.unlink(path) if ret: raise SysExitRecommendedError("One or more contract tests failed") diff --git a/tests/test_test.py b/tests/test_test.py index e8ad173a..f7b00c4f 100644 --- a/tests/test_test.py +++ b/tests/test_test.py @@ -337,6 +337,9 @@ def test_temporary_ini_file(): with path.open("r", encoding="utf-8") as f: assert "[pytest]" in f.read() + # Manually clean up temporary file before exiting - issue with NamedTemporaryFile method on Windows + os.unlink(path_str) + def test_get_overrides_no_root(): From 417b250431f448198b276bd395a6acf6676cc0c3 Mon Sep 17 00:00:00 2001 From: Michael Maeng Date: Thu, 20 Oct 2022 11:13:55 -0400 Subject: [PATCH 2/3] Fix formating and unit tests --- src/rpdk/core/test.py | 15 ++++++++++++--- tests/test_test.py | 6 ++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/rpdk/core/test.py b/src/rpdk/core/test.py index ffdc5fe9..dc48e8f5 100644 --- a/src/rpdk/core/test.py +++ b/src/rpdk/core/test.py @@ -76,16 +76,18 @@ def empty_override(): def empty_hook_override(): return {"CREATE_PRE_PROVISION": {}} + # As per Python docs NamedTemporaryFile does NOT work the same in Windows. Setting delete=False as workaround. # # Temporary file must be explicitly cleaned up after temporary_ini_file() is called! -# +# # "Whether the name can be used to open the file a second time, while the named temporary file is still open, # varies across platforms (it can be so used on Unix; it cannot on Windows)." # https://docs.python.org/3.9/library/tempfile.html#tempfile.NamedTemporaryFile -# +# # Fix being tracked here https://github.com/python/cpython/issues/58451 + @contextmanager def temporary_ini_file(): with NamedTemporaryFile( @@ -404,9 +406,16 @@ def invoke_test(args, project, overrides, inputs): LOG.debug("pytest args: %s", pytest_args) ret = pytest.main(pytest_args, plugins=[plugin]) # Manually clean up temporary file before exiting - issue with NamedTemporaryFile method on Windows - os.unlink(path) if ret: + try: + os.unlink(path) + except FileNotFoundError: + pass raise SysExitRecommendedError("One or more contract tests failed") + try: + os.unlink(path) + except FileNotFoundError: + pass def setup_subparser(subparsers, parents): diff --git a/tests/test_test.py b/tests/test_test.py index f7b00c4f..3ce08fed 100644 --- a/tests/test_test.py +++ b/tests/test_test.py @@ -337,9 +337,11 @@ def test_temporary_ini_file(): with path.open("r", encoding="utf-8") as f: assert "[pytest]" in f.read() - # Manually clean up temporary file before exiting - issue with NamedTemporaryFile method on Windows + # Manually clean up temporary file before exiting - issue with NamedTemporaryFile method on Windows + try: os.unlink(path_str) - + except FileNotFoundError: + pass def test_get_overrides_no_root(): From 6fd2bf6a1fc44c37d60921398cfc2e1af42e1f28 Mon Sep 17 00:00:00 2001 From: Michael Maeng Date: Thu, 20 Oct 2022 12:18:56 -0400 Subject: [PATCH 3/3] Explicitly close file in temporary_ini_file and catch FileNotFoundError exceptions --- src/rpdk/core/test.py | 14 ++++++-------- tests/test_test.py | 10 +++++----- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/rpdk/core/test.py b/src/rpdk/core/test.py index dc48e8f5..d2ea8f80 100644 --- a/src/rpdk/core/test.py +++ b/src/rpdk/core/test.py @@ -96,6 +96,8 @@ def temporary_ini_file(): LOG.debug("temporary pytest.ini path: %s", temp.name) path = Path(temp.name).resolve(strict=True) copy_resource(__name__, "data/pytest-contract.ini", path) + # Close temporary file for other processes to use, needed on Windows + temp.close() yield str(path) @@ -406,16 +408,12 @@ def invoke_test(args, project, overrides, inputs): LOG.debug("pytest args: %s", pytest_args) ret = pytest.main(pytest_args, plugins=[plugin]) # Manually clean up temporary file before exiting - issue with NamedTemporaryFile method on Windows + try: + os.unlink(path) + except FileNotFoundError: + pass if ret: - try: - os.unlink(path) - except FileNotFoundError: - pass raise SysExitRecommendedError("One or more contract tests failed") - try: - os.unlink(path) - except FileNotFoundError: - pass def setup_subparser(subparsers, parents): diff --git a/tests/test_test.py b/tests/test_test.py index 3ce08fed..66eaaad2 100644 --- a/tests/test_test.py +++ b/tests/test_test.py @@ -337,11 +337,11 @@ def test_temporary_ini_file(): with path.open("r", encoding="utf-8") as f: assert "[pytest]" in f.read() - # Manually clean up temporary file before exiting - issue with NamedTemporaryFile method on Windows - try: - os.unlink(path_str) - except FileNotFoundError: - pass + # Manually clean up temporary file before exiting - issue with NamedTemporaryFile method on Windows + try: + os.unlink(path_str) + except FileNotFoundError: + pass def test_get_overrides_no_root():