From 14d69fe07e3ea3d9e672e24e77fceeb8feabe1cf Mon Sep 17 00:00:00 2001 From: Dan Ryan Date: Mon, 19 Nov 2018 21:01:11 -0500 Subject: [PATCH] Update pythonfinder for resilient parsing Signed-off-by: Dan Ryan --- pipenv/vendor/pythonfinder/models/python.py | 49 +++------------------ pipenv/vendor/pythonfinder/utils.py | 34 +++++++++++++- 2 files changed, 37 insertions(+), 46 deletions(-) diff --git a/pipenv/vendor/pythonfinder/models/python.py b/pipenv/vendor/pythonfinder/models/python.py index d7de3e05..1e5caa16 100644 --- a/pipenv/vendor/pythonfinder/models/python.py +++ b/pipenv/vendor/pythonfinder/models/python.py @@ -10,7 +10,7 @@ from collections import defaultdict import attr -from packaging.version import Version +from packaging.version import Version, LegacyVersion from packaging.version import parse as parse_version from vistir.compat import Path @@ -355,49 +355,10 @@ class PythonVersion(object): :rtype: dict. """ - is_debug = False - if version.endswith("-debug"): - is_debug = True - version, _, _ = version.rpartition("-") - try: - version = parse_version(str(version)) - except TypeError: - try: - version_dict = parse_python_version(str(version)) - except Exception: - raise ValueError("Unable to parse version: %s" % version) - else: - if not version_dict: - raise ValueError("Not a valid python version: %r" % version) - major = int(version_dict.get("major")) - minor = int(version_dict.get("minor")) - patch = version_dict.get("patch") - if patch: - patch = int(patch) - version = ".".join([v for v in [major, minor, patch] if v is not None]) - version = parse_version(version) - else: - if not version or not version.release: - raise ValueError("Not a valid python version: %r" % version) - if len(version.release) >= 3: - major, minor, patch = version.release[:3] - elif len(version.release) == 2: - major, minor = version.release - patch = None - else: - major = version.release[0] - minor = None - patch = None - return { - "major": major, - "minor": minor, - "patch": patch, - "is_prerelease": version.is_prerelease, - "is_postrelease": version.is_postrelease, - "is_devrelease": version.is_devrelease, - "is_debug": is_debug, - "version": version, - } + version_dict = parse_python_version(str(version)) + if not version_dict: + raise ValueError("Not a valid python version: %r" % version) + return version_dict def get_architecture(self): if self.architecture: diff --git a/pipenv/vendor/pythonfinder/utils.py b/pipenv/vendor/pythonfinder/utils.py index f8ec1972..ea2b71de 100644 --- a/pipenv/vendor/pythonfinder/utils.py +++ b/pipenv/vendor/pythonfinder/utils.py @@ -25,7 +25,9 @@ except ImportError: from backports.functools_lru_cache import lru_cache -version_re = re.compile(r"(?P[0-9]+)\.(?P[0-9]+)\.?(?P(?<=\.)[0-9]+)") +version_re = re.compile(r"(?P\d+)\.(?P\d+)\.?(?P(?<=\.)[0-9]+)?\.?" + r"(?:(?P[abc]|rc|dev)(?:(?P\d+(?:\.\d+)*))?)" + r"?(?P(\.post(?P\d+))?(\.dev(?P\d+))?)?") PYTHON_IMPLEMENTATIONS = ( @@ -66,10 +68,38 @@ def get_python_version(path): @lru_cache(maxsize=1024) def parse_python_version(version_str): + from packaging.version import parse as parse_version + is_debug = False + if version_str.endswith("-debug"): + is_debug = True + version_str, _, _ = version.rpartition("-") m = version_re.match(version_str) if not m: raise InvalidPythonVersion("%s is not a python version" % version_str) - return m.groupdict() + version_dict = m.groupdict() + major = int(version_dict.get("major")) + minor = int(version_dict.get("minor")) + patch = version_dict.get("patch") + is_postrelease = True if version_dict.get("post") else False + is_prerelease = True if version_dict.get("prerel") else False + is_devrelease = True if version_dict.get("dev") else False + if patch: + patch = int(patch) + try: + version = parse_version(version_str) + except TypeError: + version_parts = [str(v) for v in [major, minor, patch] if v is not None] + version = parse_version(".".join(v)) + return { + "major": major, + "minor": minor, + "patch": patch, + "is_postrelease": is_postrelease, + "is_prerelease": is_prerelease, + "is_devrelease": is_devrelease, + "is_debug": is_debug, + "version": version + } def optional_instance_of(cls):