Source code for semantic_release.settings
"""Helpers to read settings from setup.cfg or pyproject.toml
"""
import configparser
import importlib
import logging
import os
from collections import UserDict
from functools import wraps
from os import getcwd
from typing import Callable, List
import tomlkit
from tomlkit.exceptions import TOMLKitError
from .errors import ImproperConfigurationError
logger = logging.getLogger(__name__)
def _config():
cwd = getcwd()
ini_paths = [
os.path.join(os.path.dirname(__file__), "defaults.cfg"),
os.path.join(cwd, "setup.cfg"),
]
ini_config = _config_from_ini(ini_paths)
toml_path = os.path.join(cwd, "pyproject.toml")
toml_config = _config_from_pyproject(toml_path)
# Cast to a UserDict so that we can mock the get() method.
return UserDict({**ini_config, **toml_config})
def _config_from_ini(paths):
parser = configparser.ConfigParser()
parser.read(paths)
flags = {
"changelog_capitalize",
"changelog_scope",
"use_textual_changelog_sections",
"check_build_status",
"commit_version_number",
"ignore_token_for_push",
"patch_without_tag",
"major_on_zero",
"remove_dist",
"upload_to_pypi",
"upload_to_repository",
"upload_to_release",
"tag_commit",
}
# Iterate through the sections so that default values are applied
# correctly. See:
# https://stackoverflow.com/questions/1773793/convert-configparser-items-to-dictionary
config = {}
for key, _ in parser.items("semantic_release"):
if key in flags:
config[key] = parser.getboolean("semantic_release", key)
else:
config[key] = parser.get("semantic_release", key)
return config
def _config_from_pyproject(path):
if os.path.isfile(path):
try:
with open(path, "r") as f:
pyproject = tomlkit.loads(f.read())
if pyproject:
return pyproject.get("tool", {}).get("semantic_release", {})
except TOMLKitError as e:
logger.warning(f"Could not decode pyproject.toml: {e}")
return {}
config = _config()
[docs]def current_commit_parser() -> Callable:
"""Get the currently-configured commit parser
:raises ImproperConfigurationError: if ImportError or AttributeError is raised
:returns: Commit parser
"""
try:
# All except the last part is the import path
parts = config.get("commit_parser").split(".")
module = ".".join(parts[:-1])
# The final part is the name of the parse function
return getattr(importlib.import_module(module), parts[-1])
except (ImportError, AttributeError) as error:
raise ImproperConfigurationError(f'Unable to import parser "{error}"')
[docs]def current_changelog_components() -> List[Callable]:
"""Get the currently-configured changelog components
:raises ImproperConfigurationError: if ImportError or AttributeError is raised
:returns: List of component functions
"""
component_paths = config.get("changelog_components").split(",")
components = list()
for path in component_paths:
try:
# All except the last part is the import path
parts = path.split(".")
module = ".".join(parts[:-1])
# The final part is the name of the component function
components.append(getattr(importlib.import_module(module), parts[-1]))
except (ImportError, AttributeError) as error:
raise ImproperConfigurationError(
f'Unable to import changelog component "{path}"'
)
return components
[docs]def overload_configuration(func):
"""This decorator gets the content of the "define" array and edits "config"
according to the pairs of key/value.
"""
@wraps(func)
def wrap(*args, **kwargs):
if "define" in kwargs:
for defined_param in kwargs["define"]:
pair = defined_param.split("=", maxsplit=1)
if len(pair) == 2:
config[str(pair[0])] = pair[1]
return func(*args, **kwargs)
return wrap