Add dom_short, doy_short and regression tests

This commit is contained in:
Manuel Barkhau 2019-07-25 12:17:52 +02:00
parent 8833bb8b2b
commit 052d01445c
4 changed files with 141 additions and 83 deletions

View file

@ -3,7 +3,7 @@
## v201907.003x ## v201907.003x
- Fix gitlab#6: Add {month_short} as a minimal fix. - Fix gitlab#6: Add parts `{month_short}`, `{dom_short}`, `{doy_short}`.
- Fix gitlab#5: Better warning when using bump with semver (one of --major/--minor/--patch is required) - Fix gitlab#5: Better warning when using bump with semver (one of --major/--minor/--patch is required)
- Fix gitlab#4: Make {release} part optional, so that versions generated by --release=final are parsed. - Fix gitlab#4: Make {release} part optional, so that versions generated by --release=final are parsed.

View file

@ -97,7 +97,9 @@ PART_PATTERNS = {
'iso_week' : r"(?:[0-4]\d|5[0-3])", 'iso_week' : r"(?:[0-4]\d|5[0-3])",
'us_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])", 'dom' : r"(0[1-9]|[1-2][0-9]|3[0-1])",
'dom_short' : r"([1-9]|[1-2][0-9]|3[0-1])",
'doy' : r"(?:[0-2]\d\d|3[0-5][0-9]|36[0-6])", 'doy' : r"(?:[0-2]\d\d|3[0-5][0-9]|36[0-6])",
'doy_short' : r"(?:[0-2]\d\d|3[0-5][0-9]|36[0-6])",
'MAJOR' : r"\d+", 'MAJOR' : r"\d+",
'MINOR' : r"\d+", 'MINOR' : r"\d+",
'MM' : r"\d{2,}", 'MM' : r"\d{2,}",
@ -137,6 +139,8 @@ FULL_PART_FORMATS = {
'us_week' : "{us_week:02}", 'us_week' : "{us_week:02}",
'dom' : "{dom:02}", 'dom' : "{dom:02}",
'doy' : "{doy:03}", 'doy' : "{doy:03}",
'dom_short' : "{dom}",
'doy_short' : "{doy}",
# depricated # depricated
'pep440_version': "{year}{month:02}.{BID}{pep440_tag}", 'pep440_version': "{year}{month:02}.{BID}{pep440_tag}",
'version' : "v{year}{month:02}.{bid}{release}", 'version' : "v{year}{month:02}.{bid}{release}",

View file

@ -33,6 +33,8 @@ PATTERN_PART_FIELDS = {
'us_week' : 'us_week', 'us_week' : 'us_week',
'dom' : 'dom', 'dom' : 'dom',
'doy' : 'doy', 'doy' : 'doy',
'dom_short' : 'dom',
'doy_short' : 'doy',
'MAJOR' : 'major', 'MAJOR' : 'major',
'MINOR' : 'minor', 'MINOR' : 'minor',
'MM' : 'minor', 'MM' : 'minor',
@ -147,6 +149,74 @@ class VersionInfo(typ.NamedTuple):
tag : str tag : str
FieldKey = str
MatchGroupKey = str
MatchGroupStr = str
PatternGroups = typ.Dict[MatchGroupKey, MatchGroupStr]
FieldValues = typ.Dict[FieldKey , MatchGroupStr]
def _parse_field_values(field_values: FieldValues) -> VersionInfo:
fv = field_values
tag = fv.get('tag')
if tag is None:
tag = "final"
tag = TAG_ALIASES.get(tag, tag)
assert tag is not None
bid = fv['bid'] if 'bid' in fv else "0001"
year = int(fv['year']) if 'year' in fv else None
doy = int(fv['doy' ]) if 'doy' in fv 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:
month = int(fv['month']) if 'month' in fv else None
dom = int(fv['dom' ]) if 'dom' in fv else 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:
iso_week = None
us_week = None
quarter = int(fv['quarter']) if 'quarter' in fv else None
if quarter is None and month:
quarter = _quarter_from_month(month)
major = int(fv['major']) if 'major' in fv else 0
minor = int(fv['minor']) if 'minor' in fv else 0
patch = int(fv['patch']) if 'patch' in fv 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 _is_calver(nfo: typ.Union[CalendarInfo, VersionInfo]) -> bool: def _is_calver(nfo: typ.Union[CalendarInfo, VersionInfo]) -> bool:
"""Check pattern for any calendar based parts. """Check pattern for any calendar based parts.
@ -189,7 +259,7 @@ class PatternError(Exception):
pass pass
def _parse_pattern_groups(pattern_groups: typ.Dict[str, str]) -> typ.Dict[str, str]: def _parse_pattern_groups(pattern_groups: PatternGroups) -> FieldValues:
for part_name in pattern_groups.keys(): for part_name in pattern_groups.keys():
is_valid_part_name = ( is_valid_part_name = (
part_name in patterns.COMPOSITE_PART_PATTERNS or part_name in PATTERN_PART_FIELDS part_name in patterns.COMPOSITE_PART_PATTERNS or part_name in PATTERN_PART_FIELDS
@ -198,12 +268,13 @@ def _parse_pattern_groups(pattern_groups: typ.Dict[str, str]) -> typ.Dict[str, s
err_msg = f"Invalid part '{part_name}'" err_msg = f"Invalid part '{part_name}'"
raise PatternError(err_msg) raise PatternError(err_msg)
items = [ field_value_items = [
(field_name, pattern_groups[part_name]) (field_name, pattern_groups[part_name])
for part_name, field_name in PATTERN_PART_FIELDS.items() for part_name, field_name in PATTERN_PART_FIELDS.items()
if part_name in pattern_groups.keys() if part_name in pattern_groups.keys()
] ]
all_fields = [field_name for field_name, _ in items]
all_fields = [field_name for field_name, _ in field_value_items]
unique_fields = set(all_fields) unique_fields = set(all_fields)
duplicate_fields = [f for f in unique_fields if all_fields.count(f) > 1] duplicate_fields = [f for f in unique_fields if all_fields.count(f) > 1]
@ -211,10 +282,10 @@ def _parse_pattern_groups(pattern_groups: typ.Dict[str, str]) -> typ.Dict[str, s
err_msg = f"Multiple parts for same field {duplicate_fields}." err_msg = f"Multiple parts for same field {duplicate_fields}."
raise PatternError(err_msg) raise PatternError(err_msg)
return dict(items) return dict(field_value_items)
def _parse_version_info(pattern_groups: typ.Dict[str, str]) -> VersionInfo: def _parse_version_info(pattern_groups: PatternGroups) -> VersionInfo:
"""Parse normalized VersionInfo from groups of a matched pattern. """Parse normalized VersionInfo from groups of a matched pattern.
>>> vnfo = _parse_version_info({'year': "2018", 'month': "11", 'bid': "0099"}) >>> vnfo = _parse_version_info({'year': "2018", 'month': "11", 'bid': "0099"})
@ -233,64 +304,8 @@ def _parse_version_info(pattern_groups: typ.Dict[str, str]) -> VersionInfo:
>>> (vnfo.major, vnfo.minor, vnfo.patch) >>> (vnfo.major, vnfo.minor, vnfo.patch)
(1, 23, 45) (1, 23, 45)
""" """
kw = _parse_pattern_groups(pattern_groups) field_values = _parse_pattern_groups(pattern_groups)
return _parse_field_values(field_values)
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:
month = int(kw['month']) if 'month' in kw else None
dom = int(kw['dom' ]) if 'dom' in kw else 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:
iso_week = None
us_week = None
quarter = int(kw['quarter']) if 'quarter' in kw else None
if quarter is None and month:
quarter = _quarter_from_month(month)
major = int(kw['major']) if 'major' in kw else 0
minor = int(kw['minor']) if 'minor' in kw else 0
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: def parse_version_info(version_str: str, pattern: str = "{pycalver}") -> VersionInfo:

View file

@ -1,6 +1,8 @@
import random import random
import datetime as dt import datetime as dt
import pytest
from pycalver import version from pycalver import version
from pycalver import patterns from pycalver import patterns
@ -50,25 +52,25 @@ def test_bump_random(monkeypatch):
def test_parse_version_info(): def test_parse_version_info():
version_str = "v201712.0001-alpha" version_str = "v201712.0001-alpha"
version_nfo = version.parse_version_info(version_str) version_info = version.parse_version_info(version_str)
# assert version_nfo.pep440_version == "201712.1a0" # assert version_info.pep440_version == "201712.1a0"
# assert version_nfo.version == "v201712.0001-alpha" # assert version_info.version == "v201712.0001-alpha"
assert version_nfo.year == 2017 assert version_info.year == 2017
assert version_nfo.month == 12 assert version_info.month == 12
assert version_nfo.bid == "0001" assert version_info.bid == "0001"
assert version_nfo.tag == "alpha" assert version_info.tag == "alpha"
version_str = "v201712.0001" version_str = "v201712.0001"
version_nfo = version.parse_version_info(version_str) version_info = version.parse_version_info(version_str)
# assert version_nfo.pep440_version == "201712.1" # assert version_info.pep440_version == "201712.1"
# assert version_nfo.version == "v201712.0001" # assert version_info.version == "v201712.0001"
assert version_nfo.year == 2017 assert version_info.year == 2017
assert version_nfo.month == 12 assert version_info.month == 12
assert version_nfo.bid == "0001" assert version_info.bid == "0001"
assert version_nfo.tag == "final" assert version_info.tag == "final"
def test_readme_pycalver1(): def test_readme_pycalver1():
@ -108,7 +110,7 @@ def test_parse_error_empty():
version.parse_version_info("") version.parse_version_info("")
assert False assert False
except version.PatternError as err: except version.PatternError as err:
pass assert "Invalid version string" in str(err)
def test_parse_error_noprefix(): def test_parse_error_noprefix():
@ -116,7 +118,7 @@ def test_parse_error_noprefix():
version.parse_version_info("201809.0002") version.parse_version_info("201809.0002")
assert False assert False
except version.PatternError as err: except version.PatternError as err:
pass assert "Invalid version string" in str(err)
def test_parse_error_nopadding(): def test_parse_error_nopadding():
@ -124,7 +126,7 @@ def test_parse_error_nopadding():
version.parse_version_info("v201809.2b0") version.parse_version_info("v201809.2b0")
assert False assert False
except version.PatternError as err: except version.PatternError as err:
pass assert "Invalid version string" in str(err)
def test_part_field_mapping(): def test_part_field_mapping():
@ -141,3 +143,40 @@ def test_part_field_mapping():
b_fields = set(version.VersionInfo._fields) b_fields = set(version.VersionInfo._fields)
assert a_fields == b_fields assert a_fields == b_fields
def vnfo(**field_values):
return version._parse_field_values(field_values)
PARSE_VERSION_TEST_CASES = [
["{year}.{month}.{dom}" , "2017.06.07", vnfo(year="2017", month="06", dom="07")],
["{year}.{month}.{dom_short}" , "2017.06.7" , vnfo(year="2017", month="06", dom="7" )],
["{year}.{month}.{dom_short}" , "2017.06.7" , vnfo(year="2017", month="06", dom="7" )],
["{year}.{month_short}.{dom_short}", "2017.6.7" , vnfo(year="2017", month="6" , dom="7" )],
["{year}.{month}.{dom}" , "2017.6.07" , None],
["{year}.{month}.{dom}" , "2017.06.7" , None],
["{year}.{month_short}.{dom}" , "2017.06.7" , None],
["{year}.{month}.{dom_short}" , "2017.6.07" , None],
["{year}.{month_short}.{MINOR}" , "2017.6.7" , vnfo(year="2017", month="6" , minor="7" )],
["{year}.{month}.{MINOR}" , "2017.06.7" , vnfo(year="2017", month="06", minor="7" )],
["{year}.{month}.{MINOR}" , "2017.06.07", vnfo(year="2017", month="06", minor="07")],
["{year}.{month}.{MINOR}" , "2017.6.7" , None],
]
@pytest.mark.parametrize("pattern_str, line, expected_vinfo", PARSE_VERSION_TEST_CASES)
def test_parse_versions(pattern_str, line, expected_vinfo):
pattern_re = patterns.compile_pattern(pattern_str)
version_match = pattern_re.search(line)
if expected_vinfo is None:
assert version_match is None
return
assert version_match is not None
version_str = version_match.group(0)
version_info = version.parse_version_info(version_str, pattern_str)
assert version_info == expected_vinfo