diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..f4a811a --- /dev/null +++ b/.travis.yml @@ -0,0 +1,22 @@ +language: python + +python: + # - "3.7" + - "3.6" + # NOTE (mb 2018-09-03): These should be installed via + # a bdist wheel, generated with python3.6. + # - "3.5" + # - "3.4" + # - "2.7" + +install: + - pip install rst2html5 flake8 pytest pytest-cov mypy typing-extensions + - pip install . + +script: + - export PYTHONPATH=src/:$PYTHONPATH + - export MYPYPATH=stubs/ + - python -m flake8 src/lib3to6/ + - python -m mypy src/lib3to6/ + - python -m pytest --cov=lib3to6 test/ + - rst2html5 --strict README.rst > /dev/null diff --git a/src/pycalver/lex_id.py b/src/pycalver/lex_id.py index 9f70c4a..00b8453 100644 --- a/src/pycalver/lex_id.py +++ b/src/pycalver/lex_id.py @@ -107,16 +107,9 @@ def main(): print(f"{_curr_id:<13} {ord_val(_curr_id):>12}") _next_id = next_id(_curr_id) - assert _curr_id < next_id - assert int(_curr_id, 10) < int(next_id, 10) - assert ord_val(_curr_id) < ord_val(next_id) - - # while next_id.startswith("0") and int(next_id) < 1000: - # _next_id = next_id(_next_id) - - if next_id.count("9") == len(next_id): + if _next_id.count("9") == len(_next_id): # all nines, we're done - print(f"{next_id:<13} {ord_val(next_id):>12}") + print(f"{_next_id:<13} {ord_val(_next_id):>12}") break if _next_id[0] != _curr_id[0] and len(_curr_id) > 1: diff --git a/src/pycalver/parse.py b/src/pycalver/parse.py index b8970c8..afe4e85 100644 --- a/src/pycalver/parse.py +++ b/src/pycalver/parse.py @@ -5,15 +5,10 @@ # SPDX-License-Identifier: MIT import re -import io -import os -import sys import logging import typing as typ -import datetime as dt import pkg_resources -from . import lex_id log = logging.getLogger("pycalver.parse") @@ -42,12 +37,19 @@ PYCALVER_RE: typ.re.Pattern[str] = re.compile(r""" """, flags=re.VERBOSE) +# NOTE (mb 2018-09-03): These are matchers for parts, which are +# used in the patterns, they're not for validation. This means +# that they may find strings, which are not valid pycalver +# strings, when parsed in their full context. For such cases, +# the patterns should be expanded. + + RE_PATTERN_PARTS = { - "pep440_version" : r"\b\d{6}\.[1-9]\d*(a|b|dev|rc|post)?\d*(?:\s|$)", - "version" : r"v\d{6}\.\d{4,}\-(?:alpha|beta|dev|rc|post)", + "pep440_version" : r"\d{6}\.[1-9]\d*(a|b|dev|rc|post)?\d*", + "version" : r"v\d{6}\.\d{4,}(\-(alpha|beta|dev|rc|post))?", "calver" : r"v\d{6}", "build" : r"\.\d{4,}", - "release" : r"(\-(?:alpha|beta|dev|rc|post))?", + "release" : r"(\-(alpha|beta|dev|rc|post))?", } @@ -73,7 +75,7 @@ class VersionInfo(typ.NamedTuple): def parse_version_info(version: str) -> VersionInfo: match = PYCALVER_RE.match(version) - pep440_version = pkg_resources.parse_version(version) + pep440_version = str(pkg_resources.parse_version(version)) return VersionInfo(pep440_version=pep440_version, **match.groupdict()) diff --git a/test/test_lex_id.py b/test/test_lex_id.py new file mode 100644 index 0000000..37a2a4a --- /dev/null +++ b/test/test_lex_id.py @@ -0,0 +1,52 @@ +import random +from pycalver import lex_id + + +def test_next_id_basic(): + assert lex_id.next_id("01") == "02" + assert lex_id.next_id("09") == "110" + + +def test_next_id_random(): + for i in range(1000): + prev_id = str(random.randint(1, 100000)) + next_id = lex_id.next_id(prev_id) + assert prev_id < next_id + + +def test_ord_val(): + assert lex_id.ord_val("1") == 1 + assert lex_id.ord_val("01") == 1 + assert lex_id.ord_val("02") == 2 + assert lex_id.ord_val("09") == 9 + assert lex_id.ord_val("110") == 10 + + +def test_main(capsys): + lex_id.main() + captured = capsys.readouterr() + assert len(captured.err) == 0 + + lines = iter(captured.out.splitlines()) + header = next(lines) + + assert "lexical" in header + assert "numerical" in header + + ids = [] + ord_vals = [] + + for line in lines: + if "..." in line: + continue + _id, _ord_val = line.split() + + assert _id.endswith(_ord_val) + assert int(_ord_val) == int(_ord_val, 10) + + ids.append(_id.strip()) + ord_vals.append(int(_ord_val.strip())) + + assert len(ids) > 0 + assert sorted(ids) == ids + assert sorted(ord_vals) == ord_vals diff --git a/test/test_parse.py b/test/test_parse.py index e69de29..4f22688 100644 --- a/test/test_parse.py +++ b/test/test_parse.py @@ -0,0 +1,181 @@ +import re +from pycalver import parse + + +def test_readme_pycalver1(): + version_str = "v201712.0001-alpha" + version_info = parse.PYCALVER_RE.match(version_str).groupdict() + + assert version_info == { + "version" : "v201712.0001-alpha", + "calver" : "v201712", + "year" : "2017", + "month" : "12", + "build" : ".0001", + "release" : "-alpha", + } + + +def test_readme_pycalver2(): + version_str = "v201712.0033" + version_info = parse.PYCALVER_RE.match(version_str).groupdict() + + assert version_info == { + "version" : "v201712.0033", + "calver" : "v201712", + "year" : "2017", + "month" : "12", + "build" : ".0033", + "release" : None, + } + + +def test_re_pattern_parts(): + part_re_by_name = { + part_name: re.compile(part_re_str) + for part_name, part_re_str in parse.RE_PATTERN_PARTS.items() + } + + cases = [ + ("pep440_version", "201712.31", "201712.31"), + ("pep440_version", "v201712.0032", None), + ("pep440_version", "201712.0033-alpha", None), + ("version", "v201712.0034", "v201712.0034"), + ("version", "v201712.0035-alpha", "v201712.0035-alpha"), + ("version", "v201712.0036-alpha0", "v201712.0036-alpha"), + ("version", "v201712.0037-pre", "v201712.0037"), + ("version", "201712.38a0", None), + ("version", "201712.0039", None), + ("calver", "v201712", "v201712"), + ("calver", "v201799", "v201799"), # maybe date validation should be a thing + ("calver", "201712", None), + ("calver", "v20171", None), + ("build", ".0012", ".0012"), + ("build", ".11012", ".11012"), + ("build", ".012", None), + ("build", "11012", None), + ("release", "-alpha", "-alpha"), + ("release", "-beta", "-beta"), + ("release", "-dev", "-dev"), + ("release", "-rc", "-rc"), + ("release", "-post", "-post"), + ("release", "-pre", ""), + ("release", "alpha", ""), + ] + + for part_name, line, expected in cases: + part_re = part_re_by_name[part_name] + result = part_re.search(line) + if result is None: + assert expected is None, (part_name, line) + else: + result_val = result.group(0) + assert result_val == expected, (part_name, line) + + +def test_parse_version_info(): + version_str = "v201712.0001-alpha" + version_nfo = parse.parse_version_info(version_str) + + assert version_nfo.pep440_version == "201712.1a0" + assert version_nfo.version == "v201712.0001-alpha" + assert version_nfo.calver == "v201712" + assert version_nfo.year == "2017" + assert version_nfo.month == "12" + assert version_nfo.build == ".0001" + assert version_nfo.release == "-alpha" + + version_str = "v201712.0001" + version_nfo = parse.parse_version_info(version_str) + + assert version_nfo.pep440_version == "201712.1" + assert version_nfo.version == "v201712.0001" + assert version_nfo.calver == "v201712" + assert version_nfo.year == "2017" + assert version_nfo.month == "12" + assert version_nfo.build == ".0001" + assert version_nfo.release is None + + +def test_default_parse_patterns(): + lines = [ + "# setup.py", + "import setuptools", + "__version__ = 'v201712.0002-alpha'", + "setuptools.setup(", + "...", + " version='201712.2a0',", + ] + + patterns = [ + "{version}", + "{pep440_version}", + ] + + matches = parse.parse_patterns(lines, patterns) + assert len(matches) == 2 + + assert matches[0].lineno == 2 + assert matches[1].lineno == 5 + + assert matches[0].pattern == patterns[0] + assert matches[1].pattern == patterns[1] + + assert matches[0].match == "v201712.0002-alpha" + assert matches[1].match == "201712.2a0" + + +def test_explicit_parse_patterns(): + lines = [ + "# setup.py", + "import setuptools", + "__version__ = 'v201712.0002-alpha'", + "setuptools.setup(", + "...", + " version='201712.2a0',", + ] + + patterns = [ + "__version__ = '{version}'", + "version='{pep440_version}'", + ] + + matches = parse.parse_patterns(lines, patterns) + assert len(matches) == 2 + + assert matches[0].lineno == 2 + assert matches[1].lineno == 5 + + assert matches[0].pattern == patterns[0] + assert matches[1].pattern == patterns[1] + + assert matches[0].match == "__version__ = 'v201712.0002-alpha'" + assert matches[1].match == "version='201712.2a0'" + + +def test_badge_parse_patterns(): + lines = [ + ":alt: PyPI version", + "", + ".. |version| image:: https://img.shields.io/badge/CalVer-v201809.0002--beta-blue.svg", + ":target: https://calver.org/", + ":alt: CalVer v201809.0002-beta", + "", + ] + + patterns = [ + "badge/CalVer-{calver}{build}-{release}-blue.svg", + ":alt: CalVer {version}", + ] + + matches = parse.parse_patterns(lines, patterns) + assert len(matches) == 2 + + assert matches[0].lineno == 2 + assert matches[1].lineno == 4 + + assert matches[0].pattern == patterns[0] + assert matches[1].pattern == patterns[1] + + assert matches[0].match == "badge/CalVer-v201809.0002--beta-blue.svg" + assert matches[1].match == ":alt: CalVer v201809.0002-beta" diff --git a/test/test_version.py b/test/test_version.py index bfcea44..089a3a5 100644 --- a/test/test_version.py +++ b/test/test_version.py @@ -1,30 +1,37 @@ import datetime as dt -import pycalver.version +from pycalver import version -def test_calver(): - import random +def test_current_calver(): + v = version.current_calver() + assert len(v) == 7 + assert v.startswith("v") + assert v[1:].isdigit() - first_version_str = "v201808.0001-dev" - padding = len(first_version_str) + 3 - version_str = first_version_str - def _current_calver() -> str: - _current_calver.delta += dt.timedelta(days=int(random.random() * 5)) +# def test_calver(): +# import random - return (dt.datetime.utcnow() + _current_calver.delta).strftime("v%Y%m") +# first_version_str = "v201808.0001-dev" +# padding = len(first_version_str) + 3 +# version_str = first_version_str - _current_calver.delta = dt.timedelta(days=1) +# def _current_calver() -> str: +# _current_calver.delta += dt.timedelta(days=int(random.random() * 5)) - global current_calver - current_calver = _current_calver +# return (dt.datetime.utcnow() + _current_calver.delta).strftime("v%Y%m") - for i in range(1050): - version_str = incr_version(version_str, tag=random.choice([ - None, "alpha", "beta", "rc" - ])) - print(f"{version_str:<{padding}}", end=" ") - if (i + 1) % 8 == 0: - print() +# _current_calver.delta = dt.timedelta(days=1) - print() +# global current_calver +# current_calver = _current_calver + +# for i in range(1050): +# version_str = incr_version(version_str, tag=random.choice([ +# None, "alpha", "beta", "rc" +# ])) +# print(f"{version_str:<{padding}}", end=" ") +# if (i + 1) % 8 == 0: +# print() + +# print()