0
# Configuration Management
1
2
Load and manage towncrier configuration from TOML files with validation, defaults, and flexible discovery.
3
4
## Capabilities
5
6
### Configuration Loading
7
8
Load configuration from directories or specific files with automatic discovery.
9
10
```python { .api }
11
def load_config_from_options(
12
directory: str | None,
13
config_path: str | None
14
) -> tuple[str, Config]:
15
"""
16
Load configuration from directory or file.
17
18
Args:
19
directory: Directory to search for config (None for current)
20
config_path: Explicit config file path (None for auto-discovery)
21
22
Returns:
23
tuple: (base_directory, config_object)
24
25
Raises:
26
ConfigError: If configuration is invalid or not found
27
"""
28
29
def load_config(directory: str) -> Config | None:
30
"""
31
Load configuration from a directory.
32
33
Args:
34
directory: Directory to search for towncrier configuration
35
36
Returns:
37
Config | None: Configuration object or None if not found
38
"""
39
40
def traverse_for_config(path: str | None) -> tuple[str, Config]:
41
"""
42
Search for configuration in current and parent directories.
43
44
Args:
45
path: Starting directory (None for current directory)
46
47
Returns:
48
tuple: (base_directory, config_object)
49
50
Raises:
51
ConfigError: If no configuration found in directory tree
52
"""
53
```
54
55
### File Operations
56
57
Load and parse TOML configuration files.
58
59
```python { .api }
60
def load_config_from_file(directory: str, config_file: str) -> Config:
61
"""
62
Load configuration from a specific file.
63
64
Args:
65
directory: Base directory for relative paths
66
config_file: Path to configuration file
67
68
Returns:
69
Config: Parsed configuration object
70
71
Raises:
72
ConfigError: If file cannot be loaded or parsed
73
"""
74
75
def load_toml_from_file(config_file: str) -> Mapping[str, Any]:
76
"""
77
Load raw TOML data from file.
78
79
Args:
80
config_file: Path to TOML file
81
82
Returns:
83
Mapping[str, Any]: Raw TOML data
84
85
Raises:
86
ConfigError: If file cannot be read or parsed
87
"""
88
89
def parse_toml(base_path: str, config: Mapping[str, Any]) -> Config:
90
"""
91
Parse TOML data into Config object.
92
93
Args:
94
base_path: Base directory for resolving relative paths
95
config: Raw TOML configuration data
96
97
Returns:
98
Config: Validated configuration object
99
100
Raises:
101
ConfigError: If configuration is invalid
102
"""
103
```
104
105
## Configuration Object
106
107
### Config Class
108
109
The main configuration dataclass containing all towncrier settings.
110
111
```python { .api }
112
@dataclasses.dataclass
113
class Config:
114
"""Complete towncrier configuration."""
115
116
# Required fields
117
sections: Mapping[str, str]
118
types: Mapping[str, Mapping[str, Any]]
119
template: str | tuple[str, str]
120
start_string: str
121
122
# Optional fields with defaults
123
package: str = ""
124
package_dir: str = "."
125
single_file: bool = True
126
filename: str = "NEWS.rst"
127
directory: str | None = None
128
version: str | None = None
129
name: str = ""
130
title_format: str | Literal[False] = ""
131
issue_format: str | None = None
132
underlines: Sequence[str] = ("=", "-", "~")
133
wrap: bool = False
134
all_bullets: bool = True
135
orphan_prefix: str = "+"
136
create_eof_newline: bool = True
137
create_add_extension: bool = True
138
ignore: list[str] | None = None
139
issue_pattern: str = ""
140
```
141
142
### ConfigError Exception
143
144
Exception raised for configuration validation errors.
145
146
```python { .api }
147
class ConfigError(ClickException):
148
"""Configuration validation or loading error."""
149
150
def __init__(self, *args: str, **kwargs: str):
151
"""
152
Initialize configuration error.
153
154
Args:
155
*args: Error message components
156
**kwargs: Additional error context
157
failing_option: The configuration option that failed
158
"""
159
self.failing_option = kwargs.get("failing_option")
160
super().__init__(*args)
161
```
162
163
## Configuration Discovery
164
165
### File Discovery Order
166
167
Towncrier searches for configuration files in this order:
168
169
1. Explicit `--config` parameter
170
2. `towncrier.toml` in current/parent directories
171
3. `pyproject.toml` with `[tool.towncrier]` section
172
4. Continue searching up directory tree
173
174
### Section Locations
175
176
Configuration sections are searched in:
177
178
- `[tool.towncrier]` in `pyproject.toml`
179
- Root level in `towncrier.toml`
180
181
## Configuration Options
182
183
### Core Settings
184
185
```toml
186
[tool.towncrier]
187
package = "mypackage" # Package name for version detection
188
package_dir = "src" # Directory containing package
189
filename = "CHANGELOG.md" # Output changelog filename
190
directory = "news" # Fragment directory (alternative to package-based)
191
```
192
193
### Template Configuration
194
195
```toml
196
template = "path/to/template.j2" # Local template file
197
template = "mypackage:template.j2" # Package resource template
198
start_string = "<!-- towncrier release notes start -->" # Insertion marker
199
```
200
201
### Fragment Types
202
203
```toml
204
[[tool.towncrier.type]]
205
directory = "feature"
206
name = "Features"
207
showcontent = true
208
209
[[tool.towncrier.type]]
210
directory = "bugfix"
211
name = "Bugfixes"
212
showcontent = true
213
214
[[tool.towncrier.type]]
215
directory = "doc"
216
name = "Improved Documentation"
217
showcontent = true
218
219
[[tool.towncrier.type]]
220
directory = "removal"
221
name = "Deprecations and Removals"
222
showcontent = true
223
224
[[tool.towncrier.type]]
225
directory = "misc"
226
name = "Misc"
227
showcontent = false
228
```
229
230
### Multi-Section Configuration
231
232
```toml
233
[[tool.towncrier.section]]
234
name = "" # Main section
235
path = ""
236
237
[[tool.towncrier.section]]
238
name = "Web"
239
path = "web"
240
241
[[tool.towncrier.section]]
242
name = "Core"
243
path = "core"
244
```
245
246
### Formatting Options
247
248
```toml
249
title_format = "{name} {version} ({date})"
250
issue_format = "`#{issue} <https://github.com/user/repo/issues/{issue}>`_"
251
underlines = ["=", "-", "~"]
252
wrap = false
253
all_bullets = true
254
```
255
256
### File Management
257
258
```toml
259
create_eof_newline = true # Add newline at end of fragments
260
create_add_extension = true # Auto-add file extensions
261
orphan_prefix = "+" # Prefix for orphan fragments
262
ignore = ["README.rst"] # Files to ignore in fragment directory
263
```
264
265
## Usage Examples
266
267
### Basic Configuration Loading
268
269
```python
270
from towncrier._settings import load_config_from_options
271
272
# Load from current directory
273
base_dir, config = load_config_from_options(
274
directory=None, # Current directory
275
config_path=None # Auto-discover config file
276
)
277
278
print(f"Base directory: {base_dir}")
279
print(f"Package: {config.package}")
280
print(f"Fragment types: {list(config.types.keys())}")
281
```
282
283
### Custom Configuration File
284
285
```python
286
# Load specific config file
287
base_dir, config = load_config_from_options(
288
directory="/path/to/project",
289
config_path="/path/to/custom-towncrier.toml"
290
)
291
```
292
293
### Configuration Traversal
294
295
```python
296
from towncrier._settings.load import traverse_for_config
297
298
# Search up directory tree
299
base_dir, config = traverse_for_config("/deep/nested/directory")
300
```
301
302
### Template Resolution
303
304
```python
305
# Template can be file path or package resource
306
if isinstance(config.template, tuple):
307
# Package resource: ("package.name", "template.j2")
308
package_name, template_name = config.template
309
print(f"Using template {template_name} from package {package_name}")
310
else:
311
# File path: "path/to/template.j2"
312
print(f"Using template file: {config.template}")
313
```
314
315
## Validation Rules
316
317
### Required Settings
318
319
- At least one fragment type must be defined
320
- Template must be specified (file path or package resource)
321
- Start string must be provided for file insertion
322
323
### Path Resolution
324
325
- Relative paths are resolved against the configuration file directory
326
- Package resources use the format `"package:resource"`
327
- Fragment directories are resolved based on package structure or explicit directory
328
329
### Type Validation
330
331
- Fragment types must have `directory` and `name` fields
332
- `showcontent` is optional (defaults to `true`)
333
- Type directories must be valid filesystem names
334
335
## Error Handling
336
337
Configuration loading handles these error scenarios:
338
339
- **File not found**: Configuration file doesn't exist
340
- **Parse errors**: Invalid TOML syntax
341
- **Validation errors**: Missing required fields or invalid values
342
- **Template errors**: Template file/resource not found
343
- **Permission errors**: Cannot read configuration files
344
- **Path resolution errors**: Invalid relative paths or package resources
345
346
## Integration with CLI
347
348
The configuration system integrates with CLI commands through the `config_option_help` constant:
349
350
```python { .api }
351
config_option_help = (
352
"Pass a custom config file at FILE_PATH. "
353
"Default: towncrier.toml or pyproject.toml file, "
354
"if both files exist, the first will take precedence."
355
)
356
```
357
358
This help text is used across all CLI commands that accept a `--config` option.