diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 53079d3c..df88ddcc 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -19,7 +19,7 @@ Features * :gh:`187` (:pr:`188`): Added logo for python-semver organization and documentation * :gh:`191` (:pr:`194`): Created manpage for pysemver * :gh:`196` (:pr:`197`): Added distribution specific installation instructions - +* :gh:`201` (:pr:`202`): Reformatted source code with black Bug Fixes --------- diff --git a/README.rst b/README.rst index 62e6b92a..5c35b423 100644 --- a/README.rst +++ b/README.rst @@ -5,7 +5,7 @@ Quickstart A Python module for `semantic versioning`_. Simplifies comparing versions. -|build-status| |python-support| |downloads| |license| |docs| +|build-status| |python-support| |downloads| |license| |docs| |black| .. teaser-end @@ -125,3 +125,6 @@ There are other functions to discover. Read on! :target: http://python-semver.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status .. _semantic versioning: http://semver.org/ +.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/psf/black + :alt: Black Formatter diff --git a/docs/conf.py b/docs/conf.py index 107765f2..a07c94a8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -18,7 +18,8 @@ # import os import sys -sys.path.insert(0, os.path.abspath('..')) + +sys.path.insert(0, os.path.abspath("..")) from semver import __version__ # noqa: E402 @@ -32,27 +33,27 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.intersphinx', - 'sphinx.ext.napoleon', - 'sphinx.ext.extlinks', + "sphinx.ext.autodoc", + "sphinx.ext.intersphinx", + "sphinx.ext.napoleon", + "sphinx.ext.extlinks", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # -source_suffix = '.rst' +source_suffix = ".rst" # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = 'python-semver' -copyright = '2018, Kostiantyn Rybnikov and all' -author = 'Kostiantyn Rybnikov and all' +project = "python-semver" +copyright = "2018, Kostiantyn Rybnikov and all" +author = "Kostiantyn Rybnikov and all" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -73,21 +74,20 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # Markup to shorten external links # See https://www.sphinx-doc.org/en/master/usage/extensions/extlinks.html -extlinks = {'gh': ('https://github.com/python-semver/python-semver/issues/%s', - '#'), - 'pr': ('https://github.com/python-semver/python-semver/pull/%s', - 'PR #'), - } +extlinks = { + "gh": ("https://github.com/python-semver/python-semver/issues/%s", "#"), + "pr": ("https://github.com/python-semver/python-semver/pull/%s", "PR #"), +} # -- Options for HTML output ---------------------------------------------- @@ -95,7 +95,7 @@ # a list of builtin themes. # # html_theme = 'alabaster' -html_theme = 'sphinx_rtd_theme' +html_theme = "sphinx_rtd_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -106,9 +106,9 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] -html_css_files = ['css/default.css'] +html_css_files = ["css/default.css"] # Custom sidebar templates, must be a dictionary that maps document names # to template names. @@ -116,12 +116,12 @@ # This is required for the alabaster theme # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars html_sidebars = { - '**': [ - 'about.html', - 'navigation.html', - 'relations.html', # needs 'show_related': True theme option to display - 'searchbox.html', - 'donate.html', + "**": [ + "about.html", + "navigation.html", + "relations.html", # needs 'show_related': True theme option to display + "searchbox.html", + "donate.html", ] } @@ -130,7 +130,7 @@ # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. -htmlhelp_basename = 'semverdoc' +htmlhelp_basename = "semverdoc" # -- Options for LaTeX output --------------------------------------------- @@ -139,15 +139,12 @@ # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. # # 'preamble': '', - # Latex figure (float) alignment # # 'figure_align': 'htbp', @@ -157,8 +154,13 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'semver.tex', 'python-semver Documentation', - 'Kostiantyn Rybnikov and all', 'manual'), + ( + master_doc, + "semver.tex", + "python-semver Documentation", + "Kostiantyn Rybnikov and all", + "manual", + ) ] @@ -169,11 +171,13 @@ manpage_doc = "pysemver" man_pages = [ - (manpage_doc, - 'pysemver', - 'Helper script for Semantic Versioning', - ["Thomas Schraitle"], - 1) + ( + manpage_doc, + "pysemver", + "Helper script for Semantic Versioning", + ["Thomas Schraitle"], + 1, + ) ] @@ -183,7 +187,13 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'semver', 'python-semver Documentation', - author, 'semver', 'One line description of project.', - 'Miscellaneous'), + ( + master_doc, + "semver", + "python-semver Documentation", + author, + "semver", + "One line description of project.", + "Miscellaneous", + ) ] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..eca41891 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,20 @@ +[tool.black] +line-length = 88 +target-version = ['py37'] +include = '\.pyi?$' +# diff = true +exclude = ''' +( + /( + \.eggs # exclude a few common directories in the + | \.git # root of the project + | \.mypy_cache + | \.tox + | \.venv + | \.env + | _build + | build + | dist + )/ +) +''' diff --git a/semver.py b/semver.py index af7dec66..efe1102e 100644 --- a/semver.py +++ b/semver.py @@ -10,14 +10,14 @@ import sys -__version__ = '2.9.0' -__author__ = 'Kostiantyn Rybnikov' -__author_email__ = 'k-bx@k-bx.com' -__maintainer__ = 'Sebastien Celles' +__version__ = "2.9.0" +__author__ = "Kostiantyn Rybnikov" +__author_email__ = "k-bx@k-bx.com" +__maintainer__ = "Sebastien Celles" __maintainer_email__ = "s.celles@gmail.com" _REGEX = re.compile( - r""" + r""" ^ (?P0|[1-9]\d*) \. @@ -33,15 +33,18 @@ (?:\.[0-9a-zA-Z-]+)* ))? $ - """, re.VERBOSE) + """, + re.VERBOSE, +) -_LAST_NUMBER = re.compile(r'(?:[^\d]*(\d+)[^\d]*)+') +_LAST_NUMBER = re.compile(r"(?:[^\d]*(\d+)[^\d]*)+") #: Contains the implemented semver.org version of the spec SEMVER_SPEC_VERSION = "2.0.0" -if not hasattr(__builtins__, 'cmp'): +if not hasattr(__builtins__, "cmp"): + def cmp(a, b): return (a > b) - (a < b) @@ -69,26 +72,29 @@ def parse(version): """ match = _REGEX.match(version) if match is None: - raise ValueError('%s is not valid SemVer string' % version) + raise ValueError("%s is not valid SemVer string" % version) version_parts = match.groupdict() - version_parts['major'] = int(version_parts['major']) - version_parts['minor'] = int(version_parts['minor']) - version_parts['patch'] = int(version_parts['patch']) + version_parts["major"] = int(version_parts["major"]) + version_parts["minor"] = int(version_parts["minor"]) + version_parts["patch"] = int(version_parts["patch"]) return version_parts def comparator(operator): """ Wrap a VersionInfo binary op method in a type-check """ + @wraps(operator) def wrapper(self, other): comparable_types = (VersionInfo, dict, tuple) if not isinstance(other, comparable_types): - raise TypeError("other type %r must be in %r" - % (type(other), comparable_types)) + raise TypeError( + "other type %r must be in %r" % (type(other), comparable_types) + ) return operator(self, other) + return wrapper @@ -101,7 +107,8 @@ class VersionInfo(object): :param str prerelease: an optional prerelease string :param str build: an optional build string """ - __slots__ = ('_major', '_minor', '_patch', '_prerelease', '_build') + + __slots__ = ("_major", "_minor", "_patch", "_prerelease", "_build") def __init__(self, major, minor=0, patch=0, prerelease=None, build=None): self._major = int(major) @@ -156,17 +163,18 @@ def build(self, value): raise AttributeError("attribute 'build' is readonly") def _astuple(self): - return (self.major, self.minor, self.patch, - self.prerelease, self.build) + return (self.major, self.minor, self.patch, self.prerelease, self.build) def _asdict(self): - return collections.OrderedDict(( - ("major", self.major), - ("minor", self.minor), - ("patch", self.patch), - ("prerelease", self.prerelease), - ("build", self.build) - )) + return collections.OrderedDict( + ( + ("major", self.major), + ("minor", self.minor), + ("patch", self.patch), + ("prerelease", self.prerelease), + ("build", self.build), + ) + ) def __iter__(self): """Implement iter(self).""" @@ -213,7 +221,7 @@ def bump_patch(self): """ return parse_version_info(bump_patch(str(self))) - def bump_prerelease(self, token='rc'): + def bump_prerelease(self, token="rc"): """Raise the prerelease part of the version, return a new object but leave self untouched @@ -228,7 +236,7 @@ def bump_prerelease(self, token='rc'): """ return parse_version_info(bump_prerelease(str(self), token)) - def bump_build(self, token='build'): + def bump_build(self, token="build"): """Raise the build part of the version, return a new object but leave self untouched @@ -268,8 +276,7 @@ def __ge__(self, other): return _compare_by_keys(self._asdict(), _to_dict(other)) >= 0 def __repr__(self): - s = ", ".join("%s=%r" % (key, val) - for key, val in self._asdict().items()) + s = ", ".join("%s=%r" % (key, val) for key, val in self._asdict().items()) return "%s(%s)" % (type(self).__name__, s) def __str__(self): @@ -308,10 +315,10 @@ def replace(self, **parts): return VersionInfo(**version) except TypeError: unknownkeys = set(parts) - set(self._asdict()) - error = ("replace() got %d unexpected keyword " - "argument(s): %s" % (len(unknownkeys), - ", ".join(unknownkeys)) - ) + error = "replace() got %d unexpected keyword " "argument(s): %s" % ( + len(unknownkeys), + ", ".join(unknownkeys), + ) raise TypeError(error) @@ -344,18 +351,22 @@ def parse_version_info(version): """ parts = parse(version) version_info = VersionInfo( - parts['major'], parts['minor'], parts['patch'], - parts['prerelease'], parts['build']) + parts["major"], + parts["minor"], + parts["patch"], + parts["prerelease"], + parts["build"], + ) return version_info def _nat_cmp(a, b): def convert(text): - return int(text) if re.match('^[0-9]+$', text) else text + return int(text) if re.match("^[0-9]+$", text) else text def split_key(key): - return [convert(c) for c in key.split('.')] + return [convert(c) for c in key.split(".")] def cmp_prerelease_tag(a, b): if isinstance(a, int) and isinstance(b, int): @@ -367,7 +378,7 @@ def cmp_prerelease_tag(a, b): else: return cmp(a, b) - a, b = a or '', b or '' + a, b = a or "", b or "" a_parts, b_parts = split_key(a), split_key(b) for sub_a, sub_b in zip(a_parts, b_parts): cmp_result = cmp_prerelease_tag(sub_a, sub_b) @@ -378,12 +389,12 @@ def cmp_prerelease_tag(a, b): def _compare_by_keys(d1, d2): - for key in ['major', 'minor', 'patch']: + for key in ["major", "minor", "patch"]: v = cmp(d1.get(key), d2.get(key)) if v: return v - rc1, rc2 = d1.get('prerelease'), d2.get('prerelease') + rc1, rc2 = d1.get("prerelease"), d2.get("prerelease") rccmp = _nat_cmp(rc1, rc2) if not rccmp: @@ -438,24 +449,26 @@ def match(version, match_expr): False """ prefix = match_expr[:2] - if prefix in ('>=', '<=', '==', '!='): + if prefix in (">=", "<=", "==", "!="): match_version = match_expr[2:] - elif prefix and prefix[0] in ('>', '<'): + elif prefix and prefix[0] in (">", "<"): prefix = prefix[0] match_version = match_expr[1:] else: - raise ValueError("match_expr parameter should be in format , " - "where is one of " - "['<', '>', '==', '<=', '>=', '!=']. " - "You provided: %r" % match_expr) + raise ValueError( + "match_expr parameter should be in format , " + "where is one of " + "['<', '>', '==', '<=', '>=', '!=']. " + "You provided: %r" % match_expr + ) possibilities_dict = { - '>': (1,), - '<': (-1,), - '==': (0,), - '!=': (-1, 1), - '>=': (0, 1), - '<=': (-1, 0) + ">": (1,), + "<": (-1,), + "==": (0,), + "!=": (-1, 1), + ">=": (0, 1), + "<=": (-1, 0), } possibilities = possibilities_dict[prefix] @@ -533,7 +546,7 @@ def _increment_string(string): if match: next_ = str(int(match.group(1)) + 1) start, end = match.span(1) - string = string[:max(end - len(next_), start)] + next_ + string[end:] + string = string[: max(end - len(next_), start)] + next_ + string[end:] return string @@ -548,7 +561,7 @@ def bump_major(version): '4.0.0' """ verinfo = parse(version) - return format_version(verinfo['major'] + 1, 0, 0) + return format_version(verinfo["major"] + 1, 0, 0) def bump_minor(version): @@ -562,7 +575,7 @@ def bump_minor(version): '3.5.0' """ verinfo = parse(version) - return format_version(verinfo['major'], verinfo['minor'] + 1, 0) + return format_version(verinfo["major"], verinfo["minor"] + 1, 0) def bump_patch(version): @@ -576,11 +589,10 @@ def bump_patch(version): '3.4.6' """ verinfo = parse(version) - return format_version(verinfo['major'], verinfo['minor'], - verinfo['patch'] + 1) + return format_version(verinfo["major"], verinfo["minor"], verinfo["patch"] + 1) -def bump_prerelease(version, token='rc'): +def bump_prerelease(version, token="rc"): """Raise the prerelease part of the version :param version: version string @@ -592,14 +604,15 @@ def bump_prerelease(version, token='rc'): '3.4.5-dev.1' """ verinfo = parse(version) - verinfo['prerelease'] = _increment_string( - verinfo['prerelease'] or (token or 'rc') + '.0' + verinfo["prerelease"] = _increment_string( + verinfo["prerelease"] or (token or "rc") + ".0" + ) + return format_version( + verinfo["major"], verinfo["minor"], verinfo["patch"], verinfo["prerelease"] ) - return format_version(verinfo['major'], verinfo['minor'], verinfo['patch'], - verinfo['prerelease']) -def bump_build(version, token='build'): +def bump_build(version, token="build"): """Raise the build part of the version :param version: version string @@ -611,11 +624,14 @@ def bump_build(version, token='build'): '3.4.5-rc.1+build.10' """ verinfo = parse(version) - verinfo['build'] = _increment_string( - verinfo['build'] or (token or 'build') + '.0' + verinfo["build"] = _increment_string(verinfo["build"] or (token or "build") + ".0") + return format_version( + verinfo["major"], + verinfo["minor"], + verinfo["patch"], + verinfo["prerelease"], + verinfo["build"], ) - return format_version(verinfo['major'], verinfo['minor'], verinfo['patch'], - verinfo['prerelease'], verinfo['build']) def finalize_version(version): @@ -629,7 +645,7 @@ def finalize_version(version): '1.2.3' """ verinfo = parse(version) - return format_version(verinfo['major'], verinfo['minor'], verinfo['patch']) + return format_version(verinfo["major"], verinfo["minor"], verinfo["patch"]) def createparser(): @@ -638,49 +654,33 @@ def createparser(): :return: parser instance :rtype: :class:`argparse.ArgumentParser` """ - parser = argparse.ArgumentParser(prog=__package__, - description=__doc__) + parser = argparse.ArgumentParser(prog=__package__, description=__doc__) - parser.add_argument('--version', - action='version', - version='%(prog)s ' + __version__ - ) + parser.add_argument( + "--version", action="version", version="%(prog)s " + __version__ + ) s = parser.add_subparsers() # create compare subcommand - parser_compare = s.add_parser("compare", - help="Compare two versions" - ) + parser_compare = s.add_parser("compare", help="Compare two versions") parser_compare.set_defaults(which="compare") - parser_compare.add_argument("version1", - help="First version" - ) - parser_compare.add_argument("version2", - help="Second version" - ) + parser_compare.add_argument("version1", help="First version") + parser_compare.add_argument("version2", help="Second version") # create bump subcommand - parser_bump = s.add_parser("bump", - help="Bumps a version" - ) + parser_bump = s.add_parser("bump", help="Bumps a version") parser_bump.set_defaults(which="bump") - sb = parser_bump.add_subparsers(title="Bump commands", - dest="bump") + sb = parser_bump.add_subparsers(title="Bump commands", dest="bump") # Create subparsers for the bump subparser: - for p in (sb.add_parser("major", - help="Bump the major part of the version"), - sb.add_parser("minor", - help="Bump the minor part of the version"), - sb.add_parser("patch", - help="Bump the patch part of the version"), - sb.add_parser("prerelease", - help="Bump the prerelease part of the version"), - sb.add_parser("build", - help="Bump the build part of the version")): - p.add_argument("version", - help="Version to raise" - ) + for p in ( + sb.add_parser("major", help="Bump the major part of the version"), + sb.add_parser("minor", help="Bump the minor part of the version"), + sb.add_parser("patch", help="Bump the patch part of the version"), + sb.add_parser("prerelease", help="Bump the prerelease part of the version"), + sb.add_parser("build", help="Bump the build part of the version"), + ): + p.add_argument("version", help="Version to raise") return parser @@ -698,12 +698,13 @@ def process(args): args.parser.print_help() raise SystemExit() elif args.which == "bump": - maptable = {'major': 'bump_major', - 'minor': 'bump_minor', - 'patch': 'bump_patch', - 'prerelease': 'bump_prerelease', - 'build': 'bump_build', - } + maptable = { + "major": "bump_major", + "minor": "bump_minor", + "patch": "bump_patch", + "prerelease": "bump_prerelease", + "build": "bump_build", + } if args.bump is None: # When bump is called without arguments, # print the help and exit @@ -759,4 +760,5 @@ def replace(version, **parts): if __name__ == "__main__": import doctest + doctest.testmod() diff --git a/setup.cfg b/setup.cfg index 7967d292..b8a0ea49 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,3 +7,14 @@ addopts = --cov-report=term-missing --doctest-modules --doctest-report ndiff + +[flake8] +max-line-length = 88 +exclude = + .env, + .eggs, + .tox, + .git, + __pycache__, + build, + dist \ No newline at end of file diff --git a/setup.py b/setup.py index 7c31635b..77f19e73 100755 --- a/setup.py +++ b/setup.py @@ -5,6 +5,7 @@ from os.path import dirname, join from setuptools import setup from setuptools.command.test import test as TestCommand + try: from setuptools.command.clean import clean as CleanCommand except ImportError: @@ -14,7 +15,7 @@ class Tox(TestCommand): - user_options = [('tox-args=', 'a', "Arguments to pass to tox")] + user_options = [("tox-args=", "a", "Arguments to pass to tox")] def initialize_options(self): TestCommand.initialize_options(self) @@ -27,6 +28,7 @@ def finalize_options(self): def run_tests(self): from tox import cmdline + args = self.tox_args if args: args = split(self.tox_args) @@ -37,35 +39,25 @@ def run_tests(self): class Clean(CleanCommand): def run(self): super(CleanCommand, self).run() - delete_in_root = [ - 'build', - '.cache', - 'dist', - '.eggs', - '*.egg-info', - '.tox', - ] - delete_everywhere = [ - '__pycache__', - '*.pyc', - ] + delete_in_root = ["build", ".cache", "dist", ".eggs", "*.egg-info", ".tox"] + delete_everywhere = ["__pycache__", "*.pyc"] for candidate in delete_in_root: rmtree_glob(candidate) - for visible_dir in glob('[A-Za-z0-9]*'): + for visible_dir in glob("[A-Za-z0-9]*"): for candidate in delete_everywhere: rmtree_glob(join(visible_dir, candidate)) - rmtree_glob(join(visible_dir, '*', candidate)) + rmtree_glob(join(visible_dir, "*", candidate)) def rmtree_glob(file_glob): for fobj in glob(file_glob): try: rmtree(fobj) - print('%s/ removed ...' % fobj) + print("%s/ removed ..." % fobj) except OSError: try: remove(fobj) - print('%s removed ...' % fobj) + print("%s removed ..." % fobj) except OSError: pass @@ -79,35 +71,30 @@ def read_file(filename): name=package.__name__, version=package.__version__, description=package.__doc__.strip(), - long_description=read_file('README.rst'), + long_description=read_file("README.rst"), author=package.__author__, author_email=package.__author_email__, - url='https://github.com/python-semver/python-semver', - download_url='https://github.com/python-semver/python-semver/downloads', + url="https://github.com/python-semver/python-semver", + download_url="https://github.com/python-semver/python-semver/downloads", py_modules=[package.__name__], include_package_data=True, - license='BSD', + license="BSD", classifiers=[ - 'Environment :: Web Environment', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Topic :: Software Development :: Libraries :: Python Modules', + "Environment :: Web Environment", + "Intended Audience :: Developers", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Topic :: Software Development :: Libraries :: Python Modules", ], - tests_require=['tox', 'virtualenv'], - cmdclass={ - 'clean': Clean, - 'test': Tox, - }, - entry_points={ - 'console_scripts': ['pysemver = semver:main'], - } + tests_require=["tox", "virtualenv"], + cmdclass={"clean": Clean, "test": Tox}, + entry_points={"console_scripts": ["pysemver = semver:main"]}, ) diff --git a/test_semver.py b/test_semver.py index 053031f6..b3d0929d 100644 --- a/test_semver.py +++ b/test_semver.py @@ -1,154 +1,181 @@ from argparse import Namespace import pytest # noqa -from semver import (VersionInfo, - bump_build, - bump_major, - bump_minor, - bump_patch, - bump_prerelease, - compare, - createparser, - finalize_version, - format_version, - main, - match, - max_ver, - min_ver, - parse, - parse_version_info, - process, - replace, - ) +from semver import ( + VersionInfo, + bump_build, + bump_major, + bump_minor, + bump_patch, + bump_prerelease, + compare, + createparser, + finalize_version, + format_version, + main, + match, + max_ver, + min_ver, + parse, + parse_version_info, + process, + replace, +) SEMVERFUNCS = [ - compare, createparser, - bump_build, bump_major, bump_minor, bump_patch, bump_prerelease, - finalize_version, format_version, - match, max_ver, min_ver, parse, process, replace, - ] - - -@pytest.mark.parametrize("string,expected", [ - ("rc", "rc"), - ("rc.1", "rc.2"), - ("2x", "3x"), -]) + compare, + createparser, + bump_build, + bump_major, + bump_minor, + bump_patch, + bump_prerelease, + finalize_version, + format_version, + match, + max_ver, + min_ver, + parse, + process, + replace, +] + + +@pytest.mark.parametrize( + "string,expected", [("rc", "rc"), ("rc.1", "rc.2"), ("2x", "3x")] +) def test_should_private_increment_string(string, expected): from semver import _increment_string + assert _increment_string(string) == expected @pytest.fixture def version(): - return VersionInfo(major=1, minor=2, patch=3, - prerelease='alpha.1.2', build='build.11.e0f985a') + return VersionInfo( + major=1, minor=2, patch=3, prerelease="alpha.1.2", build="build.11.e0f985a" + ) -@pytest.mark.parametrize("func", SEMVERFUNCS, - ids=[func.__name__ for func in SEMVERFUNCS]) +@pytest.mark.parametrize( + "func", SEMVERFUNCS, ids=[func.__name__ for func in SEMVERFUNCS] +) def test_fordocstrings(func): assert func.__doc__, "Need a docstring for function %r" % func.__name -@pytest.mark.parametrize("version,expected", [ - # no. 1 - ("1.2.3-alpha.1.2+build.11.e0f985a", - { - 'major': 1, - 'minor': 2, - 'patch': 3, - 'prerelease': 'alpha.1.2', - 'build': 'build.11.e0f985a', - }), - # no. 2 - ("1.2.3-alpha-1+build.11.e0f985a", - { - 'major': 1, - 'minor': 2, - 'patch': 3, - 'prerelease': 'alpha-1', - 'build': 'build.11.e0f985a', - }), - ("0.1.0-0f", - { - 'major': 0, - 'minor': 1, - 'patch': 0, - 'prerelease': '0f', - 'build': None, - }), - ("0.0.0-0foo.1", - { - 'major': 0, - 'minor': 0, - 'patch': 0, - 'prerelease': '0foo.1', - 'build': None, - }), - ("0.0.0-0foo.1+build.1", - { - 'major': 0, - 'minor': 0, - 'patch': 0, - 'prerelease': '0foo.1', - 'build': 'build.1', - }), -]) +@pytest.mark.parametrize( + "version,expected", + [ + # no. 1 + ( + "1.2.3-alpha.1.2+build.11.e0f985a", + { + "major": 1, + "minor": 2, + "patch": 3, + "prerelease": "alpha.1.2", + "build": "build.11.e0f985a", + }, + ), + # no. 2 + ( + "1.2.3-alpha-1+build.11.e0f985a", + { + "major": 1, + "minor": 2, + "patch": 3, + "prerelease": "alpha-1", + "build": "build.11.e0f985a", + }, + ), + ( + "0.1.0-0f", + {"major": 0, "minor": 1, "patch": 0, "prerelease": "0f", "build": None}, + ), + ( + "0.0.0-0foo.1", + {"major": 0, "minor": 0, "patch": 0, "prerelease": "0foo.1", "build": None}, + ), + ( + "0.0.0-0foo.1+build.1", + { + "major": 0, + "minor": 0, + "patch": 0, + "prerelease": "0foo.1", + "build": "build.1", + }, + ), + ], +) def test_should_parse_version(version, expected): result = parse(version) assert result == expected -@pytest.mark.parametrize("version,expected", [ - # no. 1 - ("1.2.3-rc.0+build.0", - { - 'major': 1, - 'minor': 2, - 'patch': 3, - 'prerelease': 'rc.0', - 'build': 'build.0', - }), - # no. 2 - ("1.2.3-rc.0.0+build.0", - { - 'major': 1, - 'minor': 2, - 'patch': 3, - 'prerelease': 'rc.0.0', - 'build': 'build.0', - }), -]) +@pytest.mark.parametrize( + "version,expected", + [ + # no. 1 + ( + "1.2.3-rc.0+build.0", + { + "major": 1, + "minor": 2, + "patch": 3, + "prerelease": "rc.0", + "build": "build.0", + }, + ), + # no. 2 + ( + "1.2.3-rc.0.0+build.0", + { + "major": 1, + "minor": 2, + "patch": 3, + "prerelease": "rc.0.0", + "build": "build.0", + }, + ), + ], +) def test_should_parse_zero_prerelease(version, expected): result = parse(version) assert result == expected -@pytest.mark.parametrize("left,right", [ - ("1.0.0", "2.0.0"), - ('1.0.0-alpha', '1.0.0-alpha.1'), - ('1.0.0-alpha.1', '1.0.0-alpha.beta'), - ('1.0.0-alpha.beta', '1.0.0-beta'), - ('1.0.0-beta', '1.0.0-beta.2'), - ('1.0.0-beta.2', '1.0.0-beta.11'), - ('1.0.0-beta.11', '1.0.0-rc.1'), - ('1.0.0-rc.1', '1.0.0'), -]) +@pytest.mark.parametrize( + "left,right", + [ + ("1.0.0", "2.0.0"), + ("1.0.0-alpha", "1.0.0-alpha.1"), + ("1.0.0-alpha.1", "1.0.0-alpha.beta"), + ("1.0.0-alpha.beta", "1.0.0-beta"), + ("1.0.0-beta", "1.0.0-beta.2"), + ("1.0.0-beta.2", "1.0.0-beta.11"), + ("1.0.0-beta.11", "1.0.0-rc.1"), + ("1.0.0-rc.1", "1.0.0"), + ], +) def test_should_get_less(left, right): assert compare(left, right) == -1 -@pytest.mark.parametrize("left,right", [ - ("2.0.0", "1.0.0"), - ('1.0.0-alpha.1', '1.0.0-alpha'), - ('1.0.0-alpha.beta', '1.0.0-alpha.1'), - ('1.0.0-beta', '1.0.0-alpha.beta'), - ('1.0.0-beta.2', '1.0.0-beta'), - ('1.0.0-beta.11', '1.0.0-beta.2'), - ('1.0.0-rc.1', '1.0.0-beta.11'), - ('1.0.0', '1.0.0-rc.1') -]) +@pytest.mark.parametrize( + "left,right", + [ + ("2.0.0", "1.0.0"), + ("1.0.0-alpha.1", "1.0.0-alpha"), + ("1.0.0-alpha.beta", "1.0.0-alpha.1"), + ("1.0.0-beta", "1.0.0-alpha.beta"), + ("1.0.0-beta.2", "1.0.0-beta"), + ("1.0.0-beta.11", "1.0.0-beta.2"), + ("1.0.0-rc.1", "1.0.0-beta.11"), + ("1.0.0", "1.0.0-rc.1"), + ], +) def test_should_get_greater(left, right): assert compare(left, right) == 1 @@ -161,34 +188,38 @@ def test_should_no_match_simple(): assert match("2.3.7", ">=2.3.8") is False -@pytest.mark.parametrize("left,right,expected", [ - ("2.3.7", "!=2.3.8", True), - ("2.3.7", "!=2.3.6", True), - ("2.3.7", "!=2.3.7", False), -]) +@pytest.mark.parametrize( + "left,right,expected", + [ + ("2.3.7", "!=2.3.8", True), + ("2.3.7", "!=2.3.6", True), + ("2.3.7", "!=2.3.7", False), + ], +) def test_should_match_not_equal(left, right, expected): assert match(left, right) is expected -@pytest.mark.parametrize("left,right,expected", [ - ("2.3.7", "<2.4.0", True), - ("2.3.7", ">2.3.5", True), - ("2.3.7", "<=2.3.9", True), - ("2.3.7", ">=2.3.5", True), - ("2.3.7", "==2.3.7", True), - ("2.3.7", "!=2.3.7", False), -]) -def test_should_not_raise_value_error_for_expected_match_expression(left, - right, - expected): +@pytest.mark.parametrize( + "left,right,expected", + [ + ("2.3.7", "<2.4.0", True), + ("2.3.7", ">2.3.5", True), + ("2.3.7", "<=2.3.9", True), + ("2.3.7", ">=2.3.5", True), + ("2.3.7", "==2.3.7", True), + ("2.3.7", "!=2.3.7", False), + ], +) +def test_should_not_raise_value_error_for_expected_match_expression( + left, right, expected +): assert match(left, right) is expected -@pytest.mark.parametrize("left,right", [ - ("2.3.7", "=2.3.7"), - ("2.3.7", "~2.3.7"), - ("2.3.7", "^2.3.7"), -]) +@pytest.mark.parametrize( + "left,right", [("2.3.7", "=2.3.7"), ("2.3.7", "~2.3.7"), ("2.3.7", "^2.3.7")] +) def test_should_raise_value_error_for_unexpected_match_expression(left, right): with pytest.raises(ValueError): match(left, right) @@ -200,21 +231,17 @@ def test_should_raise_value_error_for_zero_prefixed_versions(version): parse(version) -@pytest.mark.parametrize("left,right", [ - ('foo', 'bar'), - ('1.0', '1.0.0'), - ('1.x', '1.0.0'), -]) +@pytest.mark.parametrize( + "left,right", [("foo", "bar"), ("1.0", "1.0.0"), ("1.x", "1.0.0")] +) def test_should_raise_value_error_for_invalid_value(left, right): with pytest.raises(ValueError): compare(left, right) -@pytest.mark.parametrize("left,right", [ - ('1.0.0', ''), - ('1.0.0', '!'), - ('1.0.0', '1.0.0'), -]) +@pytest.mark.parametrize( + "left,right", [("1.0.0", ""), ("1.0.0", "!"), ("1.0.0", "1.0.0")] +) def test_should_raise_value_error_for_invalid_match_expression(left, right): with pytest.raises(ValueError): match(left, right) @@ -229,78 +256,88 @@ def test_should_follow_specification_comparison(): and in backward too. """ chain = [ - '1.0.0-alpha', '1.0.0-alpha.1', '1.0.0-beta.2', '1.0.0-beta.11', - '1.0.0-rc.1', '1.0.0', '1.3.7+build', + "1.0.0-alpha", + "1.0.0-alpha.1", + "1.0.0-beta.2", + "1.0.0-beta.11", + "1.0.0-rc.1", + "1.0.0", + "1.3.7+build", ] versions = zip(chain[:-1], chain[1:]) for low_version, high_version in versions: - assert compare(low_version, high_version) == -1, \ - '%s should be lesser than %s' % (low_version, high_version) - assert compare(high_version, low_version) == 1, \ - '%s should be higher than %s' % (high_version, low_version) + assert ( + compare(low_version, high_version) == -1 + ), "%s should be lesser than %s" % (low_version, high_version) + assert ( + compare(high_version, low_version) == 1 + ), "%s should be higher than %s" % (high_version, low_version) -@pytest.mark.parametrize("left,right", [ - ('1.0.0-beta.2', '1.0.0-beta.11'), -]) +@pytest.mark.parametrize("left,right", [("1.0.0-beta.2", "1.0.0-beta.11")]) def test_should_compare_rc_builds(left, right): assert compare(left, right) == -1 -@pytest.mark.parametrize("left,right", [ - ('1.0.0-rc.1', '1.0.0'), - ('1.0.0-rc.1+build.1', '1.0.0'), -]) +@pytest.mark.parametrize( + "left,right", [("1.0.0-rc.1", "1.0.0"), ("1.0.0-rc.1+build.1", "1.0.0")] +) def test_should_compare_release_candidate_with_release(left, right): assert compare(left, right) == -1 -@pytest.mark.parametrize("left,right", [ - ('2.0.0', '2.0.0'), - ('1.1.9-rc.1', '1.1.9-rc.1'), - ('1.1.9+build.1', '1.1.9+build.1'), - ('1.1.9-rc.1+build.1', '1.1.9-rc.1+build.1'), -]) +@pytest.mark.parametrize( + "left,right", + [ + ("2.0.0", "2.0.0"), + ("1.1.9-rc.1", "1.1.9-rc.1"), + ("1.1.9+build.1", "1.1.9+build.1"), + ("1.1.9-rc.1+build.1", "1.1.9-rc.1+build.1"), + ], +) def test_should_say_equal_versions_are_equal(left, right): assert compare(left, right) == 0 -@pytest.mark.parametrize("left,right,expected", [ - ('1.1.9-rc.1', '1.1.9-rc.1+build.1', 0), - ('1.1.9-rc.1', '1.1.9+build.1', -1), -]) +@pytest.mark.parametrize( + "left,right,expected", + [("1.1.9-rc.1", "1.1.9-rc.1+build.1", 0), ("1.1.9-rc.1", "1.1.9+build.1", -1)], +) def test_should_compare_versions_with_build_and_release(left, right, expected): assert compare(left, right) == expected -@pytest.mark.parametrize("left,right,expected", [ - ('1.0.0+build.1', '1.0.0', 0), - ('1.0.0-alpha.1+build.1', '1.0.0-alpha.1', 0), - ('1.0.0+build.1', '1.0.0-alpha.1', 1), - ('1.0.0+build.1', '1.0.0-alpha.1+build.1', 1), -]) +@pytest.mark.parametrize( + "left,right,expected", + [ + ("1.0.0+build.1", "1.0.0", 0), + ("1.0.0-alpha.1+build.1", "1.0.0-alpha.1", 0), + ("1.0.0+build.1", "1.0.0-alpha.1", 1), + ("1.0.0+build.1", "1.0.0-alpha.1+build.1", 1), + ], +) def test_should_ignore_builds_on_compare(left, right, expected): assert compare(left, right) == expected def test_should_correctly_format_version(): - assert format_version(3, 4, 5) == '3.4.5' - assert format_version(3, 4, 5, 'rc.1') == '3.4.5-rc.1' - assert format_version(3, 4, 5, prerelease='rc.1') == '3.4.5-rc.1' - assert format_version(3, 4, 5, build='build.4') == '3.4.5+build.4' - assert format_version(3, 4, 5, 'rc.1', 'build.4') == '3.4.5-rc.1+build.4' + assert format_version(3, 4, 5) == "3.4.5" + assert format_version(3, 4, 5, "rc.1") == "3.4.5-rc.1" + assert format_version(3, 4, 5, prerelease="rc.1") == "3.4.5-rc.1" + assert format_version(3, 4, 5, build="build.4") == "3.4.5+build.4" + assert format_version(3, 4, 5, "rc.1", "build.4") == "3.4.5-rc.1+build.4" def test_should_bump_major(): - assert bump_major('3.4.5') == '4.0.0' + assert bump_major("3.4.5") == "4.0.0" def test_should_bump_minor(): - assert bump_minor('3.4.5') == '3.5.0' + assert bump_minor("3.4.5") == "3.5.0" def test_should_bump_patch(): - assert bump_patch('3.4.5') == '3.4.6' + assert bump_patch("3.4.5") == "3.4.6" def test_should_versioninfo_bump_major_and_minor(): @@ -344,159 +381,172 @@ def test_should_versioninfo_bump_multiple(): expected = parse_version_info("3.4.5-rc.2+build.2") assert v.bump_prerelease().bump_build().bump_build() == expected expected = parse_version_info("3.4.5-rc.3") - assert v.bump_prerelease().bump_build().bump_build().bump_prerelease() == \ - expected + assert v.bump_prerelease().bump_build().bump_build().bump_prerelease() == expected def test_should_ignore_extensions_for_bump(): - assert bump_patch('3.4.5-rc1+build4') == '3.4.6' + assert bump_patch("3.4.5-rc1+build4") == "3.4.6" def test_should_get_max(): - assert max_ver('3.4.5', '4.0.2') == '4.0.2' + assert max_ver("3.4.5", "4.0.2") == "4.0.2" def test_should_get_max_same(): - assert max_ver('3.4.5', '3.4.5') == '3.4.5' + assert max_ver("3.4.5", "3.4.5") == "3.4.5" def test_should_get_min(): - assert min_ver('3.4.5', '4.0.2') == '3.4.5' + assert min_ver("3.4.5", "4.0.2") == "3.4.5" def test_should_get_min_same(): - assert min_ver('3.4.5', '3.4.5') == '3.4.5' + assert min_ver("3.4.5", "3.4.5") == "3.4.5" def test_should_get_more_rc1(): assert compare("1.0.0-rc1", "1.0.0-rc0") == 1 -@pytest.mark.parametrize("left,right,expected", [ - ('1.2.3-rc.2', '1.2.3-rc.10', '1.2.3-rc.2'), - ('1.2.3-rc2', '1.2.3-rc10', '1.2.3-rc10'), - # identifiers with letters or hyphens are compared lexically in ASCII sort - # order. - ('1.2.3-Rc10', '1.2.3-rc10', '1.2.3-Rc10'), - # Numeric identifiers always have lower precedence than non-numeric - # identifiers. - ('1.2.3-2', '1.2.3-rc', '1.2.3-2'), - # A larger set of pre-release fields has a higher precedence than a - # smaller set, if all of the preceding identifiers are equal. - ('1.2.3-rc.2.1', '1.2.3-rc.2', '1.2.3-rc.2'), - # When major, minor, and patch are equal, a pre-release version has lower - # precedence than a normal version. - ('1.2.3', '1.2.3-1', '1.2.3-1'), - ('1.0.0-alpha', '1.0.0-alpha.1', '1.0.0-alpha') -]) +@pytest.mark.parametrize( + "left,right,expected", + [ + ("1.2.3-rc.2", "1.2.3-rc.10", "1.2.3-rc.2"), + ("1.2.3-rc2", "1.2.3-rc10", "1.2.3-rc10"), + # identifiers with letters or hyphens are compared lexically in ASCII sort + # order. + ("1.2.3-Rc10", "1.2.3-rc10", "1.2.3-Rc10"), + # Numeric identifiers always have lower precedence than non-numeric + # identifiers. + ("1.2.3-2", "1.2.3-rc", "1.2.3-2"), + # A larger set of pre-release fields has a higher precedence than a + # smaller set, if all of the preceding identifiers are equal. + ("1.2.3-rc.2.1", "1.2.3-rc.2", "1.2.3-rc.2"), + # When major, minor, and patch are equal, a pre-release version has lower + # precedence than a normal version. + ("1.2.3", "1.2.3-1", "1.2.3-1"), + ("1.0.0-alpha", "1.0.0-alpha.1", "1.0.0-alpha"), + ], +) def test_prerelease_order(left, right, expected): assert min_ver(left, right) == expected -@pytest.mark.parametrize("version,token,expected", [ - ('3.4.5-rc.9', None, '3.4.5-rc.10'), - ('3.4.5', None, '3.4.5-rc.1'), - ('3.4.5', 'dev', '3.4.5-dev.1'), - ('3.4.5', '', '3.4.5-rc.1'), -]) +@pytest.mark.parametrize( + "version,token,expected", + [ + ("3.4.5-rc.9", None, "3.4.5-rc.10"), + ("3.4.5", None, "3.4.5-rc.1"), + ("3.4.5", "dev", "3.4.5-dev.1"), + ("3.4.5", "", "3.4.5-rc.1"), + ], +) def test_should_bump_prerelease(version, token, expected): token = "rc" if not token else token assert bump_prerelease(version, token) == expected def test_should_ignore_build_on_prerelease_bump(): - assert bump_prerelease('3.4.5-rc.1+build.4') == '3.4.5-rc.2' - - -@pytest.mark.parametrize("version,expected", [ - ('3.4.5-rc.1+build.9', '3.4.5-rc.1+build.10'), - ('3.4.5-rc.1+0009.dev', '3.4.5-rc.1+0010.dev'), - ('3.4.5-rc.1', '3.4.5-rc.1+build.1'), - ('3.4.5', '3.4.5+build.1'), -]) + assert bump_prerelease("3.4.5-rc.1+build.4") == "3.4.5-rc.2" + + +@pytest.mark.parametrize( + "version,expected", + [ + ("3.4.5-rc.1+build.9", "3.4.5-rc.1+build.10"), + ("3.4.5-rc.1+0009.dev", "3.4.5-rc.1+0010.dev"), + ("3.4.5-rc.1", "3.4.5-rc.1+build.1"), + ("3.4.5", "3.4.5+build.1"), + ], +) def test_should_bump_build(version, expected): assert bump_build(version) == expected -@pytest.mark.parametrize("version,expected", [ - ('1.2.3', '1.2.3'), - ('1.2.3-rc.5', '1.2.3'), - ('1.2.3+build.2', '1.2.3'), - ('1.2.3-rc.1+build.5', '1.2.3'), - ('1.2.3-alpha', '1.2.3'), - ('1.2.0', '1.2.0'), -]) +@pytest.mark.parametrize( + "version,expected", + [ + ("1.2.3", "1.2.3"), + ("1.2.3-rc.5", "1.2.3"), + ("1.2.3+build.2", "1.2.3"), + ("1.2.3-rc.1+build.5", "1.2.3"), + ("1.2.3-alpha", "1.2.3"), + ("1.2.0", "1.2.0"), + ], +) def test_should_finalize_version(version, expected): assert finalize_version(version) == expected def test_should_compare_version_info_objects(): v1 = VersionInfo(major=0, minor=10, patch=4) - v2 = VersionInfo( - major=0, minor=10, patch=4, prerelease='beta.1', build=None) + v2 = VersionInfo(major=0, minor=10, patch=4, prerelease="beta.1", build=None) # use `not` to enforce using comparision operators assert v1 != v2 assert v1 > v2 assert v1 >= v2 - assert not(v1 < v2) - assert not(v1 <= v2) - assert not(v1 == v2) + assert not (v1 < v2) + assert not (v1 <= v2) + assert not (v1 == v2) v3 = VersionInfo(major=0, minor=10, patch=4) - assert not(v1 != v3) - assert not(v1 > v3) + assert not (v1 != v3) + assert not (v1 > v3) assert v1 >= v3 - assert not(v1 < v3) + assert not (v1 < v3) assert v1 <= v3 assert v1 == v3 v4 = VersionInfo(major=0, minor=10, patch=5) assert v1 != v4 - assert not(v1 > v4) - assert not(v1 >= v4) + assert not (v1 > v4) + assert not (v1 >= v4) assert v1 < v4 assert v1 <= v4 - assert not(v1 == v4) + assert not (v1 == v4) def test_should_compare_version_dictionaries(): v1 = VersionInfo(major=0, minor=10, patch=4) - v2 = dict(major=0, minor=10, patch=4, prerelease='beta.1', build=None) + v2 = dict(major=0, minor=10, patch=4, prerelease="beta.1", build=None) assert v1 != v2 assert v1 > v2 assert v1 >= v2 - assert not(v1 < v2) - assert not(v1 <= v2) - assert not(v1 == v2) + assert not (v1 < v2) + assert not (v1 <= v2) + assert not (v1 == v2) v3 = dict(major=0, minor=10, patch=4) - assert not(v1 != v3) - assert not(v1 > v3) + assert not (v1 != v3) + assert not (v1 > v3) assert v1 >= v3 - assert not(v1 < v3) + assert not (v1 < v3) assert v1 <= v3 assert v1 == v3 v4 = dict(major=0, minor=10, patch=5) assert v1 != v4 - assert not(v1 > v4) - assert not(v1 >= v4) + assert not (v1 > v4) + assert not (v1 >= v4) assert v1 < v4 assert v1 <= v4 - assert not(v1 == v4) + assert not (v1 == v4) def test_should_compare_version_tuples(): - v0 = VersionInfo(major=0, minor=4, patch=5, - prerelease='pre.2', build='build.4') - v1 = VersionInfo(major=3, minor=4, patch=5, - prerelease='pre.2', build='build.4') - for t in ((1, 0, 0), (1, 0), (1,), (1, 0, 0, 'pre.2'), - (1, 0, 0, 'pre.2', 'build.4')): + v0 = VersionInfo(major=0, minor=4, patch=5, prerelease="pre.2", build="build.4") + v1 = VersionInfo(major=3, minor=4, patch=5, prerelease="pre.2", build="build.4") + for t in ( + (1, 0, 0), + (1, 0), + (1,), + (1, 0, 0, "pre.2"), + (1, 0, 0, "pre.2", "build.4"), + ): assert v0 < t assert v0 <= t assert v0 != t @@ -513,8 +563,7 @@ def test_should_compare_version_tuples(): def test_should_not_allow_to_compare_version_with_string(): - v1 = VersionInfo(major=3, minor=4, patch=5, - prerelease='pre.2', build='build.4') + v1 = VersionInfo(major=3, minor=4, patch=5, prerelease="pre.2", build="build.4") with pytest.raises(TypeError): v1 > "1.0.0" with pytest.raises(TypeError): @@ -522,8 +571,7 @@ def test_should_not_allow_to_compare_version_with_string(): def test_should_not_allow_to_compare_version_with_int(): - v1 = VersionInfo(major=3, minor=4, patch=5, - prerelease='pre.2', build='build.4') + v1 = VersionInfo(major=3, minor=4, patch=5, prerelease="pre.2", build="build.4") with pytest.raises(TypeError): v1 > 1 with pytest.raises(TypeError): @@ -531,8 +579,8 @@ def test_should_not_allow_to_compare_version_with_int(): def test_should_compare_prerelease_with_numbers_and_letters(): - v1 = VersionInfo(major=1, minor=9, patch=1, prerelease='1unms', build=None) - v2 = VersionInfo(major=1, minor=9, patch=1, prerelease=None, build='1asd') + v1 = VersionInfo(major=1, minor=9, patch=1, prerelease="1unms", build=None) + v2 = VersionInfo(major=1, minor=9, patch=1, prerelease=None, build="1asd") assert v1 < v2 assert compare("1.9.1-1unms", "1.9.1+1") == -1 @@ -567,76 +615,96 @@ def test_immutable_patch(version): def test_immutable_prerelease(version): - with pytest.raises(AttributeError, - match="attribute 'prerelease' is readonly"): - version.prerelease = 'alpha.9.9' + with pytest.raises(AttributeError, match="attribute 'prerelease' is readonly"): + version.prerelease = "alpha.9.9" def test_immutable_build(version): with pytest.raises(AttributeError, match="attribute 'build' is readonly"): - version.build = 'build.99.e0f985a' + version.build = "build.99.e0f985a" def test_immutable_unknown_attribute(version): # "no new attribute can be set" with pytest.raises(AttributeError): - version.new_attribute = 'forbidden' + version.new_attribute = "forbidden" def test_version_info_should_be_iterable(version): - assert tuple(version) == (version.major, version.minor, version.patch, - version.prerelease, version.build) + assert tuple(version) == ( + version.major, + version.minor, + version.patch, + version.prerelease, + version.build, + ) def test_should_compare_prerelease_and_build_with_numbers(): - assert VersionInfo(major=1, minor=9, patch=1, prerelease=1, build=1) < \ - VersionInfo(major=1, minor=9, patch=1, prerelease=2, build=1) + assert VersionInfo(major=1, minor=9, patch=1, prerelease=1, build=1) < VersionInfo( + major=1, minor=9, patch=1, prerelease=2, build=1 + ) assert VersionInfo(1, 9, 1, 1, 1) < VersionInfo(1, 9, 1, 2, 1) - assert VersionInfo('2') < VersionInfo(10) - assert VersionInfo('2') < VersionInfo('10') + assert VersionInfo("2") < VersionInfo(10) + assert VersionInfo("2") < VersionInfo("10") def test_should_be_able_to_use_strings_as_major_minor_patch(): - v = VersionInfo('1', '2', '3') + v = VersionInfo("1", "2", "3") assert isinstance(v.major, int) assert isinstance(v.minor, int) assert isinstance(v.patch, int) assert v.prerelease is None assert v.build is None - assert VersionInfo('1', '2', '3') == VersionInfo(1, 2, 3) + assert VersionInfo("1", "2", "3") == VersionInfo(1, 2, 3) def test_using_non_numeric_string_as_major_minor_patch_throws(): with pytest.raises(ValueError): - VersionInfo('a') + VersionInfo("a") with pytest.raises(ValueError): - VersionInfo(1, 'a') + VersionInfo(1, "a") with pytest.raises(ValueError): - VersionInfo(1, 2, 'a') + VersionInfo(1, 2, "a") def test_should_be_able_to_use_integers_as_prerelease_build(): v = VersionInfo(1, 2, 3, 4, 5) assert isinstance(v.prerelease, str) assert isinstance(v.build, str) - assert VersionInfo(1, 2, 3, 4, 5) == VersionInfo(1, 2, 3, '4', '5') - - -@pytest.mark.parametrize("cli,expected", [ - (["bump", "major", "1.2.3"], - Namespace(which='bump', bump='major', version='1.2.3')), - (["bump", "minor", "1.2.3"], - Namespace(which='bump', bump='minor', version='1.2.3')), - (["bump", "patch", "1.2.3"], - Namespace(which='bump', bump='patch', version='1.2.3')), - (["bump", "prerelease", "1.2.3"], - Namespace(which='bump', bump='prerelease', version='1.2.3')), - (["bump", "build", "1.2.3"], - Namespace(which='bump', bump='build', version='1.2.3')), - # --- - (["compare", "1.2.3", "2.1.3"], - Namespace(which='compare', version1='1.2.3', version2='2.1.3')), -]) + assert VersionInfo(1, 2, 3, 4, 5) == VersionInfo(1, 2, 3, "4", "5") + + +@pytest.mark.parametrize( + "cli,expected", + [ + ( + ["bump", "major", "1.2.3"], + Namespace(which="bump", bump="major", version="1.2.3"), + ), + ( + ["bump", "minor", "1.2.3"], + Namespace(which="bump", bump="minor", version="1.2.3"), + ), + ( + ["bump", "patch", "1.2.3"], + Namespace(which="bump", bump="patch", version="1.2.3"), + ), + ( + ["bump", "prerelease", "1.2.3"], + Namespace(which="bump", bump="prerelease", version="1.2.3"), + ), + ( + ["bump", "build", "1.2.3"], + Namespace(which="bump", bump="build", version="1.2.3"), + ), + # --- + ( + ["compare", "1.2.3", "2.1.3"], + Namespace(which="compare", version1="1.2.3", version2="2.1.3"), + ), + ], +) def test_should_parse_cli_arguments(cli, expected): parser = createparser() assert parser @@ -644,26 +712,24 @@ def test_should_parse_cli_arguments(cli, expected): assert result == expected -@pytest.mark.parametrize("args,expected", [ - # bump subcommand - (Namespace(which='bump', bump='major', version='1.2.3'), - "2.0.0"), - (Namespace(which='bump', bump='minor', version='1.2.3'), - "1.3.0"), - (Namespace(which='bump', bump='patch', version='1.2.3'), - "1.2.4"), - (Namespace(which='bump', bump='prerelease', version='1.2.3-rc1'), - "1.2.3-rc2"), - (Namespace(which='bump', bump='build', version='1.2.3+build.13'), - "1.2.3+build.14"), - # compare subcommand - (Namespace(which='compare', version1='1.2.3', version2='2.1.3'), - "-1"), - (Namespace(which='compare', version1='1.2.3', version2='1.2.3'), - "0"), - (Namespace(which='compare', version1='2.4.0', version2='2.1.3'), - "1"), -]) +@pytest.mark.parametrize( + "args,expected", + [ + # bump subcommand + (Namespace(which="bump", bump="major", version="1.2.3"), "2.0.0"), + (Namespace(which="bump", bump="minor", version="1.2.3"), "1.3.0"), + (Namespace(which="bump", bump="patch", version="1.2.3"), "1.2.4"), + (Namespace(which="bump", bump="prerelease", version="1.2.3-rc1"), "1.2.3-rc2"), + ( + Namespace(which="bump", bump="build", version="1.2.3+build.13"), + "1.2.3+build.14", + ), + # compare subcommand + (Namespace(which="compare", version1="1.2.3", version2="2.1.3"), "-1"), + (Namespace(which="compare", version1="1.2.3", version2="1.2.3"), "0"), + (Namespace(which="compare", version1="2.4.0", version2="2.1.3"), "1"), + ], +) def test_should_process_parsed_cli_arguments(args, expected): assert process(args) == expected @@ -692,20 +758,25 @@ def test_should_raise_systemexit_when_bump_iscalled_with_empty_arguments(): main(["bump"]) -@pytest.mark.parametrize("version,parts,expected", [ - ("3.4.5", dict(major=2), '2.4.5'), - ("3.4.5", dict(major="2"), '2.4.5'), - ("3.4.5", dict(major=2, minor=5), '2.5.5'), - ("3.4.5", dict(minor=2), '3.2.5'), - ("3.4.5", dict(major=2, minor=5, patch=10), '2.5.10'), - ("3.4.5", dict(major=2, minor=5, patch=10, prerelease="rc1"), - '2.5.10-rc1'), - ("3.4.5", dict(major=2, minor=5, patch=10, prerelease="rc1", build="b1"), - '2.5.10-rc1+b1'), - ("3.4.5-alpha.1.2", dict(major=2), '2.4.5-alpha.1.2'), - ("3.4.5-alpha.1.2", dict(build="x1"), '3.4.5-alpha.1.2+x1'), - ("3.4.5+build1", dict(major=2), '2.4.5+build1'), -]) +@pytest.mark.parametrize( + "version,parts,expected", + [ + ("3.4.5", dict(major=2), "2.4.5"), + ("3.4.5", dict(major="2"), "2.4.5"), + ("3.4.5", dict(major=2, minor=5), "2.5.5"), + ("3.4.5", dict(minor=2), "3.2.5"), + ("3.4.5", dict(major=2, minor=5, patch=10), "2.5.10"), + ("3.4.5", dict(major=2, minor=5, patch=10, prerelease="rc1"), "2.5.10-rc1"), + ( + "3.4.5", + dict(major=2, minor=5, patch=10, prerelease="rc1", build="b1"), + "2.5.10-rc1+b1", + ), + ("3.4.5-alpha.1.2", dict(major=2), "2.4.5-alpha.1.2"), + ("3.4.5-alpha.1.2", dict(build="x1"), "3.4.5-alpha.1.2+x1"), + ("3.4.5+build1", dict(major=2), "2.4.5+build1"), + ], +) def test_replace_method_replaces_requested_parts(version, parts, expected): assert replace(version, **parts) == expected @@ -715,18 +786,18 @@ def test_replace_raises_TypeError_for_invalid_keyword_arg(): assert replace("1.2.3", unknown="should_raise") -@pytest.mark.parametrize("version,parts,expected", [ - ("3.4.5", dict(major=2, minor=5), '2.5.5'), - ("3.4.5", dict(major=2, minor=5, patch=10), '2.5.10'), - ("3.4.5-alpha.1.2", dict(major=2), '2.4.5-alpha.1.2'), - ("3.4.5-alpha.1.2", dict(build="x1"), '3.4.5-alpha.1.2+x1'), - ("3.4.5+build1", dict(major=2), '2.4.5+build1'), -]) -def test_should_return_versioninfo_with_replaced_parts(version, - parts, - expected): - assert VersionInfo.parse(version).replace(**parts) == \ - VersionInfo.parse(expected) +@pytest.mark.parametrize( + "version,parts,expected", + [ + ("3.4.5", dict(major=2, minor=5), "2.5.5"), + ("3.4.5", dict(major=2, minor=5, patch=10), "2.5.10"), + ("3.4.5-alpha.1.2", dict(major=2), "2.4.5-alpha.1.2"), + ("3.4.5-alpha.1.2", dict(build="x1"), "3.4.5-alpha.1.2+x1"), + ("3.4.5+build1", dict(major=2), "2.4.5+build1"), + ], +) +def test_should_return_versioninfo_with_replaced_parts(version, parts, expected): + assert VersionInfo.parse(version).replace(**parts) == VersionInfo.parse(expected) def test_replace_raises_ValueError_for_non_numeric_values():