From 438f66089568a65889ffef00f807326cd32922e5 Mon Sep 17 00:00:00 2001 From: jennypng <63012604+JennyPng@users.noreply.github.com> Date: Thu, 18 Dec 2025 15:52:51 -0800 Subject: [PATCH 1/7] refactor install and test --- .../azpysdk/install_and_test.py | 132 ++++++++++-------- 1 file changed, 74 insertions(+), 58 deletions(-) diff --git a/eng/tools/azure-sdk-tools/azpysdk/install_and_test.py b/eng/tools/azure-sdk-tools/azpysdk/install_and_test.py index 026db9b06b60..34bba87fa232 100644 --- a/eng/tools/azure-sdk-tools/azpysdk/install_and_test.py +++ b/eng/tools/azure-sdk-tools/azpysdk/install_and_test.py @@ -60,30 +60,9 @@ def run(self, args: argparse.Namespace) -> int: logger.info(f"Processing {package_name} using interpreter {executable}") try: - self._install_common_requirements(executable, package_dir) - if self.should_install_dev_requirements(): - self.install_dev_reqs(executable, args, package_dir) - self.after_dependencies_installed(executable, package_dir, staging_directory, args) + self.install_all_requiremenmts(executable, staging_directory, package_name, package_dir, args) except CalledProcessError as exc: - logger.error(f"Failed to prepare dependencies for {package_name}: {exc}") - results.append(exc.returncode) - continue - - try: - create_package_and_install( - distribution_directory=staging_directory, - target_setup=package_dir, - skip_install=False, - cache_dir=None, - work_dir=staging_directory, - force_create=False, - package_type=self.package_type, - pre_download_disabled=False, - python_executable=executable, - ) - except CalledProcessError as exc: - logger.error(f"Failed to build/install {self.package_type} for {package_name}: {exc}") - results.append(1) + results.append(exc.returncode or 1) continue try: @@ -94,47 +73,84 @@ def run(self, args: argparse.Namespace) -> int: continue pytest_args = self._build_pytest_args(package_dir, args) - pytest_command = ["-m", "pytest", *pytest_args] - pytest_result = self.run_venv_command( - executable, pytest_command, cwd=staging_directory, immediately_dump=True - ) - - if pytest_result.returncode != 0: - if pytest_result.returncode == 5 and is_error_code_5_allowed(package_dir, package_name): - logger.info( - "pytest exited with code 5 for %s, which is allowed for management or opt-out packages.", - package_name, - ) - # Align with tox: skip coverage when tests are skipped entirely - continue - else: - results.append(pytest_result.returncode) - logger.error(f"pytest failed for {package_name} with exit code {pytest_result.returncode}.") - continue + try: + self.run_pytest(executable, staging_directory, package_dir, package_name, pytest_args) + except CalledProcessError as exc: + results.append(exc.returncode or 1) + continue if not self.coverage_enabled: continue - coverage_command = [ - os.path.join(REPO_ROOT, "eng/tox/run_coverage.py"), - "-t", - package_dir, - "-r", - REPO_ROOT, - ] - coverage_result = self.run_venv_command(executable, coverage_command, cwd=package_dir) - if coverage_result.returncode != 0: - logger.error( - f"Coverage generation failed for {package_name} with exit code {coverage_result.returncode}." - ) - if coverage_result.stdout: - logger.error(coverage_result.stdout) - if coverage_result.stderr: - logger.error(coverage_result.stderr) - results.append(coverage_result.returncode) - + try: + self.check_coverage(executable, package_dir, package_name) + except CalledProcessError as exc: + results.append(exc.returncode or 1) + continue return max(results) if results else 0 + def check_coverage(self, executable: str, package_dir: str, package_name: str) -> None: + coverage_command = [ + os.path.join(REPO_ROOT, "eng/tox/run_coverage.py"), + "-t", + package_dir, + "-r", + REPO_ROOT, + ] + coverage_result = self.run_venv_command(executable, coverage_command, cwd=package_dir) + if coverage_result.returncode != 0: + logger.error(f"Coverage generation failed for {package_name} with exit code {coverage_result.returncode}.") + if coverage_result.stdout: + logger.error(coverage_result.stdout) + if coverage_result.stderr: + logger.error(coverage_result.stderr) + exit(coverage_result.returncode) + + def run_pytest( + self, executable: str, staging_directory: str, package_dir: str, package_name: str, pytest_args: List[str] + ) -> None: + pytest_command = ["-m", "pytest", *pytest_args] + pytest_result = self.run_venv_command(executable, pytest_command, cwd=staging_directory, immediately_dump=True) + if pytest_result.returncode != 0: + if pytest_result.returncode == 5 and is_error_code_5_allowed(package_dir, package_name): + logger.info( + "pytest exited with code 5 for %s, which is allowed for management or opt-out packages.", + package_name, + ) + # Align with tox: skip coverage when tests are skipped entirely + return + else: + logger.error(f"pytest failed for {package_name} with exit code {pytest_result.returncode}.") + exit(pytest_result.returncode) + + def install_all_requiremenmts( + self, executable: str, staging_directory: str, package_name: str, package_dir: str, args: argparse.Namespace + ) -> None: + try: + self._install_common_requirements(executable, package_dir) + if self.should_install_dev_requirements(): + self.install_dev_reqs(executable, args, package_dir) + self.after_dependencies_installed(executable, package_dir, staging_directory, args) + except CalledProcessError as exc: + logger.error(f"Failed to prepare dependencies for {package_name}: {exc}") + exit(exc.returncode or 1) + + try: + create_package_and_install( + distribution_directory=staging_directory, + target_setup=package_dir, + skip_install=False, + cache_dir=None, + work_dir=staging_directory, + force_create=False, + package_type=self.package_type, + pre_download_disabled=False, + python_executable=executable, + ) + except CalledProcessError as exc: + logger.error(f"Failed to build/install {self.package_type} for {package_name}: {exc}") + exit(1) + def get_env_defaults(self) -> Dict[str, str]: defaults: Dict[str, str] = {} if self.proxy_url: From 152fb89e08429de10de8581b9045a9736e2e85c8 Mon Sep 17 00:00:00 2001 From: jennypng <63012604+JennyPng@users.noreply.github.com> Date: Thu, 18 Dec 2025 16:33:57 -0800 Subject: [PATCH 2/7] refactored optional and i think it's working --- .../azpysdk/install_and_test.py | 12 ++- eng/tools/azure-sdk-tools/azpysdk/optional.py | 86 +++++++------------ 2 files changed, 39 insertions(+), 59 deletions(-) diff --git a/eng/tools/azure-sdk-tools/azpysdk/install_and_test.py b/eng/tools/azure-sdk-tools/azpysdk/install_and_test.py index 34bba87fa232..95897997f29b 100644 --- a/eng/tools/azure-sdk-tools/azpysdk/install_and_test.py +++ b/eng/tools/azure-sdk-tools/azpysdk/install_and_test.py @@ -107,10 +107,18 @@ def check_coverage(self, executable: str, package_dir: str, package_name: str) - exit(coverage_result.returncode) def run_pytest( - self, executable: str, staging_directory: str, package_dir: str, package_name: str, pytest_args: List[str] + self, + executable: str, + staging_directory: str, + package_dir: str, + package_name: str, + pytest_args: List[str], + cwd: Optional[str] = None, ) -> None: pytest_command = ["-m", "pytest", *pytest_args] - pytest_result = self.run_venv_command(executable, pytest_command, cwd=staging_directory, immediately_dump=True) + pytest_result = self.run_venv_command( + executable, pytest_command, cwd=(cwd or staging_directory), immediately_dump=True + ) if pytest_result.returncode != 0: if pytest_result.returncode == 5 and is_error_code_5_allowed(package_dir, package_name): logger.info( diff --git a/eng/tools/azure-sdk-tools/azpysdk/optional.py b/eng/tools/azure-sdk-tools/azpysdk/optional.py index 3a873d234cca..1ea3d59856bc 100644 --- a/eng/tools/azure-sdk-tools/azpysdk/optional.py +++ b/eng/tools/azure-sdk-tools/azpysdk/optional.py @@ -5,7 +5,7 @@ from typing import Optional, List -from .Check import Check +from .install_and_test import InstallAndTest from ci_tools.functions import ( install_into_venv, uninstall_from_venv, @@ -20,9 +20,9 @@ REPO_ROOT = discover_repo_root() -class optional(Check): +class optional(InstallAndTest): def __init__(self) -> None: - super().__init__() + super().__init__(package_type="sdist", proxy_url="http://localhost:5004", display_name="optional") def register( self, subparsers: "argparse._SubParsersAction", parent_parsers: Optional[List[argparse.ArgumentParser]] = None @@ -48,8 +48,14 @@ def run(self, args: argparse.Namespace) -> int: """Run the optional check command.""" logger.info("Running optional check...") - set_envvar_defaults({"PROXY_URL": "http://localhost:5004"}) + env_defaults = self.get_env_defaults() + if env_defaults: + set_envvar_defaults(env_defaults) + targeted = self.get_targeted_directories(args) + if not targeted: + logger.warning("No target packages discovered for optional check.") + return 0 results: List[int] = [] @@ -57,22 +63,17 @@ def run(self, args: argparse.Namespace) -> int: package_dir = parsed.folder package_name = parsed.name executable, staging_directory = self.get_executable(args.isolate, args.command, sys.executable, package_dir) - logger.info(f"Processing {package_name} for optional check") + logger.info(f"Processing {package_name} using interpreter {executable}") - if in_ci(): - if not is_check_enabled(package_dir, "optional", False): - logger.info(f"Package {package_name} opts-out of optional check.") - continue + # try: + # self.install_dev_reqs(executable, args, package_dir) + # except CalledProcessError as exc: + # logger.error(f"Failed to install dependencies for {package_name}: {exc}") + # results.append(exc.returncode) + # continue try: - self.install_dev_reqs(executable, args, package_dir) - except CalledProcessError as exc: - logger.error(f"Failed to install dependencies for {package_name}: {exc}") - results.append(exc.returncode) - continue - - try: - self.prepare_and_test_optional(package_name, package_dir, staging_directory, args.optional) + self.prepare_and_test_optional(package_name, package_dir, staging_directory, args.optional, args) except Exception as e: logger.error(f"Optional check for package {package_name} failed with exception: {e}") results.append(1) @@ -83,7 +84,7 @@ def run(self, args: argparse.Namespace) -> int: # TODO copying from generation.py, remove old code later # TODO remove pytest() function from ci_tools.functions as it was only used in the old version of this logic def prepare_and_test_optional( - self, package_name: str, package_dir: str, temp_dir: str, target_env_name: str + self, package_name: str, package_dir: str, temp_dir: str, target_env_name: str, args: argparse.Namespace ) -> None: """ Prepare and test the optional environment for the given package. @@ -109,26 +110,12 @@ def prepare_and_test_optional( environment_exe = prepare_environment(package_dir, temp_dir, env_name) - create_package_and_install( - distribution_directory=temp_dir, - target_setup=package_dir, - skip_install=False, - cache_dir=None, - work_dir=temp_dir, - force_create=False, - package_type="sdist", - pre_download_disabled=False, - python_executable=environment_exe, - ) - dev_reqs = os.path.join(package_dir, "dev_requirements.txt") - test_tools = os.path.join(REPO_ROOT, "eng", "test_tools.txt") - - # install the dev requirements and test_tools requirements files to ensure tests can run + # install package and testing requirements try: - install_into_venv(environment_exe, ["-r", dev_reqs, "-r", test_tools], package_dir) - except CalledProcessError as exc: + self.install_all_requiremenmts(environment_exe, temp_dir, package_name, package_dir, args) + except Exception as exc: logger.error( - f"Unable to complete installation of dev_requirements.txt and/or test_tools.txt for {package_name}, check command output above." + f"Failed to install base requirements for {package_name} in optional env {env_name}: {exc}" ) config_results.append(False) break @@ -138,7 +125,7 @@ def prepare_and_test_optional( if additional_installs: try: install_into_venv(environment_exe, additional_installs, package_dir) - except CalledProcessError as exc: + except Exception as exc: logger.error( f"Unable to complete installation of additional packages {additional_installs} for {package_name}, check command output above." ) @@ -150,7 +137,7 @@ def prepare_and_test_optional( if additional_uninstalls: try: uninstall_from_venv(environment_exe, additional_uninstalls, package_dir) - except CalledProcessError as exc: + except Exception as exc: logger.error( f"Unable to complete removal of packages targeted for uninstall {additional_uninstalls} for {package_name}, check command output above." ) @@ -181,26 +168,11 @@ def prepare_and_test_optional( logger.info(f"Invoking tests for package {package_name} and optional environment {env_name}") - pytest_command = ["-m", "pytest", *pytest_args] - pytest_result = self.run_venv_command( - environment_exe, pytest_command, cwd=package_dir, immediately_dump=True - ) - - if pytest_result.returncode != 0: - if pytest_result.returncode == 5 and is_error_code_5_allowed(package_dir, package_name): - logger.info( - "pytest exited with code 5 for %s, which is allowed for management or opt-out packages.", - package_name, - ) - # Align with tox: skip coverage when tests are skipped entirely - continue - logger.error( - f"pytest failed for {package_name} and optional environment {env_name} with exit code {pytest_result.returncode}." - ) - config_results.append(False) - else: - logger.info(f"pytest succeeded for {package_name} and optional environment {env_name}.") + try: + self.run_pytest(environment_exe, temp_dir, package_dir, package_name, pytest_args, cwd=package_dir) config_results.append(True) + except Exception as exc: + config_results.append(False) if all(config_results): logger.info(f"All optional environment(s) for {package_name} completed successfully.") From 3a80d0b64b037245342b45baed373b54b8db8576 Mon Sep 17 00:00:00 2001 From: jennypng <63012604+JennyPng@users.noreply.github.com> Date: Thu, 18 Dec 2025 16:41:35 -0800 Subject: [PATCH 3/7] minor clean --- eng/tools/azure-sdk-tools/azpysdk/optional.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/eng/tools/azure-sdk-tools/azpysdk/optional.py b/eng/tools/azure-sdk-tools/azpysdk/optional.py index 1ea3d59856bc..d16443ad9d3a 100644 --- a/eng/tools/azure-sdk-tools/azpysdk/optional.py +++ b/eng/tools/azure-sdk-tools/azpysdk/optional.py @@ -9,11 +9,9 @@ from ci_tools.functions import ( install_into_venv, uninstall_from_venv, - is_error_code_5_allowed, ) -from ci_tools.scenario.generation import create_package_and_install, prepare_environment -from ci_tools.variables import discover_repo_root, in_ci, set_envvar_defaults -from ci_tools.environment_exclusions import is_check_enabled +from ci_tools.scenario.generation import prepare_environment +from ci_tools.variables import discover_repo_root, set_envvar_defaults from ci_tools.parsing import get_config_setting from ci_tools.logging import logger @@ -65,13 +63,6 @@ def run(self, args: argparse.Namespace) -> int: executable, staging_directory = self.get_executable(args.isolate, args.command, sys.executable, package_dir) logger.info(f"Processing {package_name} using interpreter {executable}") - # try: - # self.install_dev_reqs(executable, args, package_dir) - # except CalledProcessError as exc: - # logger.error(f"Failed to install dependencies for {package_name}: {exc}") - # results.append(exc.returncode) - # continue - try: self.prepare_and_test_optional(package_name, package_dir, staging_directory, args.optional, args) except Exception as e: From 158a81d25170035edfd8c48536c673def1d0f2ba Mon Sep 17 00:00:00 2001 From: jennypng <63012604+JennyPng@users.noreply.github.com> Date: Thu, 18 Dec 2025 17:31:52 -0800 Subject: [PATCH 4/7] optional actually working properly --- .../azure-sdk-tools/azpysdk/install_and_test.py | 16 ++++++++++------ eng/tools/azure-sdk-tools/azpysdk/optional.py | 9 ++++++--- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/eng/tools/azure-sdk-tools/azpysdk/install_and_test.py b/eng/tools/azure-sdk-tools/azpysdk/install_and_test.py index 95897997f29b..b3ad9ba1034b 100644 --- a/eng/tools/azure-sdk-tools/azpysdk/install_and_test.py +++ b/eng/tools/azure-sdk-tools/azpysdk/install_and_test.py @@ -74,9 +74,12 @@ def run(self, args: argparse.Namespace) -> int: pytest_args = self._build_pytest_args(package_dir, args) try: - self.run_pytest(executable, staging_directory, package_dir, package_name, pytest_args) - except CalledProcessError as exc: - results.append(exc.returncode or 1) + pytest_result = self.run_pytest(executable, staging_directory, package_dir, package_name, pytest_args) + if pytest_result != 0: + results.append(pytest_result) + continue + except Exception as exc: + results.append(1) continue if not self.coverage_enabled: @@ -114,7 +117,7 @@ def run_pytest( package_name: str, pytest_args: List[str], cwd: Optional[str] = None, - ) -> None: + ) -> int: pytest_command = ["-m", "pytest", *pytest_args] pytest_result = self.run_venv_command( executable, pytest_command, cwd=(cwd or staging_directory), immediately_dump=True @@ -126,10 +129,11 @@ def run_pytest( package_name, ) # Align with tox: skip coverage when tests are skipped entirely - return + return 0 else: logger.error(f"pytest failed for {package_name} with exit code {pytest_result.returncode}.") - exit(pytest_result.returncode) + return pytest_result.returncode + return 0 def install_all_requiremenmts( self, executable: str, staging_directory: str, package_name: str, package_dir: str, args: argparse.Namespace diff --git a/eng/tools/azure-sdk-tools/azpysdk/optional.py b/eng/tools/azure-sdk-tools/azpysdk/optional.py index d16443ad9d3a..d72e493a55f6 100644 --- a/eng/tools/azure-sdk-tools/azpysdk/optional.py +++ b/eng/tools/azure-sdk-tools/azpysdk/optional.py @@ -87,7 +87,8 @@ def prepare_and_test_optional( exit(0) config_results = [] - + print("??") + print(optional_configs) for config in optional_configs: env_name = config.get("name") @@ -160,8 +161,10 @@ def prepare_and_test_optional( logger.info(f"Invoking tests for package {package_name} and optional environment {env_name}") try: - self.run_pytest(environment_exe, temp_dir, package_dir, package_name, pytest_args, cwd=package_dir) - config_results.append(True) + pytest_result = self.run_pytest( + environment_exe, temp_dir, package_dir, package_name, pytest_args, cwd=package_dir + ) + config_results.append(True if pytest_result == 0 else False) except Exception as exc: config_results.append(False) From d54c3f68fd92ef2e1b5b435026df48909ff1a93c Mon Sep 17 00:00:00 2001 From: jennypng <63012604+JennyPng@users.noreply.github.com> Date: Thu, 18 Dec 2025 18:03:23 -0800 Subject: [PATCH 5/7] bug fix --- eng/tools/azure-sdk-tools/azpysdk/optional.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/eng/tools/azure-sdk-tools/azpysdk/optional.py b/eng/tools/azure-sdk-tools/azpysdk/optional.py index d72e493a55f6..e084968b1517 100644 --- a/eng/tools/azure-sdk-tools/azpysdk/optional.py +++ b/eng/tools/azure-sdk-tools/azpysdk/optional.py @@ -82,13 +82,15 @@ def prepare_and_test_optional( """ optional_configs = get_config_setting(package_dir, "optional") + if not isinstance(optional_configs, list): + optional_configs = [] + if len(optional_configs) == 0: logger.info(f"No optional environments detected in pyproject.toml within {package_dir}.") exit(0) config_results = [] - print("??") - print(optional_configs) + for config in optional_configs: env_name = config.get("name") From 401478f66388dd52c269f303651287612cfed9e7 Mon Sep 17 00:00:00 2001 From: jennypng <63012604+JennyPng@users.noreply.github.com> Date: Thu, 18 Dec 2025 18:03:23 -0800 Subject: [PATCH 6/7] bug fix --- eng/tools/azure-sdk-tools/azpysdk/optional.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/eng/tools/azure-sdk-tools/azpysdk/optional.py b/eng/tools/azure-sdk-tools/azpysdk/optional.py index e084968b1517..51ee815cea4b 100644 --- a/eng/tools/azure-sdk-tools/azpysdk/optional.py +++ b/eng/tools/azure-sdk-tools/azpysdk/optional.py @@ -64,7 +64,11 @@ def run(self, args: argparse.Namespace) -> int: logger.info(f"Processing {package_name} using interpreter {executable}") try: - self.prepare_and_test_optional(package_name, package_dir, staging_directory, args.optional, args) + result = self.prepare_and_test_optional( + package_name, package_dir, staging_directory, args.optional, args + ) + if result != 0: + results.append(result) except Exception as e: logger.error(f"Optional check for package {package_name} failed with exception: {e}") results.append(1) @@ -76,7 +80,7 @@ def run(self, args: argparse.Namespace) -> int: # TODO remove pytest() function from ci_tools.functions as it was only used in the old version of this logic def prepare_and_test_optional( self, package_name: str, package_dir: str, temp_dir: str, target_env_name: str, args: argparse.Namespace - ) -> None: + ) -> int: """ Prepare and test the optional environment for the given package. """ @@ -87,7 +91,7 @@ def prepare_and_test_optional( if len(optional_configs) == 0: logger.info(f"No optional environments detected in pyproject.toml within {package_dir}.") - exit(0) + return 0 config_results = [] @@ -172,7 +176,6 @@ def prepare_and_test_optional( if all(config_results): logger.info(f"All optional environment(s) for {package_name} completed successfully.") - exit(0) else: for i, config in enumerate(optional_configs): if i >= len(config_results): @@ -182,4 +185,5 @@ def prepare_and_test_optional( logger.error( f"Optional environment {config_name} for {package_name} completed with non-zero exit-code. Check test results above." ) - exit(1) + return 1 + return 0 From 8d86c381c630ce6d720c6e2b509746b907faa679 Mon Sep 17 00:00:00 2001 From: jennypng <63012604+JennyPng@users.noreply.github.com> Date: Fri, 19 Dec 2025 09:45:28 -0800 Subject: [PATCH 7/7] minor exception handling revisions --- .../azpysdk/install_and_test.py | 38 +++++++++---------- eng/tools/azure-sdk-tools/azpysdk/optional.py | 16 +++++--- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/eng/tools/azure-sdk-tools/azpysdk/install_and_test.py b/eng/tools/azure-sdk-tools/azpysdk/install_and_test.py index b3ad9ba1034b..22c451352149 100644 --- a/eng/tools/azure-sdk-tools/azpysdk/install_and_test.py +++ b/eng/tools/azure-sdk-tools/azpysdk/install_and_test.py @@ -59,10 +59,11 @@ def run(self, args: argparse.Namespace) -> int: executable, staging_directory = self.get_executable(args.isolate, args.command, sys.executable, package_dir) logger.info(f"Processing {package_name} using interpreter {executable}") - try: - self.install_all_requiremenmts(executable, staging_directory, package_name, package_dir, args) - except CalledProcessError as exc: - results.append(exc.returncode or 1) + install_result = self.install_all_requiremenmts( + executable, staging_directory, package_name, package_dir, args + ) + if install_result != 0: + results.append(install_result) continue try: @@ -73,26 +74,21 @@ def run(self, args: argparse.Namespace) -> int: continue pytest_args = self._build_pytest_args(package_dir, args) - try: - pytest_result = self.run_pytest(executable, staging_directory, package_dir, package_name, pytest_args) - if pytest_result != 0: - results.append(pytest_result) - continue - except Exception as exc: - results.append(1) + pytest_result = self.run_pytest(executable, staging_directory, package_dir, package_name, pytest_args) + if pytest_result != 0: + results.append(pytest_result) continue if not self.coverage_enabled: continue - try: - self.check_coverage(executable, package_dir, package_name) - except CalledProcessError as exc: - results.append(exc.returncode or 1) - continue + coverage_result = self.check_coverage(executable, package_dir, package_name) + if coverage_result != 0: + results.append(coverage_result) + return max(results) if results else 0 - def check_coverage(self, executable: str, package_dir: str, package_name: str) -> None: + def check_coverage(self, executable: str, package_dir: str, package_name: str) -> int: coverage_command = [ os.path.join(REPO_ROOT, "eng/tox/run_coverage.py"), "-t", @@ -107,7 +103,8 @@ def check_coverage(self, executable: str, package_dir: str, package_name: str) - logger.error(coverage_result.stdout) if coverage_result.stderr: logger.error(coverage_result.stderr) - exit(coverage_result.returncode) + return coverage_result.returncode + return 0 def run_pytest( self, @@ -137,7 +134,7 @@ def run_pytest( def install_all_requiremenmts( self, executable: str, staging_directory: str, package_name: str, package_dir: str, args: argparse.Namespace - ) -> None: + ) -> int: try: self._install_common_requirements(executable, package_dir) if self.should_install_dev_requirements(): @@ -145,7 +142,7 @@ def install_all_requiremenmts( self.after_dependencies_installed(executable, package_dir, staging_directory, args) except CalledProcessError as exc: logger.error(f"Failed to prepare dependencies for {package_name}: {exc}") - exit(exc.returncode or 1) + return exc.returncode or 1 try: create_package_and_install( @@ -162,6 +159,7 @@ def install_all_requiremenmts( except CalledProcessError as exc: logger.error(f"Failed to build/install {self.package_type} for {package_name}: {exc}") exit(1) + return 0 def get_env_defaults(self) -> Dict[str, str]: defaults: Dict[str, str] = {} diff --git a/eng/tools/azure-sdk-tools/azpysdk/optional.py b/eng/tools/azure-sdk-tools/azpysdk/optional.py index 51ee815cea4b..16fb85b7889b 100644 --- a/eng/tools/azure-sdk-tools/azpysdk/optional.py +++ b/eng/tools/azure-sdk-tools/azpysdk/optional.py @@ -110,8 +110,14 @@ def prepare_and_test_optional( # install package and testing requirements try: - self.install_all_requiremenmts(environment_exe, temp_dir, package_name, package_dir, args) - except Exception as exc: + install_result = self.install_all_requiremenmts( + environment_exe, temp_dir, package_name, package_dir, args + ) + if install_result != 0: + logger.error(f"Failed to install base requirements for {package_name} in optional env {env_name}.") + config_results.append(False) + break + except CalledProcessError as exc: logger.error( f"Failed to install base requirements for {package_name} in optional env {env_name}: {exc}" ) @@ -123,7 +129,7 @@ def prepare_and_test_optional( if additional_installs: try: install_into_venv(environment_exe, additional_installs, package_dir) - except Exception as exc: + except CalledProcessError as exc: logger.error( f"Unable to complete installation of additional packages {additional_installs} for {package_name}, check command output above." ) @@ -135,7 +141,7 @@ def prepare_and_test_optional( if additional_uninstalls: try: uninstall_from_venv(environment_exe, additional_uninstalls, package_dir) - except Exception as exc: + except CalledProcessError as exc: logger.error( f"Unable to complete removal of packages targeted for uninstall {additional_uninstalls} for {package_name}, check command output above." ) @@ -171,7 +177,7 @@ def prepare_and_test_optional( environment_exe, temp_dir, package_dir, package_name, pytest_args, cwd=package_dir ) config_results.append(True if pytest_result == 0 else False) - except Exception as exc: + except CalledProcessError as exc: config_results.append(False) if all(config_results):