Source code for flutils.packages

# pylint: disable=E0611,E0401
from typing import (
    Any,
    Dict,
    Generator,
    List,
    NamedTuple,
    Optional,
    Tuple,
    Union,
    cast,
)

from distutils.version import StrictVersion


__all__ = ['bump_version']


_BUMP_VERSION_MAJOR: int = 0
_BUMP_VERSION_MINOR: int = 1
_BUMP_VERSION_PATCH: int = 2
_BUMP_VERSION_MINOR_ALPHA: int = 3
_BUMP_VERSION_MINOR_BETA: int = 4
_BUMP_VERSION_MINORS: Tuple[int, ...] = (
    _BUMP_VERSION_MINOR,
    _BUMP_VERSION_MINOR_ALPHA,
    _BUMP_VERSION_MINOR_BETA,
)
_BUMP_VERSION_PATCH_ALPHA: int = 5
_BUMP_VERSION_PATCH_BETA: int = 6
_BUMP_VERSION_PATCHES: Tuple[int, ...] = (
    _BUMP_VERSION_PATCH,
    _BUMP_VERSION_PATCH_ALPHA,
    _BUMP_VERSION_PATCH_BETA,
)
_BUMP_VERSION_POSITION_NAMES: Dict[int, str] = {
    _BUMP_VERSION_MAJOR: 'major',
    _BUMP_VERSION_MINOR: 'minor',
    _BUMP_VERSION_PATCH: 'patch',
}


class _VersionPart(NamedTuple):
    pos: int
    txt: str
    num: int
    pre_txt: str
    pre_num: int
    name: str


def _each_version_part(
        ver_obj: StrictVersion,
) -> Generator[_VersionPart, None, None]:
    version: Tuple[int, int, int] = ver_obj.version
    prerelease: Union[Tuple[str, int], None] = ver_obj.prerelease
    prerelease_built = False
    for pos, num in enumerate(version):
        txt = '%s' % num
        if pos == 2 and num == 0:
            txt = ''
        kwargs: Dict[str, Any] = {
            'pos': pos,
            'txt': txt,
            'num': num,
            'pre_txt': '',
            'pre_num': -1,
            'name': _BUMP_VERSION_POSITION_NAMES[pos]
        }
        if (prerelease_built is False and
                pos > 0 and
                prerelease is not None):
            prerelease = cast(Tuple[str, int], prerelease)
            should_add = True
            if pos == 1 and version[2] != 0:
                should_add = False
            if should_add is True:
                kwargs['txt'] = '%s%s%s' % (
                    kwargs['txt'],
                    prerelease[0],
                    prerelease[1]
                )
                kwargs['pre_txt'] = prerelease[0]
                kwargs['pre_num'] = prerelease[1]
                prerelease_built = True
        yield _VersionPart(**kwargs)


class _VersionInfo(NamedTuple):
    version: str
    major: _VersionPart
    minor: _VersionPart
    patch: _VersionPart
    pre_pos: int  # The pre-release position. -1 means no pre-release


def _build_version_info(
        version: str
) -> _VersionInfo:
    ver_obj = StrictVersion(version)
    pre_pos = -1
    args: List[Any] = [version]
    for part in _each_version_part(ver_obj):
        if part.pre_txt:
            pre_pos = part.pos
        args.append(part)
    args.append(pre_pos)
    return _VersionInfo(*args)


def _build_version_bump_position(
        position: int
) -> int:
    pos_min = -3
    pos_max = 2

    if (pos_min <= position <= pos_max) is False:
        raise ValueError(
            "The given value for 'position', %r, must be an 'int' "
            "between (%r) and (%r)." % (position, pos_min, pos_max)
        )
    # Turn position into a positive number
    if position < 0:
        pos_max += 1
        return pos_max + position
    return position


def _build_version_bump_type(
        position_positive: int,
        pre_release: Union[str, None]
) -> int:
    if pre_release is None:
        prerelease = ''
    else:
        pre_release = cast(str, pre_release)
        prerelease = pre_release.strip().lower()

    if prerelease == '':
        if position_positive == 0:
            return _BUMP_VERSION_MAJOR
        if position_positive == 1:
            return _BUMP_VERSION_MINOR
        return _BUMP_VERSION_PATCH
    if prerelease in ('a', 'alpha', 'b', 'beta'):
        is_alpha = False
        if prerelease in ('a', 'alpha'):
            is_alpha = True

        if position_positive == 0:
            raise ValueError(
                "Only the 'minor' or 'patch' parts of the version number "
                "can get a prerelease bump."
            )
        if position_positive == 1:
            if is_alpha is True:
                return _BUMP_VERSION_MINOR_ALPHA
            return _BUMP_VERSION_MINOR_BETA
        if is_alpha is True:
            return _BUMP_VERSION_PATCH_ALPHA
        return _BUMP_VERSION_PATCH_BETA
    raise ValueError(
        "The given value for 'pre_release', %r, can only be one of: "
        "'a', 'alpha', 'b', 'beta', None."
    )


[docs]def bump_version( version: str, position: int = 2, pre_release: Optional[str] = None ) -> str: """Increase the version number from a version number string. *New in version 0.3* Args: version (str): The version number to be bumped. position (int, optional): The position (starting with zero) of the version number component to be increased. Defaults to: ``2`` pre_release (str, Optional): A value of ``a`` or ``alpha`` will create or increase an alpha version number. A value of ``b`` or ``beta`` will create or increase a beta version number. Raises: ValueError: if the given ``version`` is an invalid version number. ValueError: if the given ``position`` does not exist. ValueError: if the given ``prerelease`` is not in: ``a, alpha, b, beta`` ValueError: if trying to 'major' part, of a version number, to a pre-release version. :rtype: :obj:`str` * The increased version number. Examples: >>> from flutils.packages import bump_version >>> bump_version('1.2.2') '1.2.3' >>> bump_version('1.2.3', position=1) '1.3' >>> bump_version('1.3.4', position=0) '2.0' >>> bump_version('1.2.3', prerelease='a') '1.2.4a0' >>> bump_version('1.2.4a0', pre_release='a') '1.2.4a1' >>> bump_version('1.2.4a1', pre_release='b') '1.2.4b0' >>> bump_version('1.2.4a1') '1.2.4' >>> bump_version('1.2.4b0') '1.2.4' >>> bump_version('2.1.3', position=1, pre_release='a') '2.2a0' >>> bump_version('1.2b0', position=2) '1.2.1' """ ver_info = _build_version_info(version) position = _build_version_bump_position(position) bump_type = _build_version_bump_type(position, pre_release) # noinspection PyUnusedLocal hold: List[Union[int, str]] = [] if bump_type == _BUMP_VERSION_MAJOR: hold = [ver_info.major.num + 1, 0] elif bump_type in _BUMP_VERSION_MINORS: if bump_type == _BUMP_VERSION_MINOR: if ver_info.minor.pre_txt: hold = [ver_info.major.num, ver_info.minor.num] else: hold = [ver_info.major.num, ver_info.minor.num + 1] else: if bump_type == _BUMP_VERSION_MINOR_ALPHA: if ver_info.minor.pre_txt == 'a': part = '%sa%s' % ( ver_info.minor.num, ver_info.minor.pre_num + 1 ) else: part = '{}a0'.format(ver_info.minor.num + 1) else: if ver_info.minor.pre_txt == 'a': part = '{}b0'.format(ver_info.minor.num) elif ver_info.minor.pre_txt == 'b': part = '%sb%s' % ( ver_info.minor.num, ver_info.minor.pre_num + 1 ) else: part = '{}b0'.format(ver_info.minor.num + 1) hold = [ver_info.major.num, part] else: if bump_type == _BUMP_VERSION_PATCH: if ver_info.patch.pre_txt: hold = [ ver_info.major.num, ver_info.minor.num, ver_info.patch.num ] else: hold = [ ver_info.major.num, ver_info.minor.num, ver_info.patch.num + 1 ] else: if bump_type == _BUMP_VERSION_PATCH_ALPHA: if ver_info.patch.pre_txt == 'a': part = '%sa%s' % ( ver_info.patch.num, ver_info.patch.pre_num + 1 ) else: part = '{}a0'.format(ver_info.patch.num + 1) else: if ver_info.patch.pre_txt == 'a': part = '{}b0'.format(ver_info.patch.num) elif ver_info.patch.pre_txt == 'b': part = '%sb%s' % ( ver_info.patch.num, ver_info.patch.pre_num + 1 ) else: part = '{}b0'.format(ver_info.patch.num + 1) hold = [ver_info.major.num, ver_info.minor.num, part] out = '.'.join(map(str, hold)) return out