def make_docutils_conf(repo_path: pathlib.Path, templates: Environment) -> List[str]: """ Add configuration for ``Docutils``. :param repo_path: Path to the repository root. :param templates: """ file = PathPlus(repo_path / templates.globals["docs_dir"] / "docutils.conf") file.parent.maybe_make(parents=True) if not file.is_file(): file.write_text('\n'.join([ "[restructuredtext parser]", "tab_width = 4", '', '', ])) conf = ConfigUpdater() conf.read(str(file)) required_sections = ["restructuredtext parser"] for section in required_sections: if section not in conf.sections(): conf.add_section(section) conf["restructuredtext parser"]["tab_width"] = 4 file.write_clean(str(conf)) return [file.relative_to(repo_path).as_posix()]
def merge_existing(self, ini_file: pathlib.Path): """ Merge existing sections in the configuration file into the new configuration. :param ini_file: The existing ``.ini`` file. """ if ini_file.is_file(): existing_config = ConfigUpdater() existing_config.read(str(ini_file)) for section in existing_config.sections_blocks(): if section.name not in self.managed_sections: self._ini.add_section(section)
def bump(self, new_version: Version, commit: Optional[bool], message: str): """ Bump to the given version. :param new_version: :param commit: Whether to commit automatically (:py:obj:`True`) or ask first (:py:obj:`None`). :param message: The commit message. .. versionchanged:: 2021.8.11 Now takes a :class:`packaging.version.Version` rather than a :class:`domdf_python_tools.versions.Version`. """ new_version_str = str(new_version) dulwich_repo = Repo(self.repo.target_repo) if f"v{new_version_str}".encode("UTF-8") in dulwich_repo.refs.as_dict(b"refs/tags"): raise abort(f"The tag 'v{new_version_str}' already exists!") bumpversion_config = self.get_bumpversion_config(str(self.current_version), new_version_str) changed_files = [self.bumpversion_file.relative_to(self.repo.target_repo).as_posix()] for filename in bumpversion_config.keys(): if not os.path.isfile(filename): raise FileNotFoundError(filename) for filename, config in bumpversion_config.items(): self.bump_version_for_file(filename, config) changed_files.append(filename) # Update number in .bumpversion.cfg bv = ConfigUpdater() bv.read(self.bumpversion_file) bv["bumpversion"]["current_version"] = new_version_str self.bumpversion_file.write_clean(str(bv)) commit_message = message.format(current_version=self.current_version, new_version=new_version) click.echo(commit_message) if commit_changed_files( self.repo.target_repo, managed_files=changed_files, commit=commit, message=commit_message.encode("UTF-8"), enable_pre_commit=False, ): tag_create(dulwich_repo, f"v{new_version_str}")
def merge_existing(self, ini_file): if ini_file.is_file(): existing_config = ConfigUpdater() existing_config.read(str(ini_file)) for section in existing_config.sections_blocks(): if section.name == "options.packages.find" and "exclude" in section: all_excludes = ( *section["exclude"].value.splitlines(), *self._ini["options.packages.find"] ["exclude"].value.splitlines(), ) exclude_packages = sorted( filter(bool, set(map(str.strip, all_excludes)))) self._ini["options.packages.find"][ "exclude"] = indent_join(exclude_packages) if section.name not in self.managed_sections: self._ini.add_section(section) elif section.name == "mypy": self.copy_existing_value(section, "incremental") if "options.entry_points" not in self._ini.sections(): self._ini.add_section("options.entry_points") # if self["console_scripts"]: # self._ini["options.entry_points"]["console_scripts"] = self["console_scripts"] # else: if not self._ini["options.entry_points"].options(): self._ini.remove_section("options.entry_points") if self["use_whey"]: self._ini.remove_section("metadata") self._ini.remove_section("options") self._ini.remove_section("options.packages.find") if float(self["mypy_version"]) >= 0.901: self._ini.remove_section("mypy")
def get_bumpversion_config( self, current_version: str, new_version: str, ) -> Dict[str, BumpversionFileConfig]: """ Returns the bumpversion config. :param current_version: :param new_version: """ bv = ConfigUpdater() bv.read(self.bumpversion_file) def default(): return {"search": current_version, "replace": new_version} # populate with the sections which are managed by repo_helper config: Dict[str, BumpversionFileConfig] = { filename: default() for filename in get_bumpversion_filenames(self.repo.templates) } if self.repo.templates.globals["enable_docs"]: config[f"{self.repo.templates.globals['docs_dir']}/index.rst"] = default() for section in bv.sections(): if not section.startswith("bumpversion:file:"): continue section_dict: Dict[str, str] = bv[section].to_dict() config[section[17:]] = dict( search=section_dict.get("search", "{current_version}").format(current_version=current_version), replace=section_dict.get("replace", "{new_version}").format(new_version=new_version), ) return config
def make_formate_toml(repo_path: pathlib.Path, templates: Environment) -> List[str]: """ Add configuration for ``formate``. https://formate.readthedocs.io :param repo_path: Path to the repository root. :param templates: """ known_third_party = set() isort_file = PathPlus(repo_path / ".isort.cfg") formate_file = PathPlus(repo_path / "formate.toml") isort_config = get_isort_config(repo_path, templates) known_third_party.update(isort_config["known_third_party"]) if formate_file.is_file(): formate_config = dom_toml.load(formate_file) else: formate_config = {} # Read the isort config file and get "known_third_party" from there if isort_file.is_file(): isort = ConfigUpdater() isort.read(str(isort_file)) if "settings" in isort.sections() and "known_third_party" in isort["settings"]: known_third_party.update(re.split(r"(\n|,\s*)", isort["settings"]["known_third_party"].value)) isort_file.unlink(missing_ok=True) if "hooks" in formate_config and "isort" in formate_config["hooks"]: if "kwargs" in formate_config["hooks"]["isort"]: known_third_party.update(formate_config["hooks"]["isort"]["kwargs"].get("known_third_party", ())) for existing_key, value in formate_config["hooks"]["isort"]["kwargs"].items(): if existing_key not in isort_config: isort_config[existing_key] = value def normalise_underscore(name: str) -> str: return normalize(name.strip()).replace('-', '_') isort_config["known_third_party"] = sorted(set(filter(bool, map(normalise_underscore, known_third_party)))) hooks = { "dynamic_quotes": 10, "collections-import-rewrite": 20, "yapf": {"priority": 30, "kwargs": {"yapf_style": ".style.yapf"}}, "reformat-generics": 40, "isort": {"priority": 50, "kwargs": isort_config}, "noqa-reformat": 60, "ellipsis-reformat": 70, "squish_stubs": 80, } config = {"indent": '\t', "line_length": 115} formate_config["hooks"] = hooks formate_config["config"] = config formate_file = PathPlus(repo_path / "formate.toml") dom_toml.dump(formate_config, formate_file, encoder=dom_toml.TomlEncoder) return [formate_file.name, isort_file.name]
def merge_existing(self, ini_file): """ Merge existing sections in the configuration file into the new configuration. :param ini_file: The existing ``.ini`` file. """ if ini_file.is_file(): existing_config = ConfigUpdater() existing_config.read(str(ini_file)) for section in existing_config.sections_blocks(): if section.name not in self.managed_sections: self._ini.add_section(section) elif section.name == "coverage:report" and "omit" in section: self._ini["coverage:report"]["omit"] = section["omit"].value elif section.name == "flake8": if "rst-directives" in section: existing_directives = section["rst-directives"].value.splitlines() new_directives = self._ini["flake8"]["rst-directives"].value.splitlines() combined_directives = set(map(str.strip, (*new_directives, *existing_directives))) self._ini["flake8"]["rst-directives"] = indent_join( sorted(filter(bool, combined_directives)) ) if "rst-roles" in section: existing_roles = section["rst-roles"].value.splitlines() new_roles = self._ini["flake8"]["rst-roles"].value.splitlines() combined_roles = set(map(str.strip, (*new_roles, *existing_roles))) self._ini["flake8"]["rst-roles"] = indent_join(sorted(filter(bool, combined_roles))) if "per-file-ignores" in section: combined_ignores = {} # Existing first, so they're always overridden by our new ones for line in section["per-file-ignores"].value.splitlines(): if not line.strip(): continue glob, ignores = line.split(':', 1) combined_ignores[glob.strip()] = ignores.strip() for line in self._ini["flake8"]["per-file-ignores"].value.splitlines(): if not line.strip(): continue glob, ignores = line.split(':', 1) combined_ignores[glob.strip()] = ignores.strip() # Always put tests/* and */*.pyi first combined_ignores_strings = [ f"tests/*: {combined_ignores.pop('tests/*')}", f"*/*.pyi: {combined_ignores.pop('*/*.pyi')}", ] combined_ignores_strings.extend( sorted(filter(bool, (map(": ".join, combined_ignores.items())))) ) self._ini["flake8"]["per-file-ignores"] = indent_join(combined_ignores_strings) elif section.name == "pytest": if "filterwarnings" in section: existing_value = set(map(str.strip, section["filterwarnings"].value.splitlines())) self._ini["pytest"]["filterwarnings"] = indent_join(sorted(filter(bool, existing_value))) if "markers" in section: existing_value = set(map(str.strip, section["markers"].value.splitlines())) self._ini["pytest"]["markers"] = indent_join(sorted(filter(bool, existing_value)))
def ensure_bumpversion(repo_path: pathlib.Path, templates: Environment) -> List[str]: """ Add configuration for ``bumpversion`` to the desired repo. https://pypi.org/project/bumpversion/ :param repo_path: Path to the repository root. :param templates: """ bumpversion_file = PathPlus(repo_path / ".bumpversion.cfg") if not bumpversion_file.is_file(): bumpversion_file.write_lines([ "[bumpversion]", f"current_version = {templates.globals['version']}", "commit = True", "tag = True", ]) bv = ConfigUpdater() bv.read(str(bumpversion_file)) old_sections = [ "bumpversion:file:git_helper.yml", "bumpversion:file:__pkginfo__.py" ] required_sections = { f"bumpversion:file:{filename}" for filename in get_bumpversion_filenames(templates) } if not templates.globals["enable_docs"]: old_sections.append( f"bumpversion:file:{templates.globals['docs_dir']}/index.rst") if not templates.globals["enable_conda"]: old_sections.append(f"bumpversion:file:.github/workflows/conda_ci.yml") if templates.globals["use_whey"]: old_sections.append("bumpversion:file:setup.cfg") for section in old_sections: if section in bv.sections(): bv.remove_section(section) if section in required_sections: required_sections.remove(section) for section in sorted(required_sections): if section not in bv.sections(): bv.add_section(section) init_filename = get_init_filename(templates) if init_filename is not None: init_section = bv[f"bumpversion:file:{init_filename}"] if "search" not in init_section: init_section["search"] = ': str = "{current_version}"' init_section["replace"] = ': str = "{new_version}"' if "bumpversion:file:setup.cfg" in bv.sections(): setup_cfg_section = bv["bumpversion:file:setup.cfg"] if ("search" not in setup_cfg_section or ("search" in setup_cfg_section and setup_cfg_section["search"].value == "name = {current_version}")): setup_cfg_section["search"] = "version = {current_version}" setup_cfg_section["replace"] = "version = {new_version}" if "bumpversion:file:pyproject.toml" in bv.sections(): pp_toml_section = bv["bumpversion:file:pyproject.toml"] if "search" not in pp_toml_section: pp_toml_section["search"] = 'version = "{current_version}"' pp_toml_section["replace"] = 'version = "{new_version}"' bv["bumpversion"]["current_version"] = templates.globals["version"] bv["bumpversion"]["commit"] = "True" bv["bumpversion"]["tag"] = "True" bumpversion_file.write_clean(str(bv)) return [bumpversion_file.name]