Skip to content

Commit 55dc6ea

Browse files
authored
Merge pull request #65 from ArcanaFramework/exception-type-cleanup
Removed direct references to FileFormatsError in favour of more specific errors
2 parents feeadef + 1afa94d commit 55dc6ea

File tree

11 files changed

+77
-57
lines changed

11 files changed

+77
-57
lines changed

fileformats/core/classifier.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from .utils import classproperty
2-
from .exceptions import FileFormatsError
2+
from .exceptions import FormatDefinitionError
33

44

55
class Classifier:
@@ -17,7 +17,7 @@ def namespace(cls):
1717
namespace"""
1818
module_parts = cls.__module__.split(".")
1919
if module_parts[0] != "fileformats":
20-
raise FileFormatsError(
20+
raise FormatDefinitionError(
2121
f"Cannot create reversible MIME type for {cls} as it is not in the "
2222
"fileformats namespace"
2323
)

fileformats/core/converter.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import typing as ty
33
import logging
44
from .utils import describe_task, matching_source
5-
from .exceptions import FileFormatsError
5+
from .exceptions import FormatDefinitionError
66

77
if ty.TYPE_CHECKING:
88
from .datatype import DataType
@@ -127,7 +127,7 @@ def register_converter(
127127
"""
128128
# Ensure "converters" dict is defined in the target class and not in a superclass
129129
if len(source_format.wildcard_classifiers()) > 1:
130-
raise FileFormatsError(
130+
raise FormatDefinitionError(
131131
"Cannot register a conversion to a generic type from a type with more "
132132
f"than one wildcard {source_format} ({list(source_format.wildcard_classifiers())})"
133133
)
@@ -151,7 +151,7 @@ def register_converter(
151151
describe_task(task),
152152
)
153153
return # actually the same task but just imported twice for some reason
154-
raise FileFormatsError(
154+
raise FormatDefinitionError(
155155
f"Cannot register converter from {source_format} to the generic type "
156156
f"'{tuple(prev_task.wildcard_classifiers())[0]}', {describe_task(task)} "
157157
f"because there is already one registered, {describe_task(prev_task)}"

fileformats/core/exceptions.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
1-
class FileFormatsError(RuntimeError):
1+
class FileFormatsError(Exception):
22
"Base exception class"
33

44

5-
class FormatMismatchError(FileFormatsError):
6-
"File formats don't match"
5+
class FormatDefinitionError(FileFormatsError):
6+
"When the file-formats class hasn't been properly defined"
7+
8+
9+
class FormatMismatchError(TypeError, FileFormatsError):
10+
"Provided paths/values do not match the specified file format"
11+
12+
13+
class UnsatisfiableCopyModeError(FileFormatsError):
14+
"Error copying files"
715

816

917
class FormatConversionError(FileFormatsError):

fileformats/core/field.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@
33
from .utils import (
44
classproperty,
55
)
6-
from .exceptions import (
7-
FileFormatsError,
8-
)
96
from .datatype import DataType
7+
from .exceptions import FormatMismatchError
108

119

1210
class Field(DataType):
@@ -52,7 +50,7 @@ def from_primitive(cls, dtype: type):
5250
datatype = next(iter(f for f in cls.all_fields if f.primitive is dtype))
5351
except StopIteration as e:
5452
field_types_str = ", ".join(t.__name__ for t in cls.all_fields)
55-
raise FileFormatsError(
53+
raise FormatMismatchError(
5654
f"{dtype} doesn't not correspond to a valid fileformats field type "
5755
f"({field_types_str})"
5856
) from e

fileformats/core/fileset.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@
2828
from .converter import SubtypeVar
2929
from .classifier import Classifier
3030
from .exceptions import (
31-
FileFormatsError,
3231
FormatMismatchError,
3332
UnconstrainedExtensionException,
3433
FormatConversionError,
34+
UnsatisfiableCopyModeError,
35+
FormatDefinitionError,
3536
FileFormatsExtrasError,
3637
FileFormatsExtrasPkgUninstalledError,
3738
FileFormatsExtrasPkgNotCheckedError,
@@ -106,7 +107,7 @@ def __init__(self, fspaths, metadata=False):
106107

107108
def _validate_fspaths(self):
108109
if not self.fspaths:
109-
raise FileFormatsError(f"No file-system paths provided to {self}")
110+
raise ValueError(f"No file-system paths provided to {self}")
110111
missing = [p for p in self.fspaths if not p or not p.exists()]
111112
if missing:
112113
missing_str = "\n".join(str(p) for p in missing)
@@ -1343,7 +1344,7 @@ def copy(
13431344
)
13441345
if constraints:
13451346
msg += ", and the following constraints:\n" + "\n".join(constraints)
1346-
raise FileFormatsError(msg)
1347+
raise UnsatisfiableCopyModeError(msg)
13471348
if selected_mode & self.CopyMode.leave:
13481349
return self # Don't need to do anything
13491350

@@ -1394,7 +1395,7 @@ def hardlink_dir(src: Path, dest: Path):
13941395
else:
13951396
os.unlink(new_path)
13961397
else:
1397-
raise FileFormatsError(
1398+
raise FileExistsError(
13981399
f"Destination path '{str(new_path)}' exists, set "
13991400
"'overwrite' to overwrite it"
14001401
)
@@ -1474,7 +1475,7 @@ def move(
14741475
else:
14751476
os.unlink(new_path)
14761477
else:
1477-
raise FileFormatsError(
1478+
raise FileExistsError(
14781479
f"Destination path '{str(new_path)}' exists, set "
14791480
"'overwrite' to overwrite it"
14801481
)
@@ -1523,7 +1524,7 @@ def _fspaths_to_copy(
15231524
n for n, c in Counter(p.name for p in self.fspaths).items() if c > 1
15241525
]
15251526
if duplicate_names:
1526-
raise FileFormatsError(
1527+
raise UnsatisfiableCopyModeError(
15271528
f"Cannot copy/move {self} with collation mode "
15281529
f'"{collation}", as there are duplicate filenames, {duplicate_names}, '
15291530
f"in file paths: " + "\n".join(str(p) for p in self.fspaths)
@@ -1535,7 +1536,7 @@ def _fspaths_to_copy(
15351536
exts = [d[-1] for d in decomposed_fspaths]
15361537
duplicate_exts = [n for n, c in Counter(exts).items() if c > 1]
15371538
if duplicate_exts:
1538-
raise FileFormatsError(
1539+
raise UnsatisfiableCopyModeError(
15391540
f"Cannot copy/move {self} with collation mode "
15401541
f'"{collation}", as there are duplicate extensions, {duplicate_exts}, '
15411542
f"in file paths: " + "\n".join(str(p) for p in self.fspaths)
@@ -1545,7 +1546,7 @@ def _fspaths_to_copy(
15451546
else:
15461547
fspaths_to_copy = self.fspaths
15471548
if not fspaths_to_copy:
1548-
raise FileFormatsError(
1549+
raise UnsatisfiableCopyModeError(
15491550
f"Cannot copy {self} because none of the fspaths in the file-set are "
15501551
"required. Set trim=False to copy all file-paths"
15511552
)
@@ -1645,8 +1646,8 @@ def namespace(cls):
16451646
continue
16461647
try:
16471648
return base.namespace
1648-
except FileFormatsError:
1649+
except FormatDefinitionError:
16491650
pass
1650-
raise FileFormatsError(
1651+
raise FormatDefinitionError(
16511652
f"None of of the bases classes of {cls} ({cls.__mro__}) have a valid namespace"
16521653
)

fileformats/core/identification.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import typing as ty
44
import re
55
from fileformats.core.exceptions import (
6-
FileFormatsError,
6+
FormatDefinitionError,
77
FormatRecognitionError,
88
)
99
import fileformats.core
@@ -223,7 +223,7 @@ def unwrap(candidate):
223223
ignore_re = re.compile(ignore)
224224
remaining = [p for p in remaining if not ignore_re.match(p.name)]
225225
if remaining:
226-
raise FileFormatsError(
226+
raise FormatRecognitionError(
227227
"the following file-system paths were not recognised by any of the "
228228
f"candidate formats ({candidates_str}):\n"
229229
+ "\n".join(str(p) for p in remaining)
@@ -233,7 +233,7 @@ def unwrap(candidate):
233233

234234
def to_mime_format_name(format_name: str):
235235
if "___" in format_name:
236-
raise FileFormatsError(
236+
raise FormatDefinitionError(
237237
f"Cannot convert name of format class {format_name} to mime string as it "
238238
"contains triple underscore"
239239
)

fileformats/core/mixin.py

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77
from .utils import classproperty, describe_task, matching_source
88
from .identification import to_mime_format_name
99
from .converter import SubtypeVar
10-
from .exceptions import FileFormatsError, FormatMismatchError, FormatRecognitionError
10+
from .exceptions import (
11+
FormatMismatchError,
12+
FormatRecognitionError,
13+
FormatDefinitionError,
14+
)
1115

1216

1317
logger = logging.getLogger("fileformats")
@@ -95,7 +99,7 @@ def version(self) -> ty.Union[str, ty.Tuple[str]]:
9599
)
96100
version = tuple(b.decode("utf-8") for b in match.groups())
97101
if not version:
98-
raise FileFormatsError(
102+
raise FormatDefinitionError(
99103
f"No version patterns found in magic pattern of {type(self).__name__} "
100104
f"class, {self.magic_pattern}"
101105
)
@@ -278,7 +282,7 @@ def my_func(file: MyFormatWithClassifiers[Integer]):
278282

279283
def _validate_class(self):
280284
if self.wildcard_classifiers():
281-
raise FileFormatsError(
285+
raise FormatDefinitionError(
282286
f"Can instantiate {type(self)} class as it has wildcard classifiers "
283287
"and therefore should only be used for converter specifications"
284288
)
@@ -317,7 +321,7 @@ def __class_getitem__(cls, classifiers):
317321
if not any(issubclass(q, t) for t in cls.allowed_classifiers)
318322
]
319323
if not_allowed:
320-
raise FileFormatsError(
324+
raise FormatDefinitionError(
321325
f"Invalid content types provided to {cls} (must be subclasses of "
322326
f"{cls.allowed_classifiers}): {not_allowed}"
323327
)
@@ -339,7 +343,7 @@ def __class_getitem__(cls, classifiers):
339343
repetitions[exc_classifier].append(classifier)
340344
repeated = [t for t in repetitions.items() if len(t[1]) > 1]
341345
if repeated:
342-
raise FileFormatsError(
346+
raise FormatDefinitionError(
343347
"Cannot have more than one occurrence of a classifier "
344348
f"or subclasses for {cls} class when "
345349
f"{cls.__name__}.ordered_classifiers is false:\n"
@@ -351,8 +355,9 @@ def __class_getitem__(cls, classifiers):
351355
classifiers = frozenset(classifiers)
352356
else:
353357
if len(classifiers) > 1:
354-
raise FileFormatsError(
355-
f"Multiple classifiers not permitted for {cls} types, provided: ({classifiers})"
358+
raise FormatDefinitionError(
359+
f"Multiple classifiers not permitted for {cls} types, provided: "
360+
f"({classifiers})"
356361
)
357362
# Make sure that the "classified" dictionary is present in this class not super
358363
# classes
@@ -363,26 +368,26 @@ def __class_getitem__(cls, classifiers):
363368
classified = cls._classified_subtypes[classifiers]
364369
except KeyError:
365370
if not hasattr(cls, "classifiers_attr_name"):
366-
raise FileFormatsError(
371+
raise FormatDefinitionError(
367372
f"{cls} needs to define the 'classifiers_attr_name' class attribute "
368373
"with the name of the (different) class attribute to hold the "
369374
"classified types"
370375
)
371376
if cls.classifiers_attr_name is None:
372-
raise FileFormatsError(
377+
raise FormatDefinitionError(
373378
f"Inherited classifiers have been disabled in {cls} (by setting "
374379
f'"classifiers_attr_name)" to None)'
375380
)
376381
try:
377382
classifiers_attr = getattr(cls, cls.classifiers_attr_name)
378383
except AttributeError:
379-
raise FileFormatsError(
384+
raise FormatDefinitionError(
380385
f"Default value for classifiers attribute "
381386
f"'{cls.classifiers_attr_name}' needs to be set in {cls}"
382387
)
383388
else:
384389
if classifiers_attr:
385-
raise FileFormatsError(
390+
raise FormatDefinitionError(
386391
f"Default value for classifiers attribute "
387392
f"'{cls.classifiers_attr_name}' needs to be set in {cls}"
388393
)
@@ -571,18 +576,18 @@ def register_converter(
571576
if cls.wildcard_classifiers():
572577
if issubclass(source_format, SubtypeVar):
573578
if len(cls.wildcard_classifiers()) > 1:
574-
raise FileFormatsError(
579+
raise FormatDefinitionError(
575580
"Can only have one wildcard qualifier when registering a converter "
576581
f"to {cls} from a generic type, found {cls.wildcard_classifiers()}"
577582
)
578583
elif not source_format.is_classified:
579-
raise FileFormatsError(
584+
raise FormatDefinitionError(
580585
"Can only use wildcard classifiers when registering a converter "
581586
f"from a generic type or similarly classified type, not {source_format}"
582587
)
583588
else:
584589
if cls.wildcard_classifiers() != source_format.wildcard_classifiers():
585-
raise FileFormatsError(
590+
raise FormatDefinitionError(
586591
f"Mismatching wildcards between source format, {source_format} "
587592
f"({list(source_format.wildcard_classifiers())}), and target "
588593
f"{cls} ({cls.wildcard_classifiers()})"
@@ -612,7 +617,7 @@ def register_converter(
612617
describe_task(task),
613618
)
614619
return # actually the same task but just imported twice for some reason
615-
raise FileFormatsError(
620+
raise FormatDefinitionError(
616621
f"Cannot register converter from {prev.unclassified} "
617622
f"to {cls.unclassified} with non-wildcard classifiers "
618623
f"{list(prev.non_wildcard_classifiers())}, {describe_task(task)}, "

fileformats/core/tests/test_classifiers.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from fileformats.generic import DirectoryContaining
99
from fileformats.field import Array, Integer, Decimal, Text, Boolean
1010
from fileformats.core.exceptions import (
11-
FileFormatsError,
11+
FormatDefinitionError,
1212
FormatConversionError,
1313
FormatRecognitionError,
1414
FormatMismatchError,
@@ -149,13 +149,16 @@ def test_file_classifiers2():
149149

150150

151151
def test_file_classifiers3():
152-
with pytest.raises(FileFormatsError, match="Invalid content types provided to"):
152+
with pytest.raises(
153+
FormatDefinitionError, match="Invalid content types provided to"
154+
):
153155
H[D]
154156

155157

156158
def test_file_classifiers4():
157159
with pytest.raises(
158-
FileFormatsError, match="Cannot have more than one occurrence of a classifier "
160+
FormatDefinitionError,
161+
match="Cannot have more than one occurrence of a classifier ",
159162
):
160163
H[A, B, A]
161164

@@ -166,15 +169,15 @@ def test_file_classifiers5():
166169

167170
def test_file_classifiers6():
168171
with pytest.raises(
169-
FileFormatsError,
172+
FormatDefinitionError,
170173
match="Default value for classifiers attribute 'new_classifiers_attr' needs to be set",
171174
):
172175
Q[A]
173176

174177

175178
def test_file_classifiers7():
176179
with pytest.raises(
177-
FileFormatsError, match="Multiple classifiers not permitted for "
180+
FormatDefinitionError, match="Multiple classifiers not permitted for "
178181
):
179182
M[A, B]
180183

@@ -404,12 +407,16 @@ def test_classifier_categories2():
404407

405408

406409
def test_classifier_categories3():
407-
with pytest.raises(FileFormatsError, match="Cannot have more than one occurrence"):
410+
with pytest.raises(
411+
FormatDefinitionError, match="Cannot have more than one occurrence"
412+
):
408413
Classified[U, V]
409414

410415

411416
def test_classifier_categories4():
412-
with pytest.raises(FileFormatsError, match="Cannot have more than one occurrence"):
417+
with pytest.raises(
418+
FormatDefinitionError, match="Cannot have more than one occurrence"
419+
):
413420
Classified[U, W]
414421

415422

@@ -419,6 +426,7 @@ def test_classifier_categories5():
419426

420427
def test_classifier_categories6():
421428
with pytest.raises(
422-
FileFormatsError, match="Cannot have more than one occurrence of a classifier "
429+
FormatDefinitionError,
430+
match="Cannot have more than one occurrence of a classifier ",
423431
):
424432
Classified[C, E]

0 commit comments

Comments
 (0)