formatting updates

This commit is contained in:
Manuel Barkhau 2018-11-06 21:45:33 +01:00
parent 3385f2d675
commit 1129de0beb
11 changed files with 153 additions and 128 deletions

View file

@ -3,9 +3,6 @@
# #
# Copyright (c) 2018 Manuel Barkhau (@mbarkhau) - MIT License # Copyright (c) 2018 Manuel Barkhau (@mbarkhau) - MIT License
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
"""PyCalVer: Automatic CalVer Versioning for Python Packages."""
import os
__version__ = "v201809.0002-beta" __version__ = "v201809.0002-beta"
DEBUG = os.environ.get("PYDEBUG", "0") == "1"

View file

@ -4,6 +4,11 @@
# #
# Copyright (c) 2018 Manuel Barkhau (@mbarkhau) - MIT License # Copyright (c) 2018 Manuel Barkhau (@mbarkhau) - MIT License
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
"""
CLI module for pycalver.
Provided subcommands: show, init, incr, bump
"""
import io import io
import os import os
@ -24,13 +29,13 @@ log = logging.getLogger("pycalver.__main__")
def _init_loggers(verbose: bool) -> None: def _init_loggers(verbose: bool) -> None:
if DEBUG: if DEBUG:
log_formatter = logging.Formatter('%(levelname)s - %(name)s - %(message)s') log_formatter = logging.Formatter("%(levelname)s - %(name)s - %(message)s")
log_level = logging.DEBUG log_level = logging.DEBUG
elif verbose: elif verbose:
log_formatter = logging.Formatter('%(levelname)s - %(message)s') log_formatter = logging.Formatter("%(levelname)s - %(message)s")
log_level = logging.INFO log_level = logging.INFO
else: else:
log_formatter = logging.Formatter('%(message)s') log_formatter = logging.Formatter("%(message)s")
log_level = logging.WARNING log_level = logging.WARNING
loggers = [log, vcs.log, parse.log, config.log, rewrite.log, version.log] loggers = [log, vcs.log, parse.log, config.log, rewrite.log, version.log]
@ -47,11 +52,12 @@ def _init_loggers(verbose: bool) -> None:
@click.group() @click.group()
def cli(): def cli():
"""parse and update project versions automatically.""" """Parse and update project versions."""
@cli.command() @cli.command()
def show() -> None: def show() -> None:
"""Show current version."""
_init_loggers(verbose=False) _init_loggers(verbose=False)
cfg: config.MaybeConfig = config.parse() cfg: config.MaybeConfig = config.parse()
@ -69,6 +75,7 @@ def show() -> None:
"--release", default=None, metavar="<name>", help="Override release name of current_version" "--release", default=None, metavar="<name>", help="Override release name of current_version"
) )
def incr(old_version: str, release: str = None) -> None: def incr(old_version: str, release: str = None) -> None:
"""Increment a version number for demo purposes."""
_init_loggers(verbose=False) _init_loggers(verbose=False)
if release and release not in parse.VALID_RELESE_VALUES: if release and release not in parse.VALID_RELESE_VALUES:
@ -76,7 +83,7 @@ def incr(old_version: str, release: str = None) -> None:
log.error(f"Valid arguments are: {', '.join(parse.VALID_RELESE_VALUES)}") log.error(f"Valid arguments are: {', '.join(parse.VALID_RELESE_VALUES)}")
sys.exit(1) sys.exit(1)
new_version = version.bump(old_version, release=release) new_version = version.incr(old_version, release=release)
new_version_nfo = parse.parse_version_info(new_version) new_version_nfo = parse.parse_version_info(new_version)
print("PyCalVer Version:", new_version) print("PyCalVer Version:", new_version)
@ -88,7 +95,7 @@ def incr(old_version: str, release: str = None) -> None:
"--dry", default=False, is_flag=True, help="Display diff of changes, don't rewrite files." "--dry", default=False, is_flag=True, help="Display diff of changes, don't rewrite files."
) )
def init(dry: bool) -> None: def init(dry: bool) -> None:
"""Initialize [pycalver] configuration in setup.cfg""" """Initialize [pycalver] configuration."""
_init_loggers(verbose=False) _init_loggers(verbose=False)
cfg : config.MaybeConfig = config.parse() cfg : config.MaybeConfig = config.parse()
@ -138,6 +145,7 @@ def init(dry: bool) -> None:
def bump( def bump(
release: str, verbose: bool, dry: bool, commit: bool, tag: bool, allow_dirty: bool release: str, verbose: bool, dry: bool, commit: bool, tag: bool, allow_dirty: bool
) -> None: ) -> None:
"""Increment the current version string and update project files."""
_init_loggers(verbose) _init_loggers(verbose)
if release and release not in parse.VALID_RELESE_VALUES: if release and release not in parse.VALID_RELESE_VALUES:
@ -152,7 +160,7 @@ def bump(
sys.exit(1) sys.exit(1)
old_version = cfg.current_version old_version = cfg.current_version
new_version = version.bump(old_version, release=release) new_version = version.incr(old_version, release=release)
log.info(f"Old Version: {old_version}") log.info(f"Old Version: {old_version}")
log.info(f"New Version: {new_version}") log.info(f"New Version: {new_version}")
@ -163,15 +171,17 @@ def bump(
file_patterns = cfg.file_patterns file_patterns = cfg.file_patterns
filepaths = set(file_patterns.keys()) filepaths = set(file_patterns.keys())
_vcs = vcs.get_vcs()
if _vcs is None:
log.warn("Version Control System not found, aborting commit.")
else:
_vcs.assert_not_dirty(filepaths, allow_dirty)
rewrite.rewrite(new_version, file_patterns, dry, verbose) rewrite.rewrite(new_version, file_patterns, dry, verbose)
if dry or not commit or _vcs is None: try:
_vcs = vcs.get_vcs()
except OSError:
log.warn("Version Control System not found, aborting commit.")
return
_vcs.assert_not_dirty(filepaths, allow_dirty)
if dry or not commit:
return return
# TODO (mb 2018-09-04): add files and commit # TODO (mb 2018-09-04): add files and commit

View file

@ -1,7 +1,7 @@
# This file is part of the pycalver project # This file is part of the pycalver project
# https://github.com/mbarkhau/pycalver # https://github.com/mbarkhau/pycalver
# #
# (C) 2018 Manuel Barkhau (@mbarkhau) # Copyright (c) 2018 Manuel Barkhau (@mbarkhau) - MIT License
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
import io import io

View file

@ -1,7 +1,7 @@
# This file is part of the pycalver project # This file is part of the pycalver project
# https://github.com/mbarkhau/pycalver # https://github.com/mbarkhau/pycalver
# #
# (C) 2018 Manuel Barkhau (@mbarkhau) # Copyright (c) 2018 Manuel Barkhau (@mbarkhau) - MIT License
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
""" """

View file

@ -1,7 +1,7 @@
# This file is part of the pycalver project # This file is part of the pycalver project
# https://github.com/mbarkhau/pycalver # https://github.com/mbarkhau/pycalver
# #
# (C) 2018 Manuel Barkhau (@mbarkhau) # Copyright (c) 2018 Manuel Barkhau (@mbarkhau) - MIT License
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
import re import re
@ -17,7 +17,7 @@ VALID_RELESE_VALUES = ("alpha", "beta", "dev", "rc", "post")
# https://regex101.com/r/fnj60p/10 # https://regex101.com/r/fnj60p/10
PYCALVER_RE: typ.Pattern[str] = re.compile(r""" PYCALVER_PATTERN = r"""
\b \b
(?P<version> (?P<version>
(?P<calver> (?P<calver>
@ -34,8 +34,19 @@ PYCALVER_RE: typ.Pattern[str] = re.compile(r"""
(?:alpha|beta|dev|rc|post) (?:alpha|beta|dev|rc|post)
)? )?
)(?:\s|$) )(?:\s|$)
""", flags=re.VERBOSE) """
PYCALVER_RE: typ.Pattern[str] = re.compile(PYCALVER_PATTERN, flags=re.VERBOSE)
PATTERN_ESCAPES = [
("\u005c", "\u005c\u005c"),
("-" , "\u005c-"),
("." , "\u005c."),
("+" , "\u005c+"),
("*" , "\u005c*"),
("[" , "\u005c["),
("(" , "\u005c("),
]
# NOTE (mb 2018-09-03): These are matchers for parts, which are # NOTE (mb 2018-09-03): These are matchers for parts, which are
# used in the patterns, they're not for validation. This means # used in the patterns, they're not for validation. This means
@ -45,32 +56,32 @@ PYCALVER_RE: typ.Pattern[str] = re.compile(r"""
RE_PATTERN_PARTS = { RE_PATTERN_PARTS = {
"pep440_version" : r"\d{6}\.[1-9]\d*(a|b|dev|rc|post)?\d*", 'pep440_version': r"\d{6}\.[1-9]\d*(a|b|dev|rc|post)?\d*",
"version" : r"v\d{6}\.\d{4,}(\-(alpha|beta|dev|rc|post))?", 'version' : r"v\d{6}\.\d{4,}(\-(alpha|beta|dev|rc|post))?",
"calver" : r"v\d{6}", 'calver' : r"v\d{6}",
"build" : r"\.\d{4,}", 'build' : r"\.\d{4,}",
"release" : r"(\-(alpha|beta|dev|rc|post))?", 'release' : r"(\-(alpha|beta|dev|rc|post))?",
} }
class PatternMatch(typ.NamedTuple): class PatternMatch(typ.NamedTuple):
lineno : int # zero based lineno : int # zero based
line : str line : str
pattern : str pattern: str
span : typ.Tuple[int, int] span : typ.Tuple[int, int]
match : str match : str
class VersionInfo(typ.NamedTuple): class VersionInfo(typ.NamedTuple):
pep440_version : str pep440_version: str
version : str version : str
calver : str calver : str
year : str year : str
month : str month : str
build : str build : str
release : typ.Optional[str] release : typ.Optional[str]
def parse_version_info(version: str) -> VersionInfo: def parse_version_info(version: str) -> VersionInfo:
@ -81,20 +92,17 @@ def parse_version_info(version: str) -> VersionInfo:
return VersionInfo(pep440_version=pep440_version, **match.groupdict()) return VersionInfo(pep440_version=pep440_version, **match.groupdict())
def iter_pattern_matches(lines: typ.List[str], pattern: str) -> typ.Iterable[PatternMatch]: def _iter_pattern_matches(lines: typ.List[str], pattern: str) -> typ.Iterable[PatternMatch]:
# The pattern is escaped, so that everything besides the format # The pattern is escaped, so that everything besides the format
# string variables is treated literally. # string variables is treated literally.
pattern_re = re.compile(
pattern pattern_tmpl = pattern
.replace("\\", "\\\\")
.replace("-", "\\-") for char, escaped in PATTERN_ESCAPES:
.replace(".", "\\.") pattern_tmpl = pattern_tmpl.replace(char, escaped)
.replace("+", "\\+")
.replace("*", "\\*") pattern_str = pattern_tmpl.format(**RE_PATTERN_PARTS)
.replace("[", "\\[") pattern_re = re.compile(pattern_str)
.replace("(", "\\(")
.format(**RE_PATTERN_PARTS)
)
for lineno, line in enumerate(lines): for lineno, line in enumerate(lines):
match = pattern_re.search(line) match = pattern_re.search(line)
if match: if match:
@ -104,5 +112,5 @@ def iter_pattern_matches(lines: typ.List[str], pattern: str) -> typ.Iterable[Pat
def parse_patterns(lines: typ.List[str], patterns: typ.List[str]) -> typ.List[PatternMatch]: def parse_patterns(lines: typ.List[str], patterns: typ.List[str]) -> typ.List[PatternMatch]:
all_matches: typ.List[PatternMatch] = [] all_matches: typ.List[PatternMatch] = []
for pattern in patterns: for pattern in patterns:
all_matches.extend(iter_pattern_matches(lines, pattern)) all_matches.extend(_iter_pattern_matches(lines, pattern))
return all_matches return all_matches

View file

@ -1,12 +1,12 @@
# This file is part of the pycalver project # This file is part of the pycalver project
# https://github.com/mbarkhau/pycalver # https://github.com/mbarkhau/pycalver
# #
# (C) 2018 Manuel Barkhau (@mbarkhau) # Copyright (c) 2018 Manuel Barkhau (@mbarkhau) - MIT License
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
# #
# pycalver/vcs.py (this file) is based on code from the # pycalver/vcs.py (this file) is based on code from the
# bumpversion project: https://github.com/peritus/bumpversion # bumpversion project: https://github.com/peritus/bumpversion
# MIT License - (C) 2013-2014 Filip Noetzel # Copyright (c) 2013-2014 Filip Noetzel - MIT License
import os import os
import sys import sys
@ -83,9 +83,9 @@ class BaseVCS:
class Git(BaseVCS): class Git(BaseVCS):
_TEST_USABLE_COMMAND = ["git", "rev-parse", "--git-dir"] _TEST_USABLE_COMMAND = ["git", "rev-parse", '--git-dir']
_COMMIT_COMMAND = ["git", "commit" , "-F"] _COMMIT_COMMAND = ["git", "commit" , '-F']
_STATUS_COMMAND = ["git", "status" , "--porcelain"] _STATUS_COMMAND = ["git", "status" , '--porcelain']
@classmethod @classmethod
def tag(cls, name): def tag(cls, name):
@ -99,8 +99,8 @@ class Git(BaseVCS):
class Mercurial(BaseVCS): class Mercurial(BaseVCS):
_TEST_USABLE_COMMAND = ["hg", 'root'] _TEST_USABLE_COMMAND = ["hg", 'root']
_COMMIT_COMMAND = ["hg", "commit", "--logfile"] _COMMIT_COMMAND = ["hg", "commit", '--logfile']
_STATUS_COMMAND = ["hg", "status", "-mard"] _STATUS_COMMAND = ["hg", "status", '-mard']
@classmethod @classmethod
def tag(cls, name): def tag(cls, name):
@ -114,9 +114,10 @@ class Mercurial(BaseVCS):
VCS = [Git, Mercurial] VCS = [Git, Mercurial]
def get_vcs() -> typ.Optional[typ.Type[BaseVCS]]: def get_vcs() -> typ.Type[BaseVCS]:
"""Get appropriate sub"""
for vcs in VCS: for vcs in VCS:
if vcs.is_usable(): if vcs.is_usable():
return vcs return vcs
return None raise OSError("No such directory .hg/ or .git/ ")

View file

@ -1,8 +1,9 @@
# This file is part of the pycalver project # This file is part of the pycalver project
# https://github.com/mbarkhau/pycalver # https://github.com/mbarkhau/pycalver
# #
# (C) 2018 Manuel Barkhau (@mbarkhau) # Copyright (c) 2018 Manuel Barkhau (@mbarkhau) - MIT License
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
"""Functions related to version string manipulation."""
import logging import logging
import typing as typ import typing as typ
@ -15,19 +16,26 @@ log = logging.getLogger("pycalver.version")
def current_calver() -> str: def current_calver() -> str:
"""Generate calver version string based on current date.
example result: "v201812"
"""
return dt.date.today().strftime("v%Y%m") return dt.date.today().strftime("v%Y%m")
def bump(old_version: str, *, release: str = None) -> str: def incr(old_version: str, *, release: str = None) -> str:
# old_version is assumed to be a valid calver string, """Increment a full PyCalVer version string.
# validated in pycalver.config.parse.
Old_version is assumed to be a valid calver string,
already validated in pycalver.config.parse.
"""
old_ver = parse.parse_version_info(old_version) old_ver = parse.parse_version_info(old_version)
new_calver = current_calver() new_calver = current_calver()
if old_ver.calver > new_calver: if old_ver.calver > new_calver:
log.warning( log.warning(
f"'version.bump' called with '{old_version}', " f"'version.incr' called with '{old_version}', "
+ f"which is from the future, " + f"which is from the future, "
+ f"maybe your system clock is out of sync." + f"maybe your system clock is out of sync."
) )

View file

@ -23,16 +23,20 @@ def test_parse_default_config():
def test_parse(tmpdir): def test_parse(tmpdir):
setup_path = tmpdir.mkdir("minimal").join("setup.cfg") setup_path = tmpdir.mkdir("minimal").join("setup.cfg")
setup_path.write("\n".join(( setup_path.write(
"[pycalver]", "\n".join(
"current_version = v201808.0001-dev", (
"commit = False", "[pycalver]",
"tag = False", "current_version = v201808.0001-dev",
"", "commit = False",
"[pycalver:file:setup.cfg]", "tag = False",
"patterns = ", "",
" current_version = {version}", "[pycalver:file:setup.cfg]",
))) "patterns = ",
" current_version = {version}",
)
)
)
cfg = config.parse(str(setup_path)) cfg = config.parse(str(setup_path))
@ -64,11 +68,15 @@ def test_parse_empty_config(tmpdir):
def test_parse_missing_version(tmpdir): def test_parse_missing_version(tmpdir):
setup_path = tmpdir.mkdir("fail").join("setup.cfg") setup_path = tmpdir.mkdir("fail").join("setup.cfg")
setup_path.write("\n".join(( setup_path.write(
"[pycalver]", "\n".join(
# f"current_version = v201808.0001-dev", (
"commit = False", "[pycalver]",
))) # f"current_version = v201808.0001-dev",
"commit = False",
)
)
)
cfg = config.parse(str(setup_path)) cfg = config.parse(str(setup_path))
assert cfg is None assert cfg is None
@ -76,11 +84,7 @@ def test_parse_missing_version(tmpdir):
def test_parse_invalid_version(tmpdir): def test_parse_invalid_version(tmpdir):
setup_path = tmpdir.mkdir("fail").join("setup.cfg") setup_path = tmpdir.mkdir("fail").join("setup.cfg")
setup_path.write("\n".join(( setup_path.write("\n".join(("[pycalver]", "current_version = 0.1.0", "commit = False")))
"[pycalver]",
"current_version = 0.1.0",
"commit = False",
)))
cfg = config.parse(str(setup_path)) cfg = config.parse(str(setup_path))
assert cfg is None assert cfg is None

View file

@ -18,7 +18,7 @@ def test_next_id_overflow():
def test_next_id_random(): def test_next_id_random():
for i in range(1000): for i in range(1000):
prev_id = str(random.randint(1, 100000)) prev_id = str(random.randint(1, 100_000))
try: try:
next_id = lex_id.next_id(prev_id) next_id = lex_id.next_id(prev_id)
assert prev_id < next_id assert prev_id < next_id
@ -27,10 +27,10 @@ def test_next_id_random():
def test_ord_val(): def test_ord_val():
assert lex_id.ord_val("1") == 1 assert lex_id.ord_val("1" ) == 1
assert lex_id.ord_val("01") == 1 assert lex_id.ord_val("01" ) == 1
assert lex_id.ord_val("02") == 2 assert lex_id.ord_val("02" ) == 2
assert lex_id.ord_val("09") == 9 assert lex_id.ord_val("09" ) == 9
assert lex_id.ord_val("110") == 10 assert lex_id.ord_val("110") == 10
@ -39,13 +39,13 @@ def test_main(capsys):
captured = capsys.readouterr() captured = capsys.readouterr()
assert len(captured.err) == 0 assert len(captured.err) == 0
lines = iter(captured.out.splitlines()) lines = iter(captured.out.splitlines())
header = next(lines) header = next(lines)
assert "lexical" in header assert "lexical" in header
assert "numerical" in header assert "numerical" in header
ids = [] ids = []
ord_vals = [] ord_vals = []
for line in lines: for line in lines:
@ -60,5 +60,5 @@ def test_main(capsys):
ord_vals.append(int(_ord_val.strip())) ord_vals.append(int(_ord_val.strip()))
assert len(ids) > 0 assert len(ids) > 0
assert sorted(ids) == ids assert sorted(ids ) == ids
assert sorted(ord_vals) == ord_vals assert sorted(ord_vals) == ord_vals

View file

@ -3,13 +3,13 @@ from pycalver import rewrite
def test_rewrite_lines(): def test_rewrite_lines():
old_lines = [ old_lines = [
'# This file is part of the pycalver project', "# This file is part of the pycalver project",
'# https://github.com/mbarkhau/pycalver', "# https://github.com/mbarkhau/pycalver",
'#', "#",
'# (C) 2018 Manuel Barkhau (@mbarkhau)', "# (C) 2018 Manuel Barkhau (@mbarkhau)",
'# SPDX-License-Identifier: MIT', "# SPDX-License-Identifier: MIT",
'', '',
'import os', "import os",
'', '',
'__version__ = "v201809.0002-beta"', '__version__ = "v201809.0002-beta"',
'DEBUG = os.environ.get("PYDEBUG", "0") == "1"', 'DEBUG = os.environ.get("PYDEBUG", "0") == "1"',

View file

@ -12,46 +12,43 @@ def test_current_calver():
def test_bump_beta(): def test_bump_beta():
calver = version.current_calver() calver = version.current_calver()
cur_version = calver + ".0001-beta" cur_version = calver + ".0001-beta"
assert cur_version < version.bump(cur_version) assert cur_version < version.incr(cur_version)
assert version.bump(cur_version).endswith("-beta") assert version.incr(cur_version).endswith("-beta")
assert version.bump(cur_version, release="alpha").endswith("-alpha") assert version.incr(cur_version, release="alpha").endswith("-alpha")
assert version.bump(cur_version, release="final").endswith("0002") assert version.incr(cur_version, release="final").endswith("0002")
def test_bump_final(): def test_bump_final():
calver = version.current_calver() calver = version.current_calver()
cur_version = calver + ".0001" cur_version = calver + ".0001"
assert cur_version < version.bump(cur_version) assert cur_version < version.incr(cur_version)
assert version.bump(cur_version, release="alpha").endswith("-alpha") assert version.incr(cur_version, release="alpha").endswith("-alpha")
assert version.bump(cur_version, release="final").endswith("0002") assert version.incr(cur_version, release="final").endswith("0002")
assert version.bump(cur_version).endswith("0002") assert version.incr(cur_version).endswith("0002")
def test_bump_future(): def test_bump_future():
future_date = dt.datetime.today() + dt.timedelta(days=300) future_date = dt.datetime.today() + dt.timedelta(days=300)
future_calver = future_date.strftime("v%Y%m") future_calver = future_date.strftime("v%Y%m")
cur_version = future_calver + ".0001" cur_version = future_calver + ".0001"
assert cur_version < version.bump(cur_version) assert cur_version < version.incr(cur_version)
def test_bump_random(): def test_bump_random(monkeypatch):
cur_date = dt.date.today() cur_date = dt.date.today()
cur_version = cur_date.strftime("v%Y%m") + ".0001-dev" cur_version = cur_date.strftime("v%Y%m") + ".0001-dev"
def _mock_current_calver(): def _mock_current_calver():
return cur_date.strftime("v%Y%m") return cur_date.strftime("v%Y%m")
_orig_current_calver = version.current_calver monkeypatch.setattr(version, 'current_calver', _mock_current_calver)
version.current_calver = _mock_current_calver
try: for i in range(1000):
for i in range(1000): cur_date += dt.timedelta(days=int((1 + random.random()) ** 10))
cur_date += dt.timedelta(days=int((1 + random.random()) ** 10)) new_version = version.incr(
new_version = version.bump(cur_version, release=random.choice([ cur_version, release=random.choice([None, "alpha", "beta", "rc", 'final'])
None, "alpha", "beta", "rc", "final" )
])) assert cur_version < new_version
assert cur_version < new_version cur_version = new_version
cur_version = new_version
finally:
version.current_calver = _orig_current_calver