mirror of
https://github.com/TECHNOFAB11/bumpver.git
synced 2025-12-12 06:20:08 +01:00
implement formatting for new style patterns
This commit is contained in:
parent
5927770ddd
commit
8edb001782
1 changed files with 155 additions and 55 deletions
|
|
@ -24,21 +24,14 @@ logger = logging.getLogger("pycalver.version")
|
||||||
TODAY = dt.datetime.utcnow().date()
|
TODAY = dt.datetime.utcnow().date()
|
||||||
|
|
||||||
|
|
||||||
ID_FIELDS_BY_PART = {
|
|
||||||
'MAJOR': 'major',
|
|
||||||
'MINOR': 'minor',
|
|
||||||
'PATCH': 'patch',
|
|
||||||
'MICRO': 'patch',
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ZERO_VALUES = {
|
ZERO_VALUES = {
|
||||||
'major': "0",
|
'MAJOR': "0",
|
||||||
'minor': "0",
|
'MINOR': "0",
|
||||||
'patch': "0",
|
'PATCH': "0",
|
||||||
'tag' : "final",
|
'MICRO': "0",
|
||||||
'pytag': "",
|
'TAG' : "final",
|
||||||
'num' : "0",
|
'PYTAG': "",
|
||||||
|
'NUM' : "0",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -170,10 +163,10 @@ class VersionInfo(typ.NamedTuple):
|
||||||
major : int
|
major : int
|
||||||
minor : int
|
minor : int
|
||||||
patch : int
|
patch : int
|
||||||
|
num : int
|
||||||
bid : str
|
bid : str
|
||||||
tag : str
|
tag : str
|
||||||
pytag : str
|
pytag : str
|
||||||
num : MaybeInt
|
|
||||||
|
|
||||||
|
|
||||||
VALID_FIELD_KEYS = set(VersionInfo._fields) | {'version'}
|
VALID_FIELD_KEYS = set(VersionInfo._fields) | {'version'}
|
||||||
|
|
@ -229,16 +222,14 @@ def _parse_version_info(field_values: FieldValues) -> VersionInfo:
|
||||||
assert key in VALID_FIELD_KEYS, key
|
assert key in VALID_FIELD_KEYS, key
|
||||||
|
|
||||||
fvals = field_values
|
fvals = field_values
|
||||||
tag = fvals.get('tag' , "final")
|
tag = fvals.get('tag' ) or "final"
|
||||||
pytag = fvals.get('pytag', "")
|
pytag = fvals.get('pytag') or ""
|
||||||
|
|
||||||
if tag and not pytag:
|
if tag and not pytag:
|
||||||
pytag = PEP440_TAG_BY_TAG[tag]
|
pytag = PEP440_TAG_BY_TAG[tag]
|
||||||
elif pytag and not tag:
|
elif pytag and not tag:
|
||||||
tag = TAG_BY_PEP440_TAG[pytag]
|
tag = TAG_BY_PEP440_TAG[pytag]
|
||||||
|
|
||||||
num: MaybeInt = int(fvals['num']) if 'num' in fvals else None
|
|
||||||
|
|
||||||
date: typ.Optional[dt.date] = None
|
date: typ.Optional[dt.date] = None
|
||||||
|
|
||||||
year_y: MaybeInt = int(fvals['year_y']) if 'year_y' in fvals else None
|
year_y: MaybeInt = int(fvals['year_y']) if 'year_y' in fvals else None
|
||||||
|
|
@ -278,11 +269,12 @@ def _parse_version_info(field_values: FieldValues) -> VersionInfo:
|
||||||
if quarter is None and month:
|
if quarter is None and month:
|
||||||
quarter = _quarter_from_month(month)
|
quarter = _quarter_from_month(month)
|
||||||
|
|
||||||
major = int(fvals['major']) if 'major' in fvals else 0
|
# NOTE (mb 2020-09-18): If a part is optional, fvals[<field>] may be None
|
||||||
minor = int(fvals['minor']) if 'minor' in fvals else 0
|
major = int(fvals.get('major') or 0)
|
||||||
patch = int(fvals['patch']) if 'patch' in fvals else 0
|
minor = int(fvals.get('minor') or 0)
|
||||||
|
patch = int(fvals.get('patch') or 0)
|
||||||
bid = fvals['bid'] if 'bid' in fvals else "1000"
|
num = int(fvals.get('num' ) or 0)
|
||||||
|
bid = fvals['bid'] if 'bid' in fvals else "1000"
|
||||||
|
|
||||||
vnfo = VersionInfo(
|
vnfo = VersionInfo(
|
||||||
year_y=year_y,
|
year_y=year_y,
|
||||||
|
|
@ -297,10 +289,10 @@ def _parse_version_info(field_values: FieldValues) -> VersionInfo:
|
||||||
major=major,
|
major=major,
|
||||||
minor=minor,
|
minor=minor,
|
||||||
patch=patch,
|
patch=patch,
|
||||||
|
num=num,
|
||||||
bid=bid,
|
bid=bid,
|
||||||
tag=tag,
|
tag=tag,
|
||||||
pytag=pytag,
|
pytag=pytag,
|
||||||
num=num,
|
|
||||||
)
|
)
|
||||||
return vnfo
|
return vnfo
|
||||||
|
|
||||||
|
|
@ -312,13 +304,21 @@ class PatternError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def parse_version_info(version_str: str, pattern: str = "vYYYY0M.BUILD[-TAG]") -> VersionInfo:
|
def parse_version_info(version_str: str, pattern: str = "vYYYY0M.BUILD[-TAG[NUM]]") -> VersionInfo:
|
||||||
"""Parse normalized VersionInfo.
|
"""Parse normalized VersionInfo.
|
||||||
|
|
||||||
>>> vnfo = parse_version_info("v201712.0033-beta", pattern="vYYYY0M.BUILD[-TAG]")
|
>>> vnfo = parse_version_info("v201712.0033-beta0", pattern="vYYYY0M.BUILD[-TAG[NUM]]")
|
||||||
|
>>> fvals = {'year_y': 2017, 'month': 12, 'bid': "0033", 'tag': "beta", 'num': 0}
|
||||||
|
>>> assert vnfo == _parse_version_info(fvals)
|
||||||
|
|
||||||
|
>>> vnfo = parse_version_info("v201712.0033-beta", pattern="vYYYY0M.BUILD[-TAG[NUM]]")
|
||||||
>>> fvals = {'year_y': 2017, 'month': 12, 'bid': "0033", 'tag': "beta"}
|
>>> fvals = {'year_y': 2017, 'month': 12, 'bid': "0033", 'tag': "beta"}
|
||||||
>>> assert vnfo == _parse_version_info(fvals)
|
>>> assert vnfo == _parse_version_info(fvals)
|
||||||
|
|
||||||
|
>>> vnfo = parse_version_info("v201712.0033", pattern="vYYYY0M.BUILD[-TAG[NUM]]")
|
||||||
|
>>> fvals = {'year_y': 2017, 'month': 12, 'bid': "0033"}
|
||||||
|
>>> assert vnfo == _parse_version_info(fvals)
|
||||||
|
|
||||||
>>> vnfo = parse_version_info("1.23.456", pattern="MAJOR.MINOR.PATCH")
|
>>> vnfo = parse_version_info("1.23.456", pattern="MAJOR.MINOR.PATCH")
|
||||||
>>> fvals = {'major': "1", 'minor': "23", 'patch': "456"}
|
>>> fvals = {'major': "1", 'minor': "23", 'patch': "456"}
|
||||||
>>> assert vnfo == _parse_version_info(fvals)
|
>>> assert vnfo == _parse_version_info(fvals)
|
||||||
|
|
@ -372,60 +372,101 @@ def _format_part_values(vinfo: VersionInfo) -> typ.Dict[str, str]:
|
||||||
('2007', '09', '1033', 'beta')
|
('2007', '09', '1033', 'beta')
|
||||||
>>> (kwargs['YY'], kwargs['0Y'], kwargs['MM'], kwargs['PYTAG'])
|
>>> (kwargs['YY'], kwargs['0Y'], kwargs['MM'], kwargs['PYTAG'])
|
||||||
('7', '07', '9', 'b')
|
('7', '07', '9', 'b')
|
||||||
|
|
||||||
|
>>> vinfo = parse_version_info("200709.1033b1", pattern="YYYY0M.BLD[PYTAGNUM]")
|
||||||
|
>>> kwargs = _format_part_values(vinfo)
|
||||||
|
>>> (kwargs['YYYY'], kwargs['0M'], kwargs['BUILD'], kwargs['PYTAG'], kwargs['NUM'])
|
||||||
|
('2007', '09', '1033', 'b', '1')
|
||||||
"""
|
"""
|
||||||
vnfo_kwargs: TemplateKwargs = vinfo._asdict()
|
vnfo_kwargs: TemplateKwargs = vinfo._asdict()
|
||||||
kwargs : typ.Dict[str, str] = {}
|
kwargs : typ.Dict[str, str] = {}
|
||||||
|
|
||||||
for part, field in v2patterns.PATTERN_PART_FIELDS.items():
|
for part, field in v2patterns.PATTERN_PART_FIELDS.items():
|
||||||
field_val = vnfo_kwargs[field]
|
field_val = vnfo_kwargs[field]
|
||||||
if field_val is None:
|
if field_val is not None:
|
||||||
continue
|
format_fn = v2patterns.PART_FORMATS[part]
|
||||||
|
kwargs[part] = format_fn(field_val)
|
||||||
format_fn = v2patterns.PART_FORMATS[part]
|
|
||||||
kwargs[part] = format_fn(field_val)
|
|
||||||
|
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
|
def _make_segments(pattern: str) -> typ.List[str]:
|
||||||
|
pattern_segs_l: typ.List[str] = []
|
||||||
|
pattern_segs_r: typ.List[str] = []
|
||||||
|
|
||||||
|
pattern_rest = pattern
|
||||||
|
while "[" in pattern_rest and "]" in pattern_rest:
|
||||||
|
try:
|
||||||
|
seg_l , pattern_rest = pattern_rest.split("[", 1)
|
||||||
|
pattern_rest, seg_r = pattern_rest.rsplit("]", 1)
|
||||||
|
except ValueError as val_err:
|
||||||
|
if "values to unpack" in str(val_err):
|
||||||
|
pat_err = PatternError(f"Unbalanced braces [] in '{pattern}'")
|
||||||
|
pat_err.__cause__ = val_err
|
||||||
|
raise pat_err
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
pattern_segs_l.append(seg_l)
|
||||||
|
pattern_segs_r.append(seg_r)
|
||||||
|
|
||||||
|
pattern_segs_l.append(pattern_rest)
|
||||||
|
|
||||||
|
# NOTE (mb 2020-09-18): The pivot makes subsequent code a bit more simple
|
||||||
|
pivot = [""]
|
||||||
|
return pattern_segs_l + pivot + list(reversed(pattern_segs_r))
|
||||||
|
|
||||||
|
|
||||||
def format_version(vinfo: VersionInfo, pattern: str) -> str:
|
def format_version(vinfo: VersionInfo, pattern: str) -> str:
|
||||||
"""Generate version string.
|
"""Generate version string.
|
||||||
|
|
||||||
>>> import datetime as dt
|
>>> import datetime as dt
|
||||||
>>> vinfo = parse_version_info("v200712.0033-beta", pattern="vYYYY0M.BUILD[-TAG]")
|
>>> vinfo = parse_version_info("v200712.0033-beta", pattern="vYYYY0M.BUILD[-TAG[NUM]]")
|
||||||
>>> vinfo_a = vinfo._replace(**cal_info(date=dt.date(2007, 1, 1))._asdict())
|
>>> vinfo_a = vinfo._replace(**cal_info(date=dt.date(2007, 1, 1))._asdict())
|
||||||
>>> vinfo_b = vinfo._replace(**cal_info(date=dt.date(2007, 12, 31))._asdict())
|
>>> vinfo_b = vinfo._replace(**cal_info(date=dt.date(2007, 12, 31))._asdict())
|
||||||
|
|
||||||
>>> format_version(vinfo_a, pattern="vYY.BUILD[-TAG]")
|
>>> format_version(vinfo_a, pattern="vYY.BLD[-PYTAGNUM]")
|
||||||
'v7.33-beta'
|
'v7.33-b0'
|
||||||
>>> format_version(vinfo_a, pattern="v0Y.BUILD[-TAG]")
|
|
||||||
|
>>> format_version(vinfo_a, pattern="YYYY0M.BUILD[PYTAG[NUM]]")
|
||||||
|
'200701.0033b'
|
||||||
|
>>> format_version(vinfo_a, pattern="vYY.BLD[-PYTAGNUM]")
|
||||||
|
'v7.33-b0'
|
||||||
|
>>> format_version(vinfo_a, pattern="v0Y.BLD[-TAG]")
|
||||||
'v07.33-beta'
|
'v07.33-beta'
|
||||||
>>> format_version(vinfo_a, pattern="YYYY0M.BUILD[PYTAG][NUM]")
|
|
||||||
'201701.33b0'
|
|
||||||
|
|
||||||
>>> format_version(vinfo_a, pattern="vYYYY0M.BUILD[-TAG]")
|
>>> format_version(vinfo_a, pattern="vYYYY0M.BUILD[-TAG]")
|
||||||
'v201701.0033-beta'
|
'v200701.0033-beta'
|
||||||
>>> format_version(vinfo_b, pattern="vYYYY0M.BUILD[-TAG]")
|
>>> format_version(vinfo_b, pattern="vYYYY0M.BUILD[-TAG]")
|
||||||
'v201712.0033-beta'
|
'v200712.0033-beta'
|
||||||
|
|
||||||
>>> format_version(vinfo_a, pattern="vYYYYwWW.BUILD[-TAG]")
|
>>> format_version(vinfo_a, pattern="vYYYYw0W.BUILD[-TAG]")
|
||||||
'v2017w00.33-beta'
|
'v2007w01.0033-beta'
|
||||||
>>> format_version(vinfo_b, pattern="vYYYYwWW.BUILD[-TAG]")
|
>>> format_version(vinfo_a, pattern="vYYYYwWW.BLD[-TAG]")
|
||||||
'v2017w52.33-beta'
|
'v2007w1.33-beta'
|
||||||
|
>>> format_version(vinfo_b, pattern="vYYYYw0W.BUILD[-TAG]")
|
||||||
|
'v2007w53.0033-beta'
|
||||||
|
|
||||||
|
>>> format_version(vinfo_a, pattern="vYYYYd00J.BUILD[-TAG]")
|
||||||
|
'v2007d001.0033-beta'
|
||||||
>>> format_version(vinfo_a, pattern="vYYYYdJJJ.BUILD[-TAG]")
|
>>> format_version(vinfo_a, pattern="vYYYYdJJJ.BUILD[-TAG]")
|
||||||
'v2017d001.0033-beta'
|
'v2007d1.0033-beta'
|
||||||
>>> format_version(vinfo_b, pattern="vYYYYdJJJ.BUILD[-TAG]")
|
>>> format_version(vinfo_b, pattern="vYYYYd00J.BUILD[-TAG]")
|
||||||
'v2017d365.0033-beta'
|
'v2007d365.0033-beta'
|
||||||
|
|
||||||
>>> format_version(vinfo_a, pattern="vGGGGwVV.BUILD[-TAG]")
|
>>> format_version(vinfo_a, pattern="vGGGGwVV.BLD[PYTAGNUM]")
|
||||||
'v2016w52.0033-beta'
|
'v2007w1.33b0'
|
||||||
|
>>> format_version(vinfo_a, pattern="vGGGGw0V.BUILD[-TAG]")
|
||||||
|
'v2007w01.0033-beta'
|
||||||
|
>>> format_version(vinfo_b, pattern="vGGGGw0V.BUILD[-TAG]")
|
||||||
|
'v2008w01.0033-beta'
|
||||||
|
|
||||||
>>> vinfo_c = vinfo_b._replace(major=1, minor=2, patch=34, tag='final')
|
>>> vinfo_c = vinfo_b._replace(major=1, minor=2, patch=34, tag='final')
|
||||||
|
|
||||||
>>> format_version(vinfo_c, pattern="vYYYYwWW.BUILD-TAG")
|
>>> format_version(vinfo_c, pattern="vYYYYwWW.BUILD-TAG")
|
||||||
'v2017w52.33-final'
|
'v2007w53.0033-final'
|
||||||
>>> format_version(vinfo_c, pattern="vYYYYwWW.BUILD[-TAG]")
|
>>> format_version(vinfo_c, pattern="vYYYYwWW.BUILD[-TAG]")
|
||||||
'v2017w52.33'
|
'v2007w53.0033'
|
||||||
|
|
||||||
>>> format_version(vinfo_c, pattern="vMAJOR.MINOR.PATCH")
|
>>> format_version(vinfo_c, pattern="vMAJOR.MINOR.PATCH")
|
||||||
'v1.2.34'
|
'v1.2.34'
|
||||||
|
|
@ -445,14 +486,73 @@ def format_version(vinfo: VersionInfo, pattern: str) -> str:
|
||||||
'v1.0'
|
'v1.0'
|
||||||
>>> format_version(vinfo_d, pattern="vMAJOR[.MINOR[.PATCH[-TAG]]]")
|
>>> format_version(vinfo_d, pattern="vMAJOR[.MINOR[.PATCH[-TAG]]]")
|
||||||
'v1'
|
'v1'
|
||||||
|
|
||||||
|
>>> vinfo_d = vinfo_b._replace(major=1, minor=0, patch=1, tag='rc', num=0)
|
||||||
|
>>> format_version(vinfo_d, pattern="vMAJOR[.MINOR[.PATCH]]")
|
||||||
|
'v1.0.1'
|
||||||
|
>>> format_version(vinfo_d, pattern="vMAJOR[.MINOR[.PATCH[-TAG[NUM]]]]")
|
||||||
|
'v1.0.1-rc'
|
||||||
|
>>> format_version(vinfo_d, pattern="vMAJOR[.MINOR[.PATCH[-TAGNUM]]]")
|
||||||
|
'v1.0.1-rc0'
|
||||||
|
>>> format_version(vinfo_d, pattern="vMAJOR[.MINOR[.PATCH]]")
|
||||||
|
'v1.0.1'
|
||||||
|
|
||||||
|
>>> vinfo_d = vinfo_b._replace(major=1, minor=0, patch=0, tag='rc', num=2)
|
||||||
|
>>> format_version(vinfo_d, pattern="vMAJOR[.MINOR[.PATCH[-TAG[NUM]]]]")
|
||||||
|
'v1.0.0-rc2'
|
||||||
"""
|
"""
|
||||||
kwargs = _format_part_values(vinfo)
|
kwargs = _format_part_values(vinfo)
|
||||||
part_values = sorted(kwargs.items(), key=lambda item: -len(item[0]))
|
part_values = sorted(kwargs.items(), key=lambda item: -len(item[0]))
|
||||||
version = pattern
|
|
||||||
for part, value in part_values:
|
|
||||||
version = version.replace(part, value)
|
|
||||||
|
|
||||||
return version
|
pattern_segs = _make_segments(pattern)
|
||||||
|
|
||||||
|
iszero_segment = [True] * len(pattern_segs)
|
||||||
|
formatted_segs_l: typ.List[str] = []
|
||||||
|
formatted_segs_r: typ.List[str] = []
|
||||||
|
|
||||||
|
used_part_values = []
|
||||||
|
|
||||||
|
idx_l = 0
|
||||||
|
idx_r = len(pattern_segs) - 1
|
||||||
|
while idx_l < idx_r:
|
||||||
|
# NOTE (mb 2020-09-18): All segments are optional,
|
||||||
|
# except the most left and the most right,
|
||||||
|
# i.e the ones NOT surrounded by braces.
|
||||||
|
# Empty string is a valid segment.
|
||||||
|
is_optional = idx_l > 0
|
||||||
|
|
||||||
|
seg_l = pattern_segs[idx_l]
|
||||||
|
seg_r = pattern_segs[idx_r]
|
||||||
|
|
||||||
|
for part, part_value in part_values:
|
||||||
|
if part in seg_l:
|
||||||
|
used_part_values.append(part + "=" + part_value)
|
||||||
|
seg_l = seg_l.replace(part, part_value)
|
||||||
|
if not (is_optional and str(part_value) == ZERO_VALUES.get(part)):
|
||||||
|
iszero_segment[idx_l] = False
|
||||||
|
|
||||||
|
if part in seg_r:
|
||||||
|
used_part_values.append(part + "=" + part_value)
|
||||||
|
seg_r = seg_r.replace(part, part_value)
|
||||||
|
if not (is_optional and str(part_value) == ZERO_VALUES[part]):
|
||||||
|
iszero_segment[idx_r] = False
|
||||||
|
|
||||||
|
formatted_segs_l.append(seg_l)
|
||||||
|
formatted_segs_r.append(seg_r)
|
||||||
|
|
||||||
|
idx_l += 1
|
||||||
|
idx_r -= 1
|
||||||
|
|
||||||
|
formatted_segs = formatted_segs_l + list(reversed(formatted_segs_r))
|
||||||
|
|
||||||
|
has_val_to_right = False
|
||||||
|
for idx, is_zero in reversed(list(enumerate(iszero_segment))):
|
||||||
|
if is_zero and not has_val_to_right:
|
||||||
|
formatted_segs[idx] = ""
|
||||||
|
else:
|
||||||
|
has_val_to_right = True
|
||||||
|
|
||||||
|
return "".join(formatted_segs)
|
||||||
|
|
||||||
|
|
||||||
def incr(
|
def incr(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue