diff --git a/README.rst b/README.rst index 80b922e..e0925d4 100644 --- a/README.rst +++ b/README.rst @@ -19,6 +19,7 @@ Contents * `Version Source Files <#version-source-files>`_ * `Configuration Section <#configuration-section>`_ + * `Global Optons <#global-options>`_ * `Search in a Webpage <#search-in-a-webpage>`_ * `Find with a Command <#find-with-a-command>`_ * `Check AUR <#check-aur>`_ @@ -124,6 +125,24 @@ proxy max_concurrent Max number of concurrent jobs. Default: 20. +Global Options +-------------- +The following options apply to all checkers. + +prefix + Strip the prefix string if the version string starts with it. Otherwise the + version string is returned as-is. + +from_pattern, to_pattern + Both are Python-compatible regular expressions. If ``from_pattern`` is found + in the version string, it will be replaced with ``to_pattern``. + +If both ``prefix`` and ``from_pattern``/``to_pattern`` are used, +``from_pattern``/``to_pattern`` are ignored. If you want to strip the prefix +and then do something special, just use ``from_pattern```/``to_pattern``. For +example, the transformation of ``v1_1_0`` => ``1.1.0`` can be achieved with +``from_pattern = v(\d+)_(\d+)_(\d+)`` and ``to_pattern = \1.\2.\3``. + Search in a Webpage ------------------- Search through a specific webpage for the version string. This type of version finding has these fields: diff --git a/nvchecker/get_version.py b/nvchecker/get_version.py index 8a94d23..1c8e6eb 100644 --- a/nvchecker/get_version.py +++ b/nvchecker/get_version.py @@ -2,6 +2,7 @@ # Copyright (c) 2013-2017 lilydjwg , et al. import logging +import re from importlib import import_module logger = logging.getLogger(__name__) @@ -13,13 +14,39 @@ handler_precedence = ( 'anitya', ) +def substitute_version(version, name, conf): + ''' + Substitute the version string via defined rules in the configuration file. + See README.rst#global-options for details. + ''' + prefix = conf.get('prefix') + if prefix: + if version.startswith(prefix): + version = version[len(prefix):] + return version + + from_pattern = conf.get('from_pattern') + if from_pattern: + to_pattern = conf.get('to_pattern') + if not to_pattern: + raise ValueError('%s: from_pattern exists but to_pattern doesn\'t', name) + + return re.sub(from_pattern, to_pattern, version) + + # No substitution rules found. Just return the original version string. + return version + async def get_version(name, conf): for key in handler_precedence: if key in conf: func = import_module('.source.' + key, __package__).get_version version = await func(name, conf) if version: - version.replace('\n', ' ') + version = version.replace('\n', ' ') + try: + version = substitute_version(version, name, conf) + except (ValueError, re.error): + logger.exception('error occured in version substitutions for %s', name) return version else: logger.error('%s: no idea to get version info.', name) diff --git a/tests/test_substitute.py b/tests/test_substitute.py new file mode 100644 index 0000000..1b033ec --- /dev/null +++ b/tests/test_substitute.py @@ -0,0 +1,20 @@ +# MIT licensed +# Copyright (c) 2013-2017 lilydjwg , et al. + +import pytest +pytestmark = pytest.mark.asyncio + +async def test_substitute_prefix(get_version): + assert await get_version("example", {"manual": "v1.0", "prefix": "v"}) == "1.0" + +async def test_substitute_prefix_missing_ok(get_version): + assert await get_version("example", {"manual": "1.0", "prefix": "v"}) == "1.0" + +async def test_substitute_regex(get_version): + assert await get_version("example", {"manual": "r15c", "from_pattern": r"r(\d+)([a-z])", "to_pattern": r"r\1.\2"}) == "r15.c" + +async def test_substitute_regex_missing_ok(get_version): + assert await get_version("example", {"manual": "r15", "from_pattern": r"r(\d+)([a-z])", "to_pattern": r"r\1.\2"}) == "r15" + +async def test_substitute_prefix_has_higher_priority(get_version): + assert await get_version("example", {"manual": "r15", "prefix": "r", "from_pattern": "r(\d+)", "to_pattern": "R\1"}) == "15"