Source code for django_test_tools.pip.utils

import contextlib
import subprocess

import pip
import pkg_resources

from django_test_tools.exceptions import DjangoTestToolsException
from django_test_tools.utils import versiontuple

if versiontuple(pip.__version__) >= (10, 0, 0):
    from pip._internal.req import parse_requirements
    from pip._internal.req.req_install import InstallRequirement
else:
    from pip.req import parse_requirements, InstallRequirement

import re

import requests
import logging

logger = logging.getLogger(__name__)


[docs]@contextlib.contextmanager def capture(): import sys from io import StringIO oldout, olderr = sys.stdout, sys.stderr try: out = [StringIO(), StringIO()] sys.stdout, sys.stderr = out yield out finally: sys.stdout, sys.stderr = oldout, olderr out[0] = out[0].getvalue() out[1] = out[1].getvalue()
[docs]def parse_pip_list(line): regexp = re.compile(r'([\w\-]*)\s\(([\w\-_\.]*)\)\s\-\sLatest:\s([\w\-_\.]*)\s\[([a-z]*)\]') regexp_new = re.compile(r'([\w\-]*)\s+([\w\-_\.]*)\s+([\w\-_\.]*)\s+([\w\-_\.]*)') regexp_list = re.compile(r'([\w\-]*)\s+([\w\-_\.]*)') match = regexp.match(line) if match is None: match = regexp_new.match(line) if match: library = dict() library['name'] = match.group(1).lower() library['current_version'] = match.group(2) library['new_version'] = match.group(3) return library match_list = regexp_list.match(line) if match_list: library = dict() library['name'] = match_list.group(1).lower() library['current_version'] = match_list.group(2) return library return None
[docs]def get_latest_version(package_name): url = 'https://pypi.python.org/pypi/{}/json'.format(package_name) r = requests.get(url) versions = sorted(r.json()["releases"], key=pkg_resources.parse_version) return versions[-1]
[docs]def parse_comes_from(comes_from): regexp = re.compile(r'(\-r)\s([/\w\.\-]*)\s\(line\s(\d*)\)') match = regexp.match(comes_from) if match: return match.group(1), match.group(2), int(match.group(3)) else: raise ValueError('Invalid comes from "{}"'.format(comes_from))
[docs]def parse_specifier(specifier): regexp = re.compile(r'((?:[=>])=)([\w\-_\.]*)') match = regexp.match(specifier) if match: return match.group(1), match.group(2) else: raise ValueError('Invalid speficier "{}"'.format(specifier))
[docs]def read_requirement_file(req_file): # print('pip version {}'.format(pip.__version__)) requirements = dict() for item in parse_requirements(req_file, session="somesession"): if isinstance(item, InstallRequirement): requirement = dict() requirement['name'] = item.name if len(str(item.req.specifier)) > 0: operator, version = parse_specifier(str(item.req.specifier)) requirement['operator'] = operator requirement['version'] = version requirement['comes_from'] = dict() requirement['comes_from']['value'] = item.comes_from file_indicator, filename, line_no = parse_comes_from(item.comes_from) requirement['comes_from']['file_indicator'] = file_indicator requirement['comes_from']['filename'] = filename requirement['comes_from']['line_no'] = line_no requirements[item.name] = requirement return requirements
[docs]def list_outdated_libraries(): return list_libraries(True)
[docs]def list_libraries(outdated=False): command_list = ['pip', 'list'] if outdated: command_list.append('--outdated') pip_list = subprocess.check_output(command_list) # pip.main(['list', '--outdated']) library_lines = pip_list.decode("utf-8").split('\n') outdated_libraries = list() for line in library_lines: library = parse_pip_list(line) if library is not None: outdated_libraries.append(library) return outdated_libraries
[docs]def update_outdated_libraries(requirement_file, **kwargs): """ Updates the requirements to their latest version. :param requirement_file: String with the path to the requirement fiel :param kwargs: :return: list of dictionaries containting the changes """ requirements = read_requirement_file(requirement_file) outdated_libraries = list_outdated_libraries() changes = list() for outdated_library in outdated_libraries: library_name = outdated_library['name'] if requirements.get(library_name): change = dict() change['library_name'] = library_name change['filename'] = requirements[library_name]['comes_from']['filename'] line_no = requirements[library_name]['comes_from']['line_no'] - 1 operator = requirements[library_name]['operator'] with open(change['filename'], 'r') as file: data = file.readlines() try: new_value = '{}{}{}\n'.format(library_name, operator, outdated_library['new_version']) except KeyError as e: msg = 'Could not find new version for library {library_name} in {filename}'.format(**change) raise DjangoTestToolsException(msg) change['previous'] = data[line_no].strip('\n') change['new'] = new_value.strip('\n') data[line_no] = new_value with open(change['filename'], 'w') as file: file.writelines(data) change['line_no'] = line_no changes.append(change) return changes