jetpytools 1.2.3__tar.gz

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.

Potentially problematic release.


This version of jetpytools might be problematic. Click here for more details.

Files changed (37) hide show
  1. jetpytools-1.2.3/LICENSE +21 -0
  2. jetpytools-1.2.3/PKG-INFO +48 -0
  3. jetpytools-1.2.3/README.md +17 -0
  4. jetpytools-1.2.3/jetpytools/__init__.py +5 -0
  5. jetpytools-1.2.3/jetpytools/_metadata.py +12 -0
  6. jetpytools-1.2.3/jetpytools/enums/__init__.py +2 -0
  7. jetpytools-1.2.3/jetpytools/enums/base.py +78 -0
  8. jetpytools-1.2.3/jetpytools/enums/other.py +59 -0
  9. jetpytools-1.2.3/jetpytools/exceptions/__init__.py +5 -0
  10. jetpytools-1.2.3/jetpytools/exceptions/base.py +213 -0
  11. jetpytools-1.2.3/jetpytools/exceptions/enum.py +11 -0
  12. jetpytools-1.2.3/jetpytools/exceptions/file.py +38 -0
  13. jetpytools-1.2.3/jetpytools/exceptions/generic.py +45 -0
  14. jetpytools-1.2.3/jetpytools/exceptions/module.py +39 -0
  15. jetpytools-1.2.3/jetpytools/functions/__init__.py +3 -0
  16. jetpytools-1.2.3/jetpytools/functions/funcs.py +152 -0
  17. jetpytools-1.2.3/jetpytools/functions/normalize.py +254 -0
  18. jetpytools-1.2.3/jetpytools/functions/other.py +18 -0
  19. jetpytools-1.2.3/jetpytools/py.typed +0 -0
  20. jetpytools-1.2.3/jetpytools/types/__init__.py +6 -0
  21. jetpytools-1.2.3/jetpytools/types/builtins.py +77 -0
  22. jetpytools-1.2.3/jetpytools/types/file.py +193 -0
  23. jetpytools-1.2.3/jetpytools/types/funcs.py +109 -0
  24. jetpytools-1.2.3/jetpytools/types/generic.py +52 -0
  25. jetpytools-1.2.3/jetpytools/types/supports.py +127 -0
  26. jetpytools-1.2.3/jetpytools/types/utils.py +669 -0
  27. jetpytools-1.2.3/jetpytools/utils/__init__.py +4 -0
  28. jetpytools-1.2.3/jetpytools/utils/file.py +256 -0
  29. jetpytools-1.2.3/jetpytools/utils/funcs.py +35 -0
  30. jetpytools-1.2.3/jetpytools/utils/math.py +158 -0
  31. jetpytools-1.2.3/jetpytools/utils/ranges.py +89 -0
  32. jetpytools-1.2.3/jetpytools.egg-info/PKG-INFO +48 -0
  33. jetpytools-1.2.3/jetpytools.egg-info/SOURCES.txt +36 -0
  34. jetpytools-1.2.3/jetpytools.egg-info/dependency_links.txt +1 -0
  35. jetpytools-1.2.3/jetpytools.egg-info/top_level.txt +1 -0
  36. jetpytools-1.2.3/setup.cfg +38 -0
  37. jetpytools-1.2.3/setup.py +51 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 LightArrowsEXE
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,48 @@
1
+ Metadata-Version: 2.2
2
+ Name: jetpytools
3
+ Version: 1.2.3
4
+ Summary: Collection of stuff that's useful in general python programming
5
+ Author: Jaded Encoding Thaumaturgy
6
+ Author-email: jaded.encoding.thaumaturgy@gmail.com
7
+ Maintainer: Jaded Encoding Thaumaturgy
8
+ Maintainer-email: jaded.encoding.thaumaturgy@gmail.com
9
+ Project-URL: Source Code, https://github.com/Jaded-Encoding-Thaumaturgy/jetpytools
10
+ Project-URL: Contact, https://discord.gg/XTpc6Fa9eB
11
+ Classifier: Natural Language :: English
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Other Audience
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Typing :: Typed
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Dynamic: author
22
+ Dynamic: author-email
23
+ Dynamic: classifier
24
+ Dynamic: description
25
+ Dynamic: description-content-type
26
+ Dynamic: maintainer
27
+ Dynamic: maintainer-email
28
+ Dynamic: project-url
29
+ Dynamic: requires-python
30
+ Dynamic: summary
31
+
32
+ # jetpytools
33
+
34
+ Collection of stuff that's useful in general python programming
35
+
36
+ ## How to install
37
+
38
+ Install `jetpytools` with the following command:
39
+
40
+ ```sh
41
+ pip install jetpytools
42
+ ```
43
+
44
+ Or if you want the latest git version, install it with this command:
45
+
46
+ ```sh
47
+ pip install git+https://github.com/Jaded-Encoding-Thaumaturgy/jetpytools.git
48
+ ```
@@ -0,0 +1,17 @@
1
+ # jetpytools
2
+
3
+ Collection of stuff that's useful in general python programming
4
+
5
+ ## How to install
6
+
7
+ Install `jetpytools` with the following command:
8
+
9
+ ```sh
10
+ pip install jetpytools
11
+ ```
12
+
13
+ Or if you want the latest git version, install it with this command:
14
+
15
+ ```sh
16
+ pip install git+https://github.com/Jaded-Encoding-Thaumaturgy/jetpytools.git
17
+ ```
@@ -0,0 +1,5 @@
1
+ from .enums import * # noqa: F401, F403
2
+ from .exceptions import * # noqa: F401, F403
3
+ from .functions import * # noqa: F401, F403
4
+ from .types import * # noqa: F401, F403
5
+ from .utils import * # noqa: F401, F403
@@ -0,0 +1,12 @@
1
+ """Collection of stuff that's useful in general python programming"""
2
+
3
+ __version__ = '1.2.3'
4
+
5
+ __author_name__, __author_email__ = 'Jaded Encoding Thaumaturgy', 'jaded.encoding.thaumaturgy@gmail.com'
6
+ __maintainer_name__, __maintainer_email__ = __author_name__, __author_email__
7
+
8
+ __author__ = f'{__author_name__} <{__author_email__}>'
9
+ __maintainer__ = __author__
10
+
11
+ if __name__ == '__github__':
12
+ print(__version__)
@@ -0,0 +1,2 @@
1
+ from .base import * # noqa: F401, F403
2
+ from .other import * # noqa: F401, F403
@@ -0,0 +1,78 @@
1
+ from __future__ import annotations
2
+
3
+ from enum import Enum
4
+ from typing import Any, TypeVar
5
+
6
+ from ..exceptions import CustomValueError, NotFoundEnumValue
7
+ from ..types import FuncExceptT
8
+
9
+ __all__ = [
10
+ 'SelfEnum',
11
+ 'CustomEnum', 'CustomIntEnum', 'CustomStrEnum'
12
+ ]
13
+
14
+
15
+ class CustomEnum(Enum):
16
+ """Base class for custom enums."""
17
+
18
+ @classmethod
19
+ def _missing_(cls: type[SelfEnum], value: Any) -> SelfEnum | None:
20
+ return cls.from_param(value)
21
+
22
+ @classmethod
23
+ def from_param(cls: type[SelfEnum], value: Any, func_except: FuncExceptT | None = None) -> SelfEnum | None:
24
+ """
25
+ Return the enum value from a parameter.
26
+
27
+ :param value: Value to instantiate the enum class.
28
+ :param func_except: Exception function.
29
+
30
+ :return: Enum value.
31
+
32
+ :raises NotFoundEnumValue: Variable not found in the given enum.
33
+ """
34
+
35
+ if value is None:
36
+ return None
37
+
38
+ if func_except is None:
39
+ func_except = cls.from_param
40
+
41
+ if isinstance(value, cls):
42
+ return value
43
+
44
+ if value is cls:
45
+ raise CustomValueError('You must select a member, not pass the enum!', func_except)
46
+
47
+ try:
48
+ return cls(value)
49
+ except Exception:
50
+ pass
51
+
52
+ if isinstance(func_except, tuple):
53
+ func_name, var_name = func_except
54
+ else:
55
+ func_name, var_name = func_except, str(cls)
56
+
57
+ raise NotFoundEnumValue(
58
+ 'The given value for "{var_name}" argument must be a valid {enum_name}, not "{value}"!\n'
59
+ 'Valid values are: [{readable_enum}].', func_name,
60
+ var_name=var_name, enum_name=cls, value=value,
61
+ readable_enum=iter([f'{x.name} ({x.value})' for x in cls]),
62
+ reason=value
63
+ )
64
+
65
+
66
+ class CustomIntEnum(int, CustomEnum):
67
+ """Base class for custom int enums."""
68
+
69
+ value: int
70
+
71
+
72
+ class CustomStrEnum(str, CustomEnum):
73
+ """Base class for custom str enums."""
74
+
75
+ value: str
76
+
77
+
78
+ SelfEnum = TypeVar('SelfEnum', bound=CustomEnum)
@@ -0,0 +1,59 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TypeVar, overload
4
+
5
+ __all__ = [
6
+ 'Coordinate',
7
+ 'Position',
8
+ 'Size'
9
+ ]
10
+
11
+
12
+ class Coordinate:
13
+ """
14
+ Positive set of (x, y) coordinates.
15
+
16
+ :raises ValueError: Negative values were passed.
17
+ """
18
+
19
+ x: int
20
+ """Horizontal coordinate."""
21
+
22
+ y: int
23
+ """Vertical coordinate."""
24
+
25
+ @overload
26
+ def __init__(self: SelfCoord, other: tuple[int, int] | SelfCoord, /) -> None:
27
+ ...
28
+
29
+ @overload
30
+ def __init__(self: SelfCoord, x: int, y: int, /) -> None:
31
+ ...
32
+
33
+ def __init__(self: SelfCoord, x_or_self: int | tuple[int, int] | SelfCoord, y: int | None = None, /) -> None:
34
+ from ..exceptions import CustomValueError
35
+
36
+ if isinstance(x_or_self, int):
37
+ x = x_or_self
38
+ else:
39
+ x, y = x_or_self if isinstance(x_or_self, tuple) else (x_or_self.x, x_or_self.y)
40
+
41
+ if y is None:
42
+ raise CustomValueError("y coordinate must be defined!", self.__class__)
43
+
44
+ if x < 0 or y < 0:
45
+ raise CustomValueError("Values can't be negative!", self.__class__)
46
+
47
+ self.x = x
48
+ self.y = y
49
+
50
+
51
+ SelfCoord = TypeVar('SelfCoord', bound=Coordinate)
52
+
53
+
54
+ class Position(Coordinate):
55
+ """Positive set of an (x,y) offset relative to the top left corner of an area."""
56
+
57
+
58
+ class Size(Coordinate):
59
+ """Positive set of an (x,y), (horizontal,vertical), size of an area."""
@@ -0,0 +1,5 @@
1
+ from .base import * # noqa: F401, F403
2
+ from .enum import * # noqa: F401, F403
3
+ from .file import * # noqa: F401, F403
4
+ from .generic import * # noqa: F401, F403
5
+ from .module import * # noqa: F401, F403
@@ -0,0 +1,213 @@
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+ from copy import deepcopy
5
+ from typing import TYPE_CHECKING, Any, TypeVar
6
+
7
+ from ..types import MISSING, FuncExceptT, SupportsString
8
+
9
+ __all__ = [
10
+ 'CustomError',
11
+
12
+ 'CustomValueError',
13
+ 'CustomIndexError',
14
+ 'CustomOverflowError',
15
+ 'CustomKeyError',
16
+ 'CustomTypeError',
17
+ 'CustomRuntimeError',
18
+ 'CustomNotImplementedError',
19
+ 'CustomPermissionError'
20
+ ]
21
+
22
+
23
+ if TYPE_CHECKING:
24
+ class ExceptionT(Exception):
25
+ __name__: str
26
+ __qualname__: str
27
+ ...
28
+ else:
29
+ ExceptionT = Exception
30
+
31
+
32
+ class CustomErrorMeta(type):
33
+ """Custom base exception meta class."""
34
+
35
+ def __new__(cls: type[SelfCErrorMeta], *args: Any) -> SelfCErrorMeta:
36
+ return CustomErrorMeta.setup_exception(type.__new__(cls, *args)) # type: ignore
37
+
38
+ @staticmethod
39
+ def setup_exception(exception: SelfCErrorMeta, override: str | ExceptionT | None = None) -> SelfCErrorMeta:
40
+ """
41
+ Setup an exception for later use in CustomError.
42
+
43
+ :param exception: Exception to update.
44
+ :param override: Optional name or exception from which get the override values.
45
+
46
+ :return: Set up exception.
47
+ """
48
+
49
+ if override:
50
+ if isinstance(override, str):
51
+ over_name = over_qual = override
52
+ else:
53
+ over_name, over_qual = override.__name__, override.__qualname__
54
+
55
+ if over_name.startswith('Custom'):
56
+ exception.__name__ = over_name
57
+ else:
58
+ exception.__name__ = f'Custom{over_name}'
59
+
60
+ exception.__qualname__ = over_qual
61
+
62
+ if exception.__qualname__.startswith('Custom'):
63
+ exception.__qualname__ = exception.__qualname__[6:]
64
+
65
+ if sys.stdout and sys.stdout.isatty():
66
+ exception.__qualname__ = f'\033[0;31;1m{exception.__qualname__}\033[0m'
67
+
68
+ exception.__module__ = Exception.__module__
69
+
70
+ return exception
71
+
72
+ if TYPE_CHECKING:
73
+ def __getitem__(self, exception: type[Exception]) -> CustomError:
74
+ ...
75
+
76
+
77
+ SelfCErrorMeta = TypeVar('SelfCErrorMeta', bound=CustomErrorMeta)
78
+
79
+
80
+ class CustomError(ExceptionT, metaclass=CustomErrorMeta):
81
+ """Custom base exception class."""
82
+
83
+ def __init__(
84
+ self, message: SupportsString | None = None, func: FuncExceptT | None = None, reason: Any = None, **kwargs: Any
85
+ ) -> None:
86
+ """
87
+ Instantiate a new exception with pretty printing and more.
88
+
89
+ :param message: Message of the error.
90
+ :param func: Function this exception was raised from.
91
+ :param reason: Reason of the exception. For example, an optional parameter.
92
+ """
93
+
94
+ self.message = message
95
+ self.func = func
96
+ self.reason = reason
97
+ self.kwargs = kwargs
98
+
99
+ super().__init__(message)
100
+
101
+ def __class_getitem__(cls, exception: str | type[ExceptionT] | ExceptionT) -> CustomError:
102
+ if isinstance(exception, str):
103
+ class inner_exception(cls): # type: ignore
104
+ ...
105
+ else:
106
+ if not issubclass(exception, type): # type: ignore
107
+ exception = exception.__class__ # type: ignore
108
+
109
+ class inner_exception(cls, exception): # type: ignore
110
+ ...
111
+
112
+ return CustomErrorMeta.setup_exception(inner_exception, exception) # type: ignore
113
+
114
+ def __call__(
115
+ self: SelfError, message: SupportsString | None = MISSING,
116
+ func: FuncExceptT | None = MISSING, reason: SupportsString | FuncExceptT | None = MISSING, # type: ignore
117
+ **kwargs: Any
118
+ ) -> SelfError:
119
+ """
120
+ Copy an existing exception with defaults and instantiate a new one.
121
+
122
+ :param message: Message of the error.
123
+ :param func: Function this exception was raised from.
124
+ :param reason: Reason of the exception. For example, an optional parameter.
125
+ """
126
+
127
+ err = deepcopy(self)
128
+
129
+ if message is not MISSING:
130
+ err.message = message
131
+
132
+ if func is not MISSING: # type: ignore[comparison-overlap]
133
+ err.func = func
134
+
135
+ if reason is not MISSING:
136
+ err.reason = reason
137
+
138
+ err.kwargs |= kwargs
139
+
140
+ return err
141
+
142
+ def __str__(self) -> str:
143
+ from ..functions import norm_display_name, norm_func_name
144
+
145
+ message = self.message
146
+
147
+ if not message:
148
+ message = 'An error occurred!'
149
+
150
+ if self.func:
151
+ func_header = norm_func_name(self.func).strip()
152
+
153
+ if sys.stdout and sys.stdout.isatty():
154
+ func_header = f'\033[0;36m{func_header}\033[0m'
155
+
156
+ func_header = f'({func_header}) '
157
+ else:
158
+ func_header = ''
159
+
160
+ if self.kwargs:
161
+ self.kwargs = {
162
+ key: norm_display_name(value) for key, value in self.kwargs.items()
163
+ }
164
+
165
+ if self.reason:
166
+ reason = self.reason = norm_display_name(self.reason)
167
+
168
+ if reason:
169
+ if not isinstance(self.reason, dict):
170
+ reason = f'({reason})'
171
+
172
+ if sys.stdout and sys.stdout.isatty():
173
+ reason = f'\033[0;33m{reason}\033[0m'
174
+ reason = f' {reason}'
175
+ else:
176
+ reason = ''
177
+
178
+ return f'{func_header}{self.message!s}{reason}'.format(**self.kwargs).strip()
179
+
180
+
181
+ SelfError = TypeVar('SelfError', bound=CustomError)
182
+
183
+
184
+ class CustomValueError(CustomError, ValueError):
185
+ """Thrown when a specified value is invalid."""
186
+
187
+
188
+ class CustomIndexError(CustomError, IndexError):
189
+ """Thrown when an index or generic numeric value is out of bound."""
190
+
191
+
192
+ class CustomOverflowError(CustomError, OverflowError):
193
+ """Thrown when a value is out of range. e.g. temporal radius too big."""
194
+
195
+
196
+ class CustomKeyError(CustomError, KeyError):
197
+ """Thrown when trying to access an non-existent key."""
198
+
199
+
200
+ class CustomTypeError(CustomError, TypeError):
201
+ """Thrown when a passed argument is of wrong type."""
202
+
203
+
204
+ class CustomRuntimeError(CustomError, RuntimeError):
205
+ """Thrown when a runtime error occurs."""
206
+
207
+
208
+ class CustomNotImplementedError(CustomError, NotImplementedError):
209
+ """Thrown when you encounter a yet not implemented branch of code."""
210
+
211
+
212
+ class CustomPermissionError(CustomError, PermissionError):
213
+ """Thrown when the user can't perform an action."""
@@ -0,0 +1,11 @@
1
+ from __future__ import annotations
2
+
3
+ from .base import CustomKeyError
4
+
5
+ __all__ = [
6
+ 'NotFoundEnumValue'
7
+ ]
8
+
9
+
10
+ class NotFoundEnumValue(CustomKeyError):
11
+ """Raised when you try to instantiate an Enum with unknown value"""
@@ -0,0 +1,38 @@
1
+ from __future__ import annotations
2
+
3
+ from .base import CustomError, CustomPermissionError
4
+
5
+
6
+ __all__ = [
7
+ 'FileNotExistsError',
8
+ 'FileWasNotFoundError',
9
+ 'FilePermissionError',
10
+ 'FileTypeMismatchError',
11
+ 'FileIsADirectoryError',
12
+
13
+ 'PathIsNotADirectoryError',
14
+ ]
15
+
16
+
17
+ class FileNotExistsError(CustomError, FileExistsError):
18
+ """Raised when a file doesn't exists"""
19
+
20
+
21
+ class FileWasNotFoundError(CustomError, FileNotFoundError):
22
+ """Raised when a file wasn't found but the path is correct, e.g. parent directory exists"""
23
+
24
+
25
+ class FilePermissionError(CustomPermissionError):
26
+ """Raised when you try to access a file but haven't got permissions to do so"""
27
+
28
+
29
+ class FileTypeMismatchError(CustomError, OSError):
30
+ """Raised when you try to access a file with a FileType != AUTO and it's another file type"""
31
+
32
+
33
+ class FileIsADirectoryError(CustomError, IsADirectoryError):
34
+ """Raised when you try to access a file but it's a directory instead"""
35
+
36
+
37
+ class PathIsNotADirectoryError(CustomError, NotADirectoryError):
38
+ """Raised when you try to access a directory but it's not a directory"""
@@ -0,0 +1,45 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Iterable
4
+
5
+ from ..types import FuncExceptT, SupportsString, T
6
+ from .base import CustomValueError
7
+
8
+ __all__ = [
9
+ 'MismatchError', 'MismatchRefError'
10
+ ]
11
+
12
+
13
+ class MismatchError(CustomValueError):
14
+ """Raised when there's a mismatch between two or more values."""
15
+
16
+ @classmethod
17
+ def _item_to_name(cls, item: Any) -> str:
18
+ return str(item)
19
+
20
+ @classmethod
21
+ def _reduce(cls, items: Iterable[Any]) -> tuple[str]:
22
+ return tuple[str](dict.fromkeys(map(cls._item_to_name, items)).keys()) # type: ignore
23
+
24
+ def __init__(
25
+ self, func: FuncExceptT, items: Iterable[Any], message: SupportsString = 'All items must be equal!',
26
+ reason: Any = '{reduced_items}', **kwargs: Any
27
+ ) -> None:
28
+ super().__init__(message, func, reason, **kwargs, reduced_items=iter(self._reduce(items)))
29
+
30
+ @classmethod
31
+ def check(cls, func: FuncExceptT, *items: Any, **kwargs: Any) -> None:
32
+ if len(cls._reduce(items)) != 1:
33
+ raise cls(func, items, **kwargs)
34
+
35
+
36
+ class MismatchRefError(MismatchError):
37
+ def __init__(
38
+ self, func: FuncExceptT, base: T, ref: T, message: SupportsString = 'All items must be equal!', **kwargs: Any
39
+ ) -> None:
40
+ super().__init__(func, [base, ref], message, **kwargs)
41
+
42
+ @classmethod
43
+ def check(cls, func: FuncExceptT, *items: Any, **kwargs: Any) -> None:
44
+ if len(cls._reduce(items)) != 1:
45
+ raise cls(func, *items, **kwargs)
@@ -0,0 +1,39 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from ..types import FuncExceptT, SupportsString
6
+ from .base import CustomError
7
+
8
+ __all__ = [
9
+ 'CustomImportError',
10
+ 'DependencyNotFoundError'
11
+ ]
12
+
13
+
14
+ class CustomImportError(CustomError, ImportError):
15
+ """Raised when there's a general import error."""
16
+
17
+ def __init__(
18
+ self, func: FuncExceptT, package: str | ImportError,
19
+ message: SupportsString = "Import failed for package '{package}'!",
20
+ **kwargs: Any
21
+ ) -> None:
22
+ """
23
+ :param func: Function this error was raised from.
24
+ :param package: Either the raised error or the name of the missing package.
25
+ :param message: Custom error message.
26
+ """
27
+
28
+ super().__init__(message, func, package=package if isinstance(package, str) else package.name, **kwargs)
29
+
30
+
31
+ class DependencyNotFoundError(CustomImportError):
32
+ """Raised when there's a missing optional dependency."""
33
+
34
+ def __init__(
35
+ self, func: FuncExceptT, package: str | ImportError,
36
+ message: SupportsString = "Missing dependency '{package}'!",
37
+ **kwargs: Any
38
+ ) -> None:
39
+ super().__init__(func, package, message, **kwargs)
@@ -0,0 +1,3 @@
1
+ from .funcs import * # noqa: F401, F403
2
+ from .normalize import * # noqa: F401, F403
3
+ from .other import * # noqa: F401, F403