diff --git a/pytest_plugins/requirements/req_updater.py b/pytest_plugins/requirements/req_updater.py index 37d1aabc573..6cf7b62e5ac 100644 --- a/pytest_plugins/requirements/req_updater.py +++ b/pytest_plugins/requirements/req_updater.py @@ -1,15 +1,23 @@ from functools import cached_property import subprocess +import sys + + +class UsageError(Exception): + """The UsageError raised when the package manager is different from uv or pip""" + + pass class ReqUpdater: # Installed package name as key and its counterpart in requirements file as value package_deviates = { 'Betelgeuse': 'betelgeuse', - 'broker': 'broker[docker]', + 'broker': 'broker[docker,podman,hussh]', 'dynaconf': 'dynaconf[vault]', 'Jinja2': 'jinja2', 'Sphinx': 'sphinx', + 'pyyaml': 'PyYAML', } @cached_property @@ -18,9 +26,9 @@ def installed_packages(self): This also normalizes any package names that deviates in requirements file vs installed names """ - installed_pkges = subprocess.run( - 'pip list --format=freeze', capture_output=True, shell=True - ).stdout.decode() + installer_args = [sys.executable, '-m', 'list', '--format=freeze'] + installer_args[2:2] = self.packagae_manager.split(' ') + installed_pkges = subprocess.run(installer_args, capture_output=True).stdout.decode() for pkg in self.package_deviates: if pkg in installed_pkges: # Replacing the installed package names with their names in requirements file @@ -56,16 +64,62 @@ def opt_deviation(self): """Returns new and updates available packages in requirements-optional file""" return set(self.optional_packages).difference(self.installed_packages) + @cached_property + def packagae_manager(self): + if ( + subprocess.run( + [sys.executable, '-m', 'pip', '--version'], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ).returncode + == 0 + ): + _manager = 'pip' + elif ( + subprocess.run( + [sys.executable, '-m', 'uv', '--version'], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ).returncode + == 0 + ): + _manager = 'uv pip' + else: + raise UsageError( + 'The package manager is not identifiable for performing package updates.' + 'Currently only pip and uv is supported. Please manually update the packages ' + 'and rerun pytest command.' + ) + return _manager + def install_req_deviations(self): """Installs new and updates available packages in requirements file""" if self.req_deviation: - subprocess.run( - f"pip install {' '.join(self.req_deviation)}", shell=True, stdout=subprocess.PIPE - ) + lst_of_reqs = ' '.join(f"'{req}'" for req in self.req_deviation) + if ( + subprocess.run( + f"{self.packagae_manager} install {lst_of_reqs}", + shell=True, + stdout=subprocess.PIPE, + ).returncode + == 0 + ): + print('Mandatory requirements are updated.') + else: + print('ERROR: Some issue occurred with updating the required requirements') def install_opt_deviations(self): """Installs new and updates available packages in requirements-optional file""" if self.opt_deviation: - subprocess.run( - f"pip install {' '.join(self.opt_deviation)}", shell=True, stdout=subprocess.PIPE - ) + lst_of_reqs = ' '.join(f"'{req}'" for req in self.opt_deviation) + if ( + subprocess.run( + f"{self.packagae_manager} install {lst_of_reqs}", + shell=True, + stdout=subprocess.PIPE, + ).returncode + == 0 + ): + print('Optional requirements are updated.') + else: + print('ERROR: Some issue occurred with updating the optional requirements') diff --git a/pytest_plugins/requirements/update_requirements.py b/pytest_plugins/requirements/update_requirements.py index 74494427ce3..7aa7dc080ed 100644 --- a/pytest_plugins/requirements/update_requirements.py +++ b/pytest_plugins/requirements/update_requirements.py @@ -5,6 +5,12 @@ updater = ReqUpdater() +def git_deviation_filter(deviation): + """Packages installed from Git branch and the version cant be compared, so ignore them from reporting""" + git_packages = ['nailgun', 'airgun'] + return all(git_pckg not in deviation for git_pckg in git_packages) + + def pytest_addoption(parser): """Options to allow user to update the requirements""" update_options = { @@ -28,24 +34,28 @@ def pytest_report_header(config): # Following will update the mandatory and optional requirements # pytest tests/foreman --collect-only --update-all-reqs """ - if updater.req_deviation: - print(f"Mandatory Requirements Mismatch: {' '.join(updater.req_deviation)}") - if config.getoption('update_required_reqs') or config.getoption('update_all_reqs'): - updater.install_req_deviations() - print('Mandatory requirements are installed to be up-to-date.') + req_deviations = updater.req_deviation + filtered_req_deviations = list(filter(git_deviation_filter, req_deviations)) + if filtered_req_deviations: + print(f"Mandatory Requirements Mismatch: {' '.join(filtered_req_deviations)}") else: print('Mandatory Requirements are up to date.') - - if updater.opt_deviation: - print(f"Optional Requirements Mismatch: {' '.join(updater.opt_deviation)}") - if config.getoption('update_all_reqs'): - updater.install_opt_deviations() - print('Optional requirements are installed to be up-to-date.') + if req_deviations and ( + config.getoption('update_required_reqs') or config.getoption('update_all_reqs') + ): + updater.install_req_deviations() + + opt_deviations = updater.opt_deviation + filtered_opt_deviations = list(filter(git_deviation_filter, opt_deviations)) + if filtered_opt_deviations: + print(f"Optional Requirements Mismatch: {' '.join(filtered_opt_deviations)}") else: print('Optional Requirements are up to date.') + if opt_deviations and config.getoption('update_all_reqs'): + updater.install_opt_deviations() - if updater.req_deviation or updater.opt_deviation: + if req_deviations or opt_deviations: print( - "To update mismatched requirements, run the pytest command with " + "To update requirements, run the pytest with " "'--update-required-reqs' OR '--update-all-reqs' option." )