jetpytools 1.2.3__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.

Potentially problematic release.


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

jetpytools/__init__.py ADDED
@@ -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
@@ -0,0 +1,152 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Callable, Concatenate, overload
4
+
5
+ from ..exceptions import CustomRuntimeError
6
+ from ..types import MISSING, KwargsT, MissingT, P, R, T
7
+
8
+ __all__ = [
9
+ 'iterate', 'fallback', 'kwargs_fallback'
10
+ ]
11
+
12
+
13
+ def iterate(
14
+ base: T, function: Callable[Concatenate[T | R, P], R],
15
+ count: int, *args: P.args, **kwargs: P.kwargs
16
+ ) -> T | R:
17
+ """
18
+ Execute a given function over the base value multiple times.
19
+
20
+ Different from regular iteration functions is that you do not need to pass a partial object.
21
+ This function accepts *args and **kwargs. These will be passed on to the given function.
22
+
23
+ Examples:
24
+
25
+ >>> iterate(5, lambda x: x * 2, 2)
26
+ 20
27
+
28
+ :param base: Base value, etc. to iterate over.
29
+ :param function: Function to iterate over the base.
30
+ :param count: Number of times to execute function.
31
+ :param *args: Positional arguments to pass to the given function.
32
+ :param **kwargs: Keyword arguments to pass to the given function.
33
+
34
+ :return: Value, etc. with the given function run over it
35
+ *n* amount of times based on the given count.
36
+ """
37
+
38
+ if count <= 0:
39
+ return base
40
+
41
+ result: T | R = base
42
+
43
+ for _ in range(count):
44
+ result = function(result, *args, **kwargs)
45
+
46
+ return result
47
+
48
+
49
+ fallback_missing = object()
50
+
51
+
52
+ @overload
53
+ def fallback(value: T | None, fallback: T, /) -> T:
54
+ ...
55
+
56
+
57
+ @overload
58
+ def fallback(value: T | None, fallback0: T | None, default: T, /) -> T:
59
+ ...
60
+
61
+
62
+ @overload
63
+ def fallback(value: T | None, fallback0: T | None, fallback1: T | None, default: T, /) -> T:
64
+ ...
65
+
66
+
67
+ @overload
68
+ def fallback(value: T | None, *fallbacks: T | None) -> T | MissingT:
69
+ ...
70
+
71
+
72
+ @overload
73
+ def fallback(value: T | None, *fallbacks: T | None, default: T) -> T:
74
+ ...
75
+
76
+
77
+ def fallback(value: T | None, *fallbacks: T | None, default: Any | T = fallback_missing) -> T | MissingT:
78
+ """
79
+ Utility function that returns a value or a fallback if the value is None.
80
+
81
+ Example:
82
+
83
+ .. code-block:: python
84
+
85
+ >>> fallback(5, 6)
86
+ 5
87
+ >>> fallback(None, 6)
88
+ 6
89
+
90
+ :param value: Input value to evaluate. Can be None.
91
+ :param fallback_value: Value to return if the input value is None.
92
+
93
+ :return: Input value or fallback value if input value is None.
94
+ """
95
+
96
+ if value is not None:
97
+ return value
98
+
99
+ for fallback in fallbacks:
100
+ if fallback is not None:
101
+ return fallback
102
+
103
+ if default is not fallback_missing:
104
+ return default
105
+ elif len(fallbacks) > 3:
106
+ return MISSING
107
+
108
+ raise CustomRuntimeError('You need to specify a default/fallback value!')
109
+
110
+
111
+ @overload
112
+ def kwargs_fallback(
113
+ input_value: T | None, kwargs: tuple[KwargsT, str], fallback: T
114
+ ) -> T:
115
+ ...
116
+
117
+
118
+ @overload
119
+ def kwargs_fallback(
120
+ input_value: T | None, kwargs: tuple[KwargsT, str], fallback0: T | None, default: T
121
+ ) -> T:
122
+ ...
123
+
124
+
125
+ @overload
126
+ def kwargs_fallback(
127
+ input_value: T | None, kwargs: tuple[KwargsT, str], fallback0: T | None, fallback1: T | None,
128
+ default: T
129
+ ) -> T:
130
+ ...
131
+
132
+
133
+ @overload
134
+ def kwargs_fallback(
135
+ input_value: T | None, kwargs: tuple[KwargsT, str], *fallbacks: T | None
136
+ ) -> T | MissingT:
137
+ ...
138
+
139
+
140
+ @overload
141
+ def kwargs_fallback(
142
+ input_value: T | None, kwargs: tuple[KwargsT, str], *fallbacks: T | None, default: T
143
+ ) -> T:
144
+ ...
145
+
146
+
147
+ def kwargs_fallback( # type: ignore
148
+ value: T | None, kwargs: tuple[KwargsT, str], *fallbacks: T | None, default: T = fallback_missing # type: ignore
149
+ ) -> T | MissingT:
150
+ """Utility function to return a fallback value from kwargs if value was not found or is None."""
151
+
152
+ return fallback(value, kwargs[0].get(kwargs[1], None), *fallbacks, default=default)