mirror of
https://github.com/TECHNOFAB11/bumpver.git
synced 2025-12-12 14:30:09 +01:00
Code quality updates
This commit is contained in:
parent
95234dfd0b
commit
54a681bf34
14 changed files with 413 additions and 187 deletions
|
|
@ -84,7 +84,7 @@ regular expression:
|
||||||
import re
|
import re
|
||||||
|
|
||||||
# https://regex101.com/r/fnj60p/10
|
# https://regex101.com/r/fnj60p/10
|
||||||
pycalver_re = re.compile(r"""
|
PYCALVER_PATTERN = r"""
|
||||||
\b
|
\b
|
||||||
(?P<version>
|
(?P<version>
|
||||||
(?P<calver>
|
(?P<calver>
|
||||||
|
|
@ -101,10 +101,11 @@ pycalver_re = re.compile(r"""
|
||||||
(?:alpha|beta|dev|rc|post)
|
(?:alpha|beta|dev|rc|post)
|
||||||
)?
|
)?
|
||||||
)(?:\s|$)
|
)(?:\s|$)
|
||||||
""", flags=re.VERBOSE)
|
"""
|
||||||
|
PYCALVER_RE = re.compile(PYCALVER_PATTERN, flags=re.VERBOSE)
|
||||||
|
|
||||||
version_str = "v201712.0001-alpha"
|
version_str = "v201712.0001-alpha"
|
||||||
version_info = pycalver_re.match(version_str).groupdict()
|
version_info = PYCALVER_RE.match(version_str).groupdict()
|
||||||
|
|
||||||
assert version_info == {
|
assert version_info == {
|
||||||
"version" : "v201712.0001-alpha",
|
"version" : "v201712.0001-alpha",
|
||||||
|
|
@ -116,7 +117,7 @@ assert version_info == {
|
||||||
}
|
}
|
||||||
|
|
||||||
version_str = "v201712.0033"
|
version_str = "v201712.0033"
|
||||||
version_info = pycalver_re.match(version_str).groupdict()
|
version_info = PYCALVER_RE.match(version_str).groupdict()
|
||||||
|
|
||||||
assert version_info == {
|
assert version_info == {
|
||||||
"version" : "v201712.0033",
|
"version" : "v201712.0033",
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,8 @@ ENV CONDA_DIR /opt/conda
|
||||||
ENV PATH $CONDA_DIR/bin:$PATH
|
ENV PATH $CONDA_DIR/bin:$PATH
|
||||||
ENV SHELL /bin/bash
|
ENV SHELL /bin/bash
|
||||||
|
|
||||||
RUN apk add --no-cache bash make sed grep gawk curl git bzip2 unzip
|
RUN apk add --no-cache bash make sed grep gawk curl bzip2 unzip
|
||||||
|
RUN apk add --no-cache git mercurial
|
||||||
|
|
||||||
CMD [ "/bin/bash" ]
|
CMD [ "/bin/bash" ]
|
||||||
|
|
||||||
|
|
|
||||||
1
makefile
1
makefile
|
|
@ -320,6 +320,7 @@ check: fmt lint mypy test
|
||||||
env:
|
env:
|
||||||
@bash --init-file <(echo '\
|
@bash --init-file <(echo '\
|
||||||
source $$HOME/.bashrc; \
|
source $$HOME/.bashrc; \
|
||||||
|
source $(CONDA_ROOT)/etc/profile.d/conda.sh \
|
||||||
export ENV=${ENV-dev}; \
|
export ENV=${ENV-dev}; \
|
||||||
export PYTHONPATH="src/:vendor/:$$PYTHONPATH"; \
|
export PYTHONPATH="src/:vendor/:$$PYTHONPATH"; \
|
||||||
conda activate $(DEV_ENV_NAME) \
|
conda activate $(DEV_ENV_NAME) \
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,8 @@ ignore =
|
||||||
# D101
|
# D101
|
||||||
# Missing docstring on __init__
|
# Missing docstring on __init__
|
||||||
D107
|
D107
|
||||||
|
# No blank lines allowed after function docstring
|
||||||
|
D202
|
||||||
# First line should be in imperative mood
|
# First line should be in imperative mood
|
||||||
D401
|
D401
|
||||||
select = A,AAA,D,C,E,F,W,H,B,D212,D404,D405,D406,B901,B950
|
select = A,AAA,D,C,E,F,W,H,B,D212,D404,D405,D406,B901,B950
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ _VERBOSE = 0
|
||||||
log = logging.getLogger("pycalver.cli")
|
log = logging.getLogger("pycalver.cli")
|
||||||
|
|
||||||
|
|
||||||
def _init_loggers(verbose: int = 0) -> None:
|
def _init_logging(verbose: int = 0) -> None:
|
||||||
if verbose >= 2:
|
if verbose >= 2:
|
||||||
log_format = "%(asctime)s.%(msecs)03d %(levelname)-7s %(name)-15s - %(message)s"
|
log_format = "%(asctime)s.%(msecs)03d %(levelname)-7s %(name)-15s - %(message)s"
|
||||||
log_level = logging.DEBUG
|
log_level = logging.DEBUG
|
||||||
|
|
@ -42,7 +42,7 @@ def _init_loggers(verbose: int = 0) -> None:
|
||||||
log_level = logging.WARNING
|
log_level = logging.WARNING
|
||||||
|
|
||||||
logging.basicConfig(level=log_level, format=log_format, datefmt="%Y-%m-%dT%H:%M:%S")
|
logging.basicConfig(level=log_level, format=log_format, datefmt="%Y-%m-%dT%H:%M:%S")
|
||||||
log.debug("Loggers initialized.")
|
log.debug("Logging initialized.")
|
||||||
|
|
||||||
|
|
||||||
@click.group()
|
@click.group()
|
||||||
|
|
@ -84,7 +84,7 @@ def _update_cfg_from_vcs(cfg: config.Config, fetch: bool) -> config.Config:
|
||||||
def show(verbose: int = 0, fetch: bool = True) -> None:
|
def show(verbose: int = 0, fetch: bool = True) -> None:
|
||||||
"""Show current version."""
|
"""Show current version."""
|
||||||
verbose = max(_VERBOSE, verbose)
|
verbose = max(_VERBOSE, verbose)
|
||||||
_init_loggers(verbose=verbose)
|
_init_logging(verbose=verbose)
|
||||||
|
|
||||||
cfg: config.MaybeConfig = config.parse()
|
cfg: config.MaybeConfig = config.parse()
|
||||||
if cfg is None:
|
if cfg is None:
|
||||||
|
|
@ -106,7 +106,7 @@ def show(verbose: int = 0, fetch: bool = True) -> None:
|
||||||
def incr(old_version: str, verbose: int = 0, release: str = None) -> None:
|
def incr(old_version: str, verbose: int = 0, release: str = None) -> None:
|
||||||
"""Increment a version number for demo purposes."""
|
"""Increment a version number for demo purposes."""
|
||||||
verbose = max(_VERBOSE, verbose)
|
verbose = max(_VERBOSE, verbose)
|
||||||
_init_loggers(verbose)
|
_init_logging(verbose)
|
||||||
|
|
||||||
if release and release not in parse.VALID_RELESE_VALUES:
|
if release and release not in parse.VALID_RELESE_VALUES:
|
||||||
log.error(f"Invalid argument --release={release}")
|
log.error(f"Invalid argument --release={release}")
|
||||||
|
|
@ -114,7 +114,7 @@ def incr(old_version: str, verbose: int = 0, release: str = None) -> None:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
new_version = version.incr(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.VersionInfo.parse(new_version)
|
||||||
|
|
||||||
print("PyCalVer Version:", new_version)
|
print("PyCalVer Version:", new_version)
|
||||||
print("PEP440 Version:" , new_version_nfo.pep440_version)
|
print("PEP440 Version:" , new_version_nfo.pep440_version)
|
||||||
|
|
@ -128,7 +128,7 @@ def incr(old_version: str, verbose: int = 0, release: str = None) -> None:
|
||||||
def init(verbose: int = 0, dry: bool = False) -> None:
|
def init(verbose: int = 0, dry: bool = False) -> None:
|
||||||
"""Initialize [pycalver] configuration."""
|
"""Initialize [pycalver] configuration."""
|
||||||
verbose = max(_VERBOSE, verbose)
|
verbose = max(_VERBOSE, verbose)
|
||||||
_init_loggers(verbose)
|
_init_logging(verbose)
|
||||||
|
|
||||||
cfg : config.MaybeConfig = config.parse()
|
cfg : config.MaybeConfig = config.parse()
|
||||||
if cfg:
|
if cfg:
|
||||||
|
|
@ -174,6 +174,35 @@ def _assert_not_dirty(vcs, filepaths: typ.Set[str], allow_dirty: bool):
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def _bump(cfg: config.Config, new_version: str, allow_dirty: bool = False) -> None:
|
||||||
|
_vcs: typ.Optional[vcs.VCS]
|
||||||
|
|
||||||
|
try:
|
||||||
|
_vcs = vcs.get_vcs()
|
||||||
|
except OSError:
|
||||||
|
log.warn("Version Control System not found, aborting commit.")
|
||||||
|
_vcs = None
|
||||||
|
|
||||||
|
filepaths = set(cfg.file_patterns.keys())
|
||||||
|
|
||||||
|
if _vcs:
|
||||||
|
_assert_not_dirty(_vcs, filepaths, allow_dirty)
|
||||||
|
|
||||||
|
rewrite.rewrite(new_version, cfg.file_patterns)
|
||||||
|
|
||||||
|
if _vcs is None or not cfg.commit:
|
||||||
|
return
|
||||||
|
|
||||||
|
for filepath in filepaths:
|
||||||
|
_vcs.add(filepath)
|
||||||
|
|
||||||
|
_vcs.commit(f"bump version to {new_version}")
|
||||||
|
|
||||||
|
if cfg.tag:
|
||||||
|
_vcs.tag(new_version)
|
||||||
|
_vcs.push(new_version)
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
@click.option("-v", "--verbose" , count=True , help="Control log level. -vv for debug level.")
|
@click.option("-v", "--verbose" , count=True , help="Control log level. -vv for debug level.")
|
||||||
@click.option('-f', "--fetch/--no-fetch", is_flag=True, default=True)
|
@click.option('-f', "--fetch/--no-fetch", is_flag=True, default=True)
|
||||||
|
|
@ -202,7 +231,7 @@ def bump(
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Increment the current version string and update project files."""
|
"""Increment the current version string and update project files."""
|
||||||
verbose = max(_VERBOSE, verbose)
|
verbose = max(_VERBOSE, verbose)
|
||||||
_init_loggers(verbose)
|
_init_logging(verbose)
|
||||||
|
|
||||||
if release and release not in parse.VALID_RELESE_VALUES:
|
if release and release not in parse.VALID_RELESE_VALUES:
|
||||||
log.error(f"Invalid argument --release={release}")
|
log.error(f"Invalid argument --release={release}")
|
||||||
|
|
@ -223,33 +252,10 @@ def bump(
|
||||||
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}")
|
||||||
|
|
||||||
|
if dry or verbose:
|
||||||
|
print(rewrite.diff(new_version, cfg.file_patterns))
|
||||||
|
|
||||||
if dry:
|
if dry:
|
||||||
log.info("Running with '--dry', showing diffs instead of updating files.")
|
|
||||||
|
|
||||||
file_patterns = cfg.file_patterns
|
|
||||||
filepaths = set(file_patterns.keys())
|
|
||||||
|
|
||||||
_vcs: typ.Optional[vcs.VCS]
|
|
||||||
|
|
||||||
try:
|
|
||||||
_vcs = vcs.get_vcs()
|
|
||||||
except OSError:
|
|
||||||
log.warn("Version Control System not found, aborting commit.")
|
|
||||||
_vcs = None
|
|
||||||
|
|
||||||
# if _vcs:
|
|
||||||
# _assert_not_dirty(_vcs, filepaths, allow_dirty)
|
|
||||||
|
|
||||||
rewrite.rewrite(new_version, file_patterns, dry=dry, verbose=verbose)
|
|
||||||
|
|
||||||
if dry or _vcs is None or not cfg.commit:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
for filepath in filepaths:
|
_bump(cfg, new_version, allow_dirty)
|
||||||
_vcs.add(filepath)
|
|
||||||
|
|
||||||
_vcs.commit(f"bump version to {new_version}")
|
|
||||||
|
|
||||||
if cfg.tag:
|
|
||||||
_vcs.tag(new_version)
|
|
||||||
_vcs.push(new_version)
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018 Manuel Barkhau (@mbarkhau) - MIT License
|
# Copyright (c) 2018 Manuel Barkhau (@mbarkhau) - MIT License
|
||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
"""Parsing code for setup.cfg or pycalver.cfg"""
|
"""Parse setup.cfg or pycalver.cfg files."""
|
||||||
|
|
||||||
import io
|
import io
|
||||||
import os
|
import os
|
||||||
|
|
@ -18,15 +18,18 @@ from .parse import PYCALVER_RE
|
||||||
|
|
||||||
log = logging.getLogger("pycalver.config")
|
log = logging.getLogger("pycalver.config")
|
||||||
|
|
||||||
|
PatternsByFilePath = typ.Dict[str, typ.List[str]]
|
||||||
|
|
||||||
|
|
||||||
class Config(typ.NamedTuple):
|
class Config(typ.NamedTuple):
|
||||||
|
"""Represents a parsed config."""
|
||||||
|
|
||||||
current_version: str
|
current_version: str
|
||||||
|
|
||||||
tag : bool
|
tag : bool
|
||||||
commit: bool
|
commit: bool
|
||||||
|
|
||||||
file_patterns: typ.Dict[str, typ.List[str]]
|
file_patterns: PatternsByFilePath
|
||||||
|
|
||||||
def _debug_str(self) -> str:
|
def _debug_str(self) -> str:
|
||||||
cfg_str_parts = [
|
cfg_str_parts = [
|
||||||
|
|
@ -46,6 +49,12 @@ class Config(typ.NamedTuple):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def pep440_version(self) -> str:
|
def pep440_version(self) -> str:
|
||||||
|
"""Derive pep440 compliant version string from PyCalVer version string.
|
||||||
|
|
||||||
|
>>> cfg = Config("v201811.0007-beta", True, True, [])
|
||||||
|
>>> cfg.pep440_version
|
||||||
|
'201811.7b0'
|
||||||
|
"""
|
||||||
return str(pkg_resources.parse_version(self.current_version))
|
return str(pkg_resources.parse_version(self.current_version))
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -131,26 +140,27 @@ def _parse_buffer(cfg_buffer: io.StringIO, config_filename: str = "<pycalver.cfg
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
|
|
||||||
def parse(config_filename: str = None) -> MaybeConfig:
|
def parse(config_filepath: str = None) -> MaybeConfig:
|
||||||
if config_filename is None:
|
"""Parse config file using configparser."""
|
||||||
|
if config_filepath is None:
|
||||||
if os.path.exists("pycalver.cfg"):
|
if os.path.exists("pycalver.cfg"):
|
||||||
config_filename = "pycalver.cfg"
|
config_filepath = "pycalver.cfg"
|
||||||
elif os.path.exists("setup.cfg"):
|
elif os.path.exists("setup.cfg"):
|
||||||
config_filename = "setup.cfg"
|
config_filepath = "setup.cfg"
|
||||||
else:
|
else:
|
||||||
log.error("File not found: pycalver.cfg or setup.cfg")
|
log.error("File not found: pycalver.cfg or setup.cfg")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if not os.path.exists(config_filename):
|
if not os.path.exists(config_filepath):
|
||||||
log.error(f"File not found: {config_filename}")
|
log.error(f"File not found: {config_filepath}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
cfg_buffer = io.StringIO()
|
cfg_buffer = io.StringIO()
|
||||||
with io.open(config_filename, mode="rt", encoding="utf-8") as fh:
|
with io.open(config_filepath, mode="rt", encoding="utf-8") as fh:
|
||||||
cfg_buffer.write(fh.read())
|
cfg_buffer.write(fh.read())
|
||||||
|
|
||||||
cfg_buffer.seek(0)
|
cfg_buffer.seek(0)
|
||||||
return _parse_buffer(cfg_buffer, config_filename)
|
return _parse_buffer(cfg_buffer, config_filepath)
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_CONFIG_BASE_STR = """
|
DEFAULT_CONFIG_BASE_STR = """
|
||||||
|
|
@ -190,6 +200,7 @@ patterns =
|
||||||
|
|
||||||
|
|
||||||
def default_config_lines() -> typ.List[str]:
|
def default_config_lines() -> typ.List[str]:
|
||||||
|
"""Generate initial default config based on PWD and current date."""
|
||||||
initial_version = dt.datetime.now().strftime("v%Y%m.0001-dev")
|
initial_version = dt.datetime.now().strftime("v%Y%m.0001-dev")
|
||||||
|
|
||||||
cfg_str = DEFAULT_CONFIG_BASE_STR.format(initial_version=initial_version)
|
cfg_str = DEFAULT_CONFIG_BASE_STR.format(initial_version=initial_version)
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,7 @@
|
||||||
# Copyright (c) 2018 Manuel Barkhau (@mbarkhau) - MIT License
|
# Copyright (c) 2018 Manuel Barkhau (@mbarkhau) - MIT License
|
||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
"""
|
"""A scheme for lexically ordered numerical ids.
|
||||||
This is a simple scheme for numerical ids which are ordered both
|
|
||||||
numerically and lexically.
|
|
||||||
|
|
||||||
Throughout the sequence this expression remains true, whether you
|
Throughout the sequence this expression remains true, whether you
|
||||||
are dealing with integers or strings:
|
are dealing with integers or strings:
|
||||||
|
|
@ -84,25 +82,56 @@ MINIMUM_ID = "0"
|
||||||
|
|
||||||
|
|
||||||
def next_id(prev_id: str) -> str:
|
def next_id(prev_id: str) -> str:
|
||||||
|
"""Generate next lexical id.
|
||||||
|
|
||||||
|
Increments by one and adds padding if required.
|
||||||
|
|
||||||
|
>>> next_id("0098")
|
||||||
|
'0099'
|
||||||
|
>>> next_id("0099")
|
||||||
|
'0100'
|
||||||
|
>>> next_id("0999")
|
||||||
|
'11000'
|
||||||
|
>>> next_id("11000")
|
||||||
|
'11001'
|
||||||
|
"""
|
||||||
|
|
||||||
num_digits = len(prev_id)
|
num_digits = len(prev_id)
|
||||||
|
|
||||||
if prev_id.count("9") == num_digits:
|
if prev_id.count("9") == num_digits:
|
||||||
raise OverflowError("max lexical version reached: " + prev_id)
|
raise OverflowError("max lexical version reached: " + prev_id)
|
||||||
|
|
||||||
_prev_id = int(prev_id, 10)
|
_prev_id_val = int(prev_id, 10)
|
||||||
_next_id = int(_prev_id) + 1
|
_next_id_val = int(_prev_id_val) + 1
|
||||||
next_id = f"{_next_id:0{num_digits}}"
|
_next_id_str = f"{_next_id_val:0{num_digits}}"
|
||||||
if prev_id[0] != next_id[0]:
|
if prev_id[0] != _next_id_str[0]:
|
||||||
next_id = str(_next_id * 11)
|
_next_id_str = str(_next_id_val * 11)
|
||||||
return next_id
|
return _next_id_str
|
||||||
|
|
||||||
|
|
||||||
def ord_val(lex_id: str) -> int:
|
def ord_val(lex_id: str) -> int:
|
||||||
|
"""Parse the ordinal value of a lexical id.
|
||||||
|
|
||||||
|
The ordinal value is the position in the sequence,
|
||||||
|
from repeated calls to next_id.
|
||||||
|
|
||||||
|
>>> ord_val("0098")
|
||||||
|
98
|
||||||
|
>>> ord_val("0099")
|
||||||
|
99
|
||||||
|
>>> ord_val("0100")
|
||||||
|
100
|
||||||
|
>>> ord_val("11000")
|
||||||
|
1000
|
||||||
|
>>> ord_val("11001")
|
||||||
|
1001
|
||||||
|
"""
|
||||||
if len(lex_id) == 1:
|
if len(lex_id) == 1:
|
||||||
return int(lex_id, 10)
|
return int(lex_id, 10)
|
||||||
return int(lex_id[1:], 10)
|
return int(lex_id[1:], 10)
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def _main() -> None:
|
||||||
_curr_id = "01"
|
_curr_id = "01"
|
||||||
print(f"{'lexical':<13} {'numerical':>12}")
|
print(f"{'lexical':<13} {'numerical':>12}")
|
||||||
|
|
||||||
|
|
@ -130,4 +159,4 @@ def main() -> None:
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
_main()
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,28 @@
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018 Manuel Barkhau (@mbarkhau) - MIT License
|
# Copyright (c) 2018 Manuel Barkhau (@mbarkhau) - MIT License
|
||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""Parse PyCalVer strings.
|
||||||
|
|
||||||
|
>>> version_info = PYCALVER_RE.match("v201712.0123-alpha").groupdict()
|
||||||
|
>>> assert version_info == {
|
||||||
|
... "version" : "v201712.0123-alpha",
|
||||||
|
... "calver" : "v201712",
|
||||||
|
... "year" : "2017",
|
||||||
|
... "month" : "12",
|
||||||
|
... "build" : ".0123",
|
||||||
|
... "release" : "-alpha",
|
||||||
|
... }
|
||||||
|
>>>
|
||||||
|
>>> version_info = PYCALVER_RE.match("v201712.0033").groupdict()
|
||||||
|
>>> assert version_info == {
|
||||||
|
... "version" : "v201712.0033",
|
||||||
|
... "calver" : "v201712",
|
||||||
|
... "year" : "2017",
|
||||||
|
... "month" : "12",
|
||||||
|
... "build" : ".0033",
|
||||||
|
... "release" : None,
|
||||||
|
... }
|
||||||
|
"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
|
|
@ -64,7 +86,49 @@ RE_PATTERN_PARTS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class VersionInfo(typ.NamedTuple):
|
||||||
|
"""Container for parsed version string."""
|
||||||
|
|
||||||
|
version: str
|
||||||
|
calver : str
|
||||||
|
year : str
|
||||||
|
month : str
|
||||||
|
build : str
|
||||||
|
release: typ.Optional[str]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pep440_version(self) -> str:
|
||||||
|
"""Generate pep440 compliant version string.
|
||||||
|
|
||||||
|
>>> vnfo = VersionInfo.parse("v201712.0033-beta")
|
||||||
|
>>> vnfo.pep440_version
|
||||||
|
'201712.33b0'
|
||||||
|
"""
|
||||||
|
return str(pkg_resources.parse_version(self.version))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse(version: str) -> 'VersionInfo':
|
||||||
|
"""Parse a PyCalVer string.
|
||||||
|
|
||||||
|
>>> vnfo = VersionInfo.parse("v201712.0033-beta")
|
||||||
|
>>> assert vnfo == VersionInfo(
|
||||||
|
... version="v201712.0033-beta",
|
||||||
|
... calver="v201712",
|
||||||
|
... year="2017",
|
||||||
|
... month="12",
|
||||||
|
... build=".0033",
|
||||||
|
... release="-beta",
|
||||||
|
... )
|
||||||
|
"""
|
||||||
|
match = PYCALVER_RE.match(version)
|
||||||
|
if match is None:
|
||||||
|
raise ValueError(f"Invalid pycalver: {version}")
|
||||||
|
|
||||||
|
return VersionInfo(**match.groupdict())
|
||||||
|
|
||||||
|
|
||||||
class PatternMatch(typ.NamedTuple):
|
class PatternMatch(typ.NamedTuple):
|
||||||
|
"""Container to mark a version string in a file."""
|
||||||
|
|
||||||
lineno : int # zero based
|
lineno : int # zero based
|
||||||
line : str
|
line : str
|
||||||
|
|
@ -72,45 +136,38 @@ class PatternMatch(typ.NamedTuple):
|
||||||
span : typ.Tuple[int, int]
|
span : typ.Tuple[int, int]
|
||||||
match : str
|
match : str
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _iter_for_pattern(lines: typ.List[str], pattern: str) -> typ.Iterable['PatternMatch']:
|
||||||
|
# The pattern is escaped, so that everything besides the format
|
||||||
|
# string variables is treated literally.
|
||||||
|
|
||||||
class VersionInfo(typ.NamedTuple):
|
pattern_tmpl = pattern
|
||||||
|
|
||||||
pep440_version: str
|
for char, escaped in PATTERN_ESCAPES:
|
||||||
version : str
|
pattern_tmpl = pattern_tmpl.replace(char, escaped)
|
||||||
calver : str
|
|
||||||
year : str
|
|
||||||
month : str
|
|
||||||
build : str
|
|
||||||
release : typ.Optional[str]
|
|
||||||
|
|
||||||
|
pattern_str = pattern_tmpl.format(**RE_PATTERN_PARTS)
|
||||||
|
pattern_re = re.compile(pattern_str)
|
||||||
|
for lineno, line in enumerate(lines):
|
||||||
|
match = pattern_re.search(line)
|
||||||
|
if match:
|
||||||
|
yield PatternMatch(lineno, line, pattern, match.span(), match.group(0))
|
||||||
|
|
||||||
def parse_version_info(version: str) -> VersionInfo:
|
@staticmethod
|
||||||
match = PYCALVER_RE.match(version)
|
def iter_matches(lines: typ.List[str], patterns: typ.List[str]) -> typ.Iterable['PatternMatch']:
|
||||||
if match is None:
|
"""Iterate over all matches of any pattern on any line.
|
||||||
raise ValueError(f"Invalid pycalver: {version}")
|
|
||||||
pep440_version = str(pkg_resources.parse_version(version))
|
|
||||||
return VersionInfo(pep440_version=pep440_version, **match.groupdict())
|
|
||||||
|
|
||||||
|
>>> lines = ["__version__ = 'v201712.0002-alpha'"]
|
||||||
def _iter_pattern_matches(lines: typ.List[str], pattern: str) -> typ.Iterable[PatternMatch]:
|
>>> patterns = ["{version}", "{pep440_version}"]
|
||||||
# The pattern is escaped, so that everything besides the format
|
>>> matches = list(PatternMatch.iter_matches(lines, patterns))
|
||||||
# string variables is treated literally.
|
>>> assert matches[0] == PatternMatch(
|
||||||
|
... lineno = 0,
|
||||||
pattern_tmpl = pattern
|
... line = "__version__ = 'v201712.0002-alpha'",
|
||||||
|
... pattern= "{version}",
|
||||||
for char, escaped in PATTERN_ESCAPES:
|
... span = (15, 33),
|
||||||
pattern_tmpl = pattern_tmpl.replace(char, escaped)
|
... match = "v201712.0002-alpha",
|
||||||
|
... )
|
||||||
pattern_str = pattern_tmpl.format(**RE_PATTERN_PARTS)
|
"""
|
||||||
pattern_re = re.compile(pattern_str)
|
for pattern in patterns:
|
||||||
for lineno, line in enumerate(lines):
|
for match in PatternMatch._iter_for_pattern(lines, pattern):
|
||||||
match = pattern_re.search(line)
|
yield match
|
||||||
if match:
|
|
||||||
yield PatternMatch(lineno, line, pattern, match.span(), match.group(0))
|
|
||||||
|
|
||||||
|
|
||||||
def parse_patterns(lines: typ.List[str], patterns: typ.List[str]) -> typ.List[PatternMatch]:
|
|
||||||
all_matches: typ.List[PatternMatch] = []
|
|
||||||
for pattern in patterns:
|
|
||||||
all_matches.extend(_iter_pattern_matches(lines, pattern))
|
|
||||||
return all_matches
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#
|
#
|
||||||
# (C) 2018 Manuel Barkhau (@mbarkhau)
|
# (C) 2018 Manuel Barkhau (@mbarkhau)
|
||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""Rewrite files, updating occurences of version strings."""
|
||||||
|
|
||||||
import io
|
import io
|
||||||
import difflib
|
import difflib
|
||||||
|
|
@ -10,11 +11,23 @@ import logging
|
||||||
import typing as typ
|
import typing as typ
|
||||||
|
|
||||||
from . import parse
|
from . import parse
|
||||||
|
from . import config
|
||||||
|
|
||||||
log = logging.getLogger("pycalver.rewrite")
|
log = logging.getLogger("pycalver.rewrite")
|
||||||
|
|
||||||
|
|
||||||
def _detect_line_sep(content: str) -> str:
|
def detect_line_sep(content: str) -> str:
|
||||||
|
r"""Parse line separator from content.
|
||||||
|
|
||||||
|
>>> detect_line_sep('\r\n')
|
||||||
|
'\r\n'
|
||||||
|
>>> detect_line_sep('\r')
|
||||||
|
'\r'
|
||||||
|
>>> detect_line_sep('\n')
|
||||||
|
'\n'
|
||||||
|
>>> detect_line_sep('')
|
||||||
|
'\n'
|
||||||
|
"""
|
||||||
if "\r\n" in content:
|
if "\r\n" in content:
|
||||||
return "\r\n"
|
return "\r\n"
|
||||||
elif "\r" in content:
|
elif "\r" in content:
|
||||||
|
|
@ -24,16 +37,21 @@ def _detect_line_sep(content: str) -> str:
|
||||||
|
|
||||||
|
|
||||||
def rewrite_lines(
|
def rewrite_lines(
|
||||||
old_lines: typ.List[str], patterns: typ.List[str], new_version: str
|
patterns: typ.List[str], new_version: str, old_lines: typ.List[str]
|
||||||
) -> typ.List[str]:
|
) -> typ.List[str]:
|
||||||
new_version_nfo = parse.parse_version_info(new_version)
|
"""Replace occurances of patterns in old_lines with new_version.
|
||||||
|
|
||||||
|
>>> old_lines = ['__version__ = "v201809.0002-beta"']
|
||||||
|
>>> patterns = ['__version__ = "{version}"']
|
||||||
|
>>> new_lines = rewrite_lines(patterns, "v201811.0123-beta", old_lines)
|
||||||
|
>>> assert new_lines == ['__version__ = "v201811.0123-beta"']
|
||||||
|
"""
|
||||||
|
new_version_nfo = parse.VersionInfo.parse(new_version)
|
||||||
new_version_fmt_kwargs = new_version_nfo._asdict()
|
new_version_fmt_kwargs = new_version_nfo._asdict()
|
||||||
|
|
||||||
new_lines = old_lines.copy()
|
new_lines = old_lines.copy()
|
||||||
|
|
||||||
matches: typ.List[parse.PatternMatch] = parse.parse_patterns(old_lines, patterns)
|
for m in parse.PatternMatch.iter_matches(old_lines, patterns):
|
||||||
|
|
||||||
for m in matches:
|
|
||||||
replacement = m.pattern.format(**new_version_fmt_kwargs)
|
replacement = m.pattern.format(**new_version_fmt_kwargs)
|
||||||
span_l, span_r = m.span
|
span_l, span_r = m.span
|
||||||
new_line = m.line[:span_l] + replacement + m.line[span_r:]
|
new_line = m.line[:span_l] + replacement + m.line[span_r:]
|
||||||
|
|
@ -42,26 +60,107 @@ def rewrite_lines(
|
||||||
return new_lines
|
return new_lines
|
||||||
|
|
||||||
|
|
||||||
def rewrite(
|
class RewrittenFileData(typ.NamedTuple):
|
||||||
new_version: str, file_patterns: typ.Dict[str, typ.List[str]], dry=False, verbose: int = 0
|
"""Container for line-wise content of rewritten files."""
|
||||||
) -> None:
|
|
||||||
for filepath, patterns in file_patterns.items():
|
|
||||||
with io.open(filepath, mode="rt", encoding="utf-8") as fh:
|
|
||||||
content = fh.read()
|
|
||||||
|
|
||||||
line_sep = _detect_line_sep(content)
|
path : str
|
||||||
old_lines = content.split(line_sep)
|
line_sep : str
|
||||||
new_lines = rewrite_lines(old_lines, patterns, new_version)
|
old_lines: typ.List[str]
|
||||||
|
new_lines: typ.List[str]
|
||||||
|
|
||||||
if dry or verbose:
|
@property
|
||||||
diff_lines = difflib.unified_diff(
|
def diff_lines(self) -> typ.List[str]:
|
||||||
old_lines, new_lines, lineterm="", fromfile="a/" + filepath, tofile="b/" + filepath
|
r"""Generate unified diff.
|
||||||
|
|
||||||
|
>>> rwd = RewrittenFileData(
|
||||||
|
... path = "<path>",
|
||||||
|
... line_sep = "\n",
|
||||||
|
... old_lines = ["foo"],
|
||||||
|
... new_lines = ["bar"],
|
||||||
|
... )
|
||||||
|
>>> rwd.diff_lines
|
||||||
|
['--- <path>', '+++ <path>', '@@ -1 +1 @@', '-foo', '+bar']
|
||||||
|
"""
|
||||||
|
return list(
|
||||||
|
difflib.unified_diff(
|
||||||
|
a=self.old_lines,
|
||||||
|
b=self.new_lines,
|
||||||
|
lineterm="",
|
||||||
|
fromfile=self.path,
|
||||||
|
tofile=self.path,
|
||||||
)
|
)
|
||||||
print("\n".join(diff_lines))
|
)
|
||||||
|
|
||||||
if dry:
|
@staticmethod
|
||||||
continue
|
def from_content(
|
||||||
|
patterns: typ.List[str], new_version: str, content: str
|
||||||
|
) -> 'RewrittenFileData':
|
||||||
|
r"""Rewrite pattern occurrences with version string.
|
||||||
|
|
||||||
new_content = line_sep.join(new_lines)
|
>>> patterns = ['__version__ = "{version}"']
|
||||||
with io.open(filepath, mode="wt", encoding="utf-8") as fh:
|
>>> content = '__version__ = "v201809.0001-alpha"'
|
||||||
|
>>> rwd = RewrittenFileData.from_content(patterns, "v201809.0123", content)
|
||||||
|
>>> assert rwd.new_lines == ['__version__ = "v201809.0123"']
|
||||||
|
"""
|
||||||
|
line_sep = detect_line_sep(content)
|
||||||
|
old_lines = content.split(line_sep)
|
||||||
|
new_lines = rewrite_lines(patterns, new_version, old_lines)
|
||||||
|
return RewrittenFileData("<path>", line_sep, old_lines, new_lines)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def iter_rewritten(
|
||||||
|
file_patterns: config.PatternsByFilePath, new_version: str
|
||||||
|
) -> typ.Iterable['RewrittenFileData']:
|
||||||
|
r'''Iterate over files with version string replaced.
|
||||||
|
|
||||||
|
>>> file_patterns = {"src/pycalver/__init__.py": ['__version__ = "{version}"']}
|
||||||
|
>>> rewritten_datas = RewrittenFileData.iter_rewritten(file_patterns, "v201809.0123")
|
||||||
|
>>> rwd = list(rewritten_datas)[0]
|
||||||
|
>>> assert rwd.new_lines == [
|
||||||
|
... '# This file is part of the pycalver project',
|
||||||
|
... '# https://gitlab.com/mbarkhau/pycalver',
|
||||||
|
... '#',
|
||||||
|
... '# Copyright (c) 2018 Manuel Barkhau (@mbarkhau) - MIT License',
|
||||||
|
... '# SPDX-License-Identifier: MIT',
|
||||||
|
... '"""PyCalVer: Automatic CalVer Versioning for Python Packages."""',
|
||||||
|
... '',
|
||||||
|
... '__version__ = "v201809.0123"',
|
||||||
|
... '',
|
||||||
|
... ]
|
||||||
|
>>>
|
||||||
|
'''
|
||||||
|
for filepath, patterns in file_patterns.items():
|
||||||
|
with io.open(filepath, mode="rt", encoding="utf-8") as fh:
|
||||||
|
content = fh.read()
|
||||||
|
|
||||||
|
rfd = RewrittenFileData.from_content(patterns, new_version, content)
|
||||||
|
yield rfd._replace(path=filepath)
|
||||||
|
|
||||||
|
|
||||||
|
def diff(new_version: str, file_patterns: config.PatternsByFilePath) -> str:
|
||||||
|
r"""Generate diffs of rewritten files.
|
||||||
|
|
||||||
|
>>> file_patterns = {"src/pycalver/__init__.py": ['__version__ = "{version}"']}
|
||||||
|
>>> diff_lines = diff("v201809.0123", file_patterns).split("\n")
|
||||||
|
>>> diff_lines[:2]
|
||||||
|
['--- src/pycalver/__init__.py', '+++ src/pycalver/__init__.py']
|
||||||
|
>>> assert diff_lines[6].startswith('-__version__ = "v2')
|
||||||
|
>>> assert not diff_lines[6].startswith('-__version__ = "v201809.0123"')
|
||||||
|
>>> diff_lines[7]
|
||||||
|
'+__version__ = "v201809.0123"'
|
||||||
|
"""
|
||||||
|
diff_lines: typ.List[str] = []
|
||||||
|
|
||||||
|
for rwd in RewrittenFileData.iter_rewritten(file_patterns, new_version):
|
||||||
|
diff_lines += rwd.diff_lines
|
||||||
|
|
||||||
|
return "\n".join(diff_lines)
|
||||||
|
|
||||||
|
|
||||||
|
def rewrite(new_version: str, file_patterns: config.PatternsByFilePath) -> None:
|
||||||
|
"""Rewrite project files, updating each with the new version."""
|
||||||
|
|
||||||
|
for file_data in RewrittenFileData.iter_rewritten(file_patterns, new_version):
|
||||||
|
new_content = file_data.line_sep.join(file_data.new_lines)
|
||||||
|
with io.open(file_data.path, mode="wt", encoding="utf-8") as fh:
|
||||||
fh.write(new_content)
|
fh.write(new_content)
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,12 @@
|
||||||
# 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
|
||||||
# Copyright (c) 2013-2014 Filip Noetzel - MIT License
|
# Copyright (c) 2013-2014 Filip Noetzel - MIT License
|
||||||
|
"""Minimal Git and Mercirial API.
|
||||||
|
|
||||||
|
If terminology for similar concepts differs between git and
|
||||||
|
mercurial, then the git terms are used. For example "fetch"
|
||||||
|
(git) instead of "pull" (hg) .
|
||||||
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
|
|
@ -42,7 +48,7 @@ VCS_SUBCOMMANDS_BY_NAME = {
|
||||||
|
|
||||||
|
|
||||||
class VCS:
|
class VCS:
|
||||||
"""Version Control System absraction for git and mercurial"""
|
"""VCS absraction for git and mercurial."""
|
||||||
|
|
||||||
def __init__(self, name: str, subcommands: typ.Dict[str, str] = None):
|
def __init__(self, name: str, subcommands: typ.Dict[str, str] = None):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
@ -51,13 +57,19 @@ class VCS:
|
||||||
else:
|
else:
|
||||||
self.subcommands = subcommands
|
self.subcommands = subcommands
|
||||||
|
|
||||||
def __call__(self, cmd_name: str, env=None, **kwargs: str) -> bytes:
|
def __call__(self, cmd_name: str, env=None, **kwargs: str) -> str:
|
||||||
cmd_str = self.subcommands[cmd_name]
|
"""Invoke subcommand and return output."""
|
||||||
cmd_parts = cmd_str.format(**kwargs).split()
|
cmd_str = self.subcommands[cmd_name]
|
||||||
return sp.check_output(cmd_parts, env=env)
|
cmd_parts = cmd_str.format(**kwargs).split()
|
||||||
|
output_data = sp.check_output(cmd_parts, env=env)
|
||||||
|
|
||||||
|
# TODO (mb 2018-11-15): Detect encoding of output?
|
||||||
|
_encoding = "utf-8"
|
||||||
|
return output_data.decode(_encoding)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_usable(self) -> bool:
|
def is_usable(self) -> bool:
|
||||||
|
"""Detect availability of subcommand."""
|
||||||
cmd = self.subcommands['is_usable'].split()
|
cmd = self.subcommands['is_usable'].split()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -70,28 +82,31 @@ class VCS:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def fetch(self) -> None:
|
def fetch(self) -> None:
|
||||||
|
"""Fetch updates from remote origin."""
|
||||||
self('fetch')
|
self('fetch')
|
||||||
|
|
||||||
def status(self) -> typ.List[str]:
|
def status(self) -> typ.List[str]:
|
||||||
|
"""Get status lines."""
|
||||||
status_output = self('status')
|
status_output = self('status')
|
||||||
return [
|
return [
|
||||||
line.decode("utf-8")[2:].strip()
|
line[2:].strip()
|
||||||
for line in status_output.splitlines()
|
for line in status_output.splitlines()
|
||||||
if not line.strip().startswith(b"??")
|
if not line.strip().startswith("??")
|
||||||
]
|
]
|
||||||
|
|
||||||
def ls_tags(self) -> typ.List[str]:
|
def ls_tags(self) -> typ.List[str]:
|
||||||
|
"""List vcs tags on all branches."""
|
||||||
ls_tag_lines = self('ls_tags').splitlines()
|
ls_tag_lines = self('ls_tags').splitlines()
|
||||||
log.debug(f"ls_tags output {ls_tag_lines}")
|
log.debug(f"ls_tags output {ls_tag_lines}")
|
||||||
return [
|
return [line.strip() for line in ls_tag_lines if line.strip().startswith("v")]
|
||||||
line.decode("utf-8").strip() for line in ls_tag_lines if line.strip().startswith(b"v")
|
|
||||||
]
|
|
||||||
|
|
||||||
def add(self, path) -> None:
|
def add(self, path: str) -> None:
|
||||||
|
"""Add updates to be included in next commit."""
|
||||||
log.info(f"{self.name} add {path}")
|
log.info(f"{self.name} add {path}")
|
||||||
self('add_path', path=path)
|
self('add_path', path=path)
|
||||||
|
|
||||||
def commit(self, message: str) -> None:
|
def commit(self, message: str) -> None:
|
||||||
|
"""Commit added files."""
|
||||||
log.info(f"{self.name} commit -m '{message}'")
|
log.info(f"{self.name} commit -m '{message}'")
|
||||||
message_data = message.encode("utf-8")
|
message_data = message.encode("utf-8")
|
||||||
|
|
||||||
|
|
@ -108,17 +123,24 @@ class VCS:
|
||||||
self('commit', env=env, path=tmp_file.name)
|
self('commit', env=env, path=tmp_file.name)
|
||||||
os.unlink(tmp_file.name)
|
os.unlink(tmp_file.name)
|
||||||
|
|
||||||
def tag(self, tag_name) -> None:
|
def tag(self, tag_name: str) -> None:
|
||||||
|
"""Create an annotated tag."""
|
||||||
self('tag', tag=tag_name)
|
self('tag', tag=tag_name)
|
||||||
|
|
||||||
def push(self, tag_name) -> None:
|
def push(self, tag_name: str) -> None:
|
||||||
|
"""Push changes to origin."""
|
||||||
self('push_tag', tag=tag_name)
|
self('push_tag', tag=tag_name)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
|
"""Generate string representation."""
|
||||||
return f"VCS(name='{self.name}')"
|
return f"VCS(name='{self.name}')"
|
||||||
|
|
||||||
|
|
||||||
def get_vcs() -> VCS:
|
def get_vcs() -> VCS:
|
||||||
|
"""Detect the appropriate VCS for a repository.
|
||||||
|
|
||||||
|
raises OSError if the directory doesn't use a supported VCS.
|
||||||
|
"""
|
||||||
for vcs_name in VCS_SUBCOMMANDS_BY_NAME.keys():
|
for vcs_name in VCS_SUBCOMMANDS_BY_NAME.keys():
|
||||||
vcs = VCS(name=vcs_name)
|
vcs = VCS(name=vcs_name)
|
||||||
if vcs.is_usable:
|
if vcs.is_usable:
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ def incr(old_version: str, *, release: str = None) -> str:
|
||||||
Old_version is assumed to be a valid calver string,
|
Old_version is assumed to be a valid calver string,
|
||||||
already validated in pycalver.config.parse.
|
already validated in pycalver.config.parse.
|
||||||
"""
|
"""
|
||||||
old_ver = parse.parse_version_info(old_version)
|
old_ver = parse.VersionInfo.parse(old_version)
|
||||||
|
|
||||||
new_calver = current_calver()
|
new_calver = current_calver()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ def test_ord_val():
|
||||||
|
|
||||||
|
|
||||||
def test_main(capsys):
|
def test_main(capsys):
|
||||||
lex_id.main()
|
lex_id._main()
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
assert len(captured.err) == 0
|
assert len(captured.err) == 0
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ def test_re_pattern_parts():
|
||||||
|
|
||||||
def test_parse_version_info():
|
def test_parse_version_info():
|
||||||
version_str = "v201712.0001-alpha"
|
version_str = "v201712.0001-alpha"
|
||||||
version_nfo = parse.parse_version_info(version_str)
|
version_nfo = parse.VersionInfo.parse(version_str)
|
||||||
|
|
||||||
assert version_nfo.pep440_version == "201712.1a0"
|
assert version_nfo.pep440_version == "201712.1a0"
|
||||||
assert version_nfo.version == "v201712.0001-alpha"
|
assert version_nfo.version == "v201712.0001-alpha"
|
||||||
|
|
@ -86,7 +86,7 @@ def test_parse_version_info():
|
||||||
assert version_nfo.release == "-alpha"
|
assert version_nfo.release == "-alpha"
|
||||||
|
|
||||||
version_str = "v201712.0001"
|
version_str = "v201712.0001"
|
||||||
version_nfo = parse.parse_version_info(version_str)
|
version_nfo = parse.VersionInfo.parse(version_str)
|
||||||
|
|
||||||
assert version_nfo.pep440_version == "201712.1"
|
assert version_nfo.pep440_version == "201712.1"
|
||||||
assert version_nfo.version == "v201712.0001"
|
assert version_nfo.version == "v201712.0001"
|
||||||
|
|
@ -97,23 +97,25 @@ def test_parse_version_info():
|
||||||
assert version_nfo.release is None
|
assert version_nfo.release is None
|
||||||
|
|
||||||
|
|
||||||
def test_default_parse_patterns():
|
SETUP_PY_FIXTURE = """
|
||||||
lines = [
|
# setup.py
|
||||||
"# setup.py",
|
import setuptools
|
||||||
"import setuptools",
|
__version__ = 'v201712.0002-alpha'
|
||||||
"__version__ = 'v201712.0002-alpha'",
|
setuptools.setup(
|
||||||
"setuptools.setup(",
|
...
|
||||||
"...",
|
version='201712.2a0',
|
||||||
" version='201712.2a0',",
|
"""
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
|
def test_default_parse_patterns():
|
||||||
|
lines = SETUP_PY_FIXTURE.splitlines()
|
||||||
patterns = ["{version}", "{pep440_version}"]
|
patterns = ["{version}", "{pep440_version}"]
|
||||||
|
|
||||||
matches = parse.parse_patterns(lines, patterns)
|
matches = list(parse.PatternMatch.iter_matches(lines, patterns))
|
||||||
assert len(matches) == 2
|
assert len(matches) == 2
|
||||||
|
|
||||||
assert matches[0].lineno == 2
|
assert matches[0].lineno == 3
|
||||||
assert matches[1].lineno == 5
|
assert matches[1].lineno == 6
|
||||||
|
|
||||||
assert matches[0].pattern == patterns[0]
|
assert matches[0].pattern == patterns[0]
|
||||||
assert matches[1].pattern == patterns[1]
|
assert matches[1].pattern == patterns[1]
|
||||||
|
|
@ -123,22 +125,15 @@ def test_default_parse_patterns():
|
||||||
|
|
||||||
|
|
||||||
def test_explicit_parse_patterns():
|
def test_explicit_parse_patterns():
|
||||||
lines = [
|
lines = SETUP_PY_FIXTURE.splitlines()
|
||||||
"# setup.py",
|
|
||||||
"import setuptools",
|
|
||||||
"__version__ = 'v201712.0002-alpha'",
|
|
||||||
"setuptools.setup(",
|
|
||||||
"...",
|
|
||||||
" version='201712.2a0',",
|
|
||||||
]
|
|
||||||
|
|
||||||
patterns = ["__version__ = '{version}'", "version='{pep440_version}'"]
|
patterns = ["__version__ = '{version}'", "version='{pep440_version}'"]
|
||||||
|
|
||||||
matches = parse.parse_patterns(lines, patterns)
|
matches = list(parse.PatternMatch.iter_matches(lines, patterns))
|
||||||
assert len(matches) == 2
|
assert len(matches) == 2
|
||||||
|
|
||||||
assert matches[0].lineno == 2
|
assert matches[0].lineno == 3
|
||||||
assert matches[1].lineno == 5
|
assert matches[1].lineno == 6
|
||||||
|
|
||||||
assert matches[0].pattern == patterns[0]
|
assert matches[0].pattern == patterns[0]
|
||||||
assert matches[1].pattern == patterns[1]
|
assert matches[1].pattern == patterns[1]
|
||||||
|
|
@ -147,23 +142,25 @@ def test_explicit_parse_patterns():
|
||||||
assert matches[1].match == "version='201712.2a0'"
|
assert matches[1].match == "version='201712.2a0'"
|
||||||
|
|
||||||
|
|
||||||
|
README_RST_FIXTURE = """
|
||||||
|
:alt: PyPI version
|
||||||
|
|
||||||
|
.. |version| image:: https://img.shields.io/badge/CalVer-v201809.0002--beta-blue.svg
|
||||||
|
:target: https://calver.org/
|
||||||
|
:alt: CalVer v201809.0002-beta
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
def test_badge_parse_patterns():
|
def test_badge_parse_patterns():
|
||||||
lines = [
|
lines = README_RST_FIXTURE.splitlines()
|
||||||
":alt: PyPI version",
|
|
||||||
"",
|
|
||||||
".. |version| image:: https://img.shields.io/badge/CalVer-v201809.0002--beta-blue.svg",
|
|
||||||
":target: https://calver.org/",
|
|
||||||
":alt: CalVer v201809.0002-beta",
|
|
||||||
"",
|
|
||||||
]
|
|
||||||
|
|
||||||
patterns = ["badge/CalVer-{calver}{build}-{release}-blue.svg", ":alt: CalVer {version}"]
|
patterns = ["badge/CalVer-{calver}{build}-{release}-blue.svg", ":alt: CalVer {version}"]
|
||||||
|
|
||||||
matches = parse.parse_patterns(lines, patterns)
|
matches = list(parse.PatternMatch.iter_matches(lines, patterns))
|
||||||
assert len(matches) == 2
|
assert len(matches) == 2
|
||||||
|
|
||||||
assert matches[0].lineno == 2
|
assert matches[0].lineno == 3
|
||||||
assert matches[1].lineno == 4
|
assert matches[1].lineno == 5
|
||||||
|
|
||||||
assert matches[0].pattern == patterns[0]
|
assert matches[0].pattern == patterns[0]
|
||||||
assert matches[1].pattern == patterns[1]
|
assert matches[1].pattern == patterns[1]
|
||||||
|
|
@ -174,19 +171,19 @@ def test_badge_parse_patterns():
|
||||||
|
|
||||||
def test_parse_error():
|
def test_parse_error():
|
||||||
try:
|
try:
|
||||||
parse.parse_version_info("")
|
parse.VersionInfo.parse("")
|
||||||
assert False
|
assert False
|
||||||
except ValueError as err:
|
except ValueError as err:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
parse.parse_version_info("201809.0002")
|
parse.VersionInfo.parse("201809.0002")
|
||||||
assert False
|
assert False
|
||||||
except ValueError as err:
|
except ValueError as err:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
parse.parse_version_info("v201809.2b0")
|
parse.VersionInfo.parse("v201809.2b0")
|
||||||
assert False
|
assert False
|
||||||
except ValueError as err:
|
except ValueError as err:
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,22 @@
|
||||||
from pycalver import rewrite
|
from pycalver import rewrite
|
||||||
|
|
||||||
|
|
||||||
|
REWRITE_FIXTURE = """
|
||||||
|
# This file is part of the pycalver project
|
||||||
|
# https://github.com/mbarkhau/pycalver
|
||||||
|
#
|
||||||
|
# (C) 2018 Manuel Barkhau (@mbarkhau)
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
__version__ = "v201809.0002-beta"
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
def test_rewrite_lines():
|
def test_rewrite_lines():
|
||||||
old_lines = [
|
old_lines = REWRITE_FIXTURE.splitlines()
|
||||||
"# This file is part of the pycalver project",
|
|
||||||
"# https://github.com/mbarkhau/pycalver",
|
|
||||||
"#",
|
|
||||||
"# (C) 2018 Manuel Barkhau (@mbarkhau)",
|
|
||||||
"# SPDX-License-Identifier: MIT",
|
|
||||||
'',
|
|
||||||
"import os",
|
|
||||||
'',
|
|
||||||
'__version__ = "v201809.0002-beta"',
|
|
||||||
'DEBUG = os.environ.get("PYDEBUG", "0") == "1"',
|
|
||||||
]
|
|
||||||
patterns = ['__version__ = "{version}"']
|
patterns = ['__version__ = "{version}"']
|
||||||
new_version = "v201809.0003"
|
new_version = "v201809.0003"
|
||||||
new_lines = rewrite.rewrite_lines(old_lines, patterns, new_version)
|
new_lines = rewrite.rewrite_lines(patterns, new_version, old_lines)
|
||||||
|
|
||||||
assert len(new_lines) == len(old_lines)
|
assert len(new_lines) == len(old_lines)
|
||||||
assert new_version not in "\n".join(old_lines)
|
assert new_version not in "\n".join(old_lines)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue