Improve version number incrementing by rewriting version module

dev
Andrew Cassidy 3 years ago
parent 17a17fea41
commit 8394fbfd94

@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file
### Changed
- improved version header parsing
- improved version number incrementing. It can now handle other text surrounding a pep440-compliant version number, which will not be modified
## 0.3.3 - 2021-04-27

@ -1,5 +1,6 @@
import os.path
import unittest
import traceback
import git
from click.testing import CliRunner
@ -9,7 +10,9 @@ from yaclog.cli.__main__ import cli
def check_result(runner, result, success: bool = True):
runner.assertEqual((result.exit_code == 0), success, f'output: {result.output}\ntraceback: {result.exc_info}')
runner.assertEqual((result.exit_code == 0), success,
f'\noutput: {result.output}\ntraceback: ' + ''.join(
traceback.format_exception(*result.exc_info)))
class TestCreation(unittest.TestCase):

@ -269,7 +269,7 @@ class Changelog:
# strip whitespace from header
self.header = markdown.join(header_segments)
def write(self, path: os.PathLike = None) -> None:
def write(self, path=None) -> None:
"""
Write a markdown changelog to a file.
@ -311,7 +311,7 @@ class Changelog:
def current_version(self, released: Optional[bool] = None, new_version: bool = False,
new_version_name: str = 'Unreleased') -> VersionEntry:
"""
Get the current version entry from the changelog
Get the current version from the changelog
:param released: if the returned version should be a released version,
an unreleased version, or ``None`` to return the most recent

@ -179,33 +179,34 @@ def entry(obj: Changelog, bullets, paragraphs, section_name, version_name):
@cli.command(short_help='Release versions.')
@click.option('-v', '--version', 'v_flag', type=str, default=None, help='The new version number to use.')
@click.option('-M', '--major', 'v_flag', flag_value='+M', help='Increment major version number.')
@click.option('-m', '--minor', 'v_flag', flag_value='+m', help='Increment minor version number.')
@click.option('-p', '--patch', 'v_flag', flag_value='+p', help='Increment patch number.')
@click.option('-a', '--alpha', 'v_flag', flag_value='+a', help='Increment alpha version number.')
@click.option('-b', '--beta', 'v_flag', flag_value='+b', help='Increment beta version number.')
@click.option('-r', '--rc', 'v_flag', flag_value='+rc', help='Increment release candidate version number.')
@click.option('-v', '--version', 'version_name', type=str, default=None, help='The new version number to use.')
@click.option('-M', '--major', 'rel_seg', flag_value=0, default=None, help='Increment major version number.')
@click.option('-m', '--minor', 'rel_seg', flag_value=1, help='Increment minor version number.')
@click.option('-p', '--patch', 'rel_seg', flag_value=2, help='Increment patch number.')
@click.option('-a', '--alpha', 'pre_seg', flag_value='a', default=None, help='Increment alpha version number.')
@click.option('-b', '--beta', 'pre_seg', flag_value='b', help='Increment beta version number.')
@click.option('-r', '--rc', 'pre_seg', flag_value='rc', help='Increment release candidate version number.')
@click.option('-c', '--commit', is_flag=True, help='Create a git commit tagged with the new version number.')
@click.pass_obj
def release(obj: Changelog, v_flag, commit):
def release(obj: Changelog, version_name, rel_seg, pre_seg, commit):
"""Release versions in the changelog and increment their version numbers"""
matches = [v for v in obj.versions if v.name.lower() != 'unreleased']
if len(matches) == 0:
try:
version = obj.current_version(released=True).name
except ValueError:
version = '0.0.0'
else:
version = matches[0].name
cur_version = obj.versions[0]
cur_version = obj.current_version()
new_name = version
old_name = cur_version.name
if v_flag:
if v_flag[0] == '+':
new_name = yaclog.version.increment_version(version, v_flag)
else:
new_name = v_flag
if version_name:
new_name = version_name
if rel_seg is not None or pre_seg is not None:
new_name = yaclog.version.increment_version(new_name, rel_seg, pre_seg)
if yaclog.version.is_release(cur_version.name):
if new_name != old_name:
if yaclog.version.is_release(old_name):
click.confirm(f'Rename release version "{cur_version.name}" to "{new_name}"?', abort=True)
cur_version.name = new_name

@ -14,19 +14,37 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from packaging.version import Version, InvalidVersion
import re
from typing import Optional, Tuple
from packaging.version import Version, VERSION_PATTERN
version_regex = re.compile(VERSION_PATTERN, re.VERBOSE | re.IGNORECASE)
def extract_version(version_str: str) -> Tuple[Optional[Version], int, int]:
"""
Extracts a PEP440 version object from a string which may have other text
:param version_str: The input string to extract from
:return: A tuple of (version, start, end), where start and end are the span of the version in the original string
"""
match = version_regex.search(version_str)
if not match:
return None, -1, -1
return (Version(match[0]),) + match.span()
def is_release(version: str) -> bool:
try:
v = Version(version)
return not (v.is_devrelease or v.is_prerelease)
except InvalidVersion:
return False
def increment_version(version_str: str, rel_seg: int = None, pre_seg: str = None) -> str:
"""
Increment the PEP440 version number in a string
def increment_version(version: str, mode: str) -> str:
v = Version(version)
:param version_str: The input string to manipulate
:param rel_seg: Which segment of the "release" value to increment, if any
:param pre_seg: Which kind of prerelease to use, if any
:return: The original string with the version number incremented
"""
v, *span = extract_version(version_str)
epoch = v.epoch
release = v.release
pre = v.pre
@ -34,27 +52,23 @@ def increment_version(version: str, mode: str) -> str:
dev = v.dev
local = v.local
if mode == '+M':
release = (release[0] + 1,) + ((0,) * len(release[1:]))
pre = post = dev = None
elif mode == '+m':
release = (release[0], release[1] + 1) + ((0,) * len(release[2:]))
pre = post = dev = None
elif mode == '+p':
release = (release[0], release[1], release[2] + 1) + ((0,) * len(release[3:]))
pre = post = dev = None
elif mode in ['+a', '+b', '+rc']:
if pre[0] == mode[1:]:
pre = (mode[1:], pre[1] + 1)
if rel_seg is not None:
if len(release) <= rel_seg:
release += (0,) * (1 + rel_seg - len(release))
release = release[0:rel_seg] + (release[rel_seg] + 1,) + (0,) * (len(release) - rel_seg - 1)
if pre_seg is not None:
if pre and pre[0] == pre_seg:
pre = (pre_seg, pre[1] + 1)
else:
pre = (mode[1:], 0)
else:
raise IndexError(f'Unknown mode {mode}')
pre = (pre_seg, 1)
return join_version(epoch, release, pre, post, dev, local)
new_v = join_version(epoch, release, pre, post, dev, local)
return version_str[0:span[0]] + new_v + version_str[span[1]:]
def join_version(epoch, release, pre, post, dev, local) -> str:
"""Join multiple segments of a PEP440 version"""
parts = []
# Epoch
@ -83,3 +97,10 @@ def join_version(epoch, release, pre, post, dev, local) -> str:
return "".join(parts)
def is_release(version_str: str) -> bool:
"""Check if a version string is a release version or not. Returns false if a PEP440 version could not be found"""
v, *span = extract_version(version_str)
if v:
return not (v.is_devrelease or v.is_prerelease)
else:
return False

Loading…
Cancel
Save