from __future__ import print_function, unicode_literals import os import os.path import re import subprocess import warnings warnings.warn('The setuptools-version-command package is deprecated, use setuptools-scm instead. ' + 'See https://github.com/j0057/setuptools-version-command for more information.') # see pep440-version-regex.png _PEP440_VERSION = re.compile(r'^(?Pv)?(?:(?P\d+)(?P!))?(?P\d+(?:\.\d+)*)(?:(?P[\._-])?(?Pa|alpha|b|beta|rc|c|pre|preview)(?P[\._-](?=\d))?(?P\d*))?(?:(?:(?P[\._-])?(?Ppost|rev|r)(?P[\._-](?=\d))?|(?P-))(?P(?(post_t)\d*|\d+)))?(?:(?P[\._-])?(?Pdev)(?P[\._-](?=\d))?(?P\d*))?(?:-(?P\d+)-(?Pg?[0-9a-f]{4,20}))?(?P-dirty)?$', re.IGNORECASE|re.VERBOSE) _PEP440_POST_MODE = re.compile(r'^(?:(?P[\._-]?)(?Ppost|rev|r)(?P[\._-]?)|(?P-))?$') def validate_version_command_keyword(dist, attr, value): egg_info_dir = dist.metadata.name.replace('-', '_') + '.egg-info' version_txt = egg_info_dir + '/version.txt' version_full_txt = egg_info_dir + '/version_full.txt' (command, pep440_mode, pep440_post) = _parse_value(value) (current_short_version, current_full_version) = _get_scm_version(command, pep440_mode, pep440_post) (cached_short_version, cached_full_version) = _get_cached_version(version_txt, version_full_txt) if current_short_version: dist.metadata.version = current_short_version dist.metadata.version_full = current_full_version elif cached_short_version: dist.metadata.version = cached_short_version dist.metadata.version_full = cached_full_version else: raise Exception('Could not find version from {0!r} or from {1}'.format(command, version_full_txt)) def write_metadata_value(command, basename, filename): attr_name = os.path.splitext(basename)[0] attr_value = getattr(command.distribution.metadata, attr_name) \ if hasattr(command.distribution.metadata, attr_name) \ else None command.write_or_delete_file(attr_name, filename, attr_value, force=True) def _parse_value(value): if isinstance(value, str): value = (value, None) if isinstance(value, tuple) and len(value) == 2: value = value + ('.post',) if isinstance(value, tuple) and len(value) == 3: if value[1] is None: pass elif value[1] in ['pep440-git', 'pep440-git-dev', 'pep440-git-local', 'pep440-git-full']: pass else: raise Exception('Unrecognized PEP440 mode {0!r}'.format(value[1])) (command, pep440_mode, pep440_post) = value match = _PEP440_POST_MODE.match(pep440_post) if not match: raise Exception('Unrecognized post mode {0!r}'.format(value[2])) pep440_post_mode = { k: v or '' for (k, v) in match.groupdict().items() } return (command, pep440_mode, pep440_post_mode) else: raise Exception('Unrecognized version_command value {0!r}'.format(value)) def _get_scm_version(command, pep440_mode, pep440_post): try: cmd = command.split() full_version = subprocess.check_output(cmd).strip().decode('ascii') except: full_version = None if full_version: short_version = _apply_pep440(full_version, pep440_mode, pep440_post) if not short_version: raise Exception('Could not transform version {0!r}'.format(full_version)) else: short_version = None return (short_version, full_version) def _get_cached_version(version_txt, version_full_txt): return (_read_version(version_txt), _read_version(version_full_txt)) def _read_version(filename): try: with open(filename, 'r') as f: return f.read() except: return None def _split_version(version): match = _PEP440_VERSION.match(version) if not match: raise Exception('Can\'t parse version {0!r} as PEP440 version'.format(version)) return { k: v or '' for (k, v) in match.groupdict().items() } def _join_version(v): return '{v}{e}{e_s}{r}{pre_ps}{pre_t}{pre_is}{pre_n}{post_ps}{post_t}{post_is}{post_im}{post_n}{dev_ps}{dev_t}{dev_is}{dev_n}'.format(**v) def _apply_pep440(version, pep440_mode, pep440_post={'post_ps':'.', 'post_t': 'post', 'post_is': '', 'post_im': ''}): if pep440_mode in ['pep440-git-local']: return version.replace('-', '+git-', 1).replace('-', '.') elif pep440_mode in ['pep440-git-dev']: # XXX: This is not compliant with PEP440. It is supported here for backwards-compatibility if '-' in version: parts = version.split('-') parts[-2] = 'dev' + parts[-2] return '.'.join(parts[:-1]) else: return version elif pep440_mode in ['pep440-git', 'pep440-git-full']: vd = _split_version(version) revs = vd['git_rev'] or '0' # has dev tag, update number if it's implicitly 0 if vd['dev_t']: if vd['dev_n'] == '': vd['dev_n'] = revs elif revs != '0': return None # has post tag, update number if it's implicitly 0 elif vd['post_t'] or vd['post_im']: if vd['post_n'] == '': vd['post_n'] = revs elif revs != '0': return None else: # update pre tag if it's implicitly 0 if vd['pre_t'] and (vd['pre_n'] == ''): vd['pre_n'] = revs # else add a post tag elif revs != '0': vd.update(pep440_post) vd['post_n'] = revs if vd['pre_t'] and not vd['pre_n']: vd['pre_n'] = '0' if (vd['post_t'] or vd['post_im']) and not vd['post_n']: vd['post_n'] = '0' if vd['dev_t'] and not vd['dev_n']: vd['dev_n'] = '0' ver = _join_version(vd) # If we have git SHA-1 and/or an optional dirty flag and are operating # in the pep440-git-full mode, include them in the local part of the # version number if pep440_mode == 'pep440-git-full': commit_hash = '' dirty = '' if vd['git_commit']: commit_hash = vd['git_commit'] if not commit_hash.startswith('g'): # Force consistent output in this mode. # This helps with bare dirty local versions being # ordered before ones that contain the git commit hash. commit_hash = 'g' + commit_hash if vd['git_dirty']: dirty = 'dirty' if commit_hash and dirty: ver = '{0}+{1}.{2}'.format(ver, commit_hash, dirty) elif commit_hash or dirty: # only one is valid ver = '{0}+{1}{2}'.format(ver, commit_hash, dirty) return ver elif pep440_mode is None: return version else: raise Exception('Unrecognized PEP440 mode {0!r}'.format(pep440_mode))