0
# Exception Handling
1
2
Comprehensive error hierarchy for handling configuration errors, VCS issues, method validation problems, and other exceptional conditions in versioningit operations.
3
4
## Capabilities
5
6
### Base Exception
7
8
Root exception class for all versioningit-specific errors.
9
10
```python { .api }
11
class Error(Exception):
12
"""Base class of all versioningit-specific errors."""
13
```
14
15
### Configuration Errors
16
17
Errors related to invalid configuration settings and values.
18
19
```python { .api }
20
class ConfigError(Error, ValueError):
21
"""
22
Raised when the versioningit configuration contains invalid settings.
23
24
This includes invalid parameter types, missing required fields,
25
invalid format strings, and other configuration validation failures.
26
"""
27
28
class NotVersioningitError(Error):
29
"""
30
Raised when versioningit is used on a project that does not have
31
versioningit enabled.
32
"""
33
34
class NoConfigFileError(NotVersioningitError):
35
"""
36
Raised when versioningit is used on a project that does not contain a
37
versioningit.toml or pyproject.toml file.
38
"""
39
40
def __init__(self, project_dir: Path) -> None:
41
self.project_dir: Path = project_dir
42
"""The path to the project directory."""
43
44
def __str__(self) -> str:
45
return f"No pyproject.toml or versioningit.toml file in {self.project_dir}"
46
47
class NoConfigSectionError(NotVersioningitError):
48
"""
49
Raised when versioningit is used on a project whose versioningit.toml or
50
pyproject.toml file does not contain a versioningit configuration table.
51
"""
52
53
def __init__(self, config_path: Path) -> None:
54
self.config_path: Path = config_path
55
"""The path to the configuration file."""
56
57
def __str__(self) -> str:
58
return f"versioningit not configured in {self.config_path}"
59
```
60
61
### Method Errors
62
63
Errors related to method loading, validation, and execution.
64
65
```python { .api }
66
class MethodError(Error):
67
"""
68
Raised when a method is invalid or returns an invalid value.
69
70
This includes entry points that don't resolve to callables,
71
methods that return wrong types, and method loading failures.
72
"""
73
```
74
75
### Version Control Errors
76
77
Errors related to VCS operations and repository state.
78
79
```python { .api }
80
class NotVCSError(Error):
81
"""
82
Raised when versioningit is run in a directory that is not under
83
version control or when the relevant VCS program is not installed.
84
"""
85
86
class NoTagError(Error):
87
"""
88
Raised when a tag cannot be found in version control.
89
90
This occurs when the repository has no tags or no tags match
91
the configured patterns.
92
"""
93
```
94
95
### Version Processing Errors
96
97
Errors related to tag and version string processing.
98
99
```python { .api }
100
class InvalidTagError(Error, ValueError):
101
"""
102
Raised by tag2version methods when passed a tag that they cannot work with.
103
104
This includes tags that don't match required patterns or contain
105
invalid version information.
106
"""
107
108
class InvalidVersionError(Error, ValueError):
109
"""
110
Raised by next-version and template-fields methods when passed a
111
version that they cannot work with.
112
113
This includes malformed version strings or versions that don't
114
conform to expected formats.
115
"""
116
```
117
118
### Source Distribution Errors
119
120
Errors related to PKG-INFO file handling and source distributions.
121
122
```python { .api }
123
class NotSdistError(Error):
124
"""
125
Raised when attempting to read a PKG-INFO file from a directory
126
that doesn't have one.
127
128
This typically occurs when fallback mode tries to read version
129
information from a source distribution but no PKG-INFO file exists.
130
"""
131
```
132
133
## Usage Examples
134
135
### Basic Error Handling
136
137
```python
138
from versioningit import get_version
139
from versioningit.errors import (
140
NotVCSError, NoConfigFileError, ConfigError,
141
MethodError, InvalidTagError
142
)
143
144
try:
145
version = get_version()
146
print(f"Version: {version}")
147
except NotVCSError:
148
print("Not in a version control repository")
149
except NoConfigFileError:
150
print("No configuration file found")
151
except ConfigError as e:
152
print(f"Configuration error: {e}")
153
except MethodError as e:
154
print(f"Method error: {e}")
155
except Exception as e:
156
print(f"Unexpected error: {e}")
157
```
158
159
### Handling Configuration Errors
160
161
```python
162
from versioningit import Versioningit
163
from versioningit.errors import ConfigError, NoConfigSectionError
164
165
config = {
166
"vcs": {"method": "git"},
167
"tag2version": {"method": "basic", "rmprefix": 123}, # Wrong type
168
"format": {"invalid-state": "template"} # Invalid state key
169
}
170
171
try:
172
vgit = Versioningit.from_project_dir(config=config)
173
except ConfigError as e:
174
print(f"Invalid configuration: {e}")
175
176
try:
177
vgit = Versioningit.from_project_dir("/path/without/config")
178
except NoConfigSectionError as e:
179
print(f"No versioningit configuration: {e}")
180
print(f"Config file: {e.config_path}")
181
```
182
183
### VCS Error Handling
184
185
```python
186
from versioningit import get_version, get_next_version
187
from versioningit.errors import NotVCSError, NoTagError
188
189
try:
190
version = get_version(fallback=False)
191
except NotVCSError:
192
print("Project must be under version control")
193
194
try:
195
next_version = get_next_version()
196
except NoTagError:
197
print("No tags found in repository")
198
except NotVCSError:
199
print("Not a version control repository")
200
```
201
202
### Method Error Handling
203
204
```python
205
from versioningit.methods import EntryPointSpec, CustomMethodSpec
206
from versioningit.errors import MethodError, ConfigError
207
208
# Entry point not found
209
try:
210
spec = EntryPointSpec(group="versioningit.tag2version", name="nonexistent")
211
method = spec.load(".")
212
except ConfigError as e:
213
print(f"Entry point error: {e}")
214
215
# Custom method not callable
216
try:
217
spec = CustomMethodSpec(
218
module="sys", # sys.path is not callable
219
value="path",
220
module_dir=None
221
)
222
method = spec.load(".")
223
except MethodError as e:
224
print(f"Method not callable: {e}")
225
```
226
227
### Version Processing Errors
228
229
```python
230
from versioningit.basics import basic_tag2version
231
from versioningit.next_version import next_minor_version
232
from versioningit.errors import InvalidTagError, InvalidVersionError
233
234
# Invalid tag
235
try:
236
version = basic_tag2version(
237
tag="not-a-version-tag",
238
params={"regex": r"v(?P<version>\d+\.\d+\.\d+)", "require-match": True}
239
)
240
except InvalidTagError as e:
241
print(f"Tag doesn't match pattern: {e}")
242
243
# Invalid version
244
try:
245
next_ver = next_minor_version(
246
version="not.a.valid.version.string",
247
branch=None,
248
params={}
249
)
250
except InvalidVersionError as e:
251
print(f"Cannot parse version: {e}")
252
```
253
254
### Fallback and Recovery
255
256
```python
257
from versioningit import get_version
258
from versioningit.errors import NotVCSError, NotSdistError
259
260
def get_version_with_fallback(project_dir="."):
261
"""Get version with multiple fallback strategies."""
262
263
# Try VCS first
264
try:
265
return get_version(project_dir, fallback=False)
266
except NotVCSError:
267
pass
268
269
# Try PKG-INFO fallback
270
try:
271
return get_version(project_dir, fallback=True)
272
except NotSdistError:
273
pass
274
275
# Try with default version
276
try:
277
config = {"default-version": "0.0.0.dev0"}
278
return get_version(project_dir, config=config)
279
except Exception:
280
pass
281
282
# Final fallback
283
return "unknown"
284
285
version = get_version_with_fallback()
286
print(f"Version: {version}")
287
```
288
289
### Error Context and Debugging
290
291
```python
292
from versioningit import Versioningit
293
from versioningit.errors import Error
294
import logging
295
296
# Enable debug logging
297
logging.basicConfig(level=logging.DEBUG)
298
299
try:
300
vgit = Versioningit.from_project_dir()
301
report = vgit.run()
302
303
if report.using_default_version:
304
print("Warning: Used default version due to errors")
305
306
print(f"Version: {report.version}")
307
308
except Error as e:
309
print(f"Versioningit error: {type(e).__name__}: {e}")
310
# Debug info available in logs
311
except Exception as e:
312
print(f"Unexpected error: {type(e).__name__}: {e}")
313
raise
314
```
315
316
### Custom Error Handling in Methods
317
318
```python
319
from versioningit.errors import ConfigError, InvalidTagError, MethodError
320
321
def custom_tag2version_method(*, tag: str, params: dict) -> str:
322
"""Custom method with proper error handling."""
323
324
# Validate parameters
325
required_prefix = params.get("required-prefix")
326
if required_prefix and not isinstance(required_prefix, str):
327
raise ConfigError("required-prefix must be a string")
328
329
# Validate tag format
330
if required_prefix and not tag.startswith(required_prefix):
331
raise InvalidTagError(f"Tag {tag!r} must start with {required_prefix!r}")
332
333
# Process tag
334
try:
335
if required_prefix:
336
tag = tag[len(required_prefix):]
337
return tag.lstrip("v")
338
except Exception as e:
339
raise MethodError(f"Failed to process tag {tag!r}: {e}")
340
341
def custom_vcs_method(*, project_dir: Path, params: dict) -> VCSDescription:
342
"""Custom VCS method with error handling."""
343
344
try:
345
# Custom VCS operations
346
result = run_custom_vcs_command(project_dir)
347
return VCSDescription(
348
tag=result["tag"],
349
state=result["state"],
350
branch=result["branch"],
351
fields=result["fields"]
352
)
353
except subprocess.CalledProcessError as e:
354
raise NotVCSError(f"VCS command failed: {e}")
355
except KeyError as e:
356
raise MethodError(f"Missing required VCS result field: {e}")
357
```
358
359
### Exception Hierarchy
360
361
```python
362
# All versioningit exceptions inherit from Error
363
from versioningit.errors import *
364
365
def handle_versioningit_error(e: Exception):
366
"""Handle different types of versioningit errors."""
367
368
if isinstance(e, NotVersioningitError):
369
if isinstance(e, NoConfigFileError):
370
print(f"Missing config file in {e.project_dir}")
371
elif isinstance(e, NoConfigSectionError):
372
print(f"No versioningit config in {e.config_path}")
373
else:
374
print("Project doesn't use versioningit")
375
376
elif isinstance(e, ConfigError):
377
print(f"Configuration problem: {e}")
378
379
elif isinstance(e, MethodError):
380
print(f"Method problem: {e}")
381
382
elif isinstance(e, NotVCSError):
383
print("Not under version control")
384
385
elif isinstance(e, NoTagError):
386
print("No version tags found")
387
388
elif isinstance(e, InvalidTagError):
389
print(f"Invalid tag format: {e}")
390
391
elif isinstance(e, InvalidVersionError):
392
print(f"Invalid version format: {e}")
393
394
elif isinstance(e, NotSdistError):
395
print("No PKG-INFO file found for fallback")
396
397
elif isinstance(e, Error):
398
print(f"Other versioningit error: {e}")
399
400
else:
401
print(f"Non-versioningit error: {e}")
402
```