From f705164e75d341e2e9ac229ac81ed3b6e45ce400 Mon Sep 17 00:00:00 2001 From: Manuel Barkhau Date: Sat, 3 Oct 2020 18:36:56 +0000 Subject: [PATCH] better error messages and fixups --- makefile.bootstrapit.make | 4 +- src/pycalver/__main__.py | 211 +++++++++++++++++++------------------- src/pycalver/pysix.py | 5 + src/pycalver/regexfmt.py | 17 ++- src/pycalver/v1rewrite.py | 9 +- src/pycalver/v2rewrite.py | 10 +- test/test_patterns.py | 22 ++-- 7 files changed, 155 insertions(+), 123 deletions(-) diff --git a/makefile.bootstrapit.make b/makefile.bootstrapit.make index fada680..ac91372 100644 --- a/makefile.bootstrapit.make +++ b/makefile.bootstrapit.make @@ -395,7 +395,7 @@ test: --cov-report term \ --html=reports/pytest/index.html \ --junitxml reports/pytest.xml \ - -k "$${PYTEST_FILTER}" \ + -k "$${PYTEST_FILTER-$${FLTR}}" \ $(shell cd src/ && ls -1 */__init__.py | awk '{ sub(/\/__init__.py/, "", $$1); print "--cov "$$1 }') \ test/ src/; @@ -515,7 +515,7 @@ devtest: --capture=no \ --exitfirst \ --failed-first \ - -k "$${PYTEST_FILTER}" \ + -k "$${PYTEST_FILTER-$${FLTR}}" \ test/ src/; @rm -rf "src/__pycache__"; diff --git a/src/pycalver/__main__.py b/src/pycalver/__main__.py index 3fa3708..54204ad 100755 --- a/src/pycalver/__main__.py +++ b/src/pycalver/__main__.py @@ -148,8 +148,8 @@ def test( minor : bool = False, patch : bool = False, release_num: bool = False, - date : typ.Optional[str] = None, pin_date : bool = False, + date : typ.Optional[str] = None, ) -> None: """Increment a version number for demo purposes.""" _configure_logging(verbose=max(_VERBOSE, verbose)) @@ -179,6 +179,109 @@ def test( click.echo(f"PEP440 : {pep440_version}") +def _grep_text(pattern: patterns.Pattern, text: str, color: bool) -> int: + match_count = 0 + all_lines = text.splitlines() + for match in pattern.regexp.finditer(text): + match_count += 1 + match_start, match_end = match.span() + + line_idx = text[:match_start].count("\n") + line_start = text.rfind("\n", 0, match_start) + 1 + line_end = text.find("\n", match_end, -1) + if color: + matched_line = ( + text[line_start:match_start] + + colorama.Style.BRIGHT + + text[match_start:match_end] + + colorama.Style.RESET_ALL + + text[match_end:line_end] + ) + else: + matched_line = ( + text[line_start:match_start] + + text[match_start:match_end] + + text[match_end:line_end] + ) + + lines_offset = max(0, line_idx - 1) + 1 + lines = all_lines[line_idx - 1 : line_idx + 2] + + if line_idx == 0: + lines[0] = matched_line + else: + lines[1] = matched_line + + for i, line in enumerate(lines): + print(f"{lines_offset + i:>4}: {line}") + + print() + return match_count + + +def _grep( + raw_pattern: str, + file_ios : typ.Tuple[io.TextIOWrapper], + color : bool, +) -> None: + pattern = v2patterns.compile_pattern(raw_pattern) + + match_count = 0 + for file_io in file_ios: + text = file_io.read() + + _match_count = _grep_text(pattern, text, color) + + print() + print(f"Found {_match_count} match for pattern '{raw_pattern}' in {file_io.name}") + print() + + match_count += _match_count + + if match_count == 0 or _VERBOSE: + pyexpr_regex = regexfmt.pyexpr_regex(pattern.regexp.pattern) + + print(f"# pycalver pattern: '{raw_pattern}'") + print("# " + regexfmt.regex101_url(pattern.regexp.pattern)) + print(pyexpr_regex) + print() + + if match_count == 0: + sys.exit(1) + + +@cli.command() +@click.option( + "-v", + "--verbose", + count=True, + help="Control log level. -vv for debug level.", +) +@click.argument("pattern") +@click.argument('files', nargs=-1, type=click.File('r')) +def grep( + pattern: str, + files : typ.Tuple[io.TextIOWrapper], + verbose: int = 0, +) -> None: + """Search file(s) for a version pattern.""" + verbose = max(_VERBOSE, verbose) + _configure_logging(verbose) + + raw_pattern = pattern # use internal naming convention + + isatty = getattr(sys.stdout, 'isatty', lambda: False) + + if isatty(): + colorama.init() + try: + _grep(raw_pattern, files, color=True) + finally: + colorama.deinit() + else: + _grep(raw_pattern, files, color=False) + + @cli.command() @click.option('-v', '--verbose', count=True, help="Control log level. -vv for debug level.") @click.option( @@ -254,7 +357,8 @@ def _incr( else: pattern = v2patterns.compile_pattern(raw_pattern) - logger.info(f"Using pattern {raw_pattern}/{pattern.regexp.pattern}") + logger.info("Using pattern " + raw_pattern) + logger.info("regex = " + regexfmt.pyexpr_regex(pattern.regexp.pattern)) if has_v1_part: return v1version.incr( @@ -495,108 +599,5 @@ def bump( _try_bump(cfg, new_version, commit_message, allow_dirty) -def _grep_text(pattern: patterns.Pattern, text: str, color: bool) -> int: - match_count = 0 - all_lines = text.splitlines() - for match in pattern.regexp.finditer(text): - match_count += 1 - match_start, match_end = match.span() - - line_idx = text[:match_start].count("\n") - line_start = text.rfind("\n", 0, match_start) + 1 - line_end = text.find("\n", match_end, -1) - if color: - matched_line = ( - text[line_start:match_start] - + colorama.Style.BRIGHT - + text[match_start:match_end] - + colorama.Style.RESET_ALL - + text[match_end:line_end] - ) - else: - matched_line = ( - text[line_start:match_start] - + text[match_start:match_end] - + text[match_end:line_end] - ) - - lines_offset = max(0, line_idx - 1) + 1 - lines = all_lines[line_idx - 1 : line_idx + 2] - - if line_idx == 0: - lines[0] = matched_line - else: - lines[1] = matched_line - - for i, line in enumerate(lines): - print(f"{lines_offset + i:>4}: {line}") - - print() - return match_count - - -def _grep( - raw_pattern: str, - file_ios : typ.Tuple[io.TextIOWrapper], - color : bool, -) -> None: - pattern = v2patterns.compile_pattern(raw_pattern) - - match_count = 0 - for file_io in file_ios: - text = file_io.read() - - _match_count = _grep_text(pattern, text, color) - - print() - print(f"Found {_match_count} match for pattern '{raw_pattern}' in {file_io.name}") - print() - - match_count += _match_count - - if match_count == 0 or _VERBOSE: - pyexpr_regex = regexfmt.pyexpr_regex(pattern.regexp.pattern) - - print(f"# pycalver pattern: '{raw_pattern}'") - print("# " + regexfmt.regex101_url(pattern)) - print(pyexpr_regex) - print() - - if match_count == 0: - sys.exit(1) - - -@cli.command() -@click.option( - "-v", - "--verbose", - count=True, - help="Control log level. -vv for debug level.", -) -@click.argument("pattern") -@click.argument('files', nargs=-1, type=click.File('r')) -def grep( - pattern: str, - files : typ.Tuple[io.TextIOWrapper], - verbose: int = 0, -) -> None: - """Search files for a version pattern.""" - verbose = max(_VERBOSE, verbose) - _configure_logging(verbose) - - raw_pattern = pattern # use internal naming convention - - isatty = getattr(sys.stdout, 'isatty', lambda: False) - - if isatty(): - colorama.init() - try: - _grep(raw_pattern, files, color=True) - finally: - colorama.deinit() - else: - _grep(raw_pattern, files, color=False) - - if __name__ == '__main__': cli() diff --git a/src/pycalver/pysix.py b/src/pycalver/pysix.py index 36e77e9..354f2d5 100644 --- a/src/pycalver/pysix.py +++ b/src/pycalver/pysix.py @@ -1,3 +1,8 @@ +# This file is part of the pycalver project +# https://github.com/mbarkhau/pycalver +# +# Copyright (c) 2018-2020 Manuel Barkhau (mbarkhau@gmail.com) - MIT License +# SPDX-License-Identifier: MIT import sys import typing as typ diff --git a/src/pycalver/regexfmt.py b/src/pycalver/regexfmt.py index 05c3ccb..7123b1a 100644 --- a/src/pycalver/regexfmt.py +++ b/src/pycalver/regexfmt.py @@ -1,8 +1,15 @@ +# This file is part of the pycalver project +# https://github.com/mbarkhau/pycalver +# +# Copyright (c) 2018-2020 Manuel Barkhau (mbarkhau@gmail.com) - MIT License +# SPDX-License-Identifier: MIT import re +import logging import textwrap from . import pysix -from . import patterns + +logger = logging.getLogger("pycalver.regexfmt") def format_regex(regex: str) -> str: @@ -53,16 +60,16 @@ def pyexpr_regex(regex: str) -> str: return f"re.compile({repr(regex)})" -def regex101_url(pattern: patterns.Pattern) -> str: +def regex101_url(regex_pattern: str) -> str: try: - regex_text = format_regex(pattern.regexp.pattern) + regex_pattern = format_regex(regex_pattern) except re.error: - regex_text = pattern.regexp.pattern + logger.warning(f"Error formatting regex '{repr(regex_pattern)}'") return "".join( ( "https://regex101.com/", "?flavor=python", - "&flags=gmx" "®ex=" + pysix.quote(regex_text), + "&flags=gmx" "®ex=" + pysix.quote(regex_pattern), ) ) diff --git a/src/pycalver/v1rewrite.py b/src/pycalver/v1rewrite.py index e2f6313..4993bfd 100644 --- a/src/pycalver/v1rewrite.py +++ b/src/pycalver/v1rewrite.py @@ -13,6 +13,7 @@ from . import parse from . import config from . import rewrite from . import version +from . import regexfmt from . import v1version from .patterns import Pattern @@ -39,7 +40,13 @@ def rewrite_lines( if non_matched_patterns: for nmp in non_matched_patterns: logger.error(f"No match for pattern '{nmp.raw_pattern}'") - logger.error(f"Pattern compiles to regex '{nmp.regexp.pattern}'") + msg = ( + "\n# " + + regexfmt.regex101_url(nmp.regexp.pattern) + + "\nregex = " + + regexfmt.pyexpr_regex(nmp.regexp.pattern) + ) + logger.error(msg) raise rewrite.NoPatternMatch("Invalid pattern(s)") else: return new_lines diff --git a/src/pycalver/v2rewrite.py b/src/pycalver/v2rewrite.py index a6d85bd..2be2022 100644 --- a/src/pycalver/v2rewrite.py +++ b/src/pycalver/v2rewrite.py @@ -13,6 +13,7 @@ from . import parse from . import config from . import rewrite from . import version +from . import regexfmt from . import v2version from . import v2patterns from .patterns import Pattern @@ -43,7 +44,14 @@ def rewrite_lines( if non_matched_patterns: for nmp in non_matched_patterns: logger.error(f"No match for pattern '{nmp.raw_pattern}'") - logger.error(f"Pattern compiles to regex '{nmp.regexp.pattern}'") + msg = ( + "\n# " + + regexfmt.regex101_url(nmp.regexp.pattern) + + "\nregex = " + + regexfmt.pyexpr_regex(nmp.regexp.pattern) + ) + logger.error(msg) + logger.error(msg) raise rewrite.NoPatternMatch("Invalid pattern(s)") else: return new_lines diff --git a/test/test_patterns.py b/test/test_patterns.py index 7c6302b..981a9af 100644 --- a/test/test_patterns.py +++ b/test/test_patterns.py @@ -148,8 +148,9 @@ V2_PART_PATTERN_CASES = [ def _compile_part_re(pattern_str): - grouped_pattern_str = r"(?:" + pattern_str + r")" - return re.compile(grouped_pattern_str) + grouped_pattern_str = r"^(?:" + pattern_str + r")$" + # print("\n", grouped_pattern_str) + return re.compile(grouped_pattern_str, flags=re.MULTILINE) @pytest.mark.parametrize("parts, testcase, expected", V2_PART_PATTERN_CASES) @@ -157,7 +158,10 @@ def test_v2_part_patterns(parts, testcase, expected): for part in parts: part_re = _compile_part_re(v2patterns.PART_PATTERNS[part]) match = part_re.match(testcase) - assert (match is None and expected is None) or (match.group(0) == expected) + if match is None: + assert expected is None + else: + assert match.group(0) == expected @pytest.mark.parametrize("part_name", v2patterns.PART_PATTERNS.keys()) @@ -176,8 +180,8 @@ PATTERN_PART_CASES = [ ("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" , "v201712.0036-alpha0", None), + ("pycalver" , "v201712.0037-pre" , None), # pre not available for v1 patterns ("pycalver" , "201712.38a0" , None), ("pycalver" , "201712.0039" , None), ("semver" , "1.23.456" , "1.23.456"), @@ -194,17 +198,17 @@ PATTERN_PART_CASES = [ ("release" , "-dev" , "-dev"), ("release" , "-rc" , "-rc"), ("release" , "-post" , "-post"), - ("release" , "-pre" , ""), - ("release" , "alpha" , ""), + ("release" , "-pre" , None), # pre not available for v1 patterns + ("release" , "alpha" , None), # missing dash "-" prefix ] @pytest.mark.parametrize("part_name, line, expected", PATTERN_PART_CASES) def test_v1_re_pattern_parts(part_name, line, expected): part_re = _compile_part_re(v1patterns.PART_PATTERNS[part_name]) - result = part_re.search(line) + result = part_re.match(line) if result is None: - assert expected is None, (part_name, line) + assert expected is None, (part_name, line, result) else: result_val = result.group(0) assert result_val == expected, (part_name, line)