diff --git a/license.header b/license.header
index 6b5623a..cb08b33 100644
--- a/license.header
+++ b/license.header
@@ -1,9 +1,9 @@
Individual files contain the following tag instead of the full license text.
This file is part of the pycalver project
- https://gitlab.com/mbarkhau/pycalver
+ https://github.com/mbarkhau/pycalver
- Copyright (c) 2020 Manuel Barkhau (mbarkhau@gmail.com) - MIT License
+ Copyright (c) 2018-2020 Manuel Barkhau (mbarkhau@gmail.com) - MIT License
SPDX-License-Identifier: MIT
This enables machine processing of license information based on the SPDX
diff --git a/makefile b/makefile
index 25099b5..61364ab 100644
--- a/makefile
+++ b/makefile
@@ -61,3 +61,5 @@ test_compat: $(COMPAT_TEST_FILES)
ENABLE_BACKTRACE=0 PYTHONPATH="" ENV=$${ENV-dev} \
$${env_py} -m pytest --verbose compat_test/; \
done;
+
+ rm -rf compat_test/
diff --git a/pycalver1k.svg b/pycalver1k.svg
index 156e809..b7133c9 100644
--- a/pycalver1k.svg
+++ b/pycalver1k.svg
@@ -14,8 +14,8 @@
version="1.1"
id="svg8"
inkscape:version="1.0 (b51213c273, 2020-08-10)"
- sodipodi:docname="pycalver_1k.svg"
- inkscape:export-filename="C:\Users\ManuelBarkhau\Dropbox\projects\pycalver\pycalver_128.png"
+ sodipodi:docname="pycalver1k.svg"
+ inkscape:export-filename="/home/mbarkhau/foss/pycalver/pycalver1k2_128.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
+ gradientTransform="matrix(1.0541065,0,0,0.87055183,-0.916204,-223.64659)" />
@@ -201,6 +201,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+ id="layer6"
+ inkscape:label="bg"
+ style="display:inline">
+
+
+
+
+ style="display:inline">
v20202020
+
+
+
+
+
+ .1001
+ id="tspan919"
+ x="4.7286582"
+ y="13.507061"
+ style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:16.9333px;font-family:'Iosevka Fixed SS05';-inkscape-font-specification:'Iosevka Fixed SS05 Medium';fill:#ffffff;stroke-width:0.265;stroke-miterlimit:4;stroke-dasharray:none">ver
+ x="1.5910856"
+ y="16.622082" />
+
+
+ mask="url(#mask1129-3)"
+ transform="matrix(1.0545213,0,0,0.89332824,-0.92322741,-236.38373)" />
+ style="display:none"
+ sodipodi:insensitive="true">
+ x="7.3927369"
+ y="1.7978847" />
+ x="24.886431"
+ y="1.7978847" />
diff --git a/pycalver1k2_128.png b/pycalver1k2_128.png
new file mode 100644
index 0000000..30ea018
Binary files /dev/null and b/pycalver1k2_128.png differ
diff --git a/requirements/pypi.txt b/requirements/pypi.txt
index 0504771..0bd5192 100644
--- a/requirements/pypi.txt
+++ b/requirements/pypi.txt
@@ -12,3 +12,4 @@ typing; python_version < "3.5"
click
toml
six
+lexid
diff --git a/scratch.py b/scratch.py
deleted file mode 100644
index cfdbc6d..0000000
--- a/scratch.py
+++ /dev/null
@@ -1,3 +0,0 @@
-import sys
-
-print(sys.version)
diff --git a/setup.py b/setup.py
index 78c823f..8cf0107 100644
--- a/setup.py
+++ b/setup.py
@@ -1,7 +1,7 @@
# This file is part of the pycalver project
-# https://gitlab.com/mbarkhau/pycalver
+# https://github.com/mbarkhau/pycalver
#
-# Copyright (c) 2019 Manuel Barkhau (mbarkhau@gmail.com) - MIT License
+# Copyright (c) 2018-2020 Manuel Barkhau (mbarkhau@gmail.com) - MIT License
# SPDX-License-Identifier: MIT
import os
@@ -26,10 +26,42 @@ install_requires = [
]
+long_description = "\n\n".join((read("README.md"), read("CHANGELOG.md")))
+
+
+# See https://pypi.python.org/pypi?%3Aaction=list_classifiers
+classifiers = [
+ "Development Status :: 4 - Beta",
+ "Environment :: Console",
+ "Environment :: Other Environment",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: MIT License",
+ "Operating System :: Unix",
+ "Operating System :: POSIX",
+ "Operating System :: Microsoft :: Windows",
+ "Operating System :: MacOS :: MacOS X",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 2.7",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: Implementation :: CPython",
+ "Programming Language :: Python :: Implementation :: PyPy",
+ "Topic :: Software Development :: Libraries",
+ "Topic :: Software Development :: Libraries :: Python Modules",
+]
+
package_dir = {"": "src"}
-if any(arg.startswith("bdist") for arg in sys.argv):
+is_lib3to6_fix_required = (
+ any(arg.startswith("bdist") for arg in sys.argv)
+ and (
+ "Programming Language :: Python :: 2.7" in classifiers
+ or "Programming Language :: Python :: 2" in classifiers
+ )
+)
+
+
+if is_lib3to6_fix_required:
try:
import lib3to6
package_dir = lib3to6.fix(package_dir)
@@ -43,9 +75,6 @@ if any(arg.startswith("bdist") for arg in sys.argv):
))
-long_description = "\n\n".join((read("README.md"), read("CHANGELOG.md")))
-
-
setuptools.setup(
name="pycalver",
license="MIT",
@@ -57,32 +86,14 @@ setuptools.setup(
description="CalVer for python libraries.",
long_description=long_description,
long_description_content_type="text/markdown",
- packages=['pycalver'],
+ packages=setuptools.find_packages("src/"),
package_dir=package_dir,
install_requires=install_requires,
entry_points="""
[console_scripts]
pycalver=pycalver.cli:cli
""",
+ python_requires=">=2.7",
zip_safe=True,
-
- # See https://pypi.python.org/pypi?%3Aaction=list_classifiers
- classifiers=[
- "Development Status :: 4 - Beta",
- "Environment :: Console",
- "Environment :: Other Environment",
- "Intended Audience :: Developers",
- "License :: OSI Approved :: MIT License",
- "Operating System :: Unix",
- "Operating System :: POSIX",
- "Operating System :: Microsoft :: Windows",
- "Operating System :: MacOS :: MacOS X",
- "Programming Language :: Python",
- "Programming Language :: Python :: 2.7",
- "Programming Language :: Python :: 3",
- "Programming Language :: Python :: Implementation :: CPython",
- "Programming Language :: Python :: Implementation :: PyPy",
- "Topic :: Software Development :: Libraries",
- "Topic :: Software Development :: Libraries :: Python Modules",
- ],
+ classifiers=classifiers,
)
diff --git a/src/pycalver/__init__.py b/src/pycalver/__init__.py
index c1658bf..0d7e37b 100644
--- a/src/pycalver/__init__.py
+++ b/src/pycalver/__init__.py
@@ -1,7 +1,7 @@
# This file is part of the pycalver project
-# https://gitlab.com/mbarkhau/pycalver
+# https://github.com/mbarkhau/pycalver
#
-# Copyright (c) 2019 Manuel Barkhau (mbarkhau@gmail.com) - MIT License
+# Copyright (c) 2018-2020 Manuel Barkhau (mbarkhau@gmail.com) - MIT License
# SPDX-License-Identifier: MIT
"""PyCalVer: CalVer for Python Packages."""
diff --git a/src/pycalver/__main__.py b/src/pycalver/__main__.py
index 8e8a3e9..80130c2 100755
--- a/src/pycalver/__main__.py
+++ b/src/pycalver/__main__.py
@@ -1,8 +1,8 @@
#!/usr/bin/env python
# This file is part of the pycalver project
-# https://gitlab.com/mbarkhau/pycalver
+# https://github.com/mbarkhau/pycalver
#
-# Copyright (c) 2019 Manuel Barkhau (mbarkhau@gmail.com) - MIT License
+# Copyright (c) 2018-2020 Manuel Barkhau (mbarkhau@gmail.com) - MIT License
# SPDX-License-Identifier: MIT
"""
__main__ module for PyCalVer.
diff --git a/src/pycalver/cli.py b/src/pycalver/cli.py
index 0316382..47b305a 100755
--- a/src/pycalver/cli.py
+++ b/src/pycalver/cli.py
@@ -1,8 +1,8 @@
#!/usr/bin/env python
# This file is part of the pycalver project
-# https://gitlab.com/mbarkhau/pycalver
+# https://github.com/mbarkhau/pycalver
#
-# Copyright (c) 2019 Manuel Barkhau (mbarkhau@gmail.com) - MIT License
+# Copyright (c) 2018-2020 Manuel Barkhau (mbarkhau@gmail.com) - MIT License
# SPDX-License-Identifier: MIT
"""
CLI module for PyCalVer.
diff --git a/src/pycalver/config.py b/src/pycalver/config.py
index a214d0c..e5b41a8 100644
--- a/src/pycalver/config.py
+++ b/src/pycalver/config.py
@@ -234,6 +234,8 @@ def _parse_config(raw_cfg: RawConfig) -> Config:
version_str: str = raw_cfg['current_version']
version_str = raw_cfg['current_version'] = version_str.strip("'\" ")
+ # TODO (mb 2020-09-06): new style pattern by default
+ # version_pattern: str = raw_cfg.get('version_pattern', "vYYYY0M.BUILD[-TAG]")
version_pattern: str = raw_cfg.get('version_pattern', "{pycalver}")
version_pattern = raw_cfg['version_pattern'] = version_pattern.strip("'\" ")
diff --git a/src/pycalver/lex_id.py b/src/pycalver/lex_id.py
deleted file mode 100644
index 5c6af28..0000000
--- a/src/pycalver/lex_id.py
+++ /dev/null
@@ -1,169 +0,0 @@
-# This file is part of the pycalver project
-# https://gitlab.com/mbarkhau/pycalver
-#
-# Copyright (c) 2019 Manuel Barkhau (mbarkhau@gmail.com) - MIT License
-# SPDX-License-Identifier: MIT
-
-"""A scheme for lexically ordered numerical ids.
-
-Throughout the sequence this expression remains true, whether you
-are dealing with integers or strings:
-
- older_id < newer_id
-
-The left most character/digit is only used to maintain lexical
-order, so that the position in the sequence is maintained in the
-remaining digits.
-
- sequence_pos = int(idval[1:], 10)
-
-lexical sequence_pos
-0 0
-11 1
-12 2
-...
-19 9
-220 20
-221 21
-...
-298 98
-299 99
-3300 300
-3301 301
-...
-3998 998
-3999 999
-44000 4000
-44001 4001
-...
-899999998 99999998
-899999999 99999999
-9900000000 900000000
-9900000001 900000001
-...
-9999999998 999999998
-9999999999 999999999 # maximum value
-
-You can add leading zeros to delay the expansion and/or increase
-the maximum possible value.
-
-lexical sequence_pos
-0001 1
-0002 2
-0003 3
-...
-0999 999
-11000 1000
-11001 1001
-11002 1002
-...
-19998 9998
-19999 9999
-220000 20000
-220001 20001
-...
-899999999998 99999999998
-899999999999 99999999999
-9900000000000 900000000000
-9900000000001 900000000001
-...
-9999999999998 999999999998
-9999999999999 999999999999 # maximum value
-
-This scheme is useful when you just want an ordered sequence of
-numbers, but the numbers don't have any particular meaning or
-arithmetical relation. The only relation they have to each other
-is that numbers generated later in the sequence are greater than
-ones generated earlier.
-"""
-
-
-MINIMUM_ID = "0"
-
-
-def next_id(prev_id: str) -> str:
- """Generate next lexical id.
-
- Increments by one and adds padding if required.
-
- >>> next_id("0098")
- '0099'
- >>> next_id("0099")
- '0100'
- >>> next_id("0999")
- '11000'
- >>> next_id("11000")
- '11001'
- """
-
- num_digits = len(prev_id)
-
- if prev_id.count("9") == num_digits:
- raise OverflowError("max lexical version reached: " + prev_id)
-
- _prev_id_val = int(prev_id, 10)
- _maybe_next_id_val = int(_prev_id_val) + 1
- _maybe_next_id_str = f"{_maybe_next_id_val:0{num_digits}}"
-
- _is_padding_ok = prev_id[0] == _maybe_next_id_str[0]
- _next_id_str: str
-
- if _is_padding_ok:
- _next_id_str = _maybe_next_id_str
- else:
- _next_id_str = str(_maybe_next_id_val * 11)
- return _next_id_str
-
-
-def ord_val(lex_id: str) -> int:
- """Parse the ordinal value of a lexical id.
-
- The ordinal value is the position in the sequence,
- from repeated calls to next_id.
-
- >>> ord_val("0098")
- 98
- >>> ord_val("0099")
- 99
- >>> ord_val("0100")
- 100
- >>> ord_val("11000")
- 1000
- >>> ord_val("11001")
- 1001
- """
- if len(lex_id) == 1:
- return int(lex_id, 10)
- else:
- return int(lex_id[1:], 10)
-
-
-def _main() -> None:
- _curr_id = "01"
- print(f"{'lexical':<13} {'numerical':>12}")
-
- while True:
- print(f"{_curr_id:<13} {ord_val(_curr_id):>12}")
- _next_id = next_id(_curr_id)
-
- if _next_id.count("9") == len(_next_id):
- # all nines, we're done
- print(f"{_next_id:<13} {ord_val(_next_id):>12}")
- break
-
- if _next_id[0] != _curr_id[0] and len(_curr_id) > 1:
- print(f"{_next_id:<13} {ord_val(_next_id):>12}")
- _next_id = next_id(_next_id)
- print(f"{_next_id:<13} {ord_val(_next_id):>12}")
- _next_id = next_id(_next_id)
-
- print("...")
-
- # skip ahead
- _next_id = _next_id[:1] + "9" * (len(_next_id) - 2) + "8"
-
- _curr_id = _next_id
-
-
-if __name__ == '__main__':
- _main()
diff --git a/src/pycalver/parse.py b/src/pycalver/parse.py
index 402ad39..aceb631 100644
--- a/src/pycalver/parse.py
+++ b/src/pycalver/parse.py
@@ -1,7 +1,7 @@
# This file is part of the pycalver project
-# https://gitlab.com/mbarkhau/pycalver
+# https://github.com/mbarkhau/pycalver
#
-# Copyright (c) 2019 Manuel Barkhau (mbarkhau@gmail.com) - MIT License
+# Copyright (c) 2018-2020 Manuel Barkhau (mbarkhau@gmail.com) - MIT License
# SPDX-License-Identifier: MIT
"""Parse PyCalVer strings from files."""
@@ -22,6 +22,8 @@ class PatternMatch(typ.NamedTuple):
PatternMatches = typ.Iterable[PatternMatch]
+RegexpPatterns = typ.List[typ.Pattern[str]]
+
def _iter_for_pattern(lines: typ.List[str], pattern: str) -> PatternMatches:
# The pattern is escaped, so that everything besides the format
diff --git a/src/pycalver/patterns.py b/src/pycalver/patterns.py
index b9b097f..23af859 100644
--- a/src/pycalver/patterns.py
+++ b/src/pycalver/patterns.py
@@ -1,7 +1,7 @@
# This file is part of the pycalver project
-# https://gitlab.com/mbarkhau/pycalver
+# https://github.com/mbarkhau/pycalver
#
-# Copyright (c) 2019 Manuel Barkhau (mbarkhau@gmail.com) - MIT License
+# Copyright (c) 2018-2020 Manuel Barkhau (mbarkhau@gmail.com) - MIT License
# SPDX-License-Identifier: MIT
"""Compose Regular Expressions from Patterns.
diff --git a/src/pycalver/rewrite.py b/src/pycalver/rewrite.py
index 2a566c3..c06fa7d 100644
--- a/src/pycalver/rewrite.py
+++ b/src/pycalver/rewrite.py
@@ -1,7 +1,7 @@
# This file is part of the pycalver project
-# https://gitlab.com/mbarkhau/pycalver
+# https://github.com/mbarkhau/pycalver
#
-# Copyright (c) 2019 Manuel Barkhau (mbarkhau@gmail.com) - MIT License
+# Copyright (c) 2018-2020 Manuel Barkhau (mbarkhau@gmail.com) - MIT License
# SPDX-License-Identifier: MIT
"""Rewrite files, updating occurences of version strings."""
@@ -13,8 +13,9 @@ import logging
import pathlib2 as pl
-from . import parse
-from . import config
+from pycalver import parse
+from pycalver import config
+
from . import version
from . import patterns
@@ -53,6 +54,28 @@ class NoPatternMatch(Exception):
"""
+class RewrittenFileData(typ.NamedTuple):
+ """Container for line-wise content of rewritten files."""
+
+ path : str
+ line_sep : str
+ old_lines: typ.List[str]
+ new_lines: typ.List[str]
+
+
+def iter_file_paths(
+ file_patterns: config.PatternsByGlob,
+) -> typ.Iterable[typ.Tuple[pl.Path, config.Patterns]]:
+ for globstr, pattern_strs in file_patterns.items():
+ file_paths = glob.glob(globstr)
+ if not any(file_paths):
+ errmsg = f"No files found for path/glob '{globstr}'"
+ raise IOError(errmsg)
+ for file_path_str in file_paths:
+ file_path = pl.Path(file_path_str)
+ yield (file_path, pattern_strs)
+
+
def rewrite_lines(
pattern_strs: typ.List[str], new_vinfo: version.VersionInfo, old_lines: typ.List[str]
) -> typ.List[str]:
@@ -88,15 +111,6 @@ def rewrite_lines(
return new_lines
-class RewrittenFileData(typ.NamedTuple):
- """Container for line-wise content of rewritten files."""
-
- path : str
- line_sep : str
- old_lines: typ.List[str]
- new_lines: typ.List[str]
-
-
def rfd_from_content(
pattern_strs: typ.List[str], new_vinfo: version.VersionInfo, content: str
) -> RewrittenFileData:
@@ -122,19 +136,6 @@ def rfd_from_content(
return RewrittenFileData("", line_sep, old_lines, new_lines)
-def _iter_file_paths(
- file_patterns: config.PatternsByGlob,
-) -> typ.Iterable[typ.Tuple[pl.Path, config.Patterns]]:
- for globstr, pattern_strs in file_patterns.items():
- file_paths = glob.glob(globstr)
- if not any(file_paths):
- errmsg = f"No files found for path/glob '{globstr}'"
- raise IOError(errmsg)
- for file_path_str in file_paths:
- file_path = pl.Path(file_path_str)
- yield (file_path, pattern_strs)
-
-
def iter_rewritten(
file_patterns: config.PatternsByGlob, new_vinfo: version.VersionInfo
) -> typ.Iterable[RewrittenFileData]:
@@ -144,23 +145,23 @@ def iter_rewritten(
>>> new_vinfo = version.parse_version_info("v201809.0123")
>>> rewritten_datas = iter_rewritten(file_patterns, new_vinfo)
>>> rfd = list(rewritten_datas)[0]
- >>> assert rfd.new_lines == [
+ >>> expected = [
... '# This file is part of the pycalver project',
- ... '# https://gitlab.com/mbarkhau/pycalver',
+ ... '# https://github.com/mbarkhau/pycalver',
... '#',
- ... '# Copyright (c) 2019 Manuel Barkhau (mbarkhau@gmail.com) - MIT License',
+ ... '# Copyright (c) 2018-2020 Manuel Barkhau (mbarkhau@gmail.com) - MIT License',
... '# SPDX-License-Identifier: MIT',
... '"""PyCalVer: CalVer for Python Packages."""',
... '',
... '__version__ = "v201809.0123"',
... '',
... ]
- >>>
+ >>> assert rfd.new_lines[:len(expected)] == expected
'''
fobj: typ.IO[str]
- for file_path, pattern_strs in _iter_file_paths(file_patterns):
+ for file_path, pattern_strs in iter_file_paths(file_patterns):
with file_path.open(mode="rt", encoding="utf-8") as fobj:
content = fobj.read()
@@ -204,7 +205,7 @@ def diff(new_vinfo: version.VersionInfo, file_patterns: config.PatternsByGlob) -
full_diff = ""
fobj: typ.IO[str]
- for file_path, pattern_strs in sorted(_iter_file_paths(file_patterns)):
+ for file_path, pattern_strs in sorted(iter_file_paths(file_patterns)):
with file_path.open(mode="rt", encoding="utf-8") as fobj:
content = fobj.read()
diff --git a/src/pycalver/vcs.py b/src/pycalver/vcs.py
index 3430ba2..3ca6102 100644
--- a/src/pycalver/vcs.py
+++ b/src/pycalver/vcs.py
@@ -1,7 +1,7 @@
# This file is part of the pycalver project
-# https://gitlab.com/mbarkhau/pycalver
+# https://github.com/mbarkhau/pycalver
#
-# Copyright (c) 2019 Manuel Barkhau (mbarkhau@gmail.com) - MIT License
+# Copyright (c) 2018-2020 Manuel Barkhau (mbarkhau@gmail.com) - MIT License
# SPDX-License-Identifier: MIT
#
# pycalver/vcs.py (this file) is based on code from the
@@ -15,11 +15,14 @@ mercurial, then the git terms are used. For example "fetch"
"""
import os
+import sys
import typing as typ
import logging
import tempfile
import subprocess as sp
+from pycalver import config
+
logger = logging.getLogger("pycalver.vcs")
@@ -179,3 +182,57 @@ def get_vcs_api() -> VCSAPI:
return vcs_api
raise OSError("No such directory .git/ or .hg/ ")
+
+
+# cli helper methods
+
+
+def assert_not_dirty(vcs_api: VCSAPI, filepaths: typ.Set[str], allow_dirty: bool) -> None:
+ dirty_files = vcs_api.status(required_files=filepaths)
+
+ if dirty_files:
+ logger.warning(f"{vcs_api.name} working directory is not clean. Uncomitted file(s):")
+ for dirty_file in dirty_files:
+ logger.warning(" " + dirty_file)
+
+ if not allow_dirty and dirty_files:
+ sys.exit(1)
+
+ dirty_pattern_files = set(dirty_files) & filepaths
+ if dirty_pattern_files:
+ logger.error("Not commiting when pattern files are dirty:")
+ for dirty_file in dirty_pattern_files:
+ logger.warning(" " + dirty_file)
+ sys.exit(1)
+
+
+def commit(
+ cfg : config.Config,
+ vcs_api : VCSAPI,
+ filepaths : typ.Set[str],
+ new_version : str,
+ commit_message: str,
+) -> None:
+ for filepath in filepaths:
+ vcs_api.add(filepath)
+
+ vcs_api.commit(commit_message)
+
+ if cfg.commit and cfg.tag:
+ vcs_api.tag(new_version)
+
+ if cfg.commit and cfg.tag and cfg.push:
+ vcs_api.push(new_version)
+
+
+def get_tags(fetch: bool) -> typ.List[str]:
+ try:
+ vcs_api = get_vcs_api()
+ logger.debug(f"vcs found: {vcs_api.name}")
+ if fetch:
+ logger.info("fetching tags from remote (to turn off use: -n / --no-fetch)")
+ vcs_api.fetch()
+ return vcs_api.ls_tags()
+ except OSError:
+ logger.debug("No vcs found")
+ return []
diff --git a/src/pycalver/version.py b/src/pycalver/version.py
index ab2bd2c..25afe53 100644
--- a/src/pycalver/version.py
+++ b/src/pycalver/version.py
@@ -1,7 +1,7 @@
# This file is part of the pycalver project
-# https://gitlab.com/mbarkhau/pycalver
+# https://github.com/mbarkhau/pycalver
#
-# Copyright (c) 2019 Manuel Barkhau (mbarkhau@gmail.com) - MIT License
+# Copyright (c) 2018-2020 Manuel Barkhau (mbarkhau@gmail.com) - MIT License
# SPDX-License-Identifier: MIT
"""Functions related to version string manipulation."""
@@ -9,9 +9,9 @@ import typing as typ
import logging
import datetime as dt
+import lexid
import pkg_resources
-from . import lex_id
from . import patterns
logger = logging.getLogger("pycalver.version")
@@ -482,7 +482,7 @@ def incr(
else:
logger.warning(f"Version appears to be from the future '{old_version}'")
- cur_vinfo = cur_vinfo._replace(bid=lex_id.next_id(cur_vinfo.bid))
+ cur_vinfo = cur_vinfo._replace(bid=lexid.next_id(cur_vinfo.bid))
if major:
cur_vinfo = cur_vinfo._replace(major=cur_vinfo.major + 1, minor=0, patch=0)
diff --git a/src/pycalver2/__init__.py b/src/pycalver2/__init__.py
new file mode 100644
index 0000000..094d463
--- /dev/null
+++ b/src/pycalver2/__init__.py
@@ -0,0 +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
+"""PyCalVer: CalVer for Python Packages."""
+
+__version__ = "v202007.1036"
diff --git a/src/pycalver2/cli.py b/src/pycalver2/cli.py
new file mode 100644
index 0000000..107d4b8
--- /dev/null
+++ b/src/pycalver2/cli.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+# 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
+"""
+CLI module for PyCalVer.
+
+Provided subcommands: show, test, init, bump
+"""
+import typing as typ
+import logging
+
+import pycalver2.rewrite as v2rewrite
+import pycalver2.version as v2version
+from pycalver import config
+
+logger = logging.getLogger("pycalver2.cli")
+
+
+def update_cfg_from_vcs(cfg: config.Config, all_tags: typ.List[str]) -> config.Config:
+ version_tags = [tag for tag in all_tags if v2version.is_valid(tag, cfg.version_pattern)]
+ if not version_tags:
+ logger.debug("no vcs tags found")
+ return cfg
+
+ version_tags.sort(reverse=True)
+ logger.debug(f"found {len(version_tags)} tags: {version_tags[:2]}")
+ latest_version_tag = version_tags[0]
+ latest_version_pep440 = v2version.to_pep440(latest_version_tag)
+ if latest_version_tag <= cfg.current_version:
+ return cfg
+
+ logger.info(f"Working dir version : {cfg.current_version}")
+ logger.info(f"Latest version from VCS tag: {latest_version_tag}")
+ return cfg._replace(
+ current_version=latest_version_tag,
+ pep440_version=latest_version_pep440,
+ )
+
+
+def rewrite(
+ cfg : config.Config,
+ new_version: str,
+) -> None:
+ new_vinfo = v2version.parse_version_info(new_version, cfg.version_pattern)
+ v2rewrite.rewrite(cfg.file_patterns, new_vinfo)
+
+
+def get_diff(cfg: config.Config, new_version: str) -> str:
+ new_vinfo = v2version.parse_version_info(new_version, cfg.version_pattern)
+ return v2rewrite.diff(new_vinfo, cfg.file_patterns)
diff --git a/src/pycalver2/patterns.py b/src/pycalver2/patterns.py
new file mode 100644
index 0000000..07bae57
--- /dev/null
+++ b/src/pycalver2/patterns.py
@@ -0,0 +1,205 @@
+# 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
+"""Compose Regular Expressions from Patterns.
+
+>>> version_info = PYCALVER_RE.match("v201712.0123-alpha").groupdict()
+>>> assert version_info == {
+... "pycalver" : "v201712.0123-alpha",
+... "vYYYYMM" : "v201712",
+... "year" : "2017",
+... "month" : "12",
+... "build" : ".0123",
+... "build_no" : "0123",
+... "release" : "-alpha",
+... "release_tag" : "alpha",
+... }
+>>>
+>>> version_info = PYCALVER_RE.match("v201712.0033").groupdict()
+>>> assert version_info == {
+... "pycalver" : "v201712.0033",
+... "vYYYYMM" : "v201712",
+... "year" : "2017",
+... "month" : "12",
+... "build" : ".0033",
+... "build_no" : "0033",
+... "release" : None,
+... "release_tag": None,
+... }
+"""
+
+import re
+import typing as typ
+
+# https://regex101.com/r/fnj60p/10
+PYCALVER_PATTERN = r"""
+\b
+(?P
+ (?P
+ v # "v" version prefix
+ (?P\d{4})
+ (?P\d{2})
+ )
+ (?P
+ \. # "." build nr prefix
+ (?P\d{4,})
+ )
+ (?P
+ \- # "-" release prefix
+ (?Palpha|beta|dev|rc|post)
+ )?
+)(?:\s|$)
+"""
+
+PYCALVER_RE: typ.Pattern[str] = re.compile(PYCALVER_PATTERN, flags=re.VERBOSE)
+
+
+PATTERN_ESCAPES = [
+ ("\u005c", "\u005c\u005c"),
+ ("-" , "\u005c-"),
+ ("." , "\u005c."),
+ ("+" , "\u005c+"),
+ ("*" , "\u005c*"),
+ ("?" , "\u005c?"),
+ ("{" , "\u005c{"),
+ ("}" , "\u005c}"),
+ ("[" , "\u005c["),
+ ("]" , "\u005c]"),
+ ("(" , "\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 = {
+ # recommended (based on calver.org)
+ 'YYYY': r"[1-9]\d{3}",
+ 'YY' : r"\d{1,2}",
+ '0Y' : r"\d{2}",
+ 'Q' : r"[1-4]",
+ 'MM' : r"(?:[1-9]|1[0-2])",
+ '0M' : r"(?:0[1-9]|1[0-2])",
+ 'DD' : r"([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])",
+ '00J' : r"(?:[0-2]\d\d|3[0-5][0-9]|36[0-6])",
+ 'WW' : r"(?:[1-9]|[1-4]\d|5[0-2])",
+ '0W' : r"(?:[0-4]\d|5[0-2])",
+ 'UU' : r"(?:[1-9]|[0-4]\d|5[0-2])",
+ '0U' : r"(?:[0-4]\d|5[0-2])",
+ 'VV' : r"(?:[1-9]|[1-4]\d|5[0-3])",
+ '0V' : r"(?:[0-4]\d|5[0-3])",
+ 'GGGG': r"[1-9]\d{3}",
+ 'GG' : r"\d{1,2}",
+ '0G' : r"\d{2}",
+ # non calver parts
+ 'MAJOR': r"\d+",
+ 'MINOR': r"\d+",
+ 'PATCH': r"\d+",
+ 'MICRO': r"\d+",
+ 'BUILD': r"\d+",
+ 'TAG' : r"(?:alpha|beta|dev|rc|post|final)",
+ 'PYTAG': r"(?:a|b|dev|rc|post)?\d*",
+ # 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}",
+}
+
+
+def _replace_pattern_parts(pattern: str) -> str:
+ for part_name, part_pattern in PART_PATTERNS.items():
+ named_part_pattern = f"(?P<{part_name}>{part_pattern})"
+ placeholder = "\u005c{" + part_name + "\u005c}"
+ pattern = pattern.replace(placeholder, named_part_pattern)
+ return pattern
+
+
+def compile_pattern_str(pattern: str) -> str:
+ for char, escaped in PATTERN_ESCAPES:
+ pattern = pattern.replace(char, escaped)
+
+ return _replace_pattern_parts(pattern)
+
+
+def compile_pattern(pattern: str) -> typ.Pattern[str]:
+ pattern_str = compile_pattern_str(pattern)
+ return re.compile(pattern_str)
+
+
+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()
diff --git a/src/pycalver2/rewrite.py b/src/pycalver2/rewrite.py
new file mode 100644
index 0000000..7595099
--- /dev/null
+++ b/src/pycalver2/rewrite.py
@@ -0,0 +1,175 @@
+# 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
+"""Rewrite files, updating occurences of version strings."""
+
+import io
+import typing as typ
+import logging
+
+from pycalver import parse
+from pycalver import config
+from pycalver import rewrite as v1rewrite
+from pycalver2 import version
+from pycalver2 import patterns
+
+logger = logging.getLogger("pycalver2.rewrite")
+
+
+def rewrite_lines(
+ pattern_strs: typ.List[str], new_vinfo: version.VersionInfo, old_lines: typ.List[str]
+) -> typ.List[str]:
+ """TODO reenable doctest"""
+ pass
+
+ """Replace occurances of pattern_strs in old_lines with new_vinfo.
+
+ >>> new_vinfo = version.parse_version_info("v201811.0123-beta")
+ >>> pattern_strs = ['__version__ = "{pycalver}"']
+ >>> rewrite_lines(pattern_strs, new_vinfo, ['__version__ = "v201809.0002-beta"'])
+ ['__version__ = "v201811.0123-beta"']
+
+ >>> pattern_strs = ['__version__ = "{pep440_version}"']
+ >>> rewrite_lines(pattern_strs, new_vinfo, ['__version__ = "201809.2b0"'])
+ ['__version__ = "201811.123b0"']
+ """
+ new_lines = old_lines[:]
+ found_patterns = set()
+
+ re_patterns = [patterns.compile_pattern(p) for p in pattern_strs]
+ for match in parse.iter_matches(old_lines, re_patterns):
+ found_patterns.add(match.pattern)
+ replacement = version.format_version(new_vinfo, match.pattern)
+ span_l, span_r = match.span
+ new_line = match.line[:span_l] + replacement + match.line[span_r:]
+ new_lines[match.lineno] = new_line
+
+ non_matched_patterns = set(pattern_strs) - found_patterns
+ if non_matched_patterns:
+ for non_matched_pattern in non_matched_patterns:
+ logger.error(f"No match for pattern '{non_matched_pattern}'")
+ compiled_pattern_str = patterns.compile_pattern_str(non_matched_pattern)
+ logger.error(f"Pattern compiles to regex '{compiled_pattern_str}'")
+ raise v1rewrite.NoPatternMatch("Invalid pattern(s)")
+ else:
+ return new_lines
+
+
+def rfd_from_content(
+ pattern_strs: typ.List[str], new_vinfo: version.VersionInfo, content: str
+) -> v1rewrite.RewrittenFileData:
+ """TODO reenable doctest"""
+ pass
+
+ r"""Rewrite pattern occurrences with version string.
+
+ >>> new_vinfo = version.parse_version_info("v201809.0123")
+ >>> pattern_strs = ['__version__ = "{pycalver}"']
+ >>> content = '__version__ = "v201809.0001-alpha"'
+ >>> rfd = rfd_from_content(pattern_strs, new_vinfo, content)
+ >>> rfd.new_lines
+ ['__version__ = "v201809.0123"']
+ >>>
+ >>> new_vinfo = version.parse_version_info("v1.2.3", "v{semver}")
+ >>> pattern_strs = ['__version__ = "v{semver}"']
+ >>> content = '__version__ = "v1.2.2"'
+ >>> rfd = rfd_from_content(pattern_strs, new_vinfo, content)
+ >>> rfd.new_lines
+ ['__version__ = "v1.2.3"']
+ """
+ line_sep = v1rewrite.detect_line_sep(content)
+ old_lines = content.split(line_sep)
+ new_lines = rewrite_lines(pattern_strs, new_vinfo, old_lines)
+ return v1rewrite.RewrittenFileData("", line_sep, old_lines, new_lines)
+
+
+def iter_rewritten(
+ file_patterns: config.PatternsByGlob, new_vinfo: version.VersionInfo
+) -> typ.Iterable[v1rewrite.RewrittenFileData]:
+ """TODO reenable doctest"""
+ pass
+
+ r'''Iterate over files with version string replaced.
+
+ >>> file_patterns = {"src/pycalver/__init__.py": ['__version__ = "{pycalver}"']}
+ >>> new_vinfo = version.parse_version_info("v201809.0123")
+ >>> rewritten_datas = iter_rewritten(file_patterns, new_vinfo)
+ >>> rfd = list(rewritten_datas)[0]
+ >>> assert rfd.new_lines == [
+ ... '# This file is part of the pycalver project',
+ ... '# https://gitlab.com/mbarkhau/pycalver',
+ ... '#',
+ ... '# Copyright (c) 2019 Manuel Barkhau (mbarkhau@gmail.com) - MIT License',
+ ... '# SPDX-License-Identifier: MIT',
+ ... '"""PyCalVer: CalVer for Python Packages."""',
+ ... '',
+ ... '__version__ = "v201809.0123"',
+ ... '',
+ ... ]
+ >>>
+ '''
+
+ fobj: typ.IO[str]
+
+ for file_path, pattern_strs in v1rewrite.iter_file_paths(file_patterns):
+ with file_path.open(mode="rt", encoding="utf-8") as fobj:
+ content = fobj.read()
+
+ rfd = rfd_from_content(pattern_strs, new_vinfo, content)
+ yield rfd._replace(path=str(file_path))
+
+
+def diff(new_vinfo: version.VersionInfo, file_patterns: config.PatternsByGlob) -> str:
+ """TODO reenable doctest"""
+ pass
+
+ r"""Generate diffs of rewritten files.
+
+ >>> new_vinfo = version.parse_version_info("v201809.0123")
+ >>> file_patterns = {"src/pycalver/__init__.py": ['__version__ = "{pycalver}"']}
+ >>> diff_str = diff(new_vinfo, file_patterns)
+ >>> lines = diff_str.split("\n")
+ >>> lines[:2]
+ ['--- src/pycalver/__init__.py', '+++ src/pycalver/__init__.py']
+ >>> assert lines[6].startswith('-__version__ = "v2')
+ >>> assert not lines[6].startswith('-__version__ = "v201809.0123"')
+ >>> lines[7]
+ '+__version__ = "v201809.0123"'
+ """
+
+ full_diff = ""
+ fobj: typ.IO[str]
+
+ for file_path, pattern_strs in sorted(v1rewrite.iter_file_paths(file_patterns)):
+ with file_path.open(mode="rt", encoding="utf-8") as fobj:
+ content = fobj.read()
+
+ try:
+ rfd = rfd_from_content(pattern_strs, new_vinfo, content)
+ except v1rewrite.NoPatternMatch:
+ # pylint:disable=raise-missing-from ; we support py2, so not an option
+ errmsg = f"No patterns matched for '{file_path}'"
+ raise v1rewrite.NoPatternMatch(errmsg)
+
+ rfd = rfd._replace(path=str(file_path))
+ lines = v1rewrite.diff_lines(rfd)
+ if len(lines) == 0:
+ errmsg = f"No patterns matched for '{file_path}'"
+ raise v1rewrite.NoPatternMatch(errmsg)
+
+ full_diff += "\n".join(lines) + "\n"
+
+ full_diff = full_diff.rstrip("\n")
+ return full_diff
+
+
+def rewrite(file_patterns: config.PatternsByGlob, new_vinfo: version.VersionInfo) -> None:
+ """Rewrite project files, updating each with the new version."""
+ fobj: typ.IO[str]
+
+ for file_data in iter_rewritten(file_patterns, new_vinfo):
+ new_content = file_data.line_sep.join(file_data.new_lines)
+ with io.open(file_data.path, mode="wt", encoding="utf-8") as fobj:
+ fobj.write(new_content)
diff --git a/src/pycalver2/version.py b/src/pycalver2/version.py
new file mode 100644
index 0000000..4abaacf
--- /dev/null
+++ b/src/pycalver2/version.py
@@ -0,0 +1,590 @@
+# 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
+"""Functions related to version string manipulation."""
+
+import typing as typ
+import logging
+import datetime as dt
+
+import lexid
+import pkg_resources
+
+from . import patterns
+
+logger = logging.getLogger("pycalver.version")
+
+
+# The test suite may replace this.
+TODAY = dt.datetime.utcnow().date()
+
+
+PATTERN_PART_FIELDS = {
+ 'YYYY' : 'year_y',
+ 'YY' : 'year_y',
+ '0Y' : 'year_y',
+ 'Q' : 'quarter',
+ 'MM' : 'month',
+ '0M' : 'month',
+ 'DD' : 'dom',
+ '0D' : 'dom',
+ 'JJJ' : 'doy',
+ '00J' : 'doy',
+ 'MAJOR': 'major',
+ 'MINOR': 'minor',
+ 'PATCH': 'patch',
+ 'MICRO': 'patch',
+ 'BUILD': 'bid',
+ 'TAG' : 'tag',
+ 'PYTAG': 'pytag',
+ 'WW' : 'week_w',
+ '0W' : 'week_w',
+ 'UU' : 'week_u',
+ '0U' : 'week_u',
+ 'VV' : 'week_v',
+ '0V' : 'week_v',
+ 'GGGG' : 'year_g',
+ 'GG' : 'year_g',
+ '0G' : 'year_g',
+}
+
+ID_FIELDS_BY_PART = {
+ 'MAJOR': 'major',
+ 'MINOR': 'minor',
+ 'PATCH': 'patch',
+ 'MICRO': 'patch',
+}
+
+
+ZERO_VALUES = {
+ 'major': "0",
+ 'minor': "0",
+ 'patch': "0",
+ 'TAG' : "final",
+ 'PYTAG': "",
+}
+
+
+class CalendarInfo(typ.NamedTuple):
+ """Container for calendar components of version strings."""
+
+ year_y : int
+ year_g : int
+ quarter: int
+ month : int
+ dom : int
+ doy : int
+ week_w : int
+ week_u : int
+ week_v : int
+
+
+def _date_from_doy(year: int, doy: int) -> dt.date:
+ """Parse date from year and day of year (1 indexed).
+
+ >>> cases = [
+ ... (2016, 1), (2016, 31), (2016, 31 + 1), (2016, 31 + 29), (2016, 31 + 30),
+ ... (2017, 1), (2017, 31), (2017, 31 + 1), (2017, 31 + 28), (2017, 31 + 29),
+ ... ]
+ >>> dates = [_date_from_doy(year, month) for year, month in cases]
+ >>> assert [(d.month, d.day) for d in dates] == [
+ ... (1, 1), (1, 31), (2, 1), (2, 29), (3, 1),
+ ... (1, 1), (1, 31), (2, 1), (2, 28), (3, 1),
+ ... ]
+ """
+ return dt.date(year, 1, 1) + dt.timedelta(days=doy - 1)
+
+
+def _quarter_from_month(month: int) -> int:
+ """Calculate quarter (1 indexed) from month (1 indexed).
+
+ >>> [_quarter_from_month(month) for month in range(1, 13)]
+ [1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4]
+ """
+ return ((month - 1) // 3) + 1
+
+
+def cal_info(date: dt.date = None) -> CalendarInfo:
+ """TODO reenable doctest"""
+ pass
+
+ """Generate calendar components for current date.
+
+ >>> from datetime import date
+
+ >>> c = cal_info(date(2019, 1, 5))
+ >>> (c.year_y, c.quarter, c.month, c.dom, c.doy, c.iso_week, c.us_week)
+ (2019, 1, 1, 5, 5, 0, 0)
+
+ >>> c = cal_info(date(2019, 1, 6))
+ >>> (c.year_y, c.quarter, c.month, c.dom, c.doy, c.iso_week, c.us_week)
+ (2019, 1, 1, 6, 6, 0, 1)
+
+ >>> c = cal_info(date(2019, 1, 7))
+ >>> (c.year_y, c.quarter, c.month, c.dom, c.doy, c.iso_week, c.us_week)
+ (2019, 1, 1, 7, 7, 1, 1)
+
+ >>> c = cal_info(date(2019, 4, 7))
+ >>> (c.year_y, c.quarter, c.month, c.dom, c.doy, c.iso_week, c.us_week)
+ (2019, 2, 4, 7, 97, 13, 14)
+ """
+ if date is None:
+ date = TODAY
+
+ kwargs = {
+ 'year_y' : date.year,
+ 'year_g' : int(date.strftime("%G"), base=10),
+ 'quarter': _quarter_from_month(date.month),
+ 'month' : date.month,
+ 'dom' : date.day,
+ 'doy' : int(date.strftime("%j"), base=10),
+ 'week_w' : int(date.strftime("%W"), base=10),
+ 'week_u' : int(date.strftime("%U"), base=10),
+ 'week_v' : int(date.strftime("%V"), base=10),
+ }
+
+ return CalendarInfo(**kwargs)
+
+
+class VersionInfo(typ.NamedTuple):
+ """Container for parsed version string."""
+
+ year_y : typ.Optional[int]
+ year_g : typ.Optional[int]
+ quarter: typ.Optional[int]
+ month : typ.Optional[int]
+ dom : typ.Optional[int]
+ doy : typ.Optional[int]
+ week_w : typ.Optional[int]
+ week_u : typ.Optional[int]
+ week_v : typ.Optional[int]
+ major : int
+ minor : int
+ patch : int
+ bid : str
+ tag : str
+ pytag : 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:
+ fvals = field_values
+ tag = fvals.get('tag')
+ if tag is None:
+ tag = "final"
+
+ tag = TAG_ALIASES.get(tag, tag)
+ assert tag is not None
+ # TODO (mb 2020-09-06): parts of course
+ pytag = "TODO"
+
+ bid = fvals['bid'] if 'bid' in fvals else "1001"
+
+ year_y = int(fvals['year_y']) if 'year_y' in fvals else None
+ year_g = int(fvals['year_g']) if 'year_g' in fvals else None
+ doy = int(fvals['doy' ]) if 'doy' in fvals else None
+
+ date: typ.Optional[dt.date] = None
+
+ month: typ.Optional[int] = None
+ dom : typ.Optional[int] = None
+
+ week_w: typ.Optional[int] = None
+ week_u: typ.Optional[int] = None
+ week_v: typ.Optional[int] = None
+
+ if year_y and doy:
+ date = _date_from_doy(year_y, doy)
+ month = date.month
+ dom = date.day
+ else:
+ month = int(fvals['month']) if 'month' in fvals else None
+ dom = int(fvals['dom' ]) if 'dom' in fvals else None
+
+ quarter = int(fvals['quarter']) if 'quarter' in fvals else None
+ if quarter is None and month:
+ quarter = _quarter_from_month(month)
+
+ if year_y and month and dom:
+ date = dt.date(year_y, month, dom)
+
+ if date:
+ # derive all fields from other previous values
+ doy = int(date.strftime("%j"), base=10)
+ week_w = int(date.strftime("%W"), base=10)
+ week_u = int(date.strftime("%U"), base=10)
+ week_v = int(date.strftime("%V"), base=10)
+ year_g = int(date.strftime("%G"), base=10)
+
+ major = int(fvals['major']) if 'major' in fvals else 0
+ minor = int(fvals['minor']) if 'minor' in fvals else 0
+ patch = int(fvals['patch']) if 'patch' in fvals else 0
+
+ return VersionInfo(
+ year_y=year_y,
+ year_g=year_g,
+ quarter=quarter,
+ month=month,
+ dom=dom,
+ doy=doy,
+ week_w=week_w,
+ week_u=week_u,
+ week_v=week_v,
+ major=major,
+ minor=minor,
+ patch=patch,
+ bid=bid,
+ tag=tag,
+ pytag=pytag,
+ )
+
+
+def _is_calver(nfo: typ.Union[CalendarInfo, VersionInfo]) -> bool:
+ """TODO reenable doctest"""
+ pass
+
+ """Check pattern for any calendar based parts.
+
+ >>> _is_calver(cal_info())
+ True
+
+ >>> vnfo = _parse_version_info({'year': "2018", 'month': "11", 'bid': "0018"})
+ >>> _is_calver(vnfo)
+ True
+
+ >>> vnfo = _parse_version_info({'MAJOR': "1", 'MINOR': "023", 'PATCH': "45"})
+ >>> _is_calver(vnfo)
+ False
+ """
+ for field in CalendarInfo._fields:
+ maybe_val: typ.Any = getattr(nfo, field, None)
+ if isinstance(maybe_val, int):
+ return True
+
+ return False
+
+
+TAG_ALIASES: typ.Dict[str, str] = {
+ 'a' : "alpha",
+ 'b' : "beta",
+ 'pre': "rc",
+}
+
+
+PEP440_TAGS: typ.Dict[str, str] = {
+ 'alpha': "a",
+ 'beta' : "b",
+ 'final': "",
+ 'rc' : "rc",
+ 'dev' : "dev",
+ 'post' : "post",
+}
+
+
+VersionInfoKW = typ.Dict[str, typ.Union[str, int, None]]
+
+
+class PatternError(Exception):
+ pass
+
+
+def _parse_pattern_groups(pattern_groups: PatternGroups) -> FieldValues:
+ for part_name in pattern_groups.keys():
+ is_valid_part_name = (
+ part_name in patterns.COMPOSITE_PART_PATTERNS or part_name in PATTERN_PART_FIELDS
+ )
+ if not is_valid_part_name:
+ err_msg = f"Invalid part '{part_name}'"
+ raise PatternError(err_msg)
+
+ field_value_items = [
+ (field_name, pattern_groups[part_name])
+ for part_name, field_name in PATTERN_PART_FIELDS.items()
+ if part_name in pattern_groups.keys()
+ ]
+
+ all_fields = [field_name for field_name, _ in field_value_items]
+ unique_fields = set(all_fields)
+ duplicate_fields = [f for f in unique_fields if all_fields.count(f) > 1]
+
+ if any(duplicate_fields):
+ err_msg = f"Multiple parts for same field {duplicate_fields}."
+ raise PatternError(err_msg)
+
+ return dict(field_value_items)
+
+
+def _parse_version_info(pattern_groups: PatternGroups) -> VersionInfo:
+ """TODO reenable doctest"""
+ pass
+
+ """Parse normalized VersionInfo from groups of a matched pattern.
+
+ >>> vnfo = _parse_version_info({'year': "2018", 'month': "11", 'bid': "0099"})
+ >>> (vnfo.year_y, vnfo.month, vnfo.quarter, vnfo.bid, vnfo.tag)
+ (2018, 11, 4, '0099', 'final')
+
+ >>> vnfo = _parse_version_info({'year': "2018", 'doy': "11", 'bid': "099", 'tag': "b"})
+ >>> (vnfo.year_y, vnfo.month, vnfo.dom, vnfo.bid, vnfo.tag)
+ (2018, 1, 11, '099', 'beta')
+
+ >>> vnfo = _parse_version_info({'MAJOR': "1", 'MINOR': "23", 'PATCH': "45"})
+ >>> (vnfo.major, vnfo.minor, vnfo.patch)
+ (1, 23, 45)
+
+ >>> vnfo = _parse_version_info({'MAJOR': "1", 'MMM': "023", 'PPPP': "0045"})
+ >>> (vnfo.major, vnfo.minor, vnfo.patch)
+ (1, 23, 45)
+ """
+ field_values = _parse_pattern_groups(pattern_groups)
+ return _parse_field_values(field_values)
+
+
+def parse_version_info(version_str: str, pattern: str = "{pycalver}") -> VersionInfo:
+ """TODO reenable doctest"""
+ pass
+
+ """Parse normalized VersionInfo.
+
+ >>> vnfo = parse_version_info("v201712.0033-beta", pattern="{pycalver}")
+ >>> assert vnfo == _parse_version_info({'year': 2017, 'month': 12, 'bid': "0033", 'tag': "beta"})
+
+ >>> vnfo = parse_version_info("1.23.456", pattern="{semver}")
+ >>> assert vnfo == _parse_version_info({'MAJOR': "1", 'MINOR': "23", 'PATCH': "456"})
+ """
+ regex = patterns.compile_pattern(pattern)
+ match = regex.match(version_str)
+ if match is None:
+ err_msg = (
+ f"Invalid version string '{version_str}' for pattern '{pattern}'/'{regex.pattern}'"
+ )
+ raise PatternError(err_msg)
+
+ return _parse_version_info(match.groupdict())
+
+
+def is_valid(version_str: str, pattern: str = "{pycalver}") -> bool:
+ """TODO reenable doctest"""
+ pass
+
+ """Check if a version matches a pattern.
+
+ >>> is_valid("v201712.0033-beta", pattern="{pycalver}")
+ True
+ >>> is_valid("v201712.0033-beta", pattern="{semver}")
+ False
+ >>> is_valid("1.2.3", pattern="{semver}")
+ True
+ >>> is_valid("v201712.0033-beta", pattern="{semver}")
+ False
+ """
+ try:
+ parse_version_info(version_str, pattern)
+ return True
+ except PatternError:
+ return False
+
+
+TemplateKwargs = typ.Dict[str, typ.Union[str, int, None]]
+
+
+def _derive_template_kwargs(vinfo: VersionInfo) -> TemplateKwargs:
+ """Generate kwargs for template from minimal VersionInfo.
+
+ The VersionInfo Tuple only has the minimal representation
+ of a parsed version, not the values suitable for formatting.
+ It may for example have month=9, but not the formatted
+ representation '09' for '0M'.
+ """
+ kwargs: TemplateKwargs = vinfo._asdict()
+
+ tag = vinfo.tag
+ kwargs['TAG'] = tag
+ if tag == 'final':
+ kwargs['PYTAG'] = ""
+ else:
+ kwargs['PYTAG'] = PEP440_TAGS[tag] + "0"
+
+ year_y = vinfo.year_y
+ if year_y:
+ kwargs['0Y' ] = str(year_y)[-2:]
+ kwargs['YY' ] = int(str(year_y)[-2:])
+ kwargs['YYYY'] = year_y
+
+ year_g = vinfo.year_g
+ if year_g:
+ kwargs['0G' ] = str(year_g)[-2:]
+ kwargs['GG' ] = int(str(year_g)[-2:])
+ kwargs['GGGG'] = year_g
+
+ kwargs['BUILD'] = int(vinfo.bid, 10)
+
+ for part_name, field in ID_FIELDS_BY_PART.items():
+ val = kwargs[field]
+ if part_name.lower() == field.lower():
+ if isinstance(val, str):
+ kwargs[part_name] = int(val, base=10)
+ else:
+ kwargs[part_name] = val
+ else:
+ assert len(set(part_name)) == 1
+ padded_len = len(part_name)
+ kwargs[part_name] = str(val).zfill(padded_len)
+
+ return kwargs
+
+
+def _compile_format_template(pattern: str, kwargs: TemplateKwargs) -> str:
+ # NOTE (mb 2020-09-04): Some parts are optional, we need the kwargs to
+ # determine if part is set to its zero value
+ format_tmpl = pattern
+ for part_name, full_part_format in patterns.FULL_PART_FORMATS.items():
+ format_tmpl = format_tmpl.replace("{" + part_name + "}", full_part_format)
+ return format_tmpl
+
+
+def format_version(vinfo: VersionInfo, pattern: str) -> str:
+ """TODO reenable doctest"""
+ pass
+
+ """Generate version string.
+
+ >>> import datetime as dt
+ >>> vinfo = parse_version_info("v201712.0033-beta", pattern="{pycalver}")
+ >>> vinfo_a = vinfo._replace(**cal_info(date=dt.date(2017, 1, 1))._asdict())
+ >>> vinfo_b = vinfo._replace(**cal_info(date=dt.date(2017, 12, 31))._asdict())
+
+ >>> format_version(vinfo_a, pattern="v{yy}.{BID}{release}")
+ 'v17.33-beta'
+ >>> format_version(vinfo_a, pattern="vYY.BUILD[-TAG]")
+ 'v17.33-beta'
+ >>> format_version(vinfo_a, pattern="YYYY0M.BUILD[PYTAG]")
+ '201701.33b0'
+
+ >>> format_version(vinfo_a, pattern="{pycalver}")
+ 'v201701.0033-beta'
+ >>> format_version(vinfo_b, pattern="{pycalver}")
+ 'v201712.0033-beta'
+
+ >>> format_version(vinfo_a, pattern="v{year}w{iso_week}.{BID}{release}")
+ 'v2017w00.33-beta'
+ >>> format_version(vinfo_a, pattern="vYYYYwWW.BUILD[-TAG]")
+ 'v2017w00.33-beta'
+ >>> format_version(vinfo_b, pattern="v{year}w{iso_week}.{BID}{release}")
+ 'v2017w52.33-beta'
+ >>> format_version(vinfo_b, pattern="vYYYYwWW.BUILD[-TAG]")
+ 'v2017w52.33-beta'
+
+ >>> format_version(vinfo_a, pattern="v{year}d{doy}.{bid}{release}")
+ 'v2017d001.0033-beta'
+ >>> format_version(vinfo_b, pattern="v{year}d{doy}.{bid}{release}")
+ 'v2017d365.0033-beta'
+ >>> format_version(vinfo_a, pattern="vYYYYdJJJ.BUILD[-TAG]")
+ 'v2017d001.0033-beta'
+ >>> format_version(vinfo_b, pattern="vYYYYdJJJ.BUILD[-TAG]")
+ 'v2017d365.0033-beta'
+
+ >>> format_version(vinfo_a, pattern="vGGGGwVV.BUILD[-TAG]")
+ 'v2016w52.0033-beta'
+
+ >>> vinfo_c = vinfo_b._replace(major=1, minor=2, patch=34, tag='final')
+
+ >>> format_version(vinfo_c, pattern="v{year}w{iso_week}.{BID}-{tag}")
+ 'v2017w52.33-final'
+ >>> format_version(vinfo_c, pattern="v{year}w{iso_week}.{BID}{release}")
+ 'v2017w52.33'
+ >>> format_version(vinfo_c, pattern="vYYYYwWW.BUILD-TAG")
+ 'v2017w52.33-final'
+ >>> format_version(vinfo_c, pattern="vYYYYwWW.BUILD[-TAG]")
+ 'v2017w52.33'
+
+ >>> format_version(vinfo_c, pattern="v{MAJOR}.{MINOR}.{PATCH}")
+ 'v1.2.34'
+ >>> format_version(vinfo_c, pattern="vMAJOR.MINOR.PATCH")
+ 'v1.2.34'
+
+ >>> vinfo_d = vinfo_b._replace(major=1, minor=0, patch=0, tag='final')
+ >>> format_version(vinfo_d, pattern="vMAJOR.MINOR.PATCH-TAG")
+ 'v1.0.0-final'
+ >>> format_version(vinfo_d, pattern="vMAJOR.MINOR.PATCH[-TAG]")
+ 'v1.0.0'
+ >>> format_version(vinfo_d, pattern="vMAJOR.MINOR[.PATCH[-TAG]]")
+ 'v1.0'
+ >>> format_version(vinfo_d, pattern="vMAJOR.MINOR[.MICRO[-TAG]]")
+ 'v1.0'
+ >>> format_version(vinfo_d, pattern="vMAJOR[.MINOR[.PATCH[-TAG]]]")
+ 'v1'
+ """
+ kwargs = _derive_template_kwargs(vinfo)
+ format_tmpl = _compile_format_template(pattern, kwargs)
+
+ return format_tmpl.format(**kwargs)
+
+
+def incr(
+ old_version: str,
+ pattern : str = "{pycalver}",
+ *,
+ release: str = None,
+ major : bool = False,
+ minor : bool = False,
+ patch : bool = False,
+) -> typ.Optional[str]:
+ """Increment version string.
+
+ 'old_version' is assumed to be a string that matches 'pattern'
+ """
+ try:
+ old_vinfo = parse_version_info(old_version, pattern)
+ except PatternError as ex:
+ logger.error(str(ex))
+ return None
+
+ cur_vinfo = old_vinfo
+
+ cur_cal_nfo = cal_info()
+
+ old_date = (old_vinfo.year_y or 0, old_vinfo.month or 0, old_vinfo.dom or 0)
+ cur_date = (cur_cal_nfo.year_y , cur_cal_nfo.month , cur_cal_nfo.dom)
+
+ if old_date <= cur_date:
+ cur_vinfo = cur_vinfo._replace(**cur_cal_nfo._asdict())
+ else:
+ logger.warning(f"Version appears to be from the future '{old_version}'")
+
+ cur_vinfo = cur_vinfo._replace(bid=lexid.incr(cur_vinfo.bid))
+
+ if major:
+ cur_vinfo = cur_vinfo._replace(major=cur_vinfo.major + 1, minor=0, patch=0)
+ if minor:
+ cur_vinfo = cur_vinfo._replace(minor=cur_vinfo.minor + 1, patch=0)
+ if patch:
+ cur_vinfo = cur_vinfo._replace(patch=cur_vinfo.patch + 1)
+
+ if release:
+ cur_vinfo = cur_vinfo._replace(tag=release)
+
+ new_version = format_version(cur_vinfo, pattern)
+ if new_version == old_version:
+ logger.error("Invalid arguments or pattern, version did not change.")
+ return None
+ else:
+ return new_version
+
+
+def to_pep440(version: str) -> str:
+ """Derive pep440 compliant version string from PyCalVer version string.
+
+ >>> to_pep440("v201811.0007-beta")
+ '201811.7b0'
+ """
+ return str(pkg_resources.parse_version(version))
diff --git a/test/fixtures/project_d/pyproject.toml b/test/fixtures/project_d/pyproject.toml
new file mode 100644
index 0000000..65faaf4
--- /dev/null
+++ b/test/fixtures/project_d/pyproject.toml
@@ -0,0 +1,6 @@
+[pycalver]
+current_version = "v2017q1.54321"
+version_pattern = "vYYYYqQ.BUILD"
+commit = true
+tag = true
+push = true
diff --git a/test/test_config.py b/test/test_config.py
index 4d15e36..ab8c4ba 100644
--- a/test/test_config.py
+++ b/test/test_config.py
@@ -2,11 +2,10 @@
# pylint:disable=protected-access ; allowed for test code
import io
+from test import util
from pycalver import config
-from . import util
-
PYCALVER_TOML_FIXTURE_1 = """
[pycalver]
current_version = "v201808.0123-alpha"
diff --git a/test/test_lex_id.py b/test/test_lex_id.py
deleted file mode 100644
index 198e3ed..0000000
--- a/test/test_lex_id.py
+++ /dev/null
@@ -1,67 +0,0 @@
-# pylint:disable=protected-access ; allowed for test code
-
-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_overflow():
- try:
- prev_id = "9999"
- next_id = lex_id.next_id(prev_id)
- assert False, (prev_id, "->", next_id)
- except OverflowError:
- pass
-
-
-def test_next_id_random():
- for _ in range(1000):
- prev_id = str(random.randint(1, 100 * 1000))
- try:
- next_id = lex_id.next_id(prev_id)
- assert prev_id < next_id
- except OverflowError:
- assert len(prev_id) == prev_id.count("9")
-
-
-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_patterns.py b/test/test_patterns.py
index 541bbfe..61d8d48 100644
--- a/test/test_patterns.py
+++ b/test/test_patterns.py
@@ -2,14 +2,17 @@ import re
import pytest
-from pycalver import patterns
+import pycalver.patterns as v1patterns
+import pycalver2.patterns as v2patterns
+
+# TODO (mb 2020-09-06): test for v2patterns
def _part_re_by_name(name):
- return re.compile(patterns.PART_PATTERNS[name])
+ return re.compile(v1patterns.PART_PATTERNS[name])
-@pytest.mark.parametrize("part_name", patterns.PART_PATTERNS.keys())
+@pytest.mark.parametrize("part_name", v1patterns.PART_PATTERNS.keys())
def test_part_compilation(part_name):
assert _part_re_by_name(part_name)
@@ -64,7 +67,7 @@ PATTERN_CASES = [
@pytest.mark.parametrize("pattern_str, line, expected", PATTERN_CASES)
def test_patterns(pattern_str, line, expected):
- pattern_re = patterns.compile_pattern(pattern_str)
+ pattern_re = v1patterns.compile_pattern(pattern_str)
result = pattern_re.search(line)
if result is None:
assert expected is None, (pattern_str, line)
@@ -82,7 +85,7 @@ CLI_MAIN_FIXTURE = """
def test_pattern_escapes():
pattern = 'click.version_option(version="{pycalver}")'
- pattern_re = patterns.compile_pattern(pattern)
+ pattern_re = v1patterns.compile_pattern(pattern)
match = pattern_re.search(CLI_MAIN_FIXTURE)
expected = 'click.version_option(version="v201812.0123-beta")'
assert match.group(0) == expected
@@ -95,7 +98,7 @@ package_metadata = {"name": "mypackage", "version": "v201812.0123-beta"}
def test_curly_escapes():
pattern = 'package_metadata = {"name": "mypackage", "version": "{pycalver}"}'
- pattern_re = patterns.compile_pattern(pattern)
+ pattern_re = v1patterns.compile_pattern(pattern)
match = pattern_re.search(CURLY_BRACE_FIXTURE)
expected = 'package_metadata = {"name": "mypackage", "version": "v201812.0123-beta"}'
assert match.group(0) == expected
diff --git a/test/test_rewrite.py b/test/test_rewrite.py
index 872fd71..1eb0dd7 100644
--- a/test/test_rewrite.py
+++ b/test/test_rewrite.py
@@ -1,12 +1,13 @@
# pylint:disable=protected-access ; allowed for test code
import copy
+from test import util
from pycalver import config
-from pycalver import rewrite
-from pycalver import version
-
-from . import util
+from pycalver import rewrite as v1rewrite
+from pycalver import version as v1version
+from pycalver2 import rewrite as v2rewrite
+from pycalver2 import version as v2version
REWRITE_FIXTURE = """
# SPDX-License-Identifier: MIT
@@ -17,8 +18,8 @@ __version__ = "v201809.0002-beta"
def test_rewrite_lines():
old_lines = REWRITE_FIXTURE.splitlines()
patterns = ['__version__ = "{pycalver}"']
- new_vinfo = version.parse_version_info("v201911.0003")
- new_lines = rewrite.rewrite_lines(patterns, new_vinfo, old_lines)
+ new_vinfo = v1version.parse_version_info("v201911.0003")
+ new_lines = v1rewrite.rewrite_lines(patterns, new_vinfo, old_lines)
assert len(new_lines) == len(old_lines)
assert "v201911.0003" not in "\n".join(old_lines)
@@ -31,8 +32,8 @@ def test_rewrite_final():
old_lines = REWRITE_FIXTURE.splitlines()
patterns = ['__version__ = "v{year}{month}.{build_no}-{release_tag}"']
- new_vinfo = version.parse_version_info("v201911.0003")
- new_lines = rewrite.rewrite_lines(patterns, new_vinfo, old_lines)
+ new_vinfo = v1version.parse_version_info("v201911.0003")
+ new_lines = v1rewrite.rewrite_lines(patterns, new_vinfo, old_lines)
assert len(new_lines) == len(old_lines)
assert "v201911.0003" not in "\n".join(old_lines)
@@ -46,9 +47,8 @@ def test_iter_file_paths():
cfg = config.parse(ctx)
assert cfg
- file_paths = {
- str(file_path) for file_path, patterns in rewrite._iter_file_paths(cfg.file_patterns)
- }
+ _paths_and_patterns = v1rewrite.iter_file_paths(cfg.file_patterns)
+ file_paths = {str(file_path) for file_path, patterns in _paths_and_patterns}
assert file_paths == {"pycalver.toml", "README.md"}
@@ -59,9 +59,8 @@ def test_iter_file_globs():
cfg = config.parse(ctx)
assert cfg
- file_paths = {
- str(file_path) for file_path, patterns in rewrite._iter_file_paths(cfg.file_patterns)
- }
+ _paths_and_patterns = v1rewrite.iter_file_paths(cfg.file_patterns)
+ file_paths = {str(file_path) for file_path, patterns in _paths_and_patterns}
assert file_paths == {
"setup.cfg",
@@ -80,7 +79,7 @@ def test_error_bad_path():
(project.dir / "setup.py").unlink()
try:
- list(rewrite._iter_file_paths(cfg.file_patterns))
+ list(v1rewrite.iter_file_paths(cfg.file_patterns))
assert False, "expected IOError"
except IOError as ex:
assert "setup.py" in str(ex)
@@ -96,10 +95,10 @@ def test_error_bad_pattern():
patterns["setup.py"] = patterns["setup.py"][0] + "invalid"
try:
- new_vinfo = version.parse_version_info("v201809.1234")
- list(rewrite.diff(new_vinfo, patterns))
- assert False, "expected rewrite.NoPatternMatch"
- except rewrite.NoPatternMatch as ex:
+ new_vinfo = v1version.parse_version_info("v201809.1234")
+ list(v1rewrite.diff(new_vinfo, patterns))
+ assert False, "expected v1rewrite.NoPatternMatch"
+ except v1rewrite.NoPatternMatch as ex:
assert "setup.py" in str(ex)
@@ -109,21 +108,21 @@ __version__ = "2018.0002-beta"
"""
-def test_optional_release():
+def test_v1_optional_release():
old_lines = OPTIONAL_RELEASE_FIXTURE.splitlines()
pattern = "{year}.{build_no}{release}"
patterns = ['__version__ = "{year}.{build_no}{release}"']
- new_vinfo = version.parse_version_info("2019.0003", pattern)
- new_lines = rewrite.rewrite_lines(patterns, new_vinfo, old_lines)
+ new_vinfo = v1version.parse_version_info("2019.0003", pattern)
+ new_lines = v1rewrite.rewrite_lines(patterns, new_vinfo, old_lines)
assert len(new_lines) == len(old_lines)
assert "2019.0003" not in "\n".join(old_lines)
new_text = "\n".join(new_lines)
assert "2019.0003" in new_text
- new_vinfo = version.parse_version_info("2019.0004-beta", pattern)
- new_lines = rewrite.rewrite_lines(patterns, new_vinfo, old_lines)
+ new_vinfo = v1version.parse_version_info("2019.0004-beta", pattern)
+ new_lines = v1rewrite.rewrite_lines(patterns, new_vinfo, old_lines)
# make sure optional release tag is added back on
assert len(new_lines) == len(old_lines)