mirror of
https://github.com/TECHNOFAB11/bumpver.git
synced 2025-12-12 14:30:09 +01:00
Add more flexible parsing and formating
This commit is contained in:
parent
32447b03d4
commit
9eda61d95b
13 changed files with 932 additions and 359 deletions
10
setup.cfg
10
setup.cfg
|
|
@ -75,15 +75,15 @@ push = True
|
||||||
|
|
||||||
[pycalver:file_patterns]
|
[pycalver:file_patterns]
|
||||||
bootstrapit.sh =
|
bootstrapit.sh =
|
||||||
PACKAGE_VERSION="{version}"
|
PACKAGE_VERSION="{pycalver}"
|
||||||
setup.cfg =
|
setup.cfg =
|
||||||
current_version = {version}
|
current_version = {pycalver}
|
||||||
setup.py =
|
setup.py =
|
||||||
version="{pep440_version}"
|
version="{pep440_pycalver}"
|
||||||
src/pycalver/__init__.py =
|
src/pycalver/__init__.py =
|
||||||
__version__ = "{version}"
|
__version__ = "{pycalver}"
|
||||||
src/pycalver/__main__.py =
|
src/pycalver/__main__.py =
|
||||||
click.version_option(version="{version}")
|
click.version_option(version="{pycalver}")
|
||||||
README.md =
|
README.md =
|
||||||
[PyCalVer {version}]
|
[PyCalVer {version}]
|
||||||
https://img.shields.io/badge/PyCalVer-{calver}{build}-blue.svg
|
https://img.shields.io/badge/PyCalVer-{calver}{build}-blue.svg
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ import logging
|
||||||
import typing as typ
|
import typing as typ
|
||||||
|
|
||||||
from . import vcs
|
from . import vcs
|
||||||
from . import parse
|
|
||||||
from . import config
|
from . import config
|
||||||
from . import version
|
from . import version
|
||||||
from . import rewrite
|
from . import rewrite
|
||||||
|
|
@ -25,19 +24,22 @@ from . import rewrite
|
||||||
_VERBOSE = 0
|
_VERBOSE = 0
|
||||||
|
|
||||||
|
|
||||||
try:
|
# try:
|
||||||
import backtrace
|
# import backtrace
|
||||||
|
|
||||||
# To enable pretty tracebacks:
|
# # To enable pretty tracebacks:
|
||||||
# echo "export ENABLE_BACKTRACE=1;" >> ~/.bashrc
|
# # echo "export ENABLE_BACKTRACE=1;" >> ~/.bashrc
|
||||||
backtrace.hook(align=True, strip_path=True, enable_on_envvar_only=True)
|
# backtrace.hook(align=True, strip_path=True, enable_on_envvar_only=True)
|
||||||
except ImportError:
|
# except ImportError:
|
||||||
pass
|
# pass
|
||||||
|
|
||||||
|
|
||||||
click.disable_unicode_literals_warning = True
|
click.disable_unicode_literals_warning = True
|
||||||
|
|
||||||
|
|
||||||
|
VALID_RELEASE_VALUES = ("alpha", "beta", "dev", "rc", "post", "final")
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger("pycalver.cli")
|
log = logging.getLogger("pycalver.cli")
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -57,11 +59,11 @@ def _init_logging(verbose: int = 0) -> None:
|
||||||
|
|
||||||
|
|
||||||
def _validate_release_tag(release: str) -> None:
|
def _validate_release_tag(release: str) -> None:
|
||||||
if release in parse.VALID_RELEASE_VALUES:
|
if release in VALID_RELEASE_VALUES:
|
||||||
return
|
return
|
||||||
|
|
||||||
log.error(f"Invalid argument --release={release}")
|
log.error(f"Invalid argument --release={release}")
|
||||||
log.error(f"Valid arguments are: {', '.join(parse.VALID_RELEASE_VALUES)}")
|
log.error(f"Valid arguments are: {', '.join(VALID_RELEASE_VALUES)}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -77,22 +79,40 @@ def cli(verbose: int = 0):
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
@click.argument("old_version")
|
@click.argument("old_version")
|
||||||
|
@click.argument("pattern", default="{pycalver}")
|
||||||
@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(
|
@click.option(
|
||||||
"--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 test(old_version: str, verbose: int = 0, release: str = None) -> None:
|
@click.option("--major", is_flag=True, default=False, help="Increment major component.")
|
||||||
|
@click.option("--minor", is_flag=True, default=False, help="Increment minor component.")
|
||||||
|
@click.option("--patch", is_flag=True, default=False, help="Increment patch component.")
|
||||||
|
def test(
|
||||||
|
old_version: str,
|
||||||
|
pattern : str = "{pycalver}",
|
||||||
|
verbose : int = 0,
|
||||||
|
release : str = None,
|
||||||
|
major : bool = False,
|
||||||
|
minor : bool = False,
|
||||||
|
patch : bool = False,
|
||||||
|
) -> None:
|
||||||
"""Increment a version number for demo purposes."""
|
"""Increment a version number for demo purposes."""
|
||||||
_init_logging(verbose=max(_VERBOSE, verbose))
|
_init_logging(verbose=max(_VERBOSE, verbose))
|
||||||
|
|
||||||
if release:
|
if release:
|
||||||
_validate_release_tag(release)
|
_validate_release_tag(release)
|
||||||
|
|
||||||
new_version = version.incr(old_version, release=release)
|
new_version = version.incr(
|
||||||
pep440_version = version.pycalver_to_pep440(new_version)
|
old_version, pattern=pattern, release=release, major=major, minor=minor, patch=patch
|
||||||
|
)
|
||||||
|
if new_version is None:
|
||||||
|
log.error(f"Invalid version '{old_version}' and/or pattern '{pattern}'.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
print("PyCalVer Version:", new_version)
|
pep440_version = version.to_pep440(new_version)
|
||||||
print("PEP440 Version :", pep440_version)
|
|
||||||
|
print("New Version:", new_version)
|
||||||
|
print("PEP440 :", pep440_version)
|
||||||
|
|
||||||
|
|
||||||
def _update_cfg_from_vcs(cfg: config.Config, fetch: bool) -> config.Config:
|
def _update_cfg_from_vcs(cfg: config.Config, fetch: bool) -> config.Config:
|
||||||
|
|
@ -103,12 +123,12 @@ def _update_cfg_from_vcs(cfg: config.Config, fetch: bool) -> config.Config:
|
||||||
log.info(f"fetching tags from remote (to turn off use: -n / --no-fetch)")
|
log.info(f"fetching tags from remote (to turn off use: -n / --no-fetch)")
|
||||||
_vcs.fetch()
|
_vcs.fetch()
|
||||||
|
|
||||||
version_tags = [tag for tag in _vcs.ls_tags() if version.PYCALVER_RE.match(tag)]
|
version_tags = [tag for tag in _vcs.ls_tags() if version.is_valid(tag, cfg.version_pattern)]
|
||||||
if version_tags:
|
if version_tags:
|
||||||
version_tags.sort(reverse=True)
|
version_tags.sort(reverse=True)
|
||||||
log.debug(f"found {len(version_tags)} tags: {version_tags[:2]}")
|
log.debug(f"found {len(version_tags)} tags: {version_tags[:2]}")
|
||||||
latest_version_tag = version_tags[0]
|
latest_version_tag = version_tags[0]
|
||||||
latest_version_pep440 = version.pycalver_to_pep440(latest_version_tag)
|
latest_version_pep440 = version.to_pep440(latest_version_tag)
|
||||||
if latest_version_tag > cfg.current_version:
|
if latest_version_tag > cfg.current_version:
|
||||||
log.info(f"Working dir version : {cfg.current_version}")
|
log.info(f"Working dir version : {cfg.current_version}")
|
||||||
log.info(f"Latest version from {_vcs.name:>3} tag: {latest_version_tag}")
|
log.info(f"Latest version from {_vcs.name:>3} tag: {latest_version_tag}")
|
||||||
|
|
@ -143,7 +163,7 @@ def show(verbose: int = 0, fetch: bool = True) -> None:
|
||||||
cfg = _update_cfg_from_vcs(cfg, fetch=fetch)
|
cfg = _update_cfg_from_vcs(cfg, fetch=fetch)
|
||||||
|
|
||||||
print(f"Current Version: {cfg.current_version}")
|
print(f"Current Version: {cfg.current_version}")
|
||||||
print(f"PEP440 Version : {cfg.pep440_version}")
|
print(f"PEP440 : {cfg.pep440_version}")
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
|
|
@ -235,7 +255,7 @@ def _bump(cfg: config.Config, new_version: str, allow_dirty: bool = False) -> No
|
||||||
metavar="<name>",
|
metavar="<name>",
|
||||||
help=(
|
help=(
|
||||||
f"Override release name of current_version. Valid options are: "
|
f"Override release name of current_version. Valid options are: "
|
||||||
f"{', '.join(parse.VALID_RELEASE_VALUES)}."
|
f"{', '.join(VALID_RELEASE_VALUES)}."
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
|
|
@ -248,12 +268,18 @@ def _bump(cfg: config.Config, new_version: str, allow_dirty: bool = False) -> No
|
||||||
"to files with version strings."
|
"to files with version strings."
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@click.option("--major", is_flag=True, default=False, help="Increment major component.")
|
||||||
|
@click.option("--minor", is_flag=True, default=False, help="Increment minor component.")
|
||||||
|
@click.option("--patch", is_flag=True, default=False, help="Increment patch component.")
|
||||||
def bump(
|
def bump(
|
||||||
release : typ.Optional[str] = None,
|
release : typ.Optional[str] = None,
|
||||||
verbose : int = 0,
|
verbose : int = 0,
|
||||||
dry : bool = False,
|
dry : bool = False,
|
||||||
allow_dirty: bool = False,
|
allow_dirty: bool = False,
|
||||||
fetch : bool = True,
|
fetch : bool = True,
|
||||||
|
major : bool = False,
|
||||||
|
minor : bool = False,
|
||||||
|
patch : bool = False,
|
||||||
) -> 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)
|
||||||
|
|
@ -272,7 +298,17 @@ def bump(
|
||||||
cfg = _update_cfg_from_vcs(cfg, fetch=fetch)
|
cfg = _update_cfg_from_vcs(cfg, fetch=fetch)
|
||||||
|
|
||||||
old_version = cfg.current_version
|
old_version = cfg.current_version
|
||||||
new_version = version.incr(old_version, release=release)
|
new_version = version.incr(
|
||||||
|
old_version,
|
||||||
|
pattern=cfg.version_pattern,
|
||||||
|
release=release,
|
||||||
|
major=major,
|
||||||
|
minor=minor,
|
||||||
|
patch=patch,
|
||||||
|
)
|
||||||
|
if new_version is None:
|
||||||
|
log.error(f"Invalid version '{old_version}' and/or pattern '{cfg.version_pattern}'.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
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}")
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@ class Config(typ.NamedTuple):
|
||||||
"""Container for parameters parsed from a config file."""
|
"""Container for parameters parsed from a config file."""
|
||||||
|
|
||||||
current_version: str
|
current_version: str
|
||||||
|
version_pattern: str
|
||||||
pep440_version : str
|
pep440_version : str
|
||||||
|
|
||||||
commit: bool
|
commit: bool
|
||||||
|
|
@ -90,6 +91,7 @@ def _debug_str(cfg: Config) -> str:
|
||||||
cfg_str_parts = [
|
cfg_str_parts = [
|
||||||
f"Config Parsed: Config(",
|
f"Config Parsed: Config(",
|
||||||
f"current_version='{cfg.current_version}'",
|
f"current_version='{cfg.current_version}'",
|
||||||
|
f"version_pattern='{{pycalver}}'",
|
||||||
f"pep440_version='{cfg.pep440_version}'",
|
f"pep440_version='{cfg.pep440_version}'",
|
||||||
f"commit={cfg.commit}",
|
f"commit={cfg.commit}",
|
||||||
f"tag={cfg.tag}",
|
f"tag={cfg.tag}",
|
||||||
|
|
@ -179,17 +181,52 @@ def _parse_toml(cfg_buffer: typ.TextIO) -> RawConfig:
|
||||||
return raw_cfg
|
return raw_cfg
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_file_patterns(raw_cfg: RawConfig) -> FilePatterns:
|
||||||
|
version_str = raw_cfg['current_version']
|
||||||
|
version_pattern = raw_cfg['version_pattern']
|
||||||
|
pep440_version = version.to_pep440(version_str)
|
||||||
|
file_patterns = raw_cfg['file_patterns']
|
||||||
|
|
||||||
|
for filepath, patterns in list(file_patterns.items()):
|
||||||
|
if not os.path.exists(filepath):
|
||||||
|
log.warning(f"Invalid config, no such file: {filepath}")
|
||||||
|
|
||||||
|
normalized_patterns: typ.List[str] = []
|
||||||
|
for pattern in patterns:
|
||||||
|
normalized_pattern = pattern.replace("{version}", version_pattern)
|
||||||
|
if version_pattern == "{pycalver}":
|
||||||
|
normalized_pattern = normalized_pattern.replace(
|
||||||
|
"{pep440_version}", "{pep440_pycalver}"
|
||||||
|
)
|
||||||
|
elif version_pattern == "{semver}":
|
||||||
|
normalized_pattern = normalized_pattern.replace("{pep440_version}", "{semver}")
|
||||||
|
elif "{pep440_version}" in pattern:
|
||||||
|
log.warning(f"Invalid config, cannot match '{pattern}' for '{filepath}'.")
|
||||||
|
log.warning(f"No mapping of '{version_pattern}' to '{pep440_version}'")
|
||||||
|
normalized_patterns.append(normalized_pattern)
|
||||||
|
|
||||||
|
file_patterns[filepath] = normalized_patterns
|
||||||
|
|
||||||
|
return file_patterns
|
||||||
|
|
||||||
|
|
||||||
def _parse_config(raw_cfg: RawConfig) -> Config:
|
def _parse_config(raw_cfg: RawConfig) -> Config:
|
||||||
|
"""Parse configuration which was loaded from an .ini/.cfg or .toml file."""
|
||||||
|
|
||||||
if 'current_version' not in raw_cfg:
|
if 'current_version' not in raw_cfg:
|
||||||
raise ValueError("Missing 'pycalver.current_version'")
|
raise ValueError("Missing 'pycalver.current_version'")
|
||||||
|
|
||||||
version_str = raw_cfg['current_version']
|
version_str = raw_cfg['current_version']
|
||||||
version_str = raw_cfg['current_version'] = version_str.strip("'\" ")
|
version_str = raw_cfg['current_version'] = version_str.strip("'\" ")
|
||||||
|
|
||||||
if version.PYCALVER_RE.match(version_str) is None:
|
version_pattern = raw_cfg.get('version_pattern', "{pycalver}")
|
||||||
raise ValueError(f"Invalid current_version = {version_str}")
|
version_pattern = raw_cfg['version_pattern'] = version_pattern.strip("'\" ")
|
||||||
|
|
||||||
pep440_version = version.pycalver_to_pep440(version_str)
|
# NOTE (mb 2019-01-05): trigger ValueError if version_pattern
|
||||||
|
# and current_version don't work together.
|
||||||
|
version.parse_version_info(version_str, version_pattern)
|
||||||
|
|
||||||
|
pep440_version = version.to_pep440(version_str)
|
||||||
|
|
||||||
commit = raw_cfg['commit']
|
commit = raw_cfg['commit']
|
||||||
tag = raw_cfg['tag']
|
tag = raw_cfg['tag']
|
||||||
|
|
@ -206,13 +243,9 @@ def _parse_config(raw_cfg: RawConfig) -> Config:
|
||||||
if push and not commit:
|
if push and not commit:
|
||||||
raise ValueError("pycalver.commit = true required if pycalver.push = true")
|
raise ValueError("pycalver.commit = true required if pycalver.push = true")
|
||||||
|
|
||||||
file_patterns = raw_cfg['file_patterns']
|
file_patterns = _normalize_file_patterns(raw_cfg)
|
||||||
|
|
||||||
for filepath in file_patterns.keys():
|
cfg = Config(version_str, version_pattern, pep440_version, tag, commit, push, file_patterns)
|
||||||
if not os.path.exists(filepath):
|
|
||||||
log.warning(f"Invalid configuration, no such file: {filepath}")
|
|
||||||
|
|
||||||
cfg = Config(version_str, pep440_version, tag, commit, push, file_patterns)
|
|
||||||
log.debug(_debug_str(cfg))
|
log.debug(_debug_str(cfg))
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
|
|
@ -241,6 +274,7 @@ def parse(ctx: ProjectContext) -> MaybeConfig:
|
||||||
DEFAULT_CONFIGPARSER_BASE_TMPL = """
|
DEFAULT_CONFIGPARSER_BASE_TMPL = """
|
||||||
[pycalver]
|
[pycalver]
|
||||||
current_version = "{initial_version}"
|
current_version = "{initial_version}"
|
||||||
|
version_pattern = "{{pycalver}}"
|
||||||
commit = True
|
commit = True
|
||||||
tag = True
|
tag = True
|
||||||
push = True
|
push = True
|
||||||
|
|
@ -279,6 +313,7 @@ README.md =
|
||||||
DEFAULT_TOML_BASE_TMPL = """
|
DEFAULT_TOML_BASE_TMPL = """
|
||||||
[pycalver]
|
[pycalver]
|
||||||
current_version = "{initial_version}"
|
current_version = "{initial_version}"
|
||||||
|
version_pattern = "{{pycalver}}"
|
||||||
commit = true
|
commit = true
|
||||||
tag = true
|
tag = true
|
||||||
push = true
|
push = true
|
||||||
|
|
|
||||||
|
|
@ -5,50 +5,14 @@
|
||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
"""Parse PyCalVer strings from files."""
|
"""Parse PyCalVer strings from files."""
|
||||||
|
|
||||||
import re
|
|
||||||
import logging
|
import logging
|
||||||
import typing as typ
|
import typing as typ
|
||||||
|
|
||||||
|
from . import patterns
|
||||||
|
|
||||||
log = logging.getLogger("pycalver.parse")
|
log = logging.getLogger("pycalver.parse")
|
||||||
|
|
||||||
|
|
||||||
VALID_RELEASE_VALUES = ("alpha", "beta", "dev", "rc", "post", "final")
|
|
||||||
|
|
||||||
|
|
||||||
PATTERN_ESCAPES = [
|
|
||||||
("\u005c", "\u005c\u005c"),
|
|
||||||
("-" , "\u005c-"),
|
|
||||||
("." , "\u005c."),
|
|
||||||
("+" , "\u005c+"),
|
|
||||||
("*" , "\u005c*"),
|
|
||||||
("{" , "\u005c{{"),
|
|
||||||
("}" , "\u005c}}"),
|
|
||||||
("[" , "\u005c["),
|
|
||||||
("]" , "\u005c]"),
|
|
||||||
("(" , "\u005c("),
|
|
||||||
(")" , "\u005c)"),
|
|
||||||
]
|
|
||||||
|
|
||||||
# NOTE (mb 2018-09-03): These are matchers for parts, which are
|
|
||||||
# used in the patterns, they're not for validation. This means
|
|
||||||
# that they may find strings, which are not valid pycalver
|
|
||||||
# strings, when parsed in their full context. For such cases,
|
|
||||||
# the patterns should be expanded.
|
|
||||||
|
|
||||||
|
|
||||||
RE_PATTERN_PARTS = {
|
|
||||||
'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|final))?",
|
|
||||||
'calver' : r"v\d{6}",
|
|
||||||
'year' : r"\d{4}",
|
|
||||||
'month' : r"\d{2}",
|
|
||||||
'build' : r"\.\d{4,}",
|
|
||||||
'build_no' : r"\d{4,}",
|
|
||||||
'release' : r"(\-(alpha|beta|dev|rc|post|final))?",
|
|
||||||
'release_tag' : r"(alpha|beta|dev|rc|post|final)?",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PatternMatch(typ.NamedTuple):
|
class PatternMatch(typ.NamedTuple):
|
||||||
"""Container to mark a version string in a file."""
|
"""Container to mark a version string in a file."""
|
||||||
|
|
||||||
|
|
@ -62,26 +26,10 @@ class PatternMatch(typ.NamedTuple):
|
||||||
PatternMatches = typ.Iterable[PatternMatch]
|
PatternMatches = typ.Iterable[PatternMatch]
|
||||||
|
|
||||||
|
|
||||||
def compile_pattern(pattern: str) -> typ.Pattern[str]:
|
|
||||||
pattern_tmpl = pattern
|
|
||||||
|
|
||||||
for char, escaped in PATTERN_ESCAPES:
|
|
||||||
pattern_tmpl = pattern_tmpl.replace(char, escaped)
|
|
||||||
|
|
||||||
# undo escaping only for valid part names
|
|
||||||
for part_name in RE_PATTERN_PARTS.keys():
|
|
||||||
pattern_tmpl = pattern_tmpl.replace(
|
|
||||||
"\u005c{{" + part_name + "\u005c}}", "{" + part_name + "}"
|
|
||||||
)
|
|
||||||
|
|
||||||
pattern_str = pattern_tmpl.format(**RE_PATTERN_PARTS)
|
|
||||||
return re.compile(pattern_str)
|
|
||||||
|
|
||||||
|
|
||||||
def _iter_for_pattern(lines: typ.List[str], pattern: str) -> PatternMatches:
|
def _iter_for_pattern(lines: typ.List[str], pattern: str) -> PatternMatches:
|
||||||
# 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 = compile_pattern(pattern)
|
pattern_re = patterns.compile_pattern(pattern)
|
||||||
|
|
||||||
for lineno, line in enumerate(lines):
|
for lineno, line in enumerate(lines):
|
||||||
match = pattern_re.search(line)
|
match = pattern_re.search(line)
|
||||||
|
|
@ -93,12 +41,12 @@ def iter_matches(lines: typ.List[str], patterns: typ.List[str]) -> PatternMatche
|
||||||
"""Iterate over all matches of any pattern on any line.
|
"""Iterate over all matches of any pattern on any line.
|
||||||
|
|
||||||
>>> lines = ["__version__ = 'v201712.0002-alpha'"]
|
>>> lines = ["__version__ = 'v201712.0002-alpha'"]
|
||||||
>>> patterns = ["{version}", "{pep440_version}"]
|
>>> patterns = ["{pycalver}", "{pep440_pycalver}"]
|
||||||
>>> matches = list(iter_matches(lines, patterns))
|
>>> matches = list(iter_matches(lines, patterns))
|
||||||
>>> assert matches[0] == PatternMatch(
|
>>> assert matches[0] == PatternMatch(
|
||||||
... lineno = 0,
|
... lineno = 0,
|
||||||
... line = "__version__ = 'v201712.0002-alpha'",
|
... line = "__version__ = 'v201712.0002-alpha'",
|
||||||
... pattern= "{version}",
|
... pattern= "{pycalver}",
|
||||||
... span = (15, 33),
|
... span = (15, 33),
|
||||||
... match = "v201712.0002-alpha",
|
... match = "v201712.0002-alpha",
|
||||||
... )
|
... )
|
||||||
|
|
|
||||||
200
src/pycalver/patterns.py
Normal file
200
src/pycalver/patterns.py
Normal file
|
|
@ -0,0 +1,200 @@
|
||||||
|
# This file is part of the pycalver project
|
||||||
|
# https://github.com/mbarkhau/pycalver
|
||||||
|
#
|
||||||
|
# Copyright (c) 2018 Manuel Barkhau (@mbarkhau) - MIT License
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""Compose Regular Expressions from Patterns.
|
||||||
|
|
||||||
|
>>> version_info = PYCALVER_RE.match("v201712.0123-alpha").groupdict()
|
||||||
|
>>> assert version_info == {
|
||||||
|
... "pycalver" : "v201712.0123-alpha",
|
||||||
|
... "vYYYYMM" : "v201712",
|
||||||
|
... "year" : "2017",
|
||||||
|
... "month" : "12",
|
||||||
|
... "build" : ".0123",
|
||||||
|
... "build_no" : "0123",
|
||||||
|
... "release" : "-alpha",
|
||||||
|
... "release_tag" : "alpha",
|
||||||
|
... }
|
||||||
|
>>>
|
||||||
|
>>> version_info = PYCALVER_RE.match("v201712.0033").groupdict()
|
||||||
|
>>> assert version_info == {
|
||||||
|
... "pycalver" : "v201712.0033",
|
||||||
|
... "vYYYYMM" : "v201712",
|
||||||
|
... "year" : "2017",
|
||||||
|
... "month" : "12",
|
||||||
|
... "build" : ".0033",
|
||||||
|
... "build_no" : "0033",
|
||||||
|
... "release" : None,
|
||||||
|
... "release_tag": None,
|
||||||
|
... }
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
import typing as typ
|
||||||
|
|
||||||
|
# https://regex101.com/r/fnj60p/10
|
||||||
|
PYCALVER_PATTERN = r"""
|
||||||
|
\b
|
||||||
|
(?P<pycalver>
|
||||||
|
(?P<vYYYYMM>
|
||||||
|
v # "v" version prefix
|
||||||
|
(?P<year>\d{4})
|
||||||
|
(?P<month>\d{2})
|
||||||
|
)
|
||||||
|
(?P<build>
|
||||||
|
\. # "." build nr prefix
|
||||||
|
(?P<build_no>\d{4,})
|
||||||
|
)
|
||||||
|
(?P<release>
|
||||||
|
\- # "-" release prefix
|
||||||
|
(?P<release_tag>alpha|beta|dev|rc|post)
|
||||||
|
)?
|
||||||
|
)(?:\s|$)
|
||||||
|
"""
|
||||||
|
|
||||||
|
PYCALVER_RE: typ.Pattern[str] = re.compile(PYCALVER_PATTERN, flags=re.VERBOSE)
|
||||||
|
|
||||||
|
|
||||||
|
PATTERN_ESCAPES = [
|
||||||
|
("\u005c", "\u005c\u005c"),
|
||||||
|
("-" , "\u005c-"),
|
||||||
|
("." , "\u005c."),
|
||||||
|
("+" , "\u005c+"),
|
||||||
|
("*" , "\u005c*"),
|
||||||
|
("{" , "\u005c{"),
|
||||||
|
("}" , "\u005c}"),
|
||||||
|
("[" , "\u005c["),
|
||||||
|
("]" , "\u005c]"),
|
||||||
|
("(" , "\u005c("),
|
||||||
|
(")" , "\u005c)"),
|
||||||
|
]
|
||||||
|
|
||||||
|
COMPOSITE_PART_PATTERNS = {
|
||||||
|
'pep440_pycalver': r"{year}{month}\.{BID}(?:{pep440_tag})?",
|
||||||
|
'pycalver' : r"v{year}{month}\.{bid}(?:-{tag})?",
|
||||||
|
'calver' : r"v{year}{month}",
|
||||||
|
'semver' : r"{MAJOR}\.{MINOR}\.{PATCH}",
|
||||||
|
'release_tag' : r"{tag}",
|
||||||
|
'build' : r"\.{bid}",
|
||||||
|
'release' : r"-{tag}",
|
||||||
|
# depricated
|
||||||
|
'pep440_version': r"{year}{month}\.{BID}(?:{pep440_tag})?",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PART_PATTERNS = {
|
||||||
|
'year' : r"\d{4}",
|
||||||
|
'month' : r"(?:0[0-9]|1[0-2])",
|
||||||
|
'build_no' : r"\d{4,}",
|
||||||
|
'pep440_tag': r"(?:a|b|dev|rc|post)?\d*",
|
||||||
|
'tag' : r"(?:alpha|beta|dev|rc|post|final)",
|
||||||
|
'yy' : r"\d{2}",
|
||||||
|
'yyyy' : r"\d{4}",
|
||||||
|
'quarter' : r"[1-4]",
|
||||||
|
'iso_week' : r"(?:[0-4]\d|5[0-3])",
|
||||||
|
'us_week' : r"(?:[0-4]\d|5[0-3])",
|
||||||
|
'dom' : r"(0[1-9]|[1-2][0-9]|3[0-1])",
|
||||||
|
'doy' : r"(?:[0-2]\d\d|3[0-5][0-9]|36[0-6])",
|
||||||
|
'MAJOR' : r"\d+",
|
||||||
|
'MINOR' : r"\d+",
|
||||||
|
'MM' : r"\d{2,}",
|
||||||
|
'MMM' : r"\d{3,}",
|
||||||
|
'MMMM' : r"\d{4,}",
|
||||||
|
'MMMMM' : r"\d{5,}",
|
||||||
|
'PATCH' : r"\d+",
|
||||||
|
'PP' : r"\d{2,}",
|
||||||
|
'PPP' : r"\d{3,}",
|
||||||
|
'PPPP' : r"\d{4,}",
|
||||||
|
'PPPPP' : r"\d{5,}",
|
||||||
|
'bid' : r"\d{4,}",
|
||||||
|
'BID' : r"[1-9]\d*",
|
||||||
|
'BB' : r"[1-9]\d{1,}",
|
||||||
|
'BBB' : r"[1-9]\d{2,}",
|
||||||
|
'BBBB' : r"[1-9]\d{3,}",
|
||||||
|
'BBBBB' : r"[1-9]\d{4,}",
|
||||||
|
'BBBBBB' : r"[1-9]\d{5,}",
|
||||||
|
'BBBBBBB' : r"[1-9]\d{6,}",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FULL_PART_FORMATS = {
|
||||||
|
'pep440_pycalver': "{year}{month:02}.{BID}{pep440_tag}",
|
||||||
|
'pycalver' : "v{year}{month:02}.{bid}{release}",
|
||||||
|
'calver' : "v{year}{month:02}",
|
||||||
|
'semver' : "{MAJOR}.{MINOR}.{PATCH}",
|
||||||
|
'release_tag' : "{tag}",
|
||||||
|
'build' : ".{bid}",
|
||||||
|
# NOTE (mb 2019-01-04): since release is optional, it
|
||||||
|
# is treates specially in version.format
|
||||||
|
# 'release' : "-{tag}",
|
||||||
|
'month' : "{month:02}",
|
||||||
|
'build_no': "{bid}",
|
||||||
|
'iso_week': "{iso_week:02}",
|
||||||
|
'us_week' : "{us_week:02}",
|
||||||
|
'dom' : "{dom:02}",
|
||||||
|
'doy' : "{doy:03}",
|
||||||
|
# depricated
|
||||||
|
'pep440_version': "{year}{month:02}.{BID}{pep440_tag}",
|
||||||
|
'version' : "v{year}{month:02}.{bid}{release}",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PART_FORMATS = {
|
||||||
|
'major' : "[0-9]+",
|
||||||
|
'minor' : "[0-9]{3,}",
|
||||||
|
'patch' : "[0-9]{3,}",
|
||||||
|
'bid' : "[0-9]{4,}",
|
||||||
|
'MAJOR' : "[0-9]+",
|
||||||
|
'MINOR' : "[0-9]+",
|
||||||
|
'MM' : "[0-9]{2,}",
|
||||||
|
'MMM' : "[0-9]{3,}",
|
||||||
|
'MMMM' : "[0-9]{4,}",
|
||||||
|
'MMMMM' : "[0-9]{5,}",
|
||||||
|
'MMMMMM' : "[0-9]{6,}",
|
||||||
|
'MMMMMMM': "[0-9]{7,}",
|
||||||
|
'PATCH' : "[0-9]+",
|
||||||
|
'PP' : "[0-9]{2,}",
|
||||||
|
'PPP' : "[0-9]{3,}",
|
||||||
|
'PPPP' : "[0-9]{4,}",
|
||||||
|
'PPPPP' : "[0-9]{5,}",
|
||||||
|
'PPPPPP' : "[0-9]{6,}",
|
||||||
|
'PPPPPPP': "[0-9]{7,}",
|
||||||
|
'BID' : "[1-9][0-9]*",
|
||||||
|
'BB' : "[1-9][0-9]{1,}",
|
||||||
|
'BBB' : "[1-9][0-9]{2,}",
|
||||||
|
'BBBB' : "[1-9][0-9]{3,}",
|
||||||
|
'BBBBB' : "[1-9][0-9]{4,}",
|
||||||
|
'BBBBBB' : "[1-9][0-9]{5,}",
|
||||||
|
'BBBBBBB': "[1-9][0-9]{6,}",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _replace_pattern_parts(pattern: str) -> str:
|
||||||
|
for part_name, part_pattern in PART_PATTERNS.items():
|
||||||
|
named_part_pattern = f"(?P<{part_name}>{part_pattern})"
|
||||||
|
placeholder = "\u005c{" + part_name + "\u005c}"
|
||||||
|
pattern = pattern.replace(placeholder, named_part_pattern)
|
||||||
|
return pattern
|
||||||
|
|
||||||
|
|
||||||
|
def _compile_pattern(pattern: str) -> str:
|
||||||
|
for char, escaped in PATTERN_ESCAPES:
|
||||||
|
pattern = pattern.replace(char, escaped)
|
||||||
|
|
||||||
|
return _replace_pattern_parts(pattern)
|
||||||
|
|
||||||
|
|
||||||
|
def compile_pattern(pattern: str) -> typ.Pattern[str]:
|
||||||
|
pattern_str = _compile_pattern(pattern)
|
||||||
|
return re.compile(pattern_str)
|
||||||
|
|
||||||
|
|
||||||
|
def _init_composite_patterns() -> None:
|
||||||
|
for part_name, part_pattern in COMPOSITE_PART_PATTERNS.items():
|
||||||
|
part_pattern = part_pattern.replace("{", "\u005c{").replace("}", "\u005c}")
|
||||||
|
pattern_str = _replace_pattern_parts(part_pattern)
|
||||||
|
PART_PATTERNS[part_name] = pattern_str
|
||||||
|
|
||||||
|
|
||||||
|
_init_composite_patterns()
|
||||||
|
|
@ -43,18 +43,20 @@ def rewrite_lines(
|
||||||
) -> typ.List[str]:
|
) -> typ.List[str]:
|
||||||
"""Replace occurances of patterns in old_lines with new_version.
|
"""Replace occurances of patterns in old_lines with new_version.
|
||||||
|
|
||||||
>>> old_lines = ['__version__ = "v201809.0002-beta"']
|
>>> patterns = ['__version__ = "{pycalver}"']
|
||||||
>>> patterns = ['__version__ = "{version}"']
|
>>> rewrite_lines(patterns, "v201811.0123-beta", ['__version__ = "v201809.0002-beta"'])
|
||||||
>>> new_lines = rewrite_lines(patterns, "v201811.0123-beta", old_lines)
|
['__version__ = "v201811.0123-beta"']
|
||||||
>>> assert new_lines == ['__version__ = "v201811.0123-beta"']
|
|
||||||
|
>>> patterns = ['__version__ = "{pep440_version}"']
|
||||||
|
>>> rewrite_lines(patterns, "v201811.0123-beta", ['__version__ = "201809.2b0"'])
|
||||||
|
['__version__ = "201811.123b0"']
|
||||||
"""
|
"""
|
||||||
new_version_nfo = version.parse_version_info(new_version)
|
new_version_nfo = version.parse_version_info(new_version)
|
||||||
new_version_fmt_kwargs = new_version_nfo._asdict()
|
|
||||||
|
|
||||||
new_lines = old_lines[:]
|
new_lines = old_lines[:]
|
||||||
|
|
||||||
for m in parse.iter_matches(old_lines, patterns):
|
for m in parse.iter_matches(old_lines, patterns):
|
||||||
replacement = m.pattern.format(**new_version_fmt_kwargs)
|
replacement = version.format_version(new_version_nfo, m.pattern)
|
||||||
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:]
|
||||||
new_lines[m.lineno] = new_line
|
new_lines[m.lineno] = new_line
|
||||||
|
|
@ -74,10 +76,11 @@ class RewrittenFileData(typ.NamedTuple):
|
||||||
def rfd_from_content(patterns: typ.List[str], new_version: str, content: str) -> RewrittenFileData:
|
def rfd_from_content(patterns: typ.List[str], new_version: str, content: str) -> RewrittenFileData:
|
||||||
r"""Rewrite pattern occurrences with version string.
|
r"""Rewrite pattern occurrences with version string.
|
||||||
|
|
||||||
>>> patterns = ['__version__ = "{version}"']
|
>>> patterns = ['__version__ = "{pycalver}"']
|
||||||
>>> content = '__version__ = "v201809.0001-alpha"'
|
>>> content = '__version__ = "v201809.0001-alpha"'
|
||||||
>>> rfd = rfd_from_content(patterns, "v201809.0123", content)
|
>>> rfd = rfd_from_content(patterns, "v201809.0123", content)
|
||||||
>>> assert rfd.new_lines == ['__version__ = "v201809.0123"']
|
>>> rfd.new_lines
|
||||||
|
['__version__ = "v201809.0123"']
|
||||||
"""
|
"""
|
||||||
line_sep = detect_line_sep(content)
|
line_sep = detect_line_sep(content)
|
||||||
old_lines = content.split(line_sep)
|
old_lines = content.split(line_sep)
|
||||||
|
|
@ -90,7 +93,7 @@ def iter_rewritten(
|
||||||
) -> typ.Iterable[RewrittenFileData]:
|
) -> typ.Iterable[RewrittenFileData]:
|
||||||
r'''Iterate over files with version string replaced.
|
r'''Iterate over files with version string replaced.
|
||||||
|
|
||||||
>>> file_patterns = {"src/pycalver/__init__.py": ['__version__ = "{version}"']}
|
>>> file_patterns = {"src/pycalver/__init__.py": ['__version__ = "{pycalver}"']}
|
||||||
>>> rewritten_datas = iter_rewritten(file_patterns, "v201809.0123")
|
>>> rewritten_datas = iter_rewritten(file_patterns, "v201809.0123")
|
||||||
>>> rfd = list(rewritten_datas)[0]
|
>>> rfd = list(rewritten_datas)[0]
|
||||||
>>> assert rfd.new_lines == [
|
>>> assert rfd.new_lines == [
|
||||||
|
|
@ -135,7 +138,7 @@ def diff_lines(rfd: RewrittenFileData) -> typ.List[str]:
|
||||||
def diff(new_version: str, file_patterns: config.PatternsByFilePath) -> str:
|
def diff(new_version: str, file_patterns: config.PatternsByFilePath) -> str:
|
||||||
r"""Generate diffs of rewritten files.
|
r"""Generate diffs of rewritten files.
|
||||||
|
|
||||||
>>> file_patterns = {"src/pycalver/__init__.py": ['__version__ = "{version}"']}
|
>>> file_patterns = {"src/pycalver/__init__.py": ['__version__ = "{pycalver}"']}
|
||||||
>>> diff_str = diff("v201809.0123", file_patterns)
|
>>> diff_str = diff("v201809.0123", file_patterns)
|
||||||
>>> lines = diff_str.split("\n")
|
>>> lines = diff_str.split("\n")
|
||||||
>>> lines[:2]
|
>>> lines[:2]
|
||||||
|
|
|
||||||
|
|
@ -3,164 +3,470 @@
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018 Manuel Barkhau (@mbarkhau) - MIT License
|
# Copyright (c) 2018 Manuel Barkhau (@mbarkhau) - MIT License
|
||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
"""Functions related to version string manipulation.
|
"""Functions related to version string manipulation."""
|
||||||
|
|
||||||
>>> version_info = PYCALVER_RE.match("v201712.0123-alpha").groupdict()
|
|
||||||
>>> assert version_info == {
|
|
||||||
... "version" : "v201712.0123-alpha",
|
|
||||||
... "calver" : "v201712",
|
|
||||||
... "year" : "2017",
|
|
||||||
... "month" : "12",
|
|
||||||
... "build" : ".0123",
|
|
||||||
... "build_no" : "0123",
|
|
||||||
... "release" : "-alpha",
|
|
||||||
... "release_tag" : "alpha",
|
|
||||||
... }
|
|
||||||
>>>
|
|
||||||
>>> version_info = PYCALVER_RE.match("v201712.0033").groupdict()
|
|
||||||
>>> assert version_info == {
|
|
||||||
... "version" : "v201712.0033",
|
|
||||||
... "calver" : "v201712",
|
|
||||||
... "year" : "2017",
|
|
||||||
... "month" : "12",
|
|
||||||
... "build" : ".0033",
|
|
||||||
... "build_no" : "0033",
|
|
||||||
... "release" : None,
|
|
||||||
... "release_tag": None,
|
|
||||||
... }
|
|
||||||
"""
|
|
||||||
|
|
||||||
import re
|
|
||||||
import logging
|
import logging
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
import typing as typ
|
import typing as typ
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
|
|
||||||
from . import lex_id
|
from . import lex_id
|
||||||
|
from . import patterns
|
||||||
|
|
||||||
log = logging.getLogger("pycalver.version")
|
log = logging.getLogger("pycalver.version")
|
||||||
|
|
||||||
|
|
||||||
# https://regex101.com/r/fnj60p/10
|
# The test suite may replace this.
|
||||||
PYCALVER_PATTERN = r"""
|
TODAY = dt.datetime.utcnow().date()
|
||||||
\b
|
|
||||||
(?P<version>
|
|
||||||
(?P<calver>
|
|
||||||
v # "v" version prefix
|
|
||||||
(?P<year>\d{4})
|
|
||||||
(?P<month>\d{2})
|
|
||||||
)
|
|
||||||
(?P<build>
|
|
||||||
\. # "." build nr prefix
|
|
||||||
(?P<build_no>\d{4,})
|
|
||||||
)
|
|
||||||
(?P<release>
|
|
||||||
\- # "-" release prefix
|
|
||||||
(?P<release_tag>alpha|beta|dev|rc|post)
|
|
||||||
)?
|
|
||||||
)(?:\s|$)
|
|
||||||
"""
|
|
||||||
|
|
||||||
PYCALVER_RE: typ.Pattern[str] = re.compile(PYCALVER_PATTERN, flags=re.VERBOSE)
|
|
||||||
|
PATTERN_PART_FIELDS = {
|
||||||
|
'year' : 'year',
|
||||||
|
'month' : 'month',
|
||||||
|
'pep440_tag': 'tag',
|
||||||
|
'tag' : 'tag',
|
||||||
|
'yy' : 'year',
|
||||||
|
'yyyy' : 'year',
|
||||||
|
'quarter' : 'quarter',
|
||||||
|
'iso_week' : 'iso_week',
|
||||||
|
'us_week' : 'us_week',
|
||||||
|
'dom' : 'dom',
|
||||||
|
'doy' : 'doy',
|
||||||
|
'MAJOR' : 'major',
|
||||||
|
'MINOR' : 'minor',
|
||||||
|
'MM' : 'minor',
|
||||||
|
'MMM' : 'minor',
|
||||||
|
'MMMM' : 'minor',
|
||||||
|
'MMMMM' : 'minor',
|
||||||
|
'PP' : 'patch',
|
||||||
|
'PPP' : 'patch',
|
||||||
|
'PPPP' : 'patch',
|
||||||
|
'PPPPP' : 'patch',
|
||||||
|
'PATCH' : 'patch',
|
||||||
|
'build_no' : 'bid',
|
||||||
|
'bid' : 'bid',
|
||||||
|
'BID' : 'bid',
|
||||||
|
'BB' : 'bid',
|
||||||
|
'BBB' : 'bid',
|
||||||
|
'BBBB' : 'bid',
|
||||||
|
'BBBBB' : 'bid',
|
||||||
|
'BBBBBB' : 'bid',
|
||||||
|
'BBBBBBB' : 'bid',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CalendarInfo(typ.NamedTuple):
|
||||||
|
"""Container for calendar components of version strings."""
|
||||||
|
|
||||||
|
year : int
|
||||||
|
quarter : int
|
||||||
|
month : int
|
||||||
|
dom : int
|
||||||
|
doy : int
|
||||||
|
iso_week: int
|
||||||
|
us_week : int
|
||||||
|
|
||||||
|
|
||||||
|
def _date_from_doy(year: int, doy: int) -> dt.date:
|
||||||
|
"""Parse date from year and day of year (1 indexed).
|
||||||
|
|
||||||
|
>>> cases = [
|
||||||
|
... (2016, 1), (2016, 31), (2016, 31 + 1), (2016, 31 + 29), (2016, 31 + 30),
|
||||||
|
... (2017, 1), (2017, 31), (2017, 31 + 1), (2017, 31 + 28), (2017, 31 + 29),
|
||||||
|
... ]
|
||||||
|
>>> dates = [_date_from_doy(year, month) for year, month in cases]
|
||||||
|
>>> assert [(d.month, d.day) for d in dates] == [
|
||||||
|
... (1, 1), (1, 31), (2, 1), (2, 29), (3, 1),
|
||||||
|
... (1, 1), (1, 31), (2, 1), (2, 28), (3, 1),
|
||||||
|
... ]
|
||||||
|
"""
|
||||||
|
return dt.date(year, 1, 1) + dt.timedelta(days=doy - 1)
|
||||||
|
|
||||||
|
|
||||||
|
def _quarter_from_month(month: int) -> int:
|
||||||
|
"""Calculate quarter (1 indexed) from month (1 indexed).
|
||||||
|
|
||||||
|
>>> [_quarter_from_month(month) for month in range(1, 13)]
|
||||||
|
[1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4]
|
||||||
|
"""
|
||||||
|
return ((month - 1) // 3) + 1
|
||||||
|
|
||||||
|
|
||||||
|
def cal_info(date: dt.date = None) -> CalendarInfo:
|
||||||
|
"""Generate calendar components for current date.
|
||||||
|
|
||||||
|
>>> from datetime import date
|
||||||
|
|
||||||
|
>>> c = cal_info(date(2019, 1, 5))
|
||||||
|
>>> (c.year, c.quarter, c.month, c.dom, c.doy, c.iso_week, c.us_week)
|
||||||
|
(2019, 1, 1, 5, 5, 0, 0)
|
||||||
|
|
||||||
|
>>> c = cal_info(date(2019, 1, 6))
|
||||||
|
>>> (c.year, c.quarter, c.month, c.dom, c.doy, c.iso_week, c.us_week)
|
||||||
|
(2019, 1, 1, 6, 6, 0, 1)
|
||||||
|
|
||||||
|
>>> c = cal_info(date(2019, 1, 7))
|
||||||
|
>>> (c.year, c.quarter, c.month, c.dom, c.doy, c.iso_week, c.us_week)
|
||||||
|
(2019, 1, 1, 7, 7, 1, 1)
|
||||||
|
|
||||||
|
>>> c = cal_info(date(2019, 4, 7))
|
||||||
|
>>> (c.year, c.quarter, c.month, c.dom, c.doy, c.iso_week, c.us_week)
|
||||||
|
(2019, 2, 4, 7, 97, 13, 14)
|
||||||
|
"""
|
||||||
|
if date is None:
|
||||||
|
date = TODAY
|
||||||
|
|
||||||
|
kw = {
|
||||||
|
'year' : date.year,
|
||||||
|
'quarter' : _quarter_from_month(date.month),
|
||||||
|
'month' : date.month,
|
||||||
|
'dom' : date.day,
|
||||||
|
'doy' : int(date.strftime("%j"), base=10),
|
||||||
|
'iso_week': int(date.strftime("%W"), base=10),
|
||||||
|
'us_week' : int(date.strftime("%U"), base=10),
|
||||||
|
}
|
||||||
|
|
||||||
|
return CalendarInfo(**kw)
|
||||||
|
|
||||||
|
|
||||||
class VersionInfo(typ.NamedTuple):
|
class VersionInfo(typ.NamedTuple):
|
||||||
"""Container for parsed version string."""
|
"""Container for parsed version string."""
|
||||||
|
|
||||||
version : str
|
year : typ.Optional[int]
|
||||||
pep440_version: str
|
quarter : typ.Optional[int]
|
||||||
calver : str
|
month : typ.Optional[int]
|
||||||
year : str
|
dom : typ.Optional[int]
|
||||||
month : str
|
doy : typ.Optional[int]
|
||||||
build : str
|
iso_week: typ.Optional[int]
|
||||||
build_no : str
|
us_week : typ.Optional[int]
|
||||||
release : typ.Optional[str]
|
major : int
|
||||||
release_tag : typ.Optional[str]
|
minor : int
|
||||||
|
patch : int
|
||||||
|
bid : str
|
||||||
|
tag : str
|
||||||
|
|
||||||
|
|
||||||
def parse_version_info(version_str: str) -> VersionInfo:
|
def _is_calver(nfo: typ.Union[CalendarInfo, VersionInfo]) -> bool:
|
||||||
"""Parse a PyCalVer string.
|
"""Check pattern for any calendar based parts.
|
||||||
|
|
||||||
>>> vnfo = parse_version_info("v201712.0033-beta")
|
>>> _is_calver(cal_info())
|
||||||
>>> assert vnfo == VersionInfo(
|
True
|
||||||
... version ="v201712.0033-beta",
|
|
||||||
... pep440_version="201712.33b0",
|
>>> vnfo = _parse_version_info({'year': "2018", 'month': "11", 'bid': "0018"})
|
||||||
... calver ="v201712",
|
>>> _is_calver(vnfo)
|
||||||
... year ="2017",
|
True
|
||||||
... month ="12",
|
|
||||||
... build =".0033",
|
>>> vnfo = _parse_version_info({'MAJOR': "1", 'MINOR': "023", 'PATCH': "45"})
|
||||||
... build_no ="0033",
|
>>> _is_calver(vnfo)
|
||||||
... release ="-beta",
|
False
|
||||||
... release_tag ="beta",
|
|
||||||
... )
|
|
||||||
"""
|
"""
|
||||||
match = PYCALVER_RE.match(version_str)
|
for field in CalendarInfo._fields:
|
||||||
if match is None:
|
if isinstance(getattr(nfo, field, None), int):
|
||||||
raise ValueError(f"Invalid PyCalVer string: {version_str}")
|
return True
|
||||||
|
|
||||||
kwargs = match.groupdict()
|
return False
|
||||||
kwargs['pep440_version'] = pycalver_to_pep440(kwargs['version'])
|
|
||||||
if kwargs['release'] is None:
|
|
||||||
kwargs['release'] = "-final"
|
|
||||||
if kwargs['release_tag'] is None:
|
|
||||||
kwargs['release_tag'] = "final"
|
|
||||||
return VersionInfo(**kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def current_calver() -> str:
|
TAG_ALIASES: typ.Dict[str, str] = {'a': "alpha", 'b': "beta", 'pre': "rc"}
|
||||||
"""Generate calver version string based on current date.
|
|
||||||
|
|
||||||
example result: "v201812"
|
|
||||||
"""
|
|
||||||
return dt.date.today().strftime("v%Y%m")
|
|
||||||
|
|
||||||
|
|
||||||
def incr(old_version: str, *, release: str = None) -> str:
|
PEP440_TAGS: typ.Dict[str, str] = {'alpha': "a", 'beta': "b", 'final': "", 'rc': "rc", 'dev': "dev"}
|
||||||
"""Increment a full PyCalVer version string.
|
|
||||||
|
|
||||||
Old_version is assumed to be a valid calver string,
|
|
||||||
already validated in pycalver.config.parse.
|
|
||||||
"""
|
|
||||||
old_ver = parse_version_info(old_version)
|
|
||||||
|
|
||||||
new_calver = current_calver()
|
VersionInfoKW = typ.Dict[str, typ.Union[str, int, None]]
|
||||||
|
|
||||||
if old_ver.calver > new_calver:
|
|
||||||
log.warning(
|
def _parse_pattern_groups(pattern_groups: typ.Dict[str, str]) -> typ.Dict[str, str]:
|
||||||
f"'version.incr' called with '{old_version}', "
|
for part_name in pattern_groups.keys():
|
||||||
+ f"which is from the future, "
|
is_valid_part_name = (
|
||||||
+ f"maybe your system clock is out of sync."
|
part_name in patterns.COMPOSITE_PART_PATTERNS or part_name in PATTERN_PART_FIELDS
|
||||||
)
|
)
|
||||||
# leave calver as is (don't go back in time)
|
if not is_valid_part_name:
|
||||||
new_calver = old_ver.calver
|
err_msg = f"Invalid part '{part_name}'"
|
||||||
|
raise ValueError(err_msg)
|
||||||
|
|
||||||
new_build = lex_id.next_id(old_ver.build[1:])
|
items = [
|
||||||
new_release: typ.Optional[str] = None
|
(field_name, pattern_groups[part_name])
|
||||||
|
for part_name, field_name in PATTERN_PART_FIELDS.items()
|
||||||
|
if part_name in pattern_groups.keys()
|
||||||
|
]
|
||||||
|
all_fields = [field_name for field_name, _ in items]
|
||||||
|
unique_fields = set(all_fields)
|
||||||
|
duplicate_fields = [f for f in unique_fields if all_fields.count(f) > 1]
|
||||||
|
|
||||||
if release is None:
|
if any(duplicate_fields):
|
||||||
if old_ver.release:
|
err_msg = f"Multiple parts for same field {duplicate_fields}."
|
||||||
# preserve existing release
|
raise ValueError(err_msg)
|
||||||
new_release = old_ver.release[1:]
|
|
||||||
|
return dict(items)
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_version_info(pattern_groups: typ.Dict[str, str]) -> VersionInfo:
|
||||||
|
"""Parse normalized VersionInfo from groups of a matched pattern.
|
||||||
|
|
||||||
|
>>> vnfo = _parse_version_info({'year': "2018", 'month': "11", 'bid': "0099"})
|
||||||
|
>>> (vnfo.year, vnfo.month, vnfo.quarter, vnfo.bid, vnfo.tag)
|
||||||
|
(2018, 11, 4, '0099', 'final')
|
||||||
|
|
||||||
|
>>> vnfo = _parse_version_info({'year': "2018", 'doy': "11", 'bid': "099", 'tag': "b"})
|
||||||
|
>>> (vnfo.year, vnfo.month, vnfo.dom, vnfo.bid, vnfo.tag)
|
||||||
|
(2018, 1, 11, '099', 'beta')
|
||||||
|
|
||||||
|
>>> vnfo = _parse_version_info({'MAJOR': "1", 'MINOR': "23", 'PATCH': "45"})
|
||||||
|
>>> (vnfo.major, vnfo.minor, vnfo.patch)
|
||||||
|
(1, 23, 45)
|
||||||
|
|
||||||
|
>>> vnfo = _parse_version_info({'MAJOR': "1", 'MMM': "023", 'PPPP': "0045"})
|
||||||
|
>>> (vnfo.major, vnfo.minor, vnfo.patch)
|
||||||
|
(1, 23, 45)
|
||||||
|
"""
|
||||||
|
kw = _parse_pattern_groups(pattern_groups)
|
||||||
|
|
||||||
|
tag = kw.get('tag')
|
||||||
|
if tag is None:
|
||||||
|
tag = "final"
|
||||||
|
tag = TAG_ALIASES.get(tag, tag)
|
||||||
|
assert tag is not None
|
||||||
|
|
||||||
|
bid = kw['bid'] if 'bid' in kw else "0001"
|
||||||
|
|
||||||
|
year = int(kw['year']) if 'year' in kw else None
|
||||||
|
doy = int(kw['doy' ]) if 'doy' in kw else None
|
||||||
|
|
||||||
|
month: typ.Optional[int]
|
||||||
|
dom : typ.Optional[int]
|
||||||
|
|
||||||
|
if year and doy:
|
||||||
|
date = _date_from_doy(year, doy)
|
||||||
|
month = date.month
|
||||||
|
dom = date.day
|
||||||
else:
|
else:
|
||||||
new_release = None
|
month = int(kw['month']) if 'month' in kw else None
|
||||||
elif release == 'final':
|
dom = int(kw['dom' ]) if 'dom' in kw else None
|
||||||
new_release = None
|
|
||||||
|
iso_week: typ.Optional[int]
|
||||||
|
us_week : typ.Optional[int]
|
||||||
|
|
||||||
|
if year and month and dom:
|
||||||
|
date = dt.date(year, month, dom)
|
||||||
|
doy = int(date.strftime("%j"), base=10)
|
||||||
|
iso_week = int(date.strftime("%W"), base=10)
|
||||||
|
us_week = int(date.strftime("%U"), base=10)
|
||||||
else:
|
else:
|
||||||
new_release = release
|
iso_week = None
|
||||||
|
us_week = None
|
||||||
|
|
||||||
if new_release == 'final':
|
quarter = int(kw['quarter']) if 'quarter' in kw else None
|
||||||
new_release = None
|
if quarter is None and month:
|
||||||
|
quarter = _quarter_from_month(month)
|
||||||
|
|
||||||
new_version = new_calver + "." + new_build
|
major = int(kw['major']) if 'major' in kw else 0
|
||||||
if new_release:
|
minor = int(kw['minor']) if 'minor' in kw else 0
|
||||||
new_version += "-" + new_release
|
patch = int(kw['patch']) if 'patch' in kw else 0
|
||||||
|
|
||||||
|
return VersionInfo(
|
||||||
|
year=year,
|
||||||
|
quarter=quarter,
|
||||||
|
month=month,
|
||||||
|
dom=dom,
|
||||||
|
doy=doy,
|
||||||
|
iso_week=iso_week,
|
||||||
|
us_week=us_week,
|
||||||
|
major=major,
|
||||||
|
minor=minor,
|
||||||
|
patch=patch,
|
||||||
|
bid=bid,
|
||||||
|
tag=tag,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_version_info(version_str: str, pattern: str = "{pycalver}") -> VersionInfo:
|
||||||
|
"""Parse normalized VersionInfo.
|
||||||
|
|
||||||
|
>>> vnfo = parse_version_info("v201712.0033-beta", pattern="{pycalver}")
|
||||||
|
>>> assert vnfo == _parse_version_info({'year': 2017, 'month': 12, 'bid': "0033", 'tag': "beta"})
|
||||||
|
|
||||||
|
>>> vnfo = parse_version_info("1.23.456", pattern="{semver}")
|
||||||
|
>>> assert vnfo == _parse_version_info({'MAJOR': "1", 'MINOR': "23", 'PATCH': "456"})
|
||||||
|
"""
|
||||||
|
regex = patterns.compile_pattern(pattern)
|
||||||
|
match = regex.match(version_str)
|
||||||
|
if match is None:
|
||||||
|
err_msg = (
|
||||||
|
f"Invalid version string '{version_str}' for pattern '{pattern}'/'{regex.pattern}'"
|
||||||
|
)
|
||||||
|
raise ValueError(err_msg)
|
||||||
|
|
||||||
|
return _parse_version_info(match.groupdict())
|
||||||
|
|
||||||
|
|
||||||
|
def is_valid(version_str: str, pattern: str = "{pycalver}") -> bool:
|
||||||
|
"""Check if a version matches a pattern.
|
||||||
|
|
||||||
|
>>> is_valid("v201712.0033-beta", pattern="{pycalver}")
|
||||||
|
True
|
||||||
|
>>> is_valid("v201712.0033-beta", pattern="{semver}")
|
||||||
|
False
|
||||||
|
>>> is_valid("1.2.3", pattern="{semver}")
|
||||||
|
True
|
||||||
|
>>> is_valid("v201712.0033-beta", pattern="{semver}")
|
||||||
|
False
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
parse_version_info(version_str, pattern)
|
||||||
|
return True
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
ID_FIELDS_BY_PART = {
|
||||||
|
'MAJOR' : 'major',
|
||||||
|
'MINOR' : 'minor',
|
||||||
|
'MM' : 'minor',
|
||||||
|
'MMM' : 'minor',
|
||||||
|
'MMMM' : 'minor',
|
||||||
|
'MMMMM' : 'minor',
|
||||||
|
'MMMMMM' : 'minor',
|
||||||
|
'MMMMMMM': 'minor',
|
||||||
|
'PATCH' : 'patch',
|
||||||
|
'PP' : 'patch',
|
||||||
|
'PPP' : 'patch',
|
||||||
|
'PPPP' : 'patch',
|
||||||
|
'PPPPP' : 'patch',
|
||||||
|
'PPPPPP' : 'patch',
|
||||||
|
'PPPPPPP': 'patch',
|
||||||
|
'BID' : 'bid',
|
||||||
|
'BB' : 'bid',
|
||||||
|
'BBB' : 'bid',
|
||||||
|
'BBBB' : 'bid',
|
||||||
|
'BBBBB' : 'bid',
|
||||||
|
'BBBBBB' : 'bid',
|
||||||
|
'BBBBBBB': 'bid',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def format_version(ver_nfo: VersionInfo, pattern: str) -> str:
|
||||||
|
"""Generate version string.
|
||||||
|
|
||||||
|
>>> import datetime as dt
|
||||||
|
>>> ver_nfo = parse_version_info("v201712.0033-beta", pattern="{pycalver}")
|
||||||
|
>>> ver_nfo_a = ver_nfo._replace(**cal_info(date=dt.date(2017, 1, 1))._asdict())
|
||||||
|
>>> ver_nfo_b = ver_nfo._replace(**cal_info(date=dt.date(2017, 12, 31))._asdict())
|
||||||
|
>>> ver_nfo_c = ver_nfo_b._replace(major=1, minor=2, patch=34, tag='final')
|
||||||
|
|
||||||
|
>>> format_version(ver_nfo_a, pattern="v{yy}.{BID}{release}")
|
||||||
|
'v17.33-beta'
|
||||||
|
>>> format_version(ver_nfo_a, pattern="{pep440_version}")
|
||||||
|
'201701.33b0'
|
||||||
|
|
||||||
|
>>> format_version(ver_nfo_a, pattern="{pycalver}")
|
||||||
|
'v201701.0033-beta'
|
||||||
|
>>> format_version(ver_nfo_b, pattern="{pycalver}")
|
||||||
|
'v201712.0033-beta'
|
||||||
|
|
||||||
|
>>> format_version(ver_nfo_a, pattern="v{year}w{iso_week}.{BID}{release}")
|
||||||
|
'v2017w00.33-beta'
|
||||||
|
>>> format_version(ver_nfo_b, pattern="v{year}w{iso_week}.{BID}{release}")
|
||||||
|
'v2017w52.33-beta'
|
||||||
|
|
||||||
|
>>> format_version(ver_nfo_a, pattern="v{year}d{doy}.{bid}{release}")
|
||||||
|
'v2017d001.0033-beta'
|
||||||
|
>>> format_version(ver_nfo_b, pattern="v{year}d{doy}.{bid}{release}")
|
||||||
|
'v2017d365.0033-beta'
|
||||||
|
|
||||||
|
>>> format_version(ver_nfo_c, pattern="v{year}w{iso_week}.{BID}-{tag}")
|
||||||
|
'v2017w52.33-final'
|
||||||
|
>>> format_version(ver_nfo_c, pattern="v{year}w{iso_week}.{BID}{release}")
|
||||||
|
'v2017w52.33'
|
||||||
|
|
||||||
|
>>> format_version(ver_nfo_c, pattern="v{MAJOR}.{MINOR}.{PATCH}")
|
||||||
|
'v1.2.34'
|
||||||
|
>>> format_version(ver_nfo_c, pattern="v{MAJOR}.{MM}.{PPP}")
|
||||||
|
'v1.02.034'
|
||||||
|
"""
|
||||||
|
full_pattern = pattern
|
||||||
|
for part_name, full_part_format in patterns.FULL_PART_FORMATS.items():
|
||||||
|
full_pattern = full_pattern.replace("{" + part_name + "}", full_part_format)
|
||||||
|
|
||||||
|
kw = ver_nfo._asdict()
|
||||||
|
if kw['tag'] == 'final':
|
||||||
|
kw['release' ] = ""
|
||||||
|
kw['pep440_tag'] = ""
|
||||||
|
else:
|
||||||
|
kw['release' ] = "-" + kw['tag']
|
||||||
|
kw['pep440_tag'] = PEP440_TAGS[kw['tag']] + "0"
|
||||||
|
|
||||||
|
kw['release_tag'] = kw['tag']
|
||||||
|
|
||||||
|
kw['yy' ] = str(kw['year'])[-2:]
|
||||||
|
kw['yyyy'] = kw['year']
|
||||||
|
kw['BID' ] = int(kw['bid'], 10)
|
||||||
|
|
||||||
|
for part_name, field in ID_FIELDS_BY_PART.items():
|
||||||
|
val = kw[field]
|
||||||
|
if part_name.lower() == field.lower():
|
||||||
|
if isinstance(val, str):
|
||||||
|
kw[part_name] = int(val, base=10)
|
||||||
|
else:
|
||||||
|
kw[part_name] = val
|
||||||
|
else:
|
||||||
|
assert len(set(part_name)) == 1
|
||||||
|
padded_len = len(part_name)
|
||||||
|
kw[part_name] = str(val).zfill(padded_len)
|
||||||
|
|
||||||
|
return full_pattern.format(**kw)
|
||||||
|
|
||||||
|
|
||||||
|
def incr(
|
||||||
|
old_version: str,
|
||||||
|
pattern : str = "{pycalver}",
|
||||||
|
*,
|
||||||
|
release: str = None,
|
||||||
|
major : bool = False,
|
||||||
|
minor : bool = False,
|
||||||
|
patch : bool = False,
|
||||||
|
) -> typ.Optional[str]:
|
||||||
|
"""Increment version string.
|
||||||
|
|
||||||
|
'old_version' is assumed to be a string that matches 'pattern'
|
||||||
|
"""
|
||||||
|
old_ver_nfo = parse_version_info(old_version, pattern)
|
||||||
|
cur_ver_nfo = old_ver_nfo
|
||||||
|
|
||||||
|
cur_cal_nfo = cal_info()
|
||||||
|
|
||||||
|
old_date = (old_ver_nfo.year or 0, old_ver_nfo.month or 0, old_ver_nfo.dom or 0)
|
||||||
|
cur_date = (cur_cal_nfo.year , cur_cal_nfo.month , cur_cal_nfo.dom)
|
||||||
|
|
||||||
|
if old_date <= cur_date:
|
||||||
|
cur_ver_nfo = cur_ver_nfo._replace(**cur_cal_nfo._asdict())
|
||||||
|
else:
|
||||||
|
log.warning(f"Version appears to be from the future '{old_version}'")
|
||||||
|
|
||||||
|
cur_ver_nfo = cur_ver_nfo._replace(bid=lex_id.next_id(cur_ver_nfo.bid))
|
||||||
|
|
||||||
|
if major:
|
||||||
|
cur_ver_nfo = cur_ver_nfo._replace(major=cur_ver_nfo.major + 1, minor=0, patch=0)
|
||||||
|
if minor:
|
||||||
|
cur_ver_nfo = cur_ver_nfo._replace(minor=cur_ver_nfo.minor + 1, patch=0)
|
||||||
|
if patch:
|
||||||
|
cur_ver_nfo = cur_ver_nfo._replace(patch=cur_ver_nfo.patch + 1)
|
||||||
|
|
||||||
|
if release:
|
||||||
|
cur_ver_nfo = cur_ver_nfo._replace(tag=release)
|
||||||
|
|
||||||
|
new_version = format_version(cur_ver_nfo, pattern)
|
||||||
|
if new_version == old_version:
|
||||||
|
log.error("Invalid arguments or pattern, version did not change.")
|
||||||
|
return None
|
||||||
|
else:
|
||||||
return new_version
|
return new_version
|
||||||
|
|
||||||
|
|
||||||
def pycalver_to_pep440(version: str) -> str:
|
def to_pep440(version: str) -> str:
|
||||||
"""Derive pep440 compliant version string from PyCalVer version string.
|
"""Derive pep440 compliant version string from PyCalVer version string.
|
||||||
|
|
||||||
>>> pycalver_to_pep440("v201811.0007-beta")
|
>>> to_pep440("v201811.0007-beta")
|
||||||
'201811.7b0'
|
'201811.7b0'
|
||||||
"""
|
"""
|
||||||
return str(pkg_resources.parse_version(version))
|
return str(pkg_resources.parse_version(version))
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import pytest
|
||||||
from click.testing import CliRunner
|
from click.testing import CliRunner
|
||||||
|
|
||||||
import pycalver.config as config
|
import pycalver.config as config
|
||||||
import pycalver.version as version
|
import pycalver.patterns as patterns
|
||||||
import pycalver.__main__ as pycalver
|
import pycalver.__main__ as pycalver
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -80,18 +80,52 @@ def test_version(runner):
|
||||||
result = runner.invoke(pycalver.cli, ['--version', "--verbose"])
|
result = runner.invoke(pycalver.cli, ['--version', "--verbose"])
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
assert " version v20" in result.output
|
assert " version v20" in result.output
|
||||||
match = version.PYCALVER_RE.search(result.output)
|
match = patterns.PYCALVER_RE.search(result.output)
|
||||||
assert match
|
assert match
|
||||||
|
|
||||||
|
|
||||||
def test_incr(runner):
|
def test_incr_default(runner):
|
||||||
old_version = "v201701.0999-alpha"
|
old_version = "v201701.0999-alpha"
|
||||||
initial_version = config._initial_version()
|
initial_version = config._initial_version()
|
||||||
|
|
||||||
result = runner.invoke(pycalver.cli, ['test', old_version, "--verbose"])
|
result = runner.invoke(pycalver.cli, ['test', "--verbose", old_version])
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
new_version = initial_version.replace(".0001-alpha", ".11000-alpha")
|
new_version = initial_version.replace(".0001-alpha", ".11000-alpha")
|
||||||
assert f"PyCalVer Version: {new_version}\n" in result.output
|
assert f"Version: {new_version}\n" in result.output
|
||||||
|
|
||||||
|
|
||||||
|
def test_incr_semver(runner):
|
||||||
|
semver_pattern = "{MAJOR}.{MINOR}.{PATCH}"
|
||||||
|
old_version = "0.1.0"
|
||||||
|
new_version = "0.1.1"
|
||||||
|
|
||||||
|
result = runner.invoke(pycalver.cli, ['test', "--verbose", "--patch", old_version, "{semver}"])
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert f"Version: {new_version}\n" in result.output
|
||||||
|
|
||||||
|
result = runner.invoke(
|
||||||
|
pycalver.cli, ['test', "--verbose", "--patch", old_version, semver_pattern]
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert f"Version: {new_version}\n" in result.output
|
||||||
|
|
||||||
|
old_version = "0.1.1"
|
||||||
|
new_version = "0.2.0"
|
||||||
|
|
||||||
|
result = runner.invoke(
|
||||||
|
pycalver.cli, ['test', "--verbose", "--minor", old_version, semver_pattern]
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert f"Version: {new_version}\n" in result.output
|
||||||
|
|
||||||
|
old_version = "0.1.1"
|
||||||
|
new_version = "1.0.0"
|
||||||
|
|
||||||
|
result = runner.invoke(
|
||||||
|
pycalver.cli, ['test', "--verbose", "--major", old_version, semver_pattern]
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert f"Version: {new_version}\n" in result.output
|
||||||
|
|
||||||
|
|
||||||
def test_incr_to_beta(runner):
|
def test_incr_to_beta(runner):
|
||||||
|
|
@ -101,7 +135,7 @@ def test_incr_to_beta(runner):
|
||||||
result = runner.invoke(pycalver.cli, ['test', old_version, "--verbose", "--release", "beta"])
|
result = runner.invoke(pycalver.cli, ['test', old_version, "--verbose", "--release", "beta"])
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
new_version = initial_version.replace(".0001-alpha", ".11000-beta")
|
new_version = initial_version.replace(".0001-alpha", ".11000-beta")
|
||||||
assert f"PyCalVer Version: {new_version}\n" in result.output
|
assert f"Version: {new_version}\n" in result.output
|
||||||
|
|
||||||
|
|
||||||
def test_incr_to_final(runner):
|
def test_incr_to_final(runner):
|
||||||
|
|
@ -111,7 +145,7 @@ def test_incr_to_final(runner):
|
||||||
result = runner.invoke(pycalver.cli, ['test', old_version, "--verbose", "--release", "final"])
|
result = runner.invoke(pycalver.cli, ['test', old_version, "--verbose", "--release", "final"])
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
new_version = initial_version.replace(".0001-alpha", ".11000")
|
new_version = initial_version.replace(".0001-alpha", ".11000")
|
||||||
assert f"PyCalVer Version: {new_version}\n" in result.output
|
assert f"Version: {new_version}\n" in result.output
|
||||||
|
|
||||||
|
|
||||||
def test_incr_invalid(runner, caplog):
|
def test_incr_invalid(runner, caplog):
|
||||||
|
|
@ -164,7 +198,7 @@ def test_novcs_nocfg_init(runner):
|
||||||
result = runner.invoke(pycalver.cli, ['show', "--verbose"])
|
result = runner.invoke(pycalver.cli, ['show', "--verbose"])
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
assert f"Current Version: {config._initial_version()}\n" in result.output
|
assert f"Current Version: {config._initial_version()}\n" in result.output
|
||||||
assert f"PEP440 Version : {config._initial_version_pep440()}\n" in result.output
|
assert f"PEP440 : {config._initial_version_pep440()}\n" in result.output
|
||||||
|
|
||||||
|
|
||||||
def test_novcs_setupcfg_init(runner):
|
def test_novcs_setupcfg_init(runner):
|
||||||
|
|
@ -184,7 +218,7 @@ def test_novcs_setupcfg_init(runner):
|
||||||
result = runner.invoke(pycalver.cli, ['show', "--verbose"])
|
result = runner.invoke(pycalver.cli, ['show', "--verbose"])
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
assert f"Current Version: {config._initial_version()}\n" in result.output
|
assert f"Current Version: {config._initial_version()}\n" in result.output
|
||||||
assert f"PEP440 Version : {config._initial_version_pep440()}\n" in result.output
|
assert f"PEP440 : {config._initial_version_pep440()}\n" in result.output
|
||||||
|
|
||||||
|
|
||||||
def test_novcs_pyproject_init(runner):
|
def test_novcs_pyproject_init(runner):
|
||||||
|
|
@ -202,7 +236,7 @@ def test_novcs_pyproject_init(runner):
|
||||||
result = runner.invoke(pycalver.cli, ['show'])
|
result = runner.invoke(pycalver.cli, ['show'])
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
assert f"Current Version: {config._initial_version()}\n" in result.output
|
assert f"Current Version: {config._initial_version()}\n" in result.output
|
||||||
assert f"PEP440 Version : {config._initial_version_pep440()}\n" in result.output
|
assert f"PEP440 : {config._initial_version_pep440()}\n" in result.output
|
||||||
|
|
||||||
|
|
||||||
def _vcs_init(vcs):
|
def _vcs_init(vcs):
|
||||||
|
|
@ -224,7 +258,7 @@ def test_git_init(runner):
|
||||||
result = runner.invoke(pycalver.cli, ['show'])
|
result = runner.invoke(pycalver.cli, ['show'])
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
assert f"Current Version: {config._initial_version()}\n" in result.output
|
assert f"Current Version: {config._initial_version()}\n" in result.output
|
||||||
assert f"PEP440 Version : {config._initial_version_pep440()}\n" in result.output
|
assert f"PEP440 : {config._initial_version_pep440()}\n" in result.output
|
||||||
|
|
||||||
|
|
||||||
def test_hg_init(runner):
|
def test_hg_init(runner):
|
||||||
|
|
@ -237,7 +271,7 @@ def test_hg_init(runner):
|
||||||
result = runner.invoke(pycalver.cli, ['show'])
|
result = runner.invoke(pycalver.cli, ['show'])
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
assert f"Current Version: {config._initial_version()}\n" in result.output
|
assert f"Current Version: {config._initial_version()}\n" in result.output
|
||||||
assert f"PEP440 Version : {config._initial_version_pep440()}\n" in result.output
|
assert f"PEP440 : {config._initial_version_pep440()}\n" in result.output
|
||||||
|
|
||||||
|
|
||||||
def test_git_tag_eval(runner):
|
def test_git_tag_eval(runner):
|
||||||
|
|
@ -257,7 +291,7 @@ def test_git_tag_eval(runner):
|
||||||
result = runner.invoke(pycalver.cli, ['show', "--verbose"])
|
result = runner.invoke(pycalver.cli, ['show', "--verbose"])
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
assert f"Current Version: {tag_version}\n" in result.output
|
assert f"Current Version: {tag_version}\n" in result.output
|
||||||
assert f"PEP440 Version : {tag_version_pep440}\n" in result.output
|
assert f"PEP440 : {tag_version_pep440}\n" in result.output
|
||||||
|
|
||||||
|
|
||||||
def test_hg_tag_eval(runner):
|
def test_hg_tag_eval(runner):
|
||||||
|
|
@ -277,7 +311,7 @@ def test_hg_tag_eval(runner):
|
||||||
result = runner.invoke(pycalver.cli, ['show', "--verbose"])
|
result = runner.invoke(pycalver.cli, ['show', "--verbose"])
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
assert f"Current Version: {tag_version}\n" in result.output
|
assert f"Current Version: {tag_version}\n" in result.output
|
||||||
assert f"PEP440 Version : {tag_version_pep440}\n" in result.output
|
assert f"PEP440 : {tag_version_pep440}\n" in result.output
|
||||||
|
|
||||||
|
|
||||||
def test_novcs_bump(runner):
|
def test_novcs_bump(runner):
|
||||||
|
|
|
||||||
|
|
@ -58,8 +58,8 @@ def test_parse_toml():
|
||||||
assert cfg.push is True
|
assert cfg.push is True
|
||||||
|
|
||||||
assert "pycalver.toml" in cfg.file_patterns
|
assert "pycalver.toml" in cfg.file_patterns
|
||||||
assert cfg.file_patterns["README.md" ] == ["{version}", "{pep440_version}"]
|
assert cfg.file_patterns["README.md" ] == ["{pycalver}", "{pep440_pycalver}"]
|
||||||
assert cfg.file_patterns["pycalver.toml"] == ['current_version = "{version}"']
|
assert cfg.file_patterns["pycalver.toml"] == ['current_version = "{pycalver}"']
|
||||||
|
|
||||||
|
|
||||||
def test_parse_cfg():
|
def test_parse_cfg():
|
||||||
|
|
@ -74,8 +74,8 @@ def test_parse_cfg():
|
||||||
assert cfg.push is True
|
assert cfg.push is True
|
||||||
|
|
||||||
assert "setup.cfg" in cfg.file_patterns
|
assert "setup.cfg" in cfg.file_patterns
|
||||||
assert cfg.file_patterns["setup.py" ] == ["{version}", "{pep440_version}"]
|
assert cfg.file_patterns["setup.py" ] == ["{pycalver}", "{pep440_pycalver}"]
|
||||||
assert cfg.file_patterns["setup.cfg"] == ['current_version = "{version}"']
|
assert cfg.file_patterns["setup.cfg"] == ['current_version = "{pycalver}"']
|
||||||
|
|
||||||
|
|
||||||
def test_parse_default_toml():
|
def test_parse_default_toml():
|
||||||
|
|
@ -168,8 +168,8 @@ def test_parse_toml_file(tmpdir):
|
||||||
assert cfg.push is True
|
assert cfg.push is True
|
||||||
|
|
||||||
assert cfg.file_patterns == {
|
assert cfg.file_patterns == {
|
||||||
"README.md" : ["{version}", "{pep440_version}"],
|
"README.md" : ["{pycalver}", "{pep440_pycalver}"],
|
||||||
"pycalver.toml": ['current_version = "{version}"'],
|
"pycalver.toml": ['current_version = "{pycalver}"'],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -190,8 +190,8 @@ def test_parse_cfg_file(tmpdir):
|
||||||
assert cfg.push is True
|
assert cfg.push is True
|
||||||
|
|
||||||
assert cfg.file_patterns == {
|
assert cfg.file_patterns == {
|
||||||
"setup.py" : ["{version}", "{pep440_version}"],
|
"setup.py" : ["{pycalver}", "{pep440_pycalver}"],
|
||||||
"setup.cfg": ['current_version = "{version}"'],
|
"setup.cfg": ['current_version = "{pycalver}"'],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,50 +1,6 @@
|
||||||
import re
|
|
||||||
from pycalver import parse
|
from pycalver import parse
|
||||||
|
|
||||||
|
|
||||||
def test_re_pattern_parts():
|
|
||||||
part_re_by_name = {
|
|
||||||
part_name: re.compile(part_re_str)
|
|
||||||
for part_name, part_re_str in parse.RE_PATTERN_PARTS.items()
|
|
||||||
}
|
|
||||||
|
|
||||||
cases = [
|
|
||||||
("pep440_version", "201712.31" , "201712.31"),
|
|
||||||
("pep440_version", "v201712.0032" , None),
|
|
||||||
("pep440_version", "201712.0033-alpha" , None),
|
|
||||||
("version" , "v201712.0034" , "v201712.0034"),
|
|
||||||
("version" , "v201712.0035-alpha" , "v201712.0035-alpha"),
|
|
||||||
("version" , "v201712.0036-alpha0", "v201712.0036-alpha"),
|
|
||||||
("version" , "v201712.0037-pre" , "v201712.0037"),
|
|
||||||
("version" , "201712.38a0" , None),
|
|
||||||
("version" , "201712.0039" , None),
|
|
||||||
("calver" , "v201712" , "v201712"),
|
|
||||||
("calver" , "v201799" , "v201799"), # maybe date validation should be a thing
|
|
||||||
("calver" , "201712" , None),
|
|
||||||
("calver" , "v20171" , None),
|
|
||||||
("build" , ".0012" , ".0012"),
|
|
||||||
("build" , ".11012" , ".11012"),
|
|
||||||
("build" , ".012" , None),
|
|
||||||
("build" , "11012" , None),
|
|
||||||
("release" , "-alpha" , "-alpha"),
|
|
||||||
("release" , "-beta" , "-beta"),
|
|
||||||
("release" , "-dev" , "-dev"),
|
|
||||||
("release" , "-rc" , "-rc"),
|
|
||||||
("release" , "-post" , "-post"),
|
|
||||||
("release" , "-pre" , ""),
|
|
||||||
("release" , "alpha" , ""),
|
|
||||||
]
|
|
||||||
|
|
||||||
for part_name, line, expected in cases:
|
|
||||||
part_re = part_re_by_name[part_name]
|
|
||||||
result = part_re.search(line)
|
|
||||||
if result is None:
|
|
||||||
assert expected is None, (part_name, line)
|
|
||||||
else:
|
|
||||||
result_val = result.group(0)
|
|
||||||
assert result_val == expected, (part_name, line)
|
|
||||||
|
|
||||||
|
|
||||||
SETUP_PY_FIXTURE = """
|
SETUP_PY_FIXTURE = """
|
||||||
# setup.py
|
# setup.py
|
||||||
import setuptools
|
import setuptools
|
||||||
|
|
@ -57,7 +13,7 @@ setuptools.setup(
|
||||||
|
|
||||||
def test_default_parse_patterns():
|
def test_default_parse_patterns():
|
||||||
lines = SETUP_PY_FIXTURE.splitlines()
|
lines = SETUP_PY_FIXTURE.splitlines()
|
||||||
patterns = ["{version}", "{pep440_version}"]
|
patterns = ["{pycalver}", "{pep440_pycalver}"]
|
||||||
|
|
||||||
matches = list(parse.iter_matches(lines, patterns))
|
matches = list(parse.iter_matches(lines, patterns))
|
||||||
assert len(matches) == 2
|
assert len(matches) == 2
|
||||||
|
|
@ -75,7 +31,7 @@ def test_default_parse_patterns():
|
||||||
def test_explicit_parse_patterns():
|
def test_explicit_parse_patterns():
|
||||||
lines = SETUP_PY_FIXTURE.splitlines()
|
lines = SETUP_PY_FIXTURE.splitlines()
|
||||||
|
|
||||||
patterns = ["__version__ = '{version}'", "version='{pep440_version}'"]
|
patterns = ["__version__ = '{pycalver}'", "version='{pep440_pycalver}'"]
|
||||||
|
|
||||||
matches = list(parse.iter_matches(lines, patterns))
|
matches = list(parse.iter_matches(lines, patterns))
|
||||||
assert len(matches) == 2
|
assert len(matches) == 2
|
||||||
|
|
@ -102,7 +58,7 @@ README_RST_FIXTURE = """
|
||||||
def test_badge_parse_patterns():
|
def test_badge_parse_patterns():
|
||||||
lines = README_RST_FIXTURE.splitlines()
|
lines = README_RST_FIXTURE.splitlines()
|
||||||
|
|
||||||
patterns = ["badge/CalVer-{calver}{build}-{release}-blue.svg", ":alt: CalVer {version}"]
|
patterns = ["badge/CalVer-{calver}{build}-{release}-blue.svg", ":alt: CalVer {pycalver}"]
|
||||||
|
|
||||||
matches = list(parse.iter_matches(lines, patterns))
|
matches = list(parse.iter_matches(lines, patterns))
|
||||||
assert len(matches) == 2
|
assert len(matches) == 2
|
||||||
|
|
@ -115,30 +71,3 @@ def test_badge_parse_patterns():
|
||||||
|
|
||||||
assert matches[0].match == "badge/CalVer-v201809.0002--beta-blue.svg"
|
assert matches[0].match == "badge/CalVer-v201809.0002--beta-blue.svg"
|
||||||
assert matches[1].match == ":alt: CalVer v201809.0002-beta"
|
assert matches[1].match == ":alt: CalVer v201809.0002-beta"
|
||||||
|
|
||||||
|
|
||||||
CLI_MAIN_FIXTURE = """
|
|
||||||
@click.group()
|
|
||||||
@click.version_option(version="v201812.0123-beta")
|
|
||||||
@click.help_option()
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def test_pattern_escapes():
|
|
||||||
pattern_re = parse.compile_pattern(r'click.version_option(version="{version}")')
|
|
||||||
match = pattern_re.search(CLI_MAIN_FIXTURE)
|
|
||||||
assert match.group(0) == 'click.version_option(version="v201812.0123-beta")'
|
|
||||||
|
|
||||||
|
|
||||||
CURLY_BRACE_FIXTURE = """
|
|
||||||
package_metadata = {"name": "mypackage", "version": "v201812.0123-beta"}
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def test_curly_escapes():
|
|
||||||
pattern = r'package_metadata = {"name": "mypackage", "version": "{version}"}'
|
|
||||||
pattern_re = parse.compile_pattern(pattern)
|
|
||||||
match = pattern_re.search(CURLY_BRACE_FIXTURE)
|
|
||||||
assert (
|
|
||||||
match.group(0) == 'package_metadata = {"name": "mypackage", "version": "v201812.0123-beta"}'
|
|
||||||
)
|
|
||||||
|
|
|
||||||
81
test/test_patterns.py
Normal file
81
test/test_patterns.py
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
import re
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from pycalver import patterns
|
||||||
|
|
||||||
|
|
||||||
|
def _part_re_by_name(name):
|
||||||
|
return re.compile(patterns.PART_PATTERNS[name])
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("part_name", patterns.PART_PATTERNS.keys())
|
||||||
|
def test_part_compilation(part_name):
|
||||||
|
assert _part_re_by_name(part_name)
|
||||||
|
|
||||||
|
|
||||||
|
PATTERN_PART_CASES = [
|
||||||
|
("pep440_pycalver", "201712.31" , "201712.31"),
|
||||||
|
("pep440_pycalver", "v201712.0032" , None),
|
||||||
|
("pep440_pycalver", "201712.0033-alpha" , None),
|
||||||
|
("pycalver" , "v201712.0034" , "v201712.0034"),
|
||||||
|
("pycalver" , "v201712.0035-alpha" , "v201712.0035-alpha"),
|
||||||
|
("pycalver" , "v201712.0036-alpha0", "v201712.0036-alpha"),
|
||||||
|
("pycalver" , "v201712.0037-pre" , "v201712.0037"),
|
||||||
|
("pycalver" , "201712.38a0" , None),
|
||||||
|
("pycalver" , "201712.0039" , None),
|
||||||
|
("semver" , "1.23.456" , "1.23.456"),
|
||||||
|
("calver" , "v201712" , "v201712"),
|
||||||
|
("calver" , "v201799" , None), # invalid date
|
||||||
|
("calver" , "201712" , None),
|
||||||
|
("calver" , "v20171" , None),
|
||||||
|
("build" , ".0012" , ".0012"),
|
||||||
|
("build" , ".11012" , ".11012"),
|
||||||
|
("build" , ".012" , None),
|
||||||
|
("build" , "11012" , None),
|
||||||
|
("release" , "-alpha" , "-alpha"),
|
||||||
|
("release" , "-beta" , "-beta"),
|
||||||
|
("release" , "-dev" , "-dev"),
|
||||||
|
("release" , "-rc" , "-rc"),
|
||||||
|
("release" , "-post" , "-post"),
|
||||||
|
("release" , "-pre" , None),
|
||||||
|
("release" , "alpha" , None),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("part_name, line, expected", PATTERN_PART_CASES)
|
||||||
|
def test_re_pattern_parts(part_name, line, expected):
|
||||||
|
part_re = _part_re_by_name(part_name)
|
||||||
|
result = part_re.search(line)
|
||||||
|
if result is None:
|
||||||
|
assert expected is None, (part_name, line)
|
||||||
|
else:
|
||||||
|
result_val = result.group(0)
|
||||||
|
assert result_val == expected, (part_name, line)
|
||||||
|
|
||||||
|
|
||||||
|
CLI_MAIN_FIXTURE = """
|
||||||
|
@click.group()
|
||||||
|
@click.version_option(version="v201812.0123-beta")
|
||||||
|
@click.help_option()
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def test_pattern_escapes():
|
||||||
|
pattern = 'click.version_option(version="{pycalver}")'
|
||||||
|
pattern_re = patterns.compile_pattern(pattern)
|
||||||
|
match = pattern_re.search(CLI_MAIN_FIXTURE)
|
||||||
|
expected = 'click.version_option(version="v201812.0123-beta")'
|
||||||
|
assert match.group(0) == expected
|
||||||
|
|
||||||
|
|
||||||
|
CURLY_BRACE_FIXTURE = """
|
||||||
|
package_metadata = {"name": "mypackage", "version": "v201812.0123-beta"}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def test_curly_escapes():
|
||||||
|
pattern = 'package_metadata = {"name": "mypackage", "version": "{pycalver}"}'
|
||||||
|
pattern_re = patterns.compile_pattern(pattern)
|
||||||
|
match = pattern_re.search(CURLY_BRACE_FIXTURE)
|
||||||
|
expected = 'package_metadata = {"name": "mypackage", "version": "v201812.0123-beta"}'
|
||||||
|
assert match.group(0) == expected
|
||||||
|
|
@ -9,7 +9,7 @@ __version__ = "v201809.0002-beta"
|
||||||
|
|
||||||
def test_rewrite_lines():
|
def test_rewrite_lines():
|
||||||
old_lines = REWRITE_FIXTURE.splitlines()
|
old_lines = REWRITE_FIXTURE.splitlines()
|
||||||
patterns = ['__version__ = "{version}"']
|
patterns = ['__version__ = "{pycalver}"']
|
||||||
new_lines = rewrite.rewrite_lines(patterns, "v201911.0003", old_lines)
|
new_lines = rewrite.rewrite_lines(patterns, "v201911.0003", old_lines)
|
||||||
|
|
||||||
assert len(new_lines) == len(old_lines)
|
assert len(new_lines) == len(old_lines)
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,11 @@ import random
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
|
|
||||||
from pycalver import version
|
from pycalver import version
|
||||||
|
from pycalver import patterns
|
||||||
|
|
||||||
def test_current_calver():
|
|
||||||
v = version.current_calver()
|
|
||||||
assert len(v) == 7
|
|
||||||
assert v.startswith("v")
|
|
||||||
assert v[1:].isdigit()
|
|
||||||
|
|
||||||
|
|
||||||
def test_bump_beta():
|
def test_bump_beta():
|
||||||
calver = version.current_calver()
|
cur_version = "v201712.0001-beta"
|
||||||
cur_version = calver + ".0001-beta"
|
|
||||||
assert cur_version < version.incr(cur_version)
|
assert cur_version < version.incr(cur_version)
|
||||||
assert version.incr(cur_version).endswith("-beta")
|
assert version.incr(cur_version).endswith("-beta")
|
||||||
assert version.incr(cur_version, release="alpha").endswith("-alpha")
|
assert version.incr(cur_version, release="alpha").endswith("-alpha")
|
||||||
|
|
@ -21,8 +14,7 @@ def test_bump_beta():
|
||||||
|
|
||||||
|
|
||||||
def test_bump_final():
|
def test_bump_final():
|
||||||
calver = version.current_calver()
|
cur_version = "v201712.0001"
|
||||||
cur_version = calver + ".0001"
|
|
||||||
assert cur_version < version.incr(cur_version)
|
assert cur_version < version.incr(cur_version)
|
||||||
assert version.incr(cur_version).endswith(".0002")
|
assert version.incr(cur_version).endswith(".0002")
|
||||||
assert version.incr(cur_version, release="alpha").endswith("-alpha")
|
assert version.incr(cur_version, release="alpha").endswith("-alpha")
|
||||||
|
|
@ -34,20 +26,19 @@ def test_bump_final():
|
||||||
|
|
||||||
|
|
||||||
def test_bump_future():
|
def test_bump_future():
|
||||||
|
"""Test that versions don't go back in time."""
|
||||||
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.incr(cur_version)
|
new_version = version.incr(cur_version)
|
||||||
|
assert cur_version < new_version
|
||||||
|
|
||||||
|
|
||||||
def test_bump_random(monkeypatch):
|
def test_bump_random(monkeypatch):
|
||||||
cur_date = dt.date.today()
|
cur_date = dt.date(2016, 1, 1) + dt.timedelta(days=random.randint(1, 2000))
|
||||||
cur_version = cur_date.strftime("v%Y%m") + ".0001-dev"
|
cur_version = cur_date.strftime("v%Y%m") + ".0001-dev"
|
||||||
|
|
||||||
def _mock_current_calver():
|
monkeypatch.setattr(version, 'TODAY', cur_date)
|
||||||
return cur_date.strftime("v%Y%m")
|
|
||||||
|
|
||||||
monkeypatch.setattr(version, 'current_calver', _mock_current_calver)
|
|
||||||
|
|
||||||
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))
|
||||||
|
|
@ -62,37 +53,31 @@ def test_parse_version_info():
|
||||||
version_str = "v201712.0001-alpha"
|
version_str = "v201712.0001-alpha"
|
||||||
version_nfo = version.parse_version_info(version_str)
|
version_nfo = version.parse_version_info(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"
|
||||||
assert version_nfo.calver == "v201712"
|
assert version_nfo.year == 2017
|
||||||
assert version_nfo.year == "2017"
|
assert version_nfo.month == 12
|
||||||
assert version_nfo.month == "12"
|
assert version_nfo.bid == "0001"
|
||||||
assert version_nfo.build == ".0001"
|
assert version_nfo.tag == "alpha"
|
||||||
assert version_nfo.release == "-alpha"
|
|
||||||
assert version_nfo.build_no == "0001"
|
|
||||||
assert version_nfo.release_tag == "alpha"
|
|
||||||
|
|
||||||
version_str = "v201712.0001"
|
version_str = "v201712.0001"
|
||||||
version_nfo = version.parse_version_info(version_str)
|
version_nfo = version.parse_version_info(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"
|
||||||
assert version_nfo.calver == "v201712"
|
assert version_nfo.year == 2017
|
||||||
assert version_nfo.year == "2017"
|
assert version_nfo.month == 12
|
||||||
assert version_nfo.month == "12"
|
assert version_nfo.bid == "0001"
|
||||||
assert version_nfo.build == ".0001"
|
assert version_nfo.tag == "final"
|
||||||
assert version_nfo.release == "-final"
|
|
||||||
assert version_nfo.build_no == "0001"
|
|
||||||
assert version_nfo.release_tag == "final"
|
|
||||||
|
|
||||||
|
|
||||||
def test_readme_pycalver1():
|
def test_readme_pycalver1():
|
||||||
version_str = "v201712.0001-alpha"
|
version_str = "v201712.0001-alpha"
|
||||||
version_info = version.PYCALVER_RE.match(version_str).groupdict()
|
version_info = patterns.PYCALVER_RE.match(version_str).groupdict()
|
||||||
|
|
||||||
assert version_info == {
|
assert version_info == {
|
||||||
'version' : "v201712.0001-alpha",
|
'pycalver' : "v201712.0001-alpha",
|
||||||
'calver' : "v201712",
|
'vYYYYMM' : "v201712",
|
||||||
'year' : "2017",
|
'year' : "2017",
|
||||||
'month' : "12",
|
'month' : "12",
|
||||||
'build' : ".0001",
|
'build' : ".0001",
|
||||||
|
|
@ -104,11 +89,11 @@ def test_readme_pycalver1():
|
||||||
|
|
||||||
def test_readme_pycalver2():
|
def test_readme_pycalver2():
|
||||||
version_str = "v201712.0033"
|
version_str = "v201712.0033"
|
||||||
version_info = version.PYCALVER_RE.match(version_str).groupdict()
|
version_info = patterns.PYCALVER_RE.match(version_str).groupdict()
|
||||||
|
|
||||||
assert version_info == {
|
assert version_info == {
|
||||||
'version' : "v201712.0033",
|
'pycalver' : "v201712.0033",
|
||||||
'calver' : "v201712",
|
'vYYYYMM' : "v201712",
|
||||||
'year' : "2017",
|
'year' : "2017",
|
||||||
'month' : "12",
|
'month' : "12",
|
||||||
'build' : ".0033",
|
'build' : ".0033",
|
||||||
|
|
@ -140,3 +125,19 @@ def test_parse_error_nopadding():
|
||||||
assert False
|
assert False
|
||||||
except ValueError as err:
|
except ValueError as err:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def test_part_field_mapping():
|
||||||
|
a_names = set(version.PATTERN_PART_FIELDS.keys())
|
||||||
|
b_names = set(patterns.PART_PATTERNS.keys())
|
||||||
|
c_names = set(patterns.COMPOSITE_PART_PATTERNS.keys())
|
||||||
|
|
||||||
|
extra_names = a_names - b_names
|
||||||
|
assert not any(extra_names)
|
||||||
|
missing_names = b_names - a_names
|
||||||
|
assert missing_names == c_names
|
||||||
|
|
||||||
|
a_fields = set(version.PATTERN_PART_FIELDS.values())
|
||||||
|
b_fields = set(version.VersionInfo._fields)
|
||||||
|
|
||||||
|
assert a_fields == b_fields
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue