Best Python code snippet using avocado_python
config.py
Source:config.py  
1# This file is part of the bumpver project2# https://gitlab.com/mbarkhau/bumpver3#4# Copyright (c) 2018-2022 Manuel Barkhau (mbarkhau@gmail.com) - MIT License5# SPDX-License-Identifier: MIT6"""Parse bumpver.toml, setup.cfg or pyproject.toml files."""7import re8import typing as typ9import logging10import datetime as dt11import configparser12import toml13import pathlib2 as pl14from . import version15from . import v1version16from . import v2version17from . import v1patterns18from . import v2patterns19from .patterns import Pattern20logger = logging.getLogger("bumpver.config")21RawPatterns         = typ.List[str]22RawPatternsByFile   = typ.Dict[str, RawPatterns]23FileRawPatternsItem = typ.Tuple[str, RawPatterns]24PatternsByFile   = typ.Dict[str, typ.List[Pattern]]25FilePatternsItem = typ.Tuple[str, typ.List[Pattern]]26SUPPORTED_CONFIGS = ["setup.cfg", "pyproject.toml", "pycalver.toml", "bumpver.toml"]27DEFAULT_COMMIT_MESSAGE = "bump version to {new_version}"28class ProjectContext(typ.NamedTuple):29    """Container class for project info."""30    path           : pl.Path31    config_filepath: pl.Path32    config_rel_path: str33    config_format  : str34    vcs_type       : typ.Optional[str]35def _pick_config_filepath(path: pl.Path) -> pl.Path:36    config_candidates: typ.List[pl.Path] = [37        path / "pycalver.toml",38        path / "bumpver.toml",39        path / "pyproject.toml",40        path / "setup.cfg",41    ]42    # Prefer to use a config which contains a bumpver and current_version43    # This means a project can have multiple different configs.44    for config_filepath in config_candidates:45        if config_filepath.exists():46            with config_filepath.open(mode="rb") as fobj:47                data = fobj.read()48            has_bumpver_section = (49                b"bumpver]" in data or b"pycalver]" in data50            ) and b"current_version" in data51            if has_bumpver_section:52                return config_filepath53    # Next pick whatever config happens to exist, even if it54    # doesn't have a [bumpver] section (yet).55    for config_filepath in config_candidates:56        if config_filepath.exists():57            return config_filepath58    # fallback to creating a new bumpver.toml59    return path / "bumpver.toml"60def _parse_config_and_format(path: pl.Path) -> typ.Tuple[pl.Path, str, str]:61    config_filepath = _pick_config_filepath(path)62    if config_filepath.is_absolute():63        config_rel_path = str(config_filepath.relative_to(path.absolute()))64    else:65        config_rel_path = str(config_filepath)66        config_filepath = pl.Path.cwd() / config_filepath67    config_format = config_filepath.suffix[1:]68    return (config_filepath, config_rel_path, config_format)69def init_project_ctx(project_path: typ.Union[str, pl.Path, None] = ".") -> ProjectContext:70    """Initialize ProjectContext from a path."""71    if isinstance(project_path, pl.Path):72        path = project_path73    elif project_path is None:74        path = pl.Path(".")75    else:76        # assume it's a str/unicode77        path = pl.Path(project_path)78    config_filepath, config_rel_path, config_format = _parse_config_and_format(path)79    vcs_type: typ.Optional[str]80    if (path / ".git").exists():81        vcs_type = 'git'82    elif (path / ".hg").exists():83        vcs_type = 'hg'84    else:85        vcs_type = None86    return ProjectContext(path, config_filepath, config_rel_path, config_format, vcs_type)87RawConfig      = typ.Dict[str, typ.Any]88MaybeRawConfig = typ.Optional[RawConfig]89class Config(typ.NamedTuple):90    """Container for parameters parsed from a config file."""91    current_version: str92    version_pattern: str93    pep440_version : str94    commit_message : str95    commit        : bool96    tag           : bool97    push          : bool98    is_new_pattern: bool99    file_patterns: PatternsByFile100MaybeConfig = typ.Optional[Config]101def _debug_str(cfg: Config) -> str:102    cfg_str_parts = [103        "Config Parsed: Config(",104        f"\n    current_version='{cfg.current_version}',",105        f"\n    version_pattern='{cfg.version_pattern}',",106        f"\n    pep440_version='{cfg.pep440_version}',",107        f"\n    commit_message='{cfg.commit_message}',",108        f"\n    commit={cfg.commit},",109        f"\n    tag={cfg.tag},",110        f"\n    push={cfg.push},",111        f"\n    is_new_pattern={cfg.is_new_pattern},",112        "\n    file_patterns={",113    ]114    for filepath, patterns in sorted(cfg.file_patterns.items()):115        for pattern in patterns:116            cfg_str_parts.append(f"\n        '{filepath}': '{pattern.raw_pattern}',")117    cfg_str_parts += ["\n    }\n)"]118    return "".join(cfg_str_parts)119def _parse_cfg_file_patterns(120    cfg_parser: configparser.RawConfigParser,121) -> typ.Iterable[FileRawPatternsItem]:122    file_pattern_items: typ.List[typ.Tuple[str, str]]123    if cfg_parser.has_section("pycalver:file_patterns"):124        file_pattern_items = cfg_parser.items("pycalver:file_patterns")125    elif cfg_parser.has_section("bumpver:file_patterns"):126        file_pattern_items = cfg_parser.items("bumpver:file_patterns")127    else:128        return129    for filepath, patterns_str in file_pattern_items:130        maybe_patterns = (line.strip() for line in patterns_str.splitlines())131        patterns       = [p for p in maybe_patterns if p]132        yield filepath, patterns133class _ConfigParser(configparser.RawConfigParser):134    # pylint:disable=too-many-ancestors ; from our perspective, it's just one135    """Custom parser, simply to override optionxform behaviour."""136    def optionxform(self, optionstr: str) -> str:137        """Non-xforming (ie. uppercase preserving) override.138        This is important because our option names are actually139        filenames, so case sensitivity is relevant. The default140        behaviour is to do optionstr.lower()141        """142        return optionstr143OptionVal = typ.Union[str, bool, None]144BOOL_OPTIONS: typ.Mapping[str, OptionVal] = {'commit': False, 'tag': None, 'push': None}145def _parse_cfg(cfg_buffer: typ.IO[str]) -> RawConfig:146    cfg_parser = _ConfigParser()147    if hasattr(cfg_parser, 'read_file'):148        cfg_parser.read_file(cfg_buffer)149    else:150        cfg_parser.readfp(cfg_buffer)  # pylint: disable=deprecated-method ; python2 compat151    raw_cfg: RawConfig152    if cfg_parser.has_section("pycalver"):153        raw_cfg = dict(cfg_parser.items("pycalver"))154    elif cfg_parser.has_section("bumpver"):155        raw_cfg = dict(cfg_parser.items("bumpver"))156    else:157        logger.warning("Perhaps try 'bumpver init'.")158        raise ValueError("Missing [bumpver] section.")159    for option, default_val in BOOL_OPTIONS.items():160        val: OptionVal = raw_cfg.get(option, default_val)161        if isinstance(val, (bytes, str)):162            val = val.lower() in ("yes", "true", "1", "on")163        raw_cfg[option] = val164    raw_cfg['file_patterns'] = dict(_parse_cfg_file_patterns(cfg_parser))165    _set_raw_config_defaults(raw_cfg)166    return raw_cfg167def _parse_toml(cfg_buffer: typ.IO[str]) -> RawConfig:168    raw_full_cfg: typ.Any = toml.load(cfg_buffer)169    raw_cfg     : RawConfig170    if 'tool' in raw_full_cfg and 'bumpver' in raw_full_cfg['tool']:171        raw_cfg = raw_full_cfg['tool']['bumpver']172    elif 'bumpver' in raw_full_cfg:173        raw_cfg = raw_full_cfg['bumpver']174    elif 'pycalver' in raw_full_cfg:175        raw_cfg = raw_full_cfg['pycalver']176    else:177        raw_cfg = {}178    for option, default_val in BOOL_OPTIONS.items():179        raw_cfg[option] = raw_cfg.get(option, default_val)180    _set_raw_config_defaults(raw_cfg)181    return raw_cfg182def _iter_glob_expanded_file_patterns(183    raw_patterns_by_file: RawPatternsByFile,184) -> typ.Iterator[FileRawPatternsItem]:185    for filepath_glob, raw_patterns in raw_patterns_by_file.items():186        filepaths = list(pl.Path().glob(filepath_glob))187        if filepaths:188            for filepath in filepaths:189                yield str(filepath), raw_patterns190        else:191            logger.warning(f"Invalid config, no such file: {filepath_glob}")192            # fallback to treating it as a simple path193            yield filepath_glob, raw_patterns194def _compile_v1_file_patterns(raw_cfg: RawConfig) -> typ.Iterator[FilePatternsItem]:195    """Create inernal/compiled representation of the file_patterns config field.196    The result the same, regardless of the config format.197    """198    # current_version: str = raw_cfg['current_version']199    # current_pep440_version = version.pep440_version(current_version)200    version_pattern     : str               = raw_cfg['version_pattern']201    raw_patterns_by_file: RawPatternsByFile = raw_cfg['file_patterns']202    for filepath, raw_patterns in _iter_glob_expanded_file_patterns(raw_patterns_by_file):203        compiled_patterns = v1patterns.compile_patterns(version_pattern, raw_patterns)204        yield filepath, compiled_patterns205def _compile_v2_file_patterns(raw_cfg: RawConfig) -> typ.Iterable[FilePatternsItem]:206    """Create inernal/compiled representation of the file_patterns config field.207    The result the same, regardless of the config format.208    """209    version_pattern     : str               = raw_cfg['version_pattern']210    raw_patterns_by_file: RawPatternsByFile = raw_cfg['file_patterns']211    for filepath, raw_patterns in _iter_glob_expanded_file_patterns(raw_patterns_by_file):212        for raw_pattern in raw_patterns:213            if raw_pattern.startswith("["):214                errmsg = (215                    f"Invalid pattern {raw_pattern} for {filepath}. "216                    + "Character not valid in this position '[' "217                )218                raise ValueError(errmsg)219            # provoke error for specifc pattern220            try:221                v2patterns.compile_pattern(version_pattern, raw_pattern)222            except re.error:223                logger.warning(f"Invalid patterns for {filepath} ({raw_pattern})")224                raise225        compiled_patterns = v2patterns.compile_patterns(version_pattern, raw_patterns)226        yield filepath, compiled_patterns227def _compile_file_patterns(raw_cfg: RawConfig, is_new_pattern: bool) -> PatternsByFile:228    if is_new_pattern:229        _file_pattern_items = _compile_v2_file_patterns(raw_cfg)230    else:231        _file_pattern_items = _compile_v1_file_patterns(raw_cfg)232    # NOTE (mb 2020-10-03): There can be multiple items for the same233    #   path, so this is not an option:234    #235    # return dict(_file_pattern_items)236    file_patterns: PatternsByFile = {}237    for path, patterns in _file_pattern_items:238        if path in file_patterns:239            file_patterns[path].extend(patterns)240        else:241            file_patterns[path] = patterns242    return file_patterns243def _validate_version_with_pattern(244    current_version: str,245    version_pattern: str,246    is_new_pattern : bool,247) -> None:248    """Provoke ValueError if version_pattern and current_version are not compatible."""249    try:250        if is_new_pattern:251            v2version.parse_version_info(current_version, version_pattern)252        else:253            v1version.parse_version_info(current_version, version_pattern)254    except version.PatternError:255        errmsg = (256            "Invalid configuration. "257            f"current_version='{current_version}' is invalid for "258            f"version_pattern='{version_pattern}'"259        )260        raise ValueError(errmsg)261    if is_new_pattern:262        invalid_chars = re.search(r"([\s]+)", version_pattern)263        if invalid_chars:264            errmsg = (265                f"Invalid character(s) '{invalid_chars.group(1)}'"266                f' in version_pattern = "{version_pattern}"'267            )268            raise ValueError(errmsg)269        if not v2version.is_valid_week_pattern(version_pattern):270            errmsg = f"Invalid week number pattern: {version_pattern}"271            raise ValueError(errmsg)272def _parse_config(raw_cfg: RawConfig) -> Config:273    """Parse configuration which was loaded from an .ini/.cfg or .toml file."""274    commit_message: str = raw_cfg.get('commit_message', DEFAULT_COMMIT_MESSAGE)275    commit_message = raw_cfg['commit_message'] = commit_message.strip("'\" ")276    current_version: str = raw_cfg['current_version']277    current_version = raw_cfg['current_version'] = current_version.strip("'\" ")278    version_pattern: str = raw_cfg['version_pattern']279    version_pattern = raw_cfg['version_pattern'] = version_pattern.strip("'\" ")280    is_new_pattern = "{" not in version_pattern and "}" not in version_pattern281    _validate_version_with_pattern(current_version, version_pattern, is_new_pattern)282    pep440_version = version.to_pep440(current_version)283    file_patterns = _compile_file_patterns(raw_cfg, is_new_pattern)284    commit = raw_cfg['commit']285    tag    = raw_cfg['tag']286    push   = raw_cfg['push']287    if tag is None:288        tag = raw_cfg['tag'] = False289    if push is None:290        push = raw_cfg['push'] = False291    if tag and not commit:292        raise ValueError("commit=True required if tag=True")293    if push and not commit:294        raise ValueError("commit=True required if push=True")295    cfg = Config(296        current_version=current_version,297        version_pattern=version_pattern,298        pep440_version=pep440_version,299        commit_message=commit_message,300        commit=commit,301        tag=tag,302        push=push,303        is_new_pattern=is_new_pattern,304        file_patterns=file_patterns,305    )306    logger.debug(_debug_str(cfg))307    return cfg308def _parse_current_version_default_pattern(raw_cfg: RawConfig, raw_cfg_text: str) -> str:309    is_config_section = False310    for line in raw_cfg_text.splitlines():311        if is_config_section and line.startswith("current_version"):312            current_version: str = raw_cfg['current_version']313            version_pattern: str = raw_cfg['version_pattern']314            return line.replace(current_version, version_pattern)315        if line.strip() == "[pycalver]":316            is_config_section = True317        elif line.strip() == "[bumpver]":318            is_config_section = True319        elif line.strip() == "[tool.bumpver]":320            is_config_section = True321        elif line and line[0] == "[" and line[-1] == "]":322            is_config_section = False323    raise ValueError("Could not parse 'current_version'")324def _set_raw_config_defaults(raw_cfg: RawConfig) -> None:325    if 'version_pattern' not in raw_cfg:326        raise TypeError("Missing version_pattern")327    elif not isinstance(raw_cfg['version_pattern'], str):328        err = f"Invalid type for version_pattern = {raw_cfg['version_pattern']}"329        raise TypeError(err)330    if 'current_version' not in raw_cfg:331        raise ValueError("Missing 'current_version' configuration")332    elif not isinstance(raw_cfg['current_version'], str):333        err = f"Invalid type for current_version = {raw_cfg['current_version']}"334        raise TypeError(err)335    if 'file_patterns' not in raw_cfg:336        raw_cfg['file_patterns'] = {}337def _parse_raw_config(ctx: ProjectContext) -> RawConfig:338    with ctx.config_filepath.open(mode="rt", encoding="utf-8") as fobj:339        if ctx.config_format == 'toml':340            raw_cfg = _parse_toml(fobj)341        elif ctx.config_format == 'cfg':342            raw_cfg = _parse_cfg(fobj)343        else:344            err_msg = (345                f"Invalid config_format='{ctx.config_format}'."346                "Supported formats are 'setup.cfg' and 'pyproject.toml'"347            )348            raise RuntimeError(err_msg)349    if ctx.config_rel_path not in raw_cfg['file_patterns']:350        with ctx.config_filepath.open(mode="rt", encoding="utf-8") as fobj:351            raw_cfg_text = fobj.read()352        # NOTE (mb 2020-09-19): By default we always add353        #   a pattern for the config section itself.354        raw_version_pattern = _parse_current_version_default_pattern(raw_cfg, raw_cfg_text)355        raw_cfg['file_patterns'][ctx.config_rel_path] = [raw_version_pattern]356    return raw_cfg357def parse(ctx: ProjectContext, cfg_missing_ok: bool = False) -> MaybeConfig:358    """Parse config file if available."""359    if ctx.config_filepath.exists():360        try:361            raw_cfg = _parse_raw_config(ctx)362            return _parse_config(raw_cfg)363        except (TypeError, ValueError) as ex:364            logger.warning(f"Couldn't parse {ctx.config_rel_path}: {str(ex)}")365            return None366    elif cfg_missing_ok:367        return None368    else:369        logger.warning(f"File not found: {ctx.config_rel_path}")370        return None371def init(372    project_path  : typ.Union[str, pl.Path, None] = ".",373    cfg_missing_ok: bool = False,374) -> typ.Tuple[ProjectContext, MaybeConfig]:375    ctx = init_project_ctx(project_path)376    cfg = parse(ctx, cfg_missing_ok)377    return (ctx, cfg)378DEFAULT_CONFIGPARSER_BASE_TMPL = """379[bumpver]380current_version = "{initial_version}"381version_pattern = "YYYY.BUILD[-TAG]"382commit_message = "bump version {{old_version}} -> {{new_version}}"383commit = True384tag = True385push = True386[bumpver:file_patterns]387""".lstrip()388DEFAULT_CONFIGPARSER_SETUP_CFG_STR = """389setup.cfg =390    current_version = "{version}"391""".lstrip()392DEFAULT_CONFIGPARSER_SETUP_PY_STR = """393setup.py =394    "{version}"395    "{pep440_version}"396""".lstrip()397DEFAULT_CONFIGPARSER_README_RST_STR = """398README.rst =399    {version}400    {pep440_version}401""".lstrip()402DEFAULT_CONFIGPARSER_README_MD_STR = """403README.md =404    {version}405    {pep440_version}406""".lstrip()407DEFAULT_PYPROJECT_TOML_BASE_TMPL = """408[tool.bumpver]409current_version = "{initial_version}"410version_pattern = "YYYY.BUILD[-TAG]"411commit_message = "bump version {{old_version}} -> {{new_version}}"412commit = true413tag = true414push = true415[tool.bumpver.file_patterns]416""".lstrip()417DEFAULT_BUMPVER_TOML_BASE_TMPL = """418[bumpver]419current_version = "{initial_version}"420version_pattern = "YYYY.BUILD[-TAG]"421commit_message = "bump version {{old_version}} -> {{new_version}}"422commit = true423tag = true424push = true425[bumpver.file_patterns]426""".lstrip()427DEFAULT_TOML_PYCALVER_STR = """428"pycalver.toml" = [429    'current_version = "{version}"',430]431""".lstrip()432DEFAULT_TOML_BUMPVER_STR = """433"bumpver.toml" = [434    'current_version = "{version}"',435]436""".lstrip()437DEFAULT_TOML_PYPROJECT_STR = """438"pyproject.toml" = [439    'current_version = "{version}"',440]441""".lstrip()442DEFAULT_TOML_SETUP_PY_STR = """443"setup.py" = [444    "{version}",445    "{pep440_version}",446]447""".lstrip()448DEFAULT_TOML_README_RST_STR = """449"README.rst" = [450    "{version}",451    "{pep440_version}",452]453""".lstrip()454DEFAULT_TOML_README_MD_STR = """455"README.md" = [456    "{version}",457    "{pep440_version}",458]459""".lstrip()460def _initial_version() -> str:461    return dt.datetime.utcnow().strftime("%Y.1001-alpha")462def _initial_version_pep440() -> str:463    return dt.datetime.utcnow().strftime("%Y.1001a0")464def default_config(ctx: ProjectContext) -> str:465    """Generate initial default config."""466    fmt = ctx.config_format467    if fmt == 'cfg':468        base_tmpl = DEFAULT_CONFIGPARSER_BASE_TMPL469        default_pattern_strs_by_filename = {470            "setup.cfg" : DEFAULT_CONFIGPARSER_SETUP_CFG_STR,471            "setup.py"  : DEFAULT_CONFIGPARSER_SETUP_PY_STR,472            "README.rst": DEFAULT_CONFIGPARSER_README_RST_STR,473            "README.md" : DEFAULT_CONFIGPARSER_README_MD_STR,474        }475    elif fmt == 'toml':476        if ctx.config_filepath.name == "pyproject.toml":477            base_tmpl = DEFAULT_PYPROJECT_TOML_BASE_TMPL478        else:479            base_tmpl = DEFAULT_BUMPVER_TOML_BASE_TMPL480        default_pattern_strs_by_filename = {481            "pyproject.toml": DEFAULT_TOML_PYPROJECT_STR,482            "pycalver.toml" : DEFAULT_TOML_PYCALVER_STR,483            "bumpver.toml"  : DEFAULT_TOML_BUMPVER_STR,484            "setup.py"      : DEFAULT_TOML_SETUP_PY_STR,485            "README.rst"    : DEFAULT_TOML_README_RST_STR,486            "README.md"     : DEFAULT_TOML_README_MD_STR,487        }488    else:489        raise ValueError(f"Invalid config_format='{fmt}', must be either 'toml' or 'cfg'.")490    cfg_str = base_tmpl.format(initial_version=_initial_version())491    for filename, default_str in default_pattern_strs_by_filename.items():492        if (ctx.path / filename).exists():493            cfg_str += default_str494    has_config_file = any((ctx.path / fn).exists() for fn in SUPPORTED_CONFIGS)495    if not has_config_file:496        if ctx.config_format == 'cfg':497            cfg_str += DEFAULT_CONFIGPARSER_SETUP_CFG_STR498        if ctx.config_format == 'toml':499            cfg_str += DEFAULT_TOML_BUMPVER_STR500    cfg_str += "\n"501    return cfg_str502def write_content(ctx: ProjectContext) -> None:503    """Update project config file with initial default config."""504    fobj: typ.IO[str]505    cfg_content = default_config(ctx)506    if ctx.config_filepath.exists():507        cfg_content = "\n" + cfg_content508    with ctx.config_filepath.open(mode="at", encoding="utf-8") as fobj:509        fobj.write(cfg_content)...test_rewrite.py
Source:test_rewrite.py  
1# -*- coding: utf-8 -*-2from __future__ import division3from __future__ import print_function4from __future__ import absolute_import5from __future__ import unicode_literals6import re7import copy8from test import util9from bumpver import config10from bumpver import rewrite11from bumpver import v1rewrite12from bumpver import v1version13from bumpver import v2rewrite14from bumpver import v2version15from bumpver import v1patterns16from bumpver import v2patterns17# pylint:disable=protected-access ; allowed for test code18# Fix for Python<3.719# https://stackoverflow.com/a/56935186/6299720copy._deepcopy_dispatch[type(re.compile(''))] = lambda r, _: r21REWRITE_FIXTURE = """22# SPDX-License-Identifier: MIT23__version__ = "v201809.0002-beta"24"""25def test_v1_rewrite_lines_basic():26    pattern   = v1patterns.compile_pattern("{pycalver}", '__version__ = "{pycalver}"')27    new_vinfo = v1version.parse_version_info("v201911.0003")28    old_lines = REWRITE_FIXTURE.splitlines()29    new_lines = v1rewrite.rewrite_lines([pattern], new_vinfo, old_lines)30    assert len(new_lines) == len(old_lines)31    assert "v201911.0003" not in "\n".join(old_lines)32    assert "v201911.0003" in "\n".join(new_lines)33def test_v1_rewrite_lines():34    version_pattern = "{pycalver}"35    new_vinfo       = v1version.parse_version_info("v201811.0123-beta", version_pattern)36    patterns        = [v1patterns.compile_pattern(version_pattern, '__version__ = "{pycalver}"')]37    lines           = v1rewrite.rewrite_lines(patterns, new_vinfo, ['__version__ = "v201809.0002-beta"'])38    assert lines == ['__version__ = "v201811.0123-beta"']39    patterns = [v1patterns.compile_pattern(version_pattern, '__version__ = "{pep440_version}"')]40    lines    = v1rewrite.rewrite_lines(patterns, new_vinfo, ['__version__ = "201809.2b0"'])41    assert lines == ['__version__ = "201811.123b0"']42def test_v2_rewrite_lines():43    version_pattern = "vYYYY0M.BUILD[-TAG]"44    new_vinfo       = v2version.parse_version_info("v201811.0123-beta", version_pattern)45    patterns        = [v2patterns.compile_pattern(version_pattern, '__version__ = "{version}"')]46    lines           = v2rewrite.rewrite_lines(patterns, new_vinfo, ['__version__ = "v201809.0002-alpha"   '])47    assert lines == ['__version__ = "v201811.0123-beta"   ']48    lines = v2rewrite.rewrite_lines(49        patterns, new_vinfo, ['__version__ = "v201809.0002-alpha"    # comment']50    )51    assert lines == ['__version__ = "v201811.0123-beta"    # comment']52    patterns  = [v2patterns.compile_pattern(version_pattern, '__version__ = "YYYY0M.BLD[PYTAGNUM]"')]53    old_lines = ['__version__ = "201809.2a0"']54    lines     = v2rewrite.rewrite_lines(patterns, new_vinfo, old_lines)55    assert lines == ['__version__ = "201811.123b0"']56def test_v1_rewrite_final():57    # Patterns written with {release_tag} placeholder preserve58    # the release tag even if the new version is -final59    pattern = v1patterns.compile_pattern(60        "v{year}{month}.{build_no}-{release_tag}",61        '__version__ = "v{year}{month}.{build_no}-{release_tag}"',62    )63    new_vinfo = v1version.parse_version_info("v201911.0003")64    old_lines = REWRITE_FIXTURE.splitlines()65    new_lines = v1rewrite.rewrite_lines([pattern], new_vinfo, old_lines)66    assert len(new_lines) == len(old_lines)67    assert "v201911.0003" not in "\n".join(old_lines)68    assert "None" not in "\n".join(new_lines)69    assert "v201911.0003-final" in "\n".join(new_lines)70def test_iter_file_paths():71    with util.Project(project="a") as project:72        ctx = config.init_project_ctx(project.dir)73        assert ctx74        cfg = config.parse(ctx)75        assert cfg76        _paths_and_patterns = rewrite.iter_path_patterns_items(cfg.file_patterns)77        file_paths          = {str(file_path) for file_path, patterns in _paths_and_patterns}78    assert file_paths == {"bumpver.toml", "README.md"}79def test_iter_file_globs():80    with util.Project(project="b") as project:81        ctx = config.init_project_ctx(project.dir)82        cfg = config.parse(ctx)83        assert cfg84        _paths_and_patterns = rewrite.iter_path_patterns_items(cfg.file_patterns)85        file_paths          = {str(file_path) for file_path, patterns in _paths_and_patterns}86    assert file_paths == {87        "setup.cfg",88        "setup.py",89        "README.rst",90        "src/module_v1/__init__.py",91        "src/module_v2/__init__.py",92    }93def test_error_bad_path():94    with util.Project(project="b") as project:95        ctx = config.init_project_ctx(project.dir)96        cfg = config.parse(ctx)97        assert cfg98        (project.dir / "setup.py").unlink()99        try:100            list(rewrite.iter_path_patterns_items(cfg.file_patterns))101            assert False, "expected IOError"102        except IOError as ex:103            assert "setup.py" in str(ex)104def test_v1_error_bad_pattern():105    with util.Project(project="b") as project:106        ctx = config.init_project_ctx(project.dir)107        cfg = config.parse(ctx)108        assert cfg109        patterns         = copy.deepcopy(cfg.file_patterns)110        original_pattern = patterns["setup.py"][0]111        invalid_pattern  = v1patterns.compile_pattern(112            original_pattern.version_pattern,113            original_pattern.raw_pattern + ".invalid",114        )115        patterns["setup.py"] = [invalid_pattern]116        try:117            old_vinfo = v1version.parse_version_info("v201808.0233")118            new_vinfo = v1version.parse_version_info("v201809.1234")119            list(v1rewrite.diff(old_vinfo, new_vinfo, patterns))120            assert False, "expected rewrite.NoPatternMatch"121        except rewrite.NoPatternMatch as ex:122            assert "setup.py" in str(ex)123OPTIONAL_RELEASE_FIXTURE = """124# SPDX-License-Identifier: BSD125__version__ = "2018.0002-beta"126"""127def test_v1_optional_release():128    version_pattern = "{year}.{build_no}{release}"129    new_vinfo       = v1version.parse_version_info("2019.0003", version_pattern)130    raw_pattern = '__version__ = "{year}.{build_no}{release}"'131    pattern     = v1patterns.compile_pattern(version_pattern, raw_pattern)132    old_lines = OPTIONAL_RELEASE_FIXTURE.splitlines()133    new_lines = v1rewrite.rewrite_lines([pattern], new_vinfo, old_lines)134    assert len(new_lines) == len(old_lines)135    assert "2019.0003" not in "\n".join(old_lines)136    assert "2019.0003" in "\n".join(new_lines)137    assert '__version__ = "2019.0003"' in "\n".join(new_lines)138    new_vinfo = v1version.parse_version_info("2019.0004-beta", version_pattern)139    new_lines = v1rewrite.rewrite_lines([pattern], new_vinfo, old_lines)140    # make sure optional release tag is added back on141    assert len(new_lines) == len(old_lines)142    assert "2019.0004-beta" not in "\n".join(old_lines)143    assert "2019.0004-beta" in "\n".join(new_lines)144    assert '__version__ = "2019.0004-beta"' in "\n".join(new_lines)145def test_v2_optional_release():146    version_pattern = "YYYY.BUILD[-TAG]"147    new_vinfo       = v2version.parse_version_info("2019.0003", version_pattern)148    raw_pattern = '__version__ = "YYYY.BUILD[-TAG]"'149    pattern     = v2patterns.compile_pattern(version_pattern, raw_pattern)150    old_lines = OPTIONAL_RELEASE_FIXTURE.splitlines()151    new_lines = v2rewrite.rewrite_lines([pattern], new_vinfo, old_lines)152    assert len(new_lines) == len(old_lines)153    assert "2019.0003" not in "\n".join(old_lines)154    assert "2019.0003" in "\n".join(new_lines)155    assert '__version__ = "2019.0003"' in "\n".join(new_lines)156    new_vinfo = v2version.parse_version_info("2019.0004-beta", version_pattern)157    new_lines = v2rewrite.rewrite_lines([pattern], new_vinfo, old_lines)158    # make sure optional release tag is added back on159    assert len(new_lines) == len(old_lines)160    assert "2019.0004-beta" not in "\n".join(old_lines)161    assert "2019.0004-beta" in "\n".join(new_lines)162    assert '__version__ = "2019.0004-beta"' in "\n".join(new_lines)163def test_v1_iter_rewritten():164    version_pattern = "{year}{build}{release}"165    new_vinfo       = v1version.parse_version_info("2018.0123", version_pattern)166    init_pattern = v1patterns.compile_pattern(167        version_pattern, '__version__ = "{year}{build}{release}"'168    )169    file_patterns   = {"src/bumpver/__init__.py": [init_pattern]}170    rewritten_datas = v1rewrite.iter_rewritten(file_patterns, new_vinfo)171    rfd             = list(rewritten_datas)[0]172    expected        = [173        "# This file is part of the bumpver project",174        "# https://github.com/mbarkhau/bumpver",175        "#",176        "# Copyright (c) 2018-2022 Manuel Barkhau (mbarkhau@gmail.com) - MIT License",177        "# SPDX-License-Identifier: MIT",178        '"""BumpVer: A CLI program for versioning."""',179        '',180        '__version__ = "2018.0123"',181        '',182    ]183    assert rfd.new_lines == expected184def test_v2_iter_rewritten():185    version_pattern = "YYYY.BUILD[-TAG]"186    new_vinfo       = v2version.parse_version_info("2018.0123", version_pattern)187    file_patterns = {188        "src/bumpver/__init__.py": [189            v2patterns.compile_pattern(version_pattern, '__version__ = "YYYY.BUILD[-TAG]"'),190        ]191    }192    rewritten_datas = v2rewrite.iter_rewritten(file_patterns, new_vinfo)193    rfd             = list(rewritten_datas)[0]194    expected        = [195        "# This file is part of the bumpver project",196        "# https://github.com/mbarkhau/bumpver",197        "#",198        "# Copyright (c) 2018-2022 Manuel Barkhau (mbarkhau@gmail.com) - MIT License",199        "# SPDX-License-Identifier: MIT",200        '"""BumpVer: A CLI program for versioning."""',201        '',202        '__version__ = "2018.0123"',203        '',204    ]205    assert rfd.new_lines == expected206def test_v1_diff():207    version_pattern = "{year}{build}{release}"208    raw_pattern     = '__version__ = "{year}{build}{release}"'209    pattern         = v1patterns.compile_pattern(version_pattern, raw_pattern)210    file_patterns   = {"src/bumpver/__init__.py": [pattern]}211    old_vinfo = v1version.parse_version_info("v201809.0123")212    new_vinfo = v1version.parse_version_info("v201911.1124")213    assert new_vinfo > old_vinfo214    old_vinfo = v1version.parse_version_info("2018.0123", version_pattern)215    new_vinfo = v1version.parse_version_info("2019.1124", version_pattern)216    diff_str = v1rewrite.diff(old_vinfo, new_vinfo, file_patterns)217    lines    = diff_str.split("\n")218    assert lines[:2] == [219        "--- src/bumpver/__init__.py",220        "+++ src/bumpver/__init__.py",221    ]222    assert lines[6].startswith('-__version__ = "20')223    assert lines[7].startswith('+__version__ = "20')224    assert not lines[6].startswith('-__version__ = "2018.0123"')225    assert lines[7] == '+__version__ = "2019.1124"'226    raw_pattern   = "Copyright (c) 2018-{year}"227    pattern       = v1patterns.compile_pattern(version_pattern, raw_pattern)228    file_patterns = {'LICENSE': [pattern]}229    diff_str      = v1rewrite.diff(old_vinfo, new_vinfo, file_patterns)230    lines = diff_str.split("\n")231    assert lines[3].startswith("-MIT License Copyright (c) 2018-20")232    assert lines[4].startswith("+MIT License Copyright (c) 2018-2019")233def test_v2_diff():234    version_pattern = "YYYY.BUILD[-TAG]"235    raw_pattern     = '__version__ = "YYYY.BUILD[-TAG]"'236    pattern         = v2patterns.compile_pattern(version_pattern, raw_pattern)237    file_patterns   = {"src/bumpver/__init__.py": [pattern]}238    old_vinfo = v2version.parse_version_info("2018.0123", version_pattern)239    new_vinfo = v2version.parse_version_info("2019.1124", version_pattern)240    diff_str = v2rewrite.diff(old_vinfo, new_vinfo, file_patterns)241    lines    = diff_str.split("\n")242    assert lines[:2] == [243        "--- src/bumpver/__init__.py",244        "+++ src/bumpver/__init__.py",245    ]246    assert lines[6].startswith('-__version__ = "20')247    assert lines[7].startswith('+__version__ = "20')248    assert not lines[6].startswith('-__version__ = "2018.0123"')249    assert lines[7] == '+__version__ = "2019.1124"'250    raw_pattern   = "Copyright (c) 2018-YYYY"251    pattern       = v2patterns.compile_pattern(version_pattern, raw_pattern)252    file_patterns = {'LICENSE': [pattern]}253    diff_str      = v2rewrite.diff(old_vinfo, new_vinfo, file_patterns)254    lines = diff_str.split("\n")255    assert lines[3].startswith("-MIT License Copyright (c) 2018-20")256    assert lines[4].startswith("+MIT License Copyright (c) 2018-2019")257def test_remove_regex_chars():258    version_pattern = "YYYY.BUILD[-TAG]"259    new_vinfo       = v2version.parse_version_info("2018.0123-beta", version_pattern)260    patterns        = [v2patterns.compile_pattern(version_pattern, '^__version__ = "{version}"')]261    lines           = v2rewrite.rewrite_lines(patterns, new_vinfo, ['__version__ = "2018.0002-alpha"   '])...app_version_control.py
Source:app_version_control.py  
1import fileinput2def replace_line(file_name, what_to_find, what_to_replace):3    with fileinput.FileInput(file_name, inplace=True) as f:4        for line in f:5            if line.find(what_to_find) > -1:6                print(what_to_replace)7            else:8                print(line, end='')9def version_inc_inf(version: str) -> str:10    segs = version.split('.')11    seg1 = int(segs[0])12    seg2 = int(segs[1])13    seg3 = int(segs[2])14    seg3 += 115    return f'{seg1}.{seg2}.{seg3}'16def version_inc_upto10(version: str) -> str:17    segs = version.split('.')18    seg1 = int(segs[0])19    seg2 = int(segs[1])20    seg3 = int(segs[2])21    seg3 += 122    if seg3 <= 9:23        return f'{seg1}.{seg2}.{seg3}'24    else:25        seg2 += 126        seg3 -= 1027    if seg2 <= 9:28        return f'{seg1}.{seg2}.{seg3}'29    else:30        seg1 += 131        seg2 -= 1032    return f'{seg1}.{seg2}.{seg3}'33def version_inc_upto100(version: str) -> str:34    segs = version.split('.')35    seg1 = int(segs[0])36    seg2 = int(segs[1])37    seg3 = int(segs[2])38    seg3 += 139    if seg3 <= 99:40        return f'{seg1}.{seg2}.{seg3}'41    else:42        seg2 += 143        seg3 -= 10044    if seg2 <= 99:45        return f'{seg1}.{seg2}.{seg3}'46    else:47        seg1 += 148        seg2 -= 10049    return f'{seg1}.{seg2}.{seg3}'50VERSION_PATTERN = '__app_version ='51def bump_version_inf(curr_version, file_name, line_pattern=VERSION_PATTERN):52    new_ver = version_inc_inf(curr_version)53    replace_line(file_name, line_pattern, VERSION_PATTERN + ' "' + new_ver + '"')54    return new_ver55def bump_version_upto10(curr_version, file_name, line_pattern=VERSION_PATTERN):56    new_ver = version_inc_upto10(curr_version)57    replace_line(file_name, line_pattern, VERSION_PATTERN + ' "' + new_ver + '"')58    return new_ver59def bump_version_upto100(curr_version, file_name, line_pattern=VERSION_PATTERN):60    new_ver = version_inc_upto100(curr_version)61    replace_line(file_name, line_pattern, VERSION_PATTERN + ' "' + new_ver + '"')62    return new_ver...Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!
