/
settings.py
139 lines (109 loc) · 4.07 KB
/
settings.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
"""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()
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}"')
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
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