nrl-tracker 1.8.0__py3-none-any.whl → 1.9.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,865 @@
1
+ """
2
+ Custom exception hierarchy for the Tracker Component Library.
3
+
4
+ This module provides a structured exception hierarchy for consistent error
5
+ handling across the library. Custom exceptions enable more specific error
6
+ catching, better error messages, and improved debugging.
7
+
8
+ Exception Hierarchy
9
+ -------------------
10
+ TCLError (base)
11
+ ├── ValidationError (input validation failures)
12
+ │ ├── DimensionError (array shape/dimension mismatches)
13
+ │ ├── ParameterError (invalid parameter values)
14
+ │ └── RangeError (out-of-range values)
15
+ ├── ComputationError (numerical computation failures)
16
+ │ ├── ConvergenceError (iterative algorithm non-convergence)
17
+ │ ├── NumericalError (numerical stability issues)
18
+ │ └── SingularMatrixError (singular/non-invertible matrix)
19
+ ├── StateError (object state violations)
20
+ │ ├── UninitializedError (object not initialized)
21
+ │ └── EmptyContainerError (container has no elements)
22
+ ├── ConfigurationError (configuration/setup issues)
23
+ │ ├── MethodError (invalid method/algorithm selection)
24
+ │ └── DependencyError (missing optional dependency)
25
+ └── DataError (data format/structure issues)
26
+ ├── FormatError (invalid data format)
27
+ └── ParseError (data parsing failures)
28
+
29
+ Examples
30
+ --------
31
+ Catching specific exception types:
32
+
33
+ >>> from pytcl.core.exceptions import ConvergenceError, ParameterError
34
+ >>> try:
35
+ ... result = solve_kepler(M=1.5, e=1.5) # Invalid eccentricity
36
+ ... except ParameterError as e:
37
+ ... print(f"Invalid parameter: {e}")
38
+
39
+ Catching all TCL errors:
40
+
41
+ >>> from pytcl.core.exceptions import TCLError
42
+ >>> try:
43
+ ... result = compute_orbit(...)
44
+ ... except TCLError as e:
45
+ ... print(f"TCL error: {e}")
46
+ """
47
+
48
+ from typing import Any, Optional, Sequence
49
+
50
+
51
+ class TCLError(Exception):
52
+ """
53
+ Base exception for all Tracker Component Library errors.
54
+
55
+ All custom exceptions in the library inherit from this class,
56
+ allowing users to catch all TCL-specific errors with a single
57
+ except clause.
58
+
59
+ Parameters
60
+ ----------
61
+ message : str
62
+ Human-readable error message.
63
+ details : dict, optional
64
+ Additional context about the error (parameter values, etc.).
65
+
66
+ Examples
67
+ --------
68
+ >>> raise TCLError("Something went wrong", details={"value": 42})
69
+ """
70
+
71
+ def __init__(self, message: str, details: Optional[dict[str, Any]] = None):
72
+ self.message = message
73
+ self.details = details or {}
74
+ super().__init__(message)
75
+
76
+ def __str__(self) -> str:
77
+ if self.details:
78
+ detail_str = ", ".join(f"{k}={v}" for k, v in self.details.items())
79
+ return f"{self.message} ({detail_str})"
80
+ return self.message
81
+
82
+
83
+ # =============================================================================
84
+ # Validation Errors
85
+ # =============================================================================
86
+
87
+
88
+ class ValidationError(TCLError, ValueError):
89
+ """
90
+ Base exception for input validation failures.
91
+
92
+ Raised when function inputs fail validation checks. This extends
93
+ both TCLError (for TCL-specific catching) and ValueError (for
94
+ compatibility with code expecting standard Python exceptions).
95
+
96
+ Parameters
97
+ ----------
98
+ message : str
99
+ Description of the validation failure.
100
+ parameter : str, optional
101
+ Name of the parameter that failed validation.
102
+ expected : str, optional
103
+ Description of what was expected.
104
+ actual : Any, optional
105
+ The actual value that was provided.
106
+
107
+ Examples
108
+ --------
109
+ >>> raise ValidationError(
110
+ ... "Invalid matrix dimensions",
111
+ ... parameter="P",
112
+ ... expected="3x3 matrix",
113
+ ... actual="2x4 array"
114
+ ... )
115
+ """
116
+
117
+ def __init__(
118
+ self,
119
+ message: str,
120
+ parameter: Optional[str] = None,
121
+ expected: Optional[str] = None,
122
+ actual: Optional[Any] = None,
123
+ **kwargs: Any,
124
+ ):
125
+ details: dict[str, Any] = {}
126
+ if parameter is not None:
127
+ details["parameter"] = parameter
128
+ if expected is not None:
129
+ details["expected"] = expected
130
+ if actual is not None:
131
+ details["actual"] = actual
132
+ details.update(kwargs)
133
+ super().__init__(message, details)
134
+ self.parameter = parameter
135
+ self.expected = expected
136
+ self.actual = actual
137
+
138
+
139
+ class DimensionError(ValidationError):
140
+ """
141
+ Exception for array dimension and shape mismatches.
142
+
143
+ Raised when array dimensions or shapes don't match requirements
144
+ or aren't compatible with each other.
145
+
146
+ Parameters
147
+ ----------
148
+ message : str
149
+ Description of the dimension mismatch.
150
+ expected_shape : tuple, optional
151
+ Expected shape.
152
+ actual_shape : tuple, optional
153
+ Actual shape.
154
+ parameter : str, optional
155
+ Name of the parameter.
156
+
157
+ Examples
158
+ --------
159
+ >>> raise DimensionError(
160
+ ... "Covariance matrix must be 3x3",
161
+ ... expected_shape=(3, 3),
162
+ ... actual_shape=(2, 4),
163
+ ... parameter="P"
164
+ ... )
165
+ """
166
+
167
+ def __init__(
168
+ self,
169
+ message: str,
170
+ expected_shape: Optional[tuple[int, ...]] = None,
171
+ actual_shape: Optional[tuple[int, ...]] = None,
172
+ parameter: Optional[str] = None,
173
+ **kwargs: Any,
174
+ ):
175
+ details: dict[str, Any] = {}
176
+ if expected_shape is not None:
177
+ details["expected_shape"] = expected_shape
178
+ if actual_shape is not None:
179
+ details["actual_shape"] = actual_shape
180
+ super().__init__(message, parameter=parameter, **details, **kwargs)
181
+ self.expected_shape = expected_shape
182
+ self.actual_shape = actual_shape
183
+
184
+
185
+ class ParameterError(ValidationError):
186
+ """
187
+ Exception for invalid parameter values.
188
+
189
+ Raised when a parameter value violates constraints (type, value
190
+ range, allowed values, etc.).
191
+
192
+ Parameters
193
+ ----------
194
+ message : str
195
+ Description of the invalid parameter.
196
+ parameter : str, optional
197
+ Name of the invalid parameter.
198
+ value : Any, optional
199
+ The invalid value.
200
+ constraint : str, optional
201
+ Description of the constraint that was violated.
202
+
203
+ Examples
204
+ --------
205
+ >>> raise ParameterError(
206
+ ... "Variance must be positive",
207
+ ... parameter="variance",
208
+ ... value=-1.0,
209
+ ... constraint="must be > 0"
210
+ ... )
211
+ """
212
+
213
+ def __init__(
214
+ self,
215
+ message: str,
216
+ parameter: Optional[str] = None,
217
+ value: Optional[Any] = None,
218
+ constraint: Optional[str] = None,
219
+ **kwargs: Any,
220
+ ):
221
+ details: dict[str, Any] = {}
222
+ if value is not None:
223
+ details["value"] = value
224
+ if constraint is not None:
225
+ details["constraint"] = constraint
226
+ super().__init__(message, parameter=parameter, **details, **kwargs)
227
+ self.value = value
228
+ self.constraint = constraint
229
+
230
+
231
+ class RangeError(ValidationError):
232
+ """
233
+ Exception for out-of-range values.
234
+
235
+ Raised when a numeric value falls outside an allowed range.
236
+
237
+ Parameters
238
+ ----------
239
+ message : str
240
+ Description of the range violation.
241
+ parameter : str, optional
242
+ Name of the parameter.
243
+ value : float, optional
244
+ The out-of-range value.
245
+ min_value : float, optional
246
+ Minimum allowed value.
247
+ max_value : float, optional
248
+ Maximum allowed value.
249
+
250
+ Examples
251
+ --------
252
+ >>> raise RangeError(
253
+ ... "Eccentricity must be in [0, 1) for elliptic orbits",
254
+ ... parameter="e",
255
+ ... value=1.5,
256
+ ... min_value=0.0,
257
+ ... max_value=1.0
258
+ ... )
259
+ """
260
+
261
+ def __init__(
262
+ self,
263
+ message: str,
264
+ parameter: Optional[str] = None,
265
+ value: Optional[float] = None,
266
+ min_value: Optional[float] = None,
267
+ max_value: Optional[float] = None,
268
+ **kwargs: Any,
269
+ ):
270
+ details: dict[str, Any] = {}
271
+ if value is not None:
272
+ details["value"] = value
273
+ if min_value is not None:
274
+ details["min"] = min_value
275
+ if max_value is not None:
276
+ details["max"] = max_value
277
+ super().__init__(message, parameter=parameter, **details, **kwargs)
278
+ self.value = value
279
+ self.min_value = min_value
280
+ self.max_value = max_value
281
+
282
+
283
+ # =============================================================================
284
+ # Computation Errors
285
+ # =============================================================================
286
+
287
+
288
+ class ComputationError(TCLError, RuntimeError):
289
+ """
290
+ Base exception for numerical computation failures.
291
+
292
+ Raised when a numerical algorithm fails to produce a valid result.
293
+ This extends RuntimeError for compatibility with code that catches
294
+ standard computation errors.
295
+
296
+ Parameters
297
+ ----------
298
+ message : str
299
+ Description of the computation failure.
300
+ algorithm : str, optional
301
+ Name of the algorithm that failed.
302
+
303
+ Examples
304
+ --------
305
+ >>> raise ComputationError(
306
+ ... "Failed to compute eigenvalues",
307
+ ... algorithm="numpy.linalg.eig"
308
+ ... )
309
+ """
310
+
311
+ def __init__(
312
+ self,
313
+ message: str,
314
+ algorithm: Optional[str] = None,
315
+ **kwargs: Any,
316
+ ):
317
+ details: dict[str, Any] = {}
318
+ if algorithm is not None:
319
+ details["algorithm"] = algorithm
320
+ details.update(kwargs)
321
+ super().__init__(message, details)
322
+ self.algorithm = algorithm
323
+
324
+
325
+ class ConvergenceError(ComputationError):
326
+ """
327
+ Exception for iterative algorithm convergence failures.
328
+
329
+ Raised when an iterative algorithm fails to converge within
330
+ the maximum number of iterations.
331
+
332
+ Parameters
333
+ ----------
334
+ message : str
335
+ Description of the convergence failure.
336
+ algorithm : str, optional
337
+ Name of the algorithm.
338
+ iterations : int, optional
339
+ Number of iterations performed.
340
+ max_iterations : int, optional
341
+ Maximum iterations allowed.
342
+ residual : float, optional
343
+ Final residual or error value.
344
+ tolerance : float, optional
345
+ Convergence tolerance.
346
+
347
+ Examples
348
+ --------
349
+ >>> raise ConvergenceError(
350
+ ... "Kepler's equation did not converge",
351
+ ... algorithm="Newton-Raphson",
352
+ ... iterations=100,
353
+ ... max_iterations=100,
354
+ ... residual=1e-5,
355
+ ... tolerance=1e-12
356
+ ... )
357
+ """
358
+
359
+ def __init__(
360
+ self,
361
+ message: str,
362
+ algorithm: Optional[str] = None,
363
+ iterations: Optional[int] = None,
364
+ max_iterations: Optional[int] = None,
365
+ residual: Optional[float] = None,
366
+ tolerance: Optional[float] = None,
367
+ **kwargs: Any,
368
+ ):
369
+ details: dict[str, Any] = {}
370
+ if iterations is not None:
371
+ details["iterations"] = iterations
372
+ if max_iterations is not None:
373
+ details["max_iterations"] = max_iterations
374
+ if residual is not None:
375
+ details["residual"] = residual
376
+ if tolerance is not None:
377
+ details["tolerance"] = tolerance
378
+ super().__init__(message, algorithm=algorithm, **details, **kwargs)
379
+ self.iterations = iterations
380
+ self.max_iterations = max_iterations
381
+ self.residual = residual
382
+ self.tolerance = tolerance
383
+
384
+
385
+ class NumericalError(ComputationError):
386
+ """
387
+ Exception for numerical stability issues.
388
+
389
+ Raised when numerical operations fail due to precision issues,
390
+ overflow, underflow, or ill-conditioned computations.
391
+
392
+ Parameters
393
+ ----------
394
+ message : str
395
+ Description of the numerical issue.
396
+ operation : str, optional
397
+ The operation that failed.
398
+ condition_number : float, optional
399
+ Matrix condition number (if applicable).
400
+
401
+ Examples
402
+ --------
403
+ >>> raise NumericalError(
404
+ ... "Matrix is ill-conditioned",
405
+ ... operation="matrix inversion",
406
+ ... condition_number=1e16
407
+ ... )
408
+ """
409
+
410
+ def __init__(
411
+ self,
412
+ message: str,
413
+ operation: Optional[str] = None,
414
+ condition_number: Optional[float] = None,
415
+ **kwargs: Any,
416
+ ):
417
+ details: dict[str, Any] = {}
418
+ if operation is not None:
419
+ details["operation"] = operation
420
+ if condition_number is not None:
421
+ details["condition_number"] = condition_number
422
+ super().__init__(message, **details, **kwargs)
423
+ self.operation = operation
424
+ self.condition_number = condition_number
425
+
426
+
427
+ class SingularMatrixError(ComputationError):
428
+ """
429
+ Exception for singular or non-invertible matrix operations.
430
+
431
+ Raised when a matrix operation requires an invertible matrix
432
+ but the matrix is singular or nearly singular.
433
+
434
+ Parameters
435
+ ----------
436
+ message : str
437
+ Description of the singularity issue.
438
+ matrix_name : str, optional
439
+ Name of the singular matrix.
440
+ determinant : float, optional
441
+ Determinant value (near zero).
442
+
443
+ Examples
444
+ --------
445
+ >>> raise SingularMatrixError(
446
+ ... "Covariance matrix is singular",
447
+ ... matrix_name="P",
448
+ ... determinant=1e-20
449
+ ... )
450
+ """
451
+
452
+ def __init__(
453
+ self,
454
+ message: str,
455
+ matrix_name: Optional[str] = None,
456
+ determinant: Optional[float] = None,
457
+ **kwargs: Any,
458
+ ):
459
+ details: dict[str, Any] = {}
460
+ if matrix_name is not None:
461
+ details["matrix"] = matrix_name
462
+ if determinant is not None:
463
+ details["determinant"] = determinant
464
+ super().__init__(message, **details, **kwargs)
465
+ self.matrix_name = matrix_name
466
+ self.determinant = determinant
467
+
468
+
469
+ # =============================================================================
470
+ # State Errors
471
+ # =============================================================================
472
+
473
+
474
+ class StateError(TCLError):
475
+ """
476
+ Base exception for object state violations.
477
+
478
+ Raised when an operation is attempted on an object that is not
479
+ in a valid state for that operation.
480
+
481
+ Parameters
482
+ ----------
483
+ message : str
484
+ Description of the state violation.
485
+ object_type : str, optional
486
+ Type of the object.
487
+ current_state : str, optional
488
+ Description of the current state.
489
+ required_state : str, optional
490
+ Description of the required state.
491
+
492
+ Examples
493
+ --------
494
+ >>> raise StateError(
495
+ ... "Cannot update without prediction",
496
+ ... object_type="KalmanFilter",
497
+ ... current_state="uninitialized",
498
+ ... required_state="predicted"
499
+ ... )
500
+ """
501
+
502
+ def __init__(
503
+ self,
504
+ message: str,
505
+ object_type: Optional[str] = None,
506
+ current_state: Optional[str] = None,
507
+ required_state: Optional[str] = None,
508
+ **kwargs: Any,
509
+ ):
510
+ details: dict[str, Any] = {}
511
+ if object_type is not None:
512
+ details["object_type"] = object_type
513
+ if current_state is not None:
514
+ details["current_state"] = current_state
515
+ if required_state is not None:
516
+ details["required_state"] = required_state
517
+ details.update(kwargs)
518
+ super().__init__(message, details)
519
+ self.object_type = object_type
520
+ self.current_state = current_state
521
+ self.required_state = required_state
522
+
523
+
524
+ class UninitializedError(StateError):
525
+ """
526
+ Exception for uninitialized object access.
527
+
528
+ Raised when an operation requires an initialized object but
529
+ the object hasn't been properly initialized.
530
+
531
+ Parameters
532
+ ----------
533
+ message : str
534
+ Description of the issue.
535
+ object_type : str, optional
536
+ Type of the uninitialized object.
537
+ required_initialization : str, optional
538
+ What initialization is required.
539
+
540
+ Examples
541
+ --------
542
+ >>> raise UninitializedError(
543
+ ... "Tracker not initialized",
544
+ ... object_type="SingleTargetTracker",
545
+ ... required_initialization="call initialize() first"
546
+ ... )
547
+ """
548
+
549
+ def __init__(
550
+ self,
551
+ message: str,
552
+ object_type: Optional[str] = None,
553
+ required_initialization: Optional[str] = None,
554
+ **kwargs: Any,
555
+ ):
556
+ super().__init__(
557
+ message,
558
+ object_type=object_type,
559
+ current_state="uninitialized",
560
+ required_state=required_initialization,
561
+ **kwargs,
562
+ )
563
+
564
+
565
+ class EmptyContainerError(StateError):
566
+ """
567
+ Exception for empty container operations.
568
+
569
+ Raised when an operation requires a non-empty container but
570
+ the container has no elements.
571
+
572
+ Parameters
573
+ ----------
574
+ message : str
575
+ Description of the issue.
576
+ container_type : str, optional
577
+ Type of the empty container.
578
+ operation : str, optional
579
+ The operation that requires non-empty container.
580
+
581
+ Examples
582
+ --------
583
+ >>> raise EmptyContainerError(
584
+ ... "Cannot query empty RTree",
585
+ ... container_type="RTree",
586
+ ... operation="nearest neighbor query"
587
+ ... )
588
+ """
589
+
590
+ def __init__(
591
+ self,
592
+ message: str,
593
+ container_type: Optional[str] = None,
594
+ operation: Optional[str] = None,
595
+ **kwargs: Any,
596
+ ):
597
+ details: dict[str, Any] = {}
598
+ if operation is not None:
599
+ details["operation"] = operation
600
+ super().__init__(
601
+ message,
602
+ object_type=container_type,
603
+ current_state="empty",
604
+ **details,
605
+ **kwargs,
606
+ )
607
+
608
+
609
+ # =============================================================================
610
+ # Configuration Errors
611
+ # =============================================================================
612
+
613
+
614
+ class ConfigurationError(TCLError):
615
+ """
616
+ Base exception for configuration and setup issues.
617
+
618
+ Raised when there are problems with algorithm configuration,
619
+ method selection, or dependency availability.
620
+
621
+ Parameters
622
+ ----------
623
+ message : str
624
+ Description of the configuration issue.
625
+
626
+ Examples
627
+ --------
628
+ >>> raise ConfigurationError("Invalid filter configuration")
629
+ """
630
+
631
+ pass
632
+
633
+
634
+ class MethodError(ConfigurationError, ValueError):
635
+ """
636
+ Exception for invalid method or algorithm selection.
637
+
638
+ Raised when an unknown or unsupported method/algorithm is specified.
639
+
640
+ Parameters
641
+ ----------
642
+ message : str
643
+ Description of the issue.
644
+ method : str, optional
645
+ The invalid method name.
646
+ valid_methods : sequence of str, optional
647
+ List of valid method names.
648
+
649
+ Examples
650
+ --------
651
+ >>> raise MethodError(
652
+ ... "Unknown assignment method",
653
+ ... method="invalid_method",
654
+ ... valid_methods=["hungarian", "auction", "greedy"]
655
+ ... )
656
+ """
657
+
658
+ def __init__(
659
+ self,
660
+ message: str,
661
+ method: Optional[str] = None,
662
+ valid_methods: Optional[Sequence[str]] = None,
663
+ **kwargs: Any,
664
+ ):
665
+ details: dict[str, Any] = {}
666
+ if method is not None:
667
+ details["method"] = method
668
+ if valid_methods is not None:
669
+ details["valid_methods"] = list(valid_methods)
670
+ super().__init__(message, details)
671
+ self.method = method
672
+ self.valid_methods = valid_methods
673
+
674
+
675
+ class DependencyError(ConfigurationError, ImportError):
676
+ """
677
+ Exception for missing optional dependencies.
678
+
679
+ Raised when an optional dependency is required but not installed.
680
+
681
+ Parameters
682
+ ----------
683
+ message : str
684
+ Description of the missing dependency.
685
+ package : str, optional
686
+ Name of the missing package.
687
+ feature : str, optional
688
+ Feature that requires the dependency.
689
+ install_command : str, optional
690
+ Command to install the dependency.
691
+
692
+ Examples
693
+ --------
694
+ >>> raise DependencyError(
695
+ ... "plotly is required for interactive plotting",
696
+ ... package="plotly",
697
+ ... feature="3D visualization",
698
+ ... install_command="pip install plotly"
699
+ ... )
700
+ """
701
+
702
+ def __init__(
703
+ self,
704
+ message: str,
705
+ package: Optional[str] = None,
706
+ feature: Optional[str] = None,
707
+ install_command: Optional[str] = None,
708
+ **kwargs: Any,
709
+ ):
710
+ details: dict[str, Any] = {}
711
+ if package is not None:
712
+ details["package"] = package
713
+ if feature is not None:
714
+ details["feature"] = feature
715
+ if install_command is not None:
716
+ details["install"] = install_command
717
+ super().__init__(message, details)
718
+ self.package = package
719
+ self.feature = feature
720
+ self.install_command = install_command
721
+
722
+
723
+ # =============================================================================
724
+ # Data Errors
725
+ # =============================================================================
726
+
727
+
728
+ class DataError(TCLError):
729
+ """
730
+ Base exception for data format and structure issues.
731
+
732
+ Raised when input data has format or structural problems.
733
+
734
+ Parameters
735
+ ----------
736
+ message : str
737
+ Description of the data issue.
738
+
739
+ Examples
740
+ --------
741
+ >>> raise DataError("Invalid input data format")
742
+ """
743
+
744
+ pass
745
+
746
+
747
+ class FormatError(DataError, ValueError):
748
+ """
749
+ Exception for invalid data format.
750
+
751
+ Raised when data doesn't conform to the expected format.
752
+
753
+ Parameters
754
+ ----------
755
+ message : str
756
+ Description of the format issue.
757
+ expected_format : str, optional
758
+ Description of the expected format.
759
+ actual_format : str, optional
760
+ Description of the actual format.
761
+
762
+ Examples
763
+ --------
764
+ >>> raise FormatError(
765
+ ... "Invalid TLE format",
766
+ ... expected_format="69 characters per line",
767
+ ... actual_format="line 1 has 65 characters"
768
+ ... )
769
+ """
770
+
771
+ def __init__(
772
+ self,
773
+ message: str,
774
+ expected_format: Optional[str] = None,
775
+ actual_format: Optional[str] = None,
776
+ **kwargs: Any,
777
+ ):
778
+ details: dict[str, Any] = {}
779
+ if expected_format is not None:
780
+ details["expected"] = expected_format
781
+ if actual_format is not None:
782
+ details["actual"] = actual_format
783
+ super().__init__(message, details)
784
+ self.expected_format = expected_format
785
+ self.actual_format = actual_format
786
+
787
+
788
+ class ParseError(DataError, ValueError):
789
+ """
790
+ Exception for data parsing failures.
791
+
792
+ Raised when data cannot be parsed or interpreted.
793
+
794
+ Parameters
795
+ ----------
796
+ message : str
797
+ Description of the parsing failure.
798
+ data_type : str, optional
799
+ Type of data being parsed.
800
+ position : int or tuple, optional
801
+ Position where parsing failed.
802
+ reason : str, optional
803
+ Reason for the parsing failure.
804
+
805
+ Examples
806
+ --------
807
+ >>> raise ParseError(
808
+ ... "Failed to parse TLE checksum",
809
+ ... data_type="TLE",
810
+ ... position=68,
811
+ ... reason="invalid checksum digit"
812
+ ... )
813
+ """
814
+
815
+ def __init__(
816
+ self,
817
+ message: str,
818
+ data_type: Optional[str] = None,
819
+ position: Optional[int | tuple[int, ...]] = None,
820
+ reason: Optional[str] = None,
821
+ **kwargs: Any,
822
+ ):
823
+ details: dict[str, Any] = {}
824
+ if data_type is not None:
825
+ details["data_type"] = data_type
826
+ if position is not None:
827
+ details["position"] = position
828
+ if reason is not None:
829
+ details["reason"] = reason
830
+ super().__init__(message, details)
831
+ self.data_type = data_type
832
+ self.position = position
833
+ self.reason = reason
834
+
835
+
836
+ # =============================================================================
837
+ # Exports
838
+ # =============================================================================
839
+
840
+ __all__ = [
841
+ # Base exception
842
+ "TCLError",
843
+ # Validation errors
844
+ "ValidationError",
845
+ "DimensionError",
846
+ "ParameterError",
847
+ "RangeError",
848
+ # Computation errors
849
+ "ComputationError",
850
+ "ConvergenceError",
851
+ "NumericalError",
852
+ "SingularMatrixError",
853
+ # State errors
854
+ "StateError",
855
+ "UninitializedError",
856
+ "EmptyContainerError",
857
+ # Configuration errors
858
+ "ConfigurationError",
859
+ "MethodError",
860
+ "DependencyError",
861
+ # Data errors
862
+ "DataError",
863
+ "FormatError",
864
+ "ParseError",
865
+ ]