diff --git a/setup.cfg b/setup.cfg index 80bf24b..daba43a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -82,6 +82,8 @@ setup.py = version="{pep440_version}" src/pycalver/__init__.py = __version__ = "{version}" +src/pycalver/__main__.py = + click.version_option(version="{version}") README.md = [PyCalVer {version}] https://img.shields.io/badge/PyCalVer-{calver}{build}-{release}-blue.svg diff --git a/src/pycalver/__main__.py b/src/pycalver/__main__.py index 8e29355..a221ce7 100644 --- a/src/pycalver/__main__.py +++ b/src/pycalver/__main__.py @@ -57,7 +57,7 @@ def _init_logging(verbose: int = 0) -> None: @click.group() -@click.version_option() +@click.version_option(version="v201812.0010-beta") @click.help_option() @click.option('-v', '--verbose', count=True, help="Control log level. -vv for debug level.") def cli(verbose: int = 0): diff --git a/src/pycalver/parse.py b/src/pycalver/parse.py index a683516..9b0b59c 100644 --- a/src/pycalver/parse.py +++ b/src/pycalver/parse.py @@ -21,8 +21,12 @@ PATTERN_ESCAPES = [ ("." , "\u005c."), ("+" , "\u005c+"), ("*" , "\u005c*"), + ("{" , "\u005c{{"), + ("}" , "\u005c}}"), ("[" , "\u005c["), + ("]" , "\u005c]"), ("(" , "\u005c("), + (")" , "\u005c)"), ] # NOTE (mb 2018-09-03): These are matchers for parts, which are @@ -54,17 +58,25 @@ class PatternMatch(typ.NamedTuple): PatternMatches = typ.Iterable[PatternMatch] -def _iter_for_pattern(lines: typ.List[str], pattern: str) -> PatternMatches: - # The pattern is escaped, so that everything besides the format - # string variables is treated literally. - +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) - pattern_re = re.compile(pattern_str) + return re.compile(pattern_str) + + +def _iter_for_pattern(lines: typ.List[str], pattern: str) -> PatternMatches: + # The pattern is escaped, so that everything besides the format + # string variables is treated literally. + pattern_re = compile_pattern(pattern) + for lineno, line in enumerate(lines): match = pattern_re.search(line) if match: diff --git a/test/test_parse.py b/test/test_parse.py index 067978a..a7b53c8 100644 --- a/test/test_parse.py +++ b/test/test_parse.py @@ -115,3 +115,28 @@ def test_badge_parse_patterns(): assert matches[0].match == "badge/CalVer-v201809.0002--beta-blue.svg" 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"}'