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!!