readme updates

This commit is contained in:
Manuel Barkhau 2020-09-17 16:24:21 +00:00
parent 32ad101b9f
commit 28e09fd60a
7 changed files with 356 additions and 214 deletions

View file

@ -1,6 +1,16 @@
# Changelog for https://gitlab.com/mbarkhau/pycalver # Changelog for https://gitlab.com/mbarkhau/pycalver
## NEXT
- New gitlab #7: New style patterns, to be in line with CalVer.org
- Better support for week numbering.
- Better support for optional parts.
- New gitlab #10: `--pin-date` to keep date parts unchanged, and only increment non-date parts.
- Fix gitlab #8: Push tags only pushed tags, not actual commit.
- Fix gitlab #9: Make commit message configurable.
## v201907.0036 ## v201907.0036
- Fix: Don't use git/hg command if `commit=False` is configured (thanks @valentin87) - Fix: Don't use git/hg command if `commit=False` is configured (thanks @valentin87)

144
README.md
View file

@ -212,10 +212,108 @@ The internally used regular expression is also shown, which you can use to debug
### Pattern Search and Replacement ### Pattern Search and Replacement
The `pycalver:file_patterns` section of the configuration is used both to search The neat thing about PyCalVer is that you don't have to separately declare the search pattern and the replacement template. You declare one pattern that is used to derive both.
and also to replace version strings in your projects files. Everything except
for valid placeholders is treated as literal text. Available placeholders are: for valid placeholders is treated as literal text. Available placeholders are:
You define your pattern in the `pycalver:version_pattern` option of your config.
You can also define custom patterns in the items of the `pycalver:file_patterns` section of your configuration, but usually it will be easier to just use the special `{version}` and `{pep440_version}` patterns, which are derived from what you configure as your `version_pattern`. is used both to search and also to replace version strings in your projects files. Everything except for valid placeholders is treated as literal text. Available placeholders are:
These patterns are closely based on https://calver.org/
| placeholder | range / example(s) | comment |
|-------------|---------------------|----------------------|
| YYYY | 2019, 2020... | `%Y` |
| YY | 18, 19..99, 1, 2 | `int(%y)` |
| 0Y | 18, 19..99, 01, 02 | `%y` |
| Q | 1, 2, 3, 4 | quarter |
| MM | 9, 10, 11, 12 | `int(%m)` |
| 0M | 09, 10, 11, 12 | `%m` |
| DD | 1, 2, 3..31 | `int(%d)` |
| 0D | 01, 02, 03..31 | `%d` |
| JJJ | 1,2,3..366 | `int(%j)` |
| 00J | 001, 002..366 | `%j` |
| BUILD | 1001, 1002, 23456 | build number (lexid) |
| TAG | alpha, beta, rc | `--release=<tag>` |
| PYTAG | a0, b0, rc | `--release=<tag>` |
| MAJOR | 0..9, 10..99, 100.. | `--major` |
| MINOR | 0..9, 10..99, 100.. | `--minor` |
| PATCH | 0..9, 10..99, 100.. | `--patch` |
| MICRO | 0..9, 10..99, 100.. | Synonym for PATCH |
### Week Numbering
Week numbering is a bit special, as it depends on your definition of "week":
- Does it start on a Monday or a Sunday?
- Range from 0-52 or 1-53 ?
- At the beginning/end of the year, do you have partial weeks or do you have a week that span mutliple years?
- If a week spans multiple years, what is the year number?
| placeholder | range / example(s) | comment |
|-------------|---------------------|------------------------------------------------------------|
| `WW` | 0, 1, 2..52 | `int(%W)` |
| `0W` | 00, 01, 02..52 | `%W` |
| `UU` | 0, 1, 2..52 | `int(%U)` us_week |
| `0U` | 00, 01, 02..52 | `%U` us_week |
| `VV` | 1, 2..53 | `int(%V)` iso week |
| `0V` | 01, 02..53 | `%U` iso_week |
| `GGGG` | 2019, 2020... | ISO 8601 week-based year (corresponds to `strftime("%G")`) |
| `GG` | 19, 20...99, 0, 1 | Short ISO 8601 week-based year |
| `0G` | 19, 20...99, 00, 01 | Zero-padded ISO 8601 week-based year |
### Normalization Caveats
Since other tools parse your version numbers, they may not care about your choice of formatting. In the case of Python, the packaging tools (such as pypi.org) follow [PEP440 normalization rules][pep_440_normalzation_ref].
According to these rules:
- Any non-numerical prefix (such as `v`) is removed
- Leading zeros in parts are truncated `XX.08` -> `XX.8`
- Tags are converted to a short form (`-alpha` -> `a0`)
For example:
- Pattern: `vYY.0M.0D[-TAG]`
- Version: `v20.08.02-beta`
- PEP440 : `20.8.2b0`
It may be confusing to your users to see versions displayed in two different forms. It is not immediately obvious that `v20.08.02-beta` is the same `20.8.2b0` on pypi. If you wish to avoid this, you should usa a pattern which is as close as possible to the normalized form of your version.
| pattern | example | lexical | PEP440 | lexical |
|-----------------------|---------|---------|--------|---------|
| `YYYY.0M` | | yes | | no |
| `YYYY.MM` | | no | | no |
| `vYYYY.0W` | | yes | | no |
| `vYYYY.WW` | | no | | no |
| `YYYY.0M.0D` | | yes | | no |
| `YYYY.MM.DD` | | no | | no |
| `YYYY0M.BUILD[-TAG]` | | yes | | yes |
| `YY0M.BUILD[-TAG]` | | yes¹ | | yes¹ |
| `YYYY.BUILD[-TAG]` | | yes | | yes |
| `YYYY0M.MINOR[-TAG]` | | yes² | | yes |
| `YYYY.MM.MINOR[-TAG]` | | no | | no |
| `YYYY.0M.MINOR[-TAG]` | | yes² | | no |
| `YYYY.WW.MINOR[-TAG]` | | no | | no |
| `YYYY.0W.MINOR[-TAG]` | | yes² | | no |
- ¹ Until 2099. If your project has new releases after 2099, future maintainers can change `YY`/`0Y` -> `YYYY` so that they don't release `00.xx`.
- ² As long as `MINOR <= 9`
### Legacy Patterns
> These patterns use curly braces `{}` and were the initial implementation. They are still supported and still follow their original semantics.
The `pycalver:file_patterns` section of the configuration uses a different set
of placeholders and does not use curly braces to mark placeholders. It is still
supported, but we don't recomend you use it.
Available placeholders are:
| placeholder | range / example(s) | comment | | placeholder | range / example(s) | comment |
|---------------------|---------------------|-----------------| |---------------------|---------------------|-----------------|
@ -298,29 +396,29 @@ bump` to update the components that are not updated automatically (eg.
based on the calendar). based on the calendar).
```shell ```shell
$ pycalver test 'v18.1.1' 'v{yy}.{MINOR}.{PATCH}' $ pycalver test 'v18.1.1' 'vYY.MINOR.PATCH'
New Version: v19.1.1 New Version: v19.1.1
PEP440 : 19.1.1 PEP440 : 19.1.1
$ pycalver test 'v18.1.1' 'v{yy}.{MINOR}.{PATCH}' --patch $ pycalver test 'v18.1.1' 'vYY.MINOR.PATCH' --patch
New Version: v19.1.2 New Version: v19.1.2
PEP440 : 19.1.2 PEP440 : 19.1.2
$ pycalver test 'v18.1.2' 'v{yy}.{MINOR}.{PATCH}' --minor $ pycalver test 'v18.1.2' 'vYY.MINOR.PATCH' --minor
New Version: v19.2.0 New Version: v19.2.0
PEP440 : 19.2.0 PEP440 : 19.2.0
$ pycalver test 'v201811.0051-beta' '{pycalver}' $ pycalver test 'v201811.1051-beta' 'vYYYYMM.BUILD[-TAG]'
New Version: v201902.0052-beta New Version: v201902.1052-beta
PEP440 : 201902.52b0 PEP440 : 201902.1052b0
$ pycalver test 'v201811.0051-beta' '{pycalver}' --release rc $ pycalver test 'v201811.0051-beta' 'vYYYYMM.BUILD[-TAG]' --release rc
New Version: v201902.0052-rc New Version: v201902.1052-rc
PEP440 : 201902.52rc0 PEP440 : 201902.1052rc0
$ pycalver test 'v201811.0051-beta' '{pycalver}' --release final $ pycalver test 'v201811.0051-beta' 'vYYYYMM.BUILD[-TAG]' --release final
New Version: v201902.0052 New Version: v201902.1052
PEP440 : 201902.52 PEP440 : 201902.1052
``` ```
Note that pypi/setuptools/pip will normalize version strings to a format Note that pypi/setuptools/pip will normalize version strings to a format
@ -391,8 +489,8 @@ section:
```ini ```ini
[pycalver] [pycalver]
current_version = "v201812.0006-beta" current_version = "202008.1006-beta"
version_pattern = "{pycalver}" version_pattern = "YYYY0M.BUILD[-TAG]"
commit = True commit = True
tag = True tag = True
push = True push = True
@ -429,8 +527,8 @@ $ pycalver bump --dry
@@ -65,7 +65,7 @@ @@ -65,7 +65,7 @@
[pycalver] [pycalver]
-current_version = v201812.0005-beta -current_version = v202008.1005-beta
+current_version = v201812.0006-beta +current_version = v202008.1006-beta
commit = True commit = True
tag = True tag = True
push = True push = True
@ -442,11 +540,11 @@ If everything looks OK, you can do `pycalver bump`.
``` ```
$ pycalver bump --verbose $ pycalver bump --verbose
INFO - fetching tags from remote (to turn off use: -n / --no-fetch) INFO - fetching tags from remote (to turn off use: -n / --no-fetch)
INFO - Old Version: v201812.0005-beta INFO - Old Version: v202008.0005-beta
INFO - New Version: v201812.0006-beta INFO - New Version: v202008.0006-beta
INFO - git commit --file /tmp/tmpph_npey9 INFO - git commit --file /tmp/tmpph_npey9
INFO - git tag --annotate v201812.0006-beta --message v201812.0006-beta INFO - git tag --annotate v202008.0006-beta --message v202008.0006-beta
INFO - git push origin v201812.0006-beta INFO - git push origin v202008.0006-beta
``` ```
@ -461,7 +559,7 @@ The PyCalVer format for version strings has three parts:
| | o Release Tag (optional) | | o Release Tag (optional)
| | | | | |
---+--- --+-- --+-- ---+--- --+-- --+--
v201812 .0123 -beta v202008 .0123 -beta
``` ```

View file

@ -3,61 +3,36 @@
# #
# Copyright (c) 2018-2020 Manuel Barkhau (mbarkhau@gmail.com) - MIT License # Copyright (c) 2018-2020 Manuel Barkhau (mbarkhau@gmail.com) - MIT License
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
"""Compose Regular Expressions from Patterns. # """Compose Regular Expressions from Patterns.
>>> version_info = PYCALVER_RE.match("v201712.0123-alpha").groupdict() # >>> pattern = compile_pattern("vYYYY0M.BUILD[-TAG]")
>>> assert version_info == { # >>> version_info = pattern.regexp.match("v201712.0123-alpha")
... "pycalver" : "v201712.0123-alpha", # >>> assert version_info == {
... "vYYYYMM" : "v201712", # ... "version": "v201712.0123-alpha",
... "year" : "2017", # ... "YYYY" : "2017",
... "month" : "12", # ... "0M" : "12",
... "build" : ".0123", # ... "BUILD" : "0123",
... "build_no" : "0123", # ... "TAG" : "alpha",
... "release" : "-alpha", # ... }
... "release_tag" : "alpha", # >>>
... } # >>> version_info = pattern.regexp.match("201712.1234")
>>> # >>> assert version_info is None
>>> version_info = PYCALVER_RE.match("v201712.0033").groupdict()
>>> assert version_info == { # >>> version_info = pattern.regexp.match("v201712.1234")
... "pycalver" : "v201712.0033", # >>> assert version_info == {
... "vYYYYMM" : "v201712", # ... "version": "v201712.0123-alpha",
... "year" : "2017", # ... "YYYY" : "2017",
... "month" : "12", # ... "0M" : "12",
... "build" : ".0033", # ... "BUILD" : "0123",
... "build_no" : "0033", # ... "TAG" : None,
... "release" : None, # ... }
... "release_tag": None, # """
... }
"""
import re import re
import typing as typ import typing as typ
import pycalver.patterns as v1patterns import pycalver.patterns as v1patterns
# 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 = [ PATTERN_ESCAPES = [
("\u005c", "\u005c\u005c"), ("\u005c", "\u005c\u005c"),
("-" , "\u005c-"), ("-" , "\u005c-"),
@ -73,107 +48,37 @@ PATTERN_ESCAPES = [
(")" , "\u005c)"), (")" , "\u005c)"),
] ]
# NOTE (mb 2020-09-04): These are depricated in favour of explicit patterns
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 = { PART_PATTERNS = {
# recommended (based on calver.org) # Based on calver.org
'YYYY': r"[1-9]\d{3}", 'YYYY': r"[1-9][0-9]{3}",
'YY' : r"\d{1,2}", 'YY' : r"[1-9][0-9]?",
'0Y' : r"\d{2}", '0Y' : r"[0-9]{2}",
'Q' : r"[1-4]", 'Q' : r"[1-4]",
'MM' : r"(?:[1-9]|1[0-2])", 'MM' : r"(?:[1-9]|1[0-2])",
'0M' : r"(?:0[1-9]|1[0-2])", '0M' : r"(?:0[1-9]|1[0-2])",
'DD' : r"([1-9]|[1-2][0-9]|3[0-1])", 'DD' : r"(?:[1-9]|[1-2][0-9]|3[0-1])",
'0D' : r"(0[1-9]|[1-2][0-9]|3[0-1])", '0D' : r"(?:0[1-9]|[1-2][0-9]|3[0-1])",
'JJJ' : r"(?:[1-9]\d|[1-9]|[1-2]\d\d|3[0-5][0-9]|36[0-6])", 'JJJ' : r"(?:[1-9]|[1-9][0-9]|[1-2][0-9][0-9]|3[0-5][0-9]|36[0-6])",
'00J' : r"(?:[0-2]\d\d|3[0-5][0-9]|36[0-6])", '00J' : r"(?:00[1-9]|0[1-9][0-9]|[1-2][0-9][0-9]|3[0-5][0-9]|36[0-6])",
'WW' : r"(?:[1-9]|[1-4]\d|5[0-2])", # week numbering parts
'0W' : r"(?:[0-4]\d|5[0-2])", 'WW' : r"(?:[0-9]|[1-4][0-9]|5[0-2])",
'UU' : r"(?:[1-9]|[0-4]\d|5[0-2])", '0W' : r"(?:[0-4][0-9]|5[0-2])",
'0U' : r"(?:[0-4]\d|5[0-2])", 'UU' : r"(?:[0-9]|[1-4][0-9]|5[0-2])",
'VV' : r"(?:[1-9]|[1-4]\d|5[0-3])", '0U' : r"(?:[0-4][0-9]|5[0-2])",
'0V' : r"(?:[0-4]\d|5[0-3])", 'VV' : r"(?:[1-9]|[1-4][0-9]|5[0-3])",
'GGGG': r"[1-9]\d{3}", '0V' : r"(?:0[1-9]|[1-4][0-9]|5[0-3])",
'GG' : r"\d{1,2}", 'GGGG': r"[1-9][0-9]{3}",
'0G' : r"\d{2}", 'GG' : r"[1-9][0-9]?",
'0G' : r"[0-9]{2}",
# non calver parts # non calver parts
'MAJOR': r"\d+", 'MAJOR': r"[0-9]+",
'MINOR': r"\d+", 'MINOR': r"[0-9]+",
'PATCH': r"\d+", 'PATCH': r"[0-9]+",
'MICRO': r"\d+", 'MICRO': r"[0-9]+",
'BUILD': r"\d+", 'BUILD': r"[0-9]+",
'TAG' : r"(?:alpha|beta|dev|rc|post|final)", 'TAG' : r"(?:alpha|beta|dev|rc|post|final)",
'PYTAG': r"(?:a|b|dev|rc|post)?\d*", 'PYTAG': r"(?:a|b|dev|rc|post)?[0-9]*",
# supported (but legacy)
'year' : r"\d{4}",
'month' : r"(?:0[0-9]|1[0-2])",
'month_short': r"(?:1[0-2]|[1-9])",
'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])",
'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_short' : r"(?:[0-2]\d\d|3[0-5][0-9]|36[0-6])",
'bid' : r"\d{4,}",
# dropped support (never documented)
# 'BID' : r"[1-9]\d*",
# 'MM' : r"\d{2,}",
# 'MMM' : r"\d{3,}",
# 'MMMM' : r"\d{4,}",
# 'MMMMM' : r"\d{5,}",
# 'PP' : r"\d{2,}",
# 'PPP' : r"\d{3,}",
# 'PPPP' : r"\d{4,}",
# 'PPPPP' : r"\d{5,}",
# '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 treated specially in version.format
# 'release' : "-{tag}",
'month' : "{month:02}",
'month_short': "{month}",
'build_no' : "{bid}",
'iso_week' : "{iso_week:02}",
'us_week' : "{us_week:02}",
'dom' : "{dom:02}",
'doy' : "{doy:03}",
'dom_short' : "{dom}",
'doy_short' : "{doy}",
# depricated
'pep440_version': "{year}{month:02}.{BID}{pep440_tag}",
'version' : "v{year}{month:02}.{bid}{release}",
} }
@ -198,13 +103,3 @@ def compile_pattern(pattern: str) -> v1patterns.Pattern:
pattern_str = compile_pattern_str(pattern) pattern_str = compile_pattern_str(pattern)
pattern_re = re.compile(pattern_str) pattern_re = re.compile(pattern_str)
return v1patterns.Pattern(pattern, pattern_re) return v1patterns.Pattern(pattern, pattern_re)
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()

View file

@ -344,14 +344,13 @@ def _parse_version_info(pattern_groups: PatternGroups) -> VersionInfo:
return _parse_field_values(field_values) return _parse_field_values(field_values)
def parse_version_info(version_str: str, pattern: str = "{pycalver}") -> VersionInfo: def parse_version_info(version_str: str, pattern: str = "vYYYY0M.BUILD[-TAG]") -> VersionInfo:
# TODO reenable doctest
# """Parse normalized VersionInfo. # """Parse normalized VersionInfo.
# >>> vnfo = parse_version_info("v201712.0033-beta", pattern="{pycalver}") # >>> vnfo = parse_version_info("v201712.0033-beta", pattern="vYYYY0M.BUILD[-TAG]")
# >>> assert vnfo == _parse_version_info({'year': 2017, 'month': 12, 'bid': "0033", 'tag': "beta"}) # >>> assert vnfo == _parse_version_info({'year': 2017, 'month': 12, 'bid': "0033", 'tag': "beta"})
# >>> vnfo = parse_version_info("1.23.456", pattern="{semver}") # >>> vnfo = parse_version_info("1.23.456", pattern="MAJOR.MINOR.PATCH")
# >>> assert vnfo == _parse_version_info({'MAJOR': "1", 'MINOR': "23", 'PATCH': "456"}) # >>> assert vnfo == _parse_version_info({'MAJOR': "1", 'MINOR': "23", 'PATCH': "456"})
# """ # """
pattern_tup = v2patterns.compile_pattern(pattern) pattern_tup = v2patterns.compile_pattern(pattern)

View file

@ -11,7 +11,7 @@ import pathlib2 as pl
from click.testing import CliRunner from click.testing import CliRunner
import pycalver.config as config import pycalver.config as config
import pycalver2.patterns as patterns import pycalver.patterns as v1patterns
from pycalver.__main__ import cli from pycalver.__main__ import cli
SETUP_CFG_FIXTURE = """ SETUP_CFG_FIXTURE = """
@ -81,7 +81,7 @@ def test_version(runner):
result = runner.invoke(cli, ['--version', "-vv"]) result = runner.invoke(cli, ['--version', "-vv"])
assert result.exit_code == 0 assert result.exit_code == 0
assert " version v20" in result.output assert " version v20" in result.output
match = patterns.PYCALVER_RE.search(result.output) match = v1patterns.PYCALVER_RE.search(result.output)
assert match assert match

View file

@ -7,6 +7,134 @@ import pycalver2.patterns as v2patterns
# TODO (mb 2020-09-06): test for v2patterns # TODO (mb 2020-09-06): test for v2patterns
V2_PART_PATTERN_CASES = [
(['YYYY', 'GGGG'], "2020" , "2020"),
(['YYYY', 'GGGG'], "" , None),
(['YYYY', 'GGGG'], "A020" , None),
(['YYYY', 'GGGG'], "020" , None),
(['YYYY', 'GGGG'], "12020", None),
(['YY' , 'GG' ], "20" , "20"),
(['YY' , 'GG' ], "3" , "3"),
(['YY' , 'GG' ], "03" , None),
(['YY' , 'GG' ], "2X" , None),
(['YY' , 'GG' ], "" , None),
(['0Y' , '0G' ], "20" , "20"),
(['0Y' , '0G' ], "03" , "03"),
(['0Y' , '0G' ], "3" , None),
(['0Y' , '0G' ], "2X" , None),
(['0Y' , '0G' ], "" , None),
# quarter
(['Q'], "0", None),
(['Q'], "1", "1"),
(['Q'], "2", "2"),
(['Q'], "3", "3"),
(['Q'], "4", "4"),
(['Q'], "5", None),
(['Q'], "X", None),
# months
(['MM'], "0" , None),
(['MM'], "01", None),
(['MM'], "1" , "1"),
(['MM'], "10", "10"),
(['MM'], "12", "12"),
(['MM'], "13", None),
(['0M'], "00", None),
(['0M'], "1" , None),
(['0M'], "01", "01"),
(['MM'], "10", "10"),
(['MM'], "12", "12"),
(['MM'], "13", None),
# day of month
(['DD'], "0" , None),
(['DD'], "01", None),
(['DD'], "1" , "1"),
(['DD'], "10", "10"),
(['DD'], "31", "31"),
(['DD'], "32", None),
(['0D'], "00", None),
(['0D'], "1" , None),
(['0D'], "01", "01"),
(['0D'], "10", "10"),
(['0D'], "31", "31"),
(['0D'], "32", None),
(['DD'], "0" , None),
(['DD'], "01", None),
(['DD'], "1" , "1"),
(['DD'], "10", "10"),
(['DD'], "31", "31"),
(['DD'], "32", None),
(['0D'], "00", None),
(['0D'], "1" , None),
(['0D'], "01", "01"),
(['0D'], "10", "10"),
(['0D'], "31", "31"),
(['0D'], "32", None),
# day of year
(['JJJ'], "0" , None),
(['JJJ'], "01" , None),
(['JJJ'], "1" , "1"),
(['JJJ'], "10" , "10"),
(['JJJ'], "31" , "31"),
(['JJJ'], "32" , "32"),
(['JJJ'], "100", "100"),
(['JJJ'], "365", "365"),
(['JJJ'], "366", "366"),
(['JJJ'], "367", None),
(['00J'], "000", None),
(['00J'], "01" , None),
(['00J'], "1" , None),
(['00J'], "001", "001"),
(['00J'], "010", "010"),
(['00J'], "031", "031"),
(['00J'], "032", "032"),
(['00J'], "100", "100"),
(['00J'], "365", "365"),
(['00J'], "366", "366"),
(['00J'], "367", None),
# week numbers
(['WW', 'UU'], "00", None),
(['WW', 'UU'], "01", None),
(['WW', 'UU'], "0" , "0"),
(['WW', 'UU'], "1" , "1"),
(['WW', 'UU'], "10", "10"),
(['WW', 'UU'], "52", "52"),
(['WW', 'UU'], "53", None),
(['0W', '0U'], "00", "00"),
(['0W', '0U'], "01", "01"),
(['0W', '0U'], "0" , None),
(['0W', '0U'], "1" , None),
(['0W', '0U'], "10", "10"),
(['0W', '0U'], "52", "52"),
(['0W', '0U'], "53", None),
(['VV'], "00", None),
(['VV'], "01", None),
(['VV'], "0" , None),
(['VV'], "1" , "1"),
(['VV'], "10", "10"),
(['VV'], "52", "52"),
(['VV'], "53", "53"),
(['VV'], "54", None),
(['0V'], "00", None),
(['0V'], "01", "01"),
(['0V'], "0" , None),
(['0V'], "1" , None),
(['0V'], "10", "10"),
(['0V'], "52", "52"),
(['0V'], "53", "53"),
(['0V'], "54", None),
(['MAJOR', 'MINOR', 'PATCH', 'MICRO'], "0", "0"),
# ('TAG', ""),
# ('PYTAG', ""),
]
@pytest.mark.parametrize("parts, testcase, expected", V2_PART_PATTERN_CASES)
def test_part_patterns(parts, testcase, expected):
for part in parts:
pattern_str = v2patterns.PART_PATTERNS[part]
match = re.match("^" + pattern_str + "$", testcase)
assert (match is None and expected is None) or (match.group(0) == expected)
def _part_re_by_name(name): def _part_re_by_name(name):
return re.compile(v1patterns.PART_PATTERNS[name]) return re.compile(v1patterns.PART_PATTERNS[name])

View file

@ -5,28 +5,30 @@ import datetime as dt
import pytest import pytest
from pycalver import version import pycalver.version as v1version
from pycalver import patterns import pycalver2.version as v2version
import pycalver.patterns as v1patterns
import pycalver2.patterns as v2patterns
def test_bump_beta(): def test_bump_beta():
cur_version = "v201712.0001-beta" cur_version = "v201712.0001-beta"
assert cur_version < version.incr(cur_version) assert cur_version < v1version.incr(cur_version)
assert version.incr(cur_version).endswith("-beta") assert v1version.incr(cur_version).endswith("-beta")
assert version.incr(cur_version, release="alpha").endswith("-alpha") assert v1version.incr(cur_version, release="alpha").endswith("-alpha")
assert version.incr(cur_version, release="final").endswith("0002") assert v1version.incr(cur_version, release="final").endswith("0002")
def test_bump_final(): def test_bump_final():
cur_version = "v201712.0001" cur_version = "v201712.0001"
assert cur_version < version.incr(cur_version) assert cur_version < v1version.incr(cur_version)
assert version.incr(cur_version).endswith(".0002") assert v1version.incr(cur_version).endswith(".0002")
assert version.incr(cur_version, release="alpha").endswith("-alpha") assert v1version.incr(cur_version, release="alpha").endswith("-alpha")
assert version.incr(cur_version, release="final").endswith(".0002") assert v1version.incr(cur_version, release="final").endswith(".0002")
pre_version = cur_version + "-beta" pre_version = cur_version + "-beta"
assert version.incr(pre_version, release="final").endswith(".0002") assert v1version.incr(pre_version, release="final").endswith(".0002")
def test_bump_future(): def test_bump_future():
@ -34,7 +36,7 @@ def test_bump_future():
future_date = dt.datetime.today() + dt.timedelta(days=300) future_date = dt.datetime.today() + dt.timedelta(days=300)
future_calver = future_date.strftime("v%Y%m") future_calver = future_date.strftime("v%Y%m")
cur_version = future_calver + ".0001" cur_version = future_calver + ".0001"
new_version = version.incr(cur_version) new_version = v1version.incr(cur_version)
assert cur_version < new_version assert cur_version < new_version
@ -42,11 +44,11 @@ def test_bump_random(monkeypatch):
cur_date = dt.date(2016, 1, 1) + dt.timedelta(days=random.randint(1, 2000)) 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"
monkeypatch.setattr(version, 'TODAY', cur_date) monkeypatch.setattr(v1version, 'TODAY', cur_date)
for _ in range(1000): for _ in range(1000):
cur_date += dt.timedelta(days=int((1 + random.random()) ** 10)) cur_date += dt.timedelta(days=int((1 + random.random()) ** 10))
new_version = version.incr( new_version = v1version.incr(
cur_version, release=random.choice([None, "alpha", "beta", "rc", "final", "post"]) cur_version, release=random.choice([None, "alpha", "beta", "rc", "final", "post"])
) )
assert cur_version < new_version assert cur_version < new_version
@ -55,7 +57,7 @@ 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_info = version.parse_version_info(version_str) version_info = v1version.parse_version_info(version_str)
# assert version_info.pep440_version == "201712.1a0" # assert version_info.pep440_version == "201712.1a0"
# assert version_info.version == "v201712.0001-alpha" # assert version_info.version == "v201712.0001-alpha"
@ -65,7 +67,7 @@ def test_parse_version_info():
assert version_info.tag == "alpha" assert version_info.tag == "alpha"
version_str = "v201712.0001" version_str = "v201712.0001"
version_info = version.parse_version_info(version_str) version_info = v1version.parse_version_info(version_str)
# assert version_info.pep440_version == "201712.1" # assert version_info.pep440_version == "201712.1"
# assert version_info.version == "v201712.0001" # assert version_info.version == "v201712.0001"
@ -77,7 +79,7 @@ def test_parse_version_info():
def test_readme_pycalver1(): def test_readme_pycalver1():
version_str = "v201712.0001-alpha" version_str = "v201712.0001-alpha"
version_info = patterns.PYCALVER_RE.match(version_str).groupdict() version_info = v1patterns.PYCALVER_RE.match(version_str).groupdict()
assert version_info == { assert version_info == {
'pycalver' : "v201712.0001-alpha", 'pycalver' : "v201712.0001-alpha",
@ -93,7 +95,7 @@ def test_readme_pycalver1():
def test_readme_pycalver2(): def test_readme_pycalver2():
version_str = "v201712.0033" version_str = "v201712.0033"
version_info = patterns.PYCALVER_RE.match(version_str).groupdict() version_info = v1patterns.PYCALVER_RE.match(version_str).groupdict()
assert version_info == { assert version_info == {
'pycalver' : "v201712.0033", 'pycalver' : "v201712.0033",
@ -109,40 +111,40 @@ def test_readme_pycalver2():
def test_parse_error_empty(): def test_parse_error_empty():
try: try:
version.parse_version_info("") v1version.parse_version_info("")
assert False assert False
except version.PatternError as err: except v1version.PatternError as err:
assert "Invalid version string" in str(err) assert "Invalid version string" in str(err)
def test_parse_error_noprefix(): def test_parse_error_noprefix():
try: try:
version.parse_version_info("201809.0002") v1version.parse_version_info("201809.0002")
assert False assert False
except version.PatternError as err: except v1version.PatternError as err:
assert "Invalid version string" in str(err) assert "Invalid version string" in str(err)
def test_parse_error_nopadding(): def test_parse_error_nopadding():
try: try:
version.parse_version_info("v201809.2b0") v1version.parse_version_info("v201809.2b0")
assert False assert False
except version.PatternError as err: except v1version.PatternError as err:
assert "Invalid version string" in str(err) assert "Invalid version string" in str(err)
def test_part_field_mapping(): def test_part_field_mapping_v1():
a_names = set(version.PATTERN_PART_FIELDS.keys()) a_names = set(v1version.PATTERN_PART_FIELDS.keys())
b_names = set(patterns.PART_PATTERNS.keys()) b_names = set(v1patterns.PART_PATTERNS.keys())
c_names = set(patterns.COMPOSITE_PART_PATTERNS.keys()) c_names = set(v1patterns.COMPOSITE_PART_PATTERNS.keys())
a_extra_names = a_names - b_names a_extra_names = a_names - b_names
assert not any(a_extra_names), sorted(a_extra_names) assert not any(a_extra_names), sorted(a_extra_names)
b_extra_names = b_names - (a_names | c_names) b_extra_names = b_names - (a_names | c_names)
assert not any(b_extra_names), sorted(b_extra_names) assert not any(b_extra_names), sorted(b_extra_names)
a_fields = set(version.PATTERN_PART_FIELDS.values()) a_fields = set(v1version.PATTERN_PART_FIELDS.values())
b_fields = set(version.VersionInfo._fields) b_fields = set(v1version.VersionInfo._fields)
a_extra_fields = a_fields - b_fields a_extra_fields = a_fields - b_fields
b_extra_fields = b_fields - a_fields b_extra_fields = b_fields - a_fields
@ -150,8 +152,18 @@ def test_part_field_mapping():
assert not any(b_extra_fields), sorted(b_extra_fields) assert not any(b_extra_fields), sorted(b_extra_fields)
def test_part_field_mapping_v2():
a_names = set(v2version.PATTERN_PART_FIELDS.keys())
b_names = set(v2patterns.PART_PATTERNS.keys())
a_extra_names = a_names - b_names
assert not any(a_extra_names), sorted(a_extra_names)
b_extra_names = b_names - a_names
assert not any(b_extra_names), sorted(b_extra_names)
def vnfo(**field_values): def vnfo(**field_values):
return version._parse_field_values(field_values) return v1version._parse_field_values(field_values)
PARSE_VERSION_TEST_CASES = [ PARSE_VERSION_TEST_CASES = [
@ -174,7 +186,7 @@ PARSE_VERSION_TEST_CASES = [
@pytest.mark.parametrize("pattern_str, line, expected_vinfo", PARSE_VERSION_TEST_CASES) @pytest.mark.parametrize("pattern_str, line, expected_vinfo", PARSE_VERSION_TEST_CASES)
def test_parse_versions(pattern_str, line, expected_vinfo): def test_parse_versions(pattern_str, line, expected_vinfo):
pattern = patterns.compile_pattern(pattern_str) pattern = v1patterns.compile_pattern(pattern_str)
version_match = pattern.regexp.search(line) version_match = pattern.regexp.search(line)
if expected_vinfo is None: if expected_vinfo is None:
@ -184,6 +196,6 @@ def test_parse_versions(pattern_str, line, expected_vinfo):
assert version_match is not None assert version_match is not None
version_str = version_match.group(0) version_str = version_match.group(0)
version_info = version.parse_version_info(version_str, pattern_str) version_info = v1version.parse_version_info(version_str, pattern_str)
assert version_info == expected_vinfo assert version_info == expected_vinfo