Server IP : 85.214.239.14 / Your IP : 3.129.20.112 Web Server : Apache/2.4.62 (Debian) System : Linux h2886529.stratoserver.net 4.9.0 #1 SMP Tue Jan 9 19:45:01 MSK 2024 x86_64 User : www-data ( 33) PHP Version : 7.4.18 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, MySQL : OFF | cURL : OFF | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /usr/share/dh-python/dhpython/ |
Upload File : |
# Copyright © 2010-2013 Piotr Ożarowski <piotr@debian.org> # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. import logging import re from os.path import exists from dhpython import _defaults RANGE_PATTERN = r'(-)?(\d\.\d+)(?:(-)(\d\.\d+)?)?' RANGE_RE = re.compile(RANGE_PATTERN) VERSION_RE = re.compile(r''' (?P<major>\d+)\.? (?P<minor>\d+)?\.? (?P<micro>\d+)?[.\s]? (?P<releaselevel>alpha|beta|candidate|final)?[.\s]? (?P<serial>\d+)?''', re.VERBOSE) log = logging.getLogger('dhpython') Interpreter = None class Version: # TODO: Upgrade to PEP-440 def __init__(self, value=None, major=None, minor=None, micro=None, releaselevel=None, serial=None): """Construct a new instance. >>> Version(major=0, minor=0, micro=0, releaselevel=0, serial=0) Version('0.0') >>> Version('0.0') Version('0.0') """ if isinstance(value, (tuple, list)): value = '.'.join(str(i) for i in value) if isinstance(value, Version): for name in ('major', 'minor', 'micro', 'releaselevel', 'serial'): setattr(self, name, getattr(value, name)) return comp = locals() del comp['self'] del comp['value'] if value: match = VERSION_RE.match(value) for name, value in match.groupdict().items() if match else []: if value is not None and comp[name] is None: comp[name] = value for name, value in comp.items(): if name != 'releaselevel' and value is not None: value = int(value) setattr(self, name, value) if self.major is None: raise ValueError('major component is required') def __str__(self): """Return major.minor or major string. >>> str(Version(major=3, minor=2, micro=1, releaselevel='final', serial=4)) '3.2' >>> str(Version(major=2)) '2' """ result = str(self.major) if self.minor is not None: result += '.{}'.format(self.minor) return result def __hash__(self): return hash(repr(self)) def __repr__(self): """Return full version string. >>> repr(Version(major=3, minor=2, micro=1, releaselevel='final', serial=4)) "Version('3.2.1.final.4')" >>> repr(Version(major=2)) "Version('2')" """ result = "Version('{}".format(self) for name in ('micro', 'releaselevel', 'serial'): value = getattr(self, name) if not value: break result += '.{}'.format(value) return result + "')" def __add__(self, other): """Return next version. >>> Version('3.1') + 1 Version('3.2') >>> Version('2') + '1' Version('3') """ result = Version(self) if self.minor is None: result.major += int(other) else: result.minor += int(other) return result def __sub__(self, other): """Return previous version. >>> Version('3.1') - 1 Version('3.0') >>> Version('3') - '1' Version('2') """ result = Version(self) if self.minor is None: result.major -= int(other) new = result.major else: result.minor -= int(other) new = result.minor if new < 0: raise ValueError('cannot decrease version further') return result def __eq__(self, other): try: other = Version(other) except Exception: return False return self.__cmp(other) == 0 def __lt__(self, other): return self.__cmp(other) < 0 def __le__(self, other): return self.__cmp(other) <= 0 def __gt__(self, other): return self.__cmp(other) > 0 def __ge__(self, other): return self.__cmp(other) >= 0 def __lshift__(self, other): """Compare major.minor or major only (if minor is not set). >>> Version('2.6') << Version('2.7') True >>> Version('2.6') << Version('2.6.6') False >>> Version('3') << Version('2') False >>> Version('3.1') << Version('2') False >>> Version('2') << Version('3.2.1.alpha.3') True """ if not isinstance(other, Version): other = Version(other) if self.minor is None or other.minor is None: return self.__cmp(other, ignore='minor') < 0 else: return self.__cmp(other, ignore='micro') < 0 def __rshift__(self, other): """Compare major.minor or major only (if minor is not set). >>> Version('2.6') >> Version('2.7') False >>> Version('2.6.7') >> Version('2.6.6') False >>> Version('3') >> Version('2') True >>> Version('3.1') >> Version('2') True >>> Version('2.1') >> Version('3.2.1.alpha.3') False """ if not isinstance(other, Version): other = Version(other) if self.minor is None or other.minor is None: return self.__cmp(other, ignore='minor') > 0 else: return self.__cmp(other, ignore='micro') > 0 def __cmp(self, other, ignore=None): if not isinstance(other, Version): other = Version(other) for name in ('major', 'minor', 'micro', 'releaselevel', 'serial'): if name == ignore: break value1 = getattr(self, name) or 0 value2 = getattr(other, name) or 0 if name == 'releaselevel': rmap = {'alpha': -3, 'beta': -2, 'candidate': -1, 'final': 0} value1 = rmap.get(value1, 0) value2 = rmap.get(value2, 0) if value1 == value2: continue return (value1 > value2) - (value1 < value2) return 0 class VersionRange: def __init__(self, value=None, minver=None, maxver=None): if minver: self.minver = Version(minver) else: self.minver = None if maxver: self.maxver = Version(maxver) else: self.maxver = None if value: minver, maxver = self.parse(value) if minver and self.minver is None: self.minver = minver if maxver and self.maxver is None: self.maxver = maxver def __bool__(self): if self.minver is not None or self.maxver is not None: return True return False def __str__(self): """Return version range string from given range. >>> str(VersionRange(minver='3.4')) '3.4-' >>> str(VersionRange(minver='3.4', maxver='3.6')) '3.4-3.6' >>> str(VersionRange(minver='3.4', maxver='4.0')) '3.4-4.0' >>> str(VersionRange(maxver='3.7')) '-3.7' >>> str(VersionRange(minver='3.5', maxver='3.5')) '3.5' >>> str(VersionRange()) '-' """ if self.minver is None is self.maxver: return '-' if self.minver == self.maxver: return str(self.minver) elif self.minver is None: return '-{}'.format(self.maxver) elif self.maxver is None: return '{}-'.format(self.minver) else: return '{}-{}'.format(self.minver, self.maxver) def __repr__(self): """Return version range string. >>> repr(VersionRange('5.0-')) "VersionRange(minver='5.0')" >>> repr(VersionRange('3.0-3.5')) "VersionRange(minver='3.0', maxver='3.5')" """ result = 'VersionRange(' if self.minver is not None: result += "minver='{}'".format(self.minver) if self.maxver is not None: result += ", maxver='{}'".format(self.maxver) result = result.replace('(, ', '(') return result + ")" @staticmethod def parse(value): """Return minimum and maximum Python version from given range. >>> VersionRange.parse('3.0-') (Version('3.0'), None) >>> VersionRange.parse('3.1-3.13') (Version('3.1'), Version('3.13')) >>> VersionRange.parse('3.2-4.0') (Version('3.2'), Version('4.0')) >>> VersionRange.parse('-3.7') (None, Version('3.7')) >>> VersionRange.parse('3.2') (Version('3.2'), Version('3.2')) >>> VersionRange.parse('') == VersionRange.parse('-') True >>> VersionRange.parse('>= 4.0') (Version('4.0'), None) """ if value in ('', '-'): return None, None match = RANGE_RE.match(value) if not match: try: minv, maxv = VersionRange._parse_pycentral(value) except Exception: raise ValueError("version range is invalid: %s" % value) else: groups = match.groups() if list(groups).count(None) == 3: # only one version is allowed minv = Version(groups[1]) return minv, minv minv = maxv = None if groups[0]: # maximum version only maxv = groups[1] else: minv = groups[1] maxv = groups[3] minv = Version(minv) if minv else None maxv = Version(maxv) if maxv else None if maxv and minv and minv > maxv: raise ValueError("version range is invalid: %s" % value) return minv, maxv @staticmethod def _parse_pycentral(value): """Parse X-Python3-Version. >>> VersionRange._parse_pycentral('>= 3.10') (Version('3.10'), None) >>> VersionRange._parse_pycentral('<< 4.0') (None, Version('4.0')) >>> VersionRange._parse_pycentral('3.1') (Version('3.1'), Version('3.1')) >>> VersionRange._parse_pycentral('3.1, 3.2') (Version('3.1'), None) """ minv = maxv = None hardcoded = set() for item in value.split(','): item = item.strip() match = re.match('>=\s*([\d\.]+)', item) if match: minv = match.group(1) continue match = re.match('<<\s*([\d\.]+)', item) if match: maxv = match.group(1) continue match = re.match('^[\d\.]+$', item) if match: hardcoded.add(match.group(0)) if len(hardcoded) == 1: ver = hardcoded.pop() return Version(ver), Version(ver) if not minv and hardcoded: # yeah, no maxv! minv = sorted(hardcoded)[0] return Version(minv) if minv else None, Version(maxv) if maxv else None def default(impl): """Return default interpreter version for given implementation.""" if impl not in _defaults.DEFAULT: raise ValueError("interpreter implementation not supported: %r" % impl) ver = _defaults.DEFAULT[impl] return Version(major=ver[0], minor=ver[1]) def supported(impl): """Return list of supported interpreter versions for given implementation.""" if impl not in _defaults.SUPPORTED: raise ValueError("interpreter implementation not supported: %r" % impl) versions = _defaults.SUPPORTED[impl] return [Version(major=v[0], minor=v[1]) for v in versions] def get_requested_versions(impl, vrange=None, available=None): """Return a set of requested and supported Python versions. :param impl: interpreter implementation :param available: if set to `True`, return installed versions only, if set to `False`, return requested versions that are not installed. By default returns all requested versions. :type available: bool >>> sorted(get_requested_versions('cpython3', '')) == sorted(supported('cpython3')) True >>> sorted(get_requested_versions('cpython3', '-')) == sorted(supported('cpython3')) True >>> get_requested_versions('cpython3', '>= 5.0') set() """ if isinstance(vrange, str): vrange = VersionRange(vrange) if not vrange: versions = set(supported(impl)) else: minv = Version(major=0, minor=0) if vrange.minver is None else vrange.minver maxv = Version(major=99, minor=99) if vrange.maxver is None else vrange.maxver if minv == maxv: versions = set([minv] if minv in supported(impl) else tuple()) else: versions = set(v for v in supported(impl) if minv <= v < maxv) if available is not None: # to avoid circular imports global Interpreter if Interpreter is None: from dhpython.interpreter import Interpreter if available: interpreter = Interpreter(impl=impl) versions = set(v for v in versions if exists(interpreter.binary(v))) elif available is False: interpreter = Interpreter(impl=impl) versions = set(v for v in versions if not exists(interpreter.binary(v))) return versions def build_sorted(versions, impl='cpython3'): """Return sorted list of versions in a build friendly order. i.e. default version, if among versions, is sorted last. >>> build_sorted([(2, 6), (3, 4), default('cpython3'), (3, 6), (2, 7)])[-1] == default('cpython3') True >>> build_sorted(('3.2', (3, 0), '3.1')) [Version('3.0'), Version('3.1'), Version('3.2')] """ default_ver = default(impl) result = sorted(Version(v) for v in versions) try: result.remove(default_ver) except ValueError: pass else: result.append(default_ver) return result