Skip to content

Commit

Permalink
New improvements and fixes to pytest req updates plugin (#16692)
Browse files Browse the repository at this point in the history
* New fixes and improvements to pytest req updates plugin

* Pre commit fixes

* More granular reporting

(cherry picked from commit 59f2b4a)
  • Loading branch information
jyejare authored and web-flow committed Oct 25, 2024
1 parent e26b7d7 commit 8c38b5d
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 23 deletions.
74 changes: 64 additions & 10 deletions pytest_plugins/requirements/req_updater.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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')
36 changes: 23 additions & 13 deletions pytest_plugins/requirements/update_requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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."
)

0 comments on commit 8c38b5d

Please sign in to comment.