jetpytools 2.2.0__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.
- jetpytools-2.2.0/.gitignore +233 -0
- jetpytools-2.2.0/LICENSE +21 -0
- jetpytools-2.2.0/PKG-INFO +34 -0
- jetpytools-2.2.0/README.md +13 -0
- jetpytools-2.2.0/jetpytools/__init__.py +16 -0
- jetpytools-2.2.0/jetpytools/_version.py +2 -0
- jetpytools-2.2.0/jetpytools/enums/__init__.py +2 -0
- jetpytools-2.2.0/jetpytools/enums/base.py +95 -0
- jetpytools-2.2.0/jetpytools/enums/other.py +52 -0
- jetpytools-2.2.0/jetpytools/exceptions/__init__.py +5 -0
- jetpytools-2.2.0/jetpytools/exceptions/base.py +205 -0
- jetpytools-2.2.0/jetpytools/exceptions/enum.py +12 -0
- jetpytools-2.2.0/jetpytools/exceptions/file.py +36 -0
- jetpytools-2.2.0/jetpytools/exceptions/generic.py +47 -0
- jetpytools-2.2.0/jetpytools/exceptions/module.py +40 -0
- jetpytools-2.2.0/jetpytools/functions/__init__.py +3 -0
- jetpytools-2.2.0/jetpytools/functions/funcs.py +133 -0
- jetpytools-2.2.0/jetpytools/functions/normalize.py +296 -0
- jetpytools-2.2.0/jetpytools/functions/other.py +16 -0
- jetpytools-2.2.0/jetpytools/py.typed +0 -0
- jetpytools-2.2.0/jetpytools/types/__init__.py +7 -0
- jetpytools-2.2.0/jetpytools/types/builtins.py +37 -0
- jetpytools-2.2.0/jetpytools/types/check.py +35 -0
- jetpytools-2.2.0/jetpytools/types/file.py +243 -0
- jetpytools-2.2.0/jetpytools/types/funcs.py +97 -0
- jetpytools-2.2.0/jetpytools/types/generic.py +43 -0
- jetpytools-2.2.0/jetpytools/types/supports.py +128 -0
- jetpytools-2.2.0/jetpytools/types/utils.py +914 -0
- jetpytools-2.2.0/jetpytools/utils/__init__.py +4 -0
- jetpytools-2.2.0/jetpytools/utils/file.py +121 -0
- jetpytools-2.2.0/jetpytools/utils/funcs.py +33 -0
- jetpytools-2.2.0/jetpytools/utils/math.py +169 -0
- jetpytools-2.2.0/jetpytools/utils/ranges.py +44 -0
- jetpytools-2.2.0/pyproject.toml +89 -0
- jetpytools-2.2.0/tests/test_file.py +9 -0
- jetpytools-2.2.0/tests/test_funcs.py +20 -0
- jetpytools-2.2.0/tests/test_normalize.py +49 -0
- jetpytools-2.2.0/tests/test_types_utils.py +235 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
# Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
# poetry.lock
|
|
109
|
+
# poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
# pdm.lock
|
|
116
|
+
# pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
# pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# Redis
|
|
135
|
+
*.rdb
|
|
136
|
+
*.aof
|
|
137
|
+
*.pid
|
|
138
|
+
|
|
139
|
+
# RabbitMQ
|
|
140
|
+
mnesia/
|
|
141
|
+
rabbitmq/
|
|
142
|
+
rabbitmq-data/
|
|
143
|
+
|
|
144
|
+
# ActiveMQ
|
|
145
|
+
activemq-data/
|
|
146
|
+
|
|
147
|
+
# SageMath parsed files
|
|
148
|
+
*.sage.py
|
|
149
|
+
|
|
150
|
+
# Environments
|
|
151
|
+
.env
|
|
152
|
+
.envrc
|
|
153
|
+
.venv
|
|
154
|
+
env/
|
|
155
|
+
venv/
|
|
156
|
+
ENV/
|
|
157
|
+
env.bak/
|
|
158
|
+
venv.bak/
|
|
159
|
+
|
|
160
|
+
# Spyder project settings
|
|
161
|
+
.spyderproject
|
|
162
|
+
.spyproject
|
|
163
|
+
|
|
164
|
+
# Rope project settings
|
|
165
|
+
.ropeproject
|
|
166
|
+
|
|
167
|
+
# mkdocs documentation
|
|
168
|
+
.cache/
|
|
169
|
+
/site
|
|
170
|
+
|
|
171
|
+
# mypy
|
|
172
|
+
.mypy_cache/
|
|
173
|
+
.dmypy.json
|
|
174
|
+
dmypy.json
|
|
175
|
+
|
|
176
|
+
# Pyre type checker
|
|
177
|
+
.pyre/
|
|
178
|
+
|
|
179
|
+
# pytype static type analyzer
|
|
180
|
+
.pytype/
|
|
181
|
+
|
|
182
|
+
# Cython debug symbols
|
|
183
|
+
cython_debug/
|
|
184
|
+
|
|
185
|
+
# PyCharm
|
|
186
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
187
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
188
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
189
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
190
|
+
# .idea/
|
|
191
|
+
|
|
192
|
+
# Abstra
|
|
193
|
+
# Abstra is an AI-powered process automation framework.
|
|
194
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
195
|
+
# Learn more at https://abstra.io/docs
|
|
196
|
+
.abstra/
|
|
197
|
+
|
|
198
|
+
# Visual Studio Code
|
|
199
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
200
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
201
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
202
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
203
|
+
# .vscode/
|
|
204
|
+
|
|
205
|
+
# Ruff stuff:
|
|
206
|
+
.ruff_cache/
|
|
207
|
+
|
|
208
|
+
# PyPI configuration file
|
|
209
|
+
.pypirc
|
|
210
|
+
|
|
211
|
+
# Marimo
|
|
212
|
+
marimo/_static/
|
|
213
|
+
marimo/_lsp/
|
|
214
|
+
__marimo__/
|
|
215
|
+
|
|
216
|
+
# Streamlit
|
|
217
|
+
.streamlit/secrets.toml
|
|
218
|
+
|
|
219
|
+
# versioningit
|
|
220
|
+
jetpytools/_version.py
|
|
221
|
+
|
|
222
|
+
# DEVELOPERS:
|
|
223
|
+
# Before adding a file directory to this .gitignore,
|
|
224
|
+
# check if it should maybe go in your local .gitignore
|
|
225
|
+
# (.git/info/exclude), or your own user-wide .gitignore
|
|
226
|
+
# (https://sebastiandedeyne.com/setting-up-a-global-gitignore-file/)
|
|
227
|
+
# instead.
|
|
228
|
+
# Repository-specific files (Python cache files, build artifacts,
|
|
229
|
+
# coverage reports, etc.) should go in here, user-specific files
|
|
230
|
+
# (IDE folders, video index files, etc.) shouldn't.
|
|
231
|
+
|
|
232
|
+
# JET files
|
|
233
|
+
.vsjet/
|
jetpytools-2.2.0/LICENSE
ADDED
|
@@ -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,34 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jetpytools
|
|
3
|
+
Version: 2.2.0
|
|
4
|
+
Summary: Collection of stuff that's useful in general python programming
|
|
5
|
+
Project-URL: Source Code, https://github.com/Jaded-Encoding-Thaumaturgy/jetpytools
|
|
6
|
+
Project-URL: Contact, https://discord.gg/XTpc6Fa9eB
|
|
7
|
+
Author: Jaded Encoding Thaumaturgy
|
|
8
|
+
Maintainer-email: Jaded Encoding Thaumaturgy <jaded.encoding.thaumaturgy@gmail.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Natural Language :: English
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Typing :: Typed
|
|
18
|
+
Requires-Python: >=3.12
|
|
19
|
+
Requires-Dist: typing-extensions>=4.15.0; python_version < '3.13'
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# jetpytools
|
|
23
|
+
|
|
24
|
+
[](https://pypi.org/project/jetpytools/)
|
|
25
|
+
|
|
26
|
+
A collection of utilities useful for general Python programming.
|
|
27
|
+
|
|
28
|
+
## How to install
|
|
29
|
+
|
|
30
|
+
`jetpytools` is distributed via **PyPI**, and the latest stable release can be installed using:
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
pip install vsjetpack
|
|
34
|
+
```
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# jetpytools
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/jetpytools/)
|
|
4
|
+
|
|
5
|
+
A collection of utilities useful for general Python programming.
|
|
6
|
+
|
|
7
|
+
## How to install
|
|
8
|
+
|
|
9
|
+
`jetpytools` is distributed via **PyPI**, and the latest stable release can be installed using:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
pip install vsjetpack
|
|
13
|
+
```
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""A collection of utilities useful for general Python programming."""
|
|
2
|
+
|
|
3
|
+
from .enums import *
|
|
4
|
+
from .exceptions import *
|
|
5
|
+
from .functions import *
|
|
6
|
+
from .types import *
|
|
7
|
+
from .utils import *
|
|
8
|
+
|
|
9
|
+
__version__: str
|
|
10
|
+
__version_tuple__: tuple[int | str, ...]
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
from ._version import __version__, __version_tuple__
|
|
14
|
+
except ImportError:
|
|
15
|
+
__version__ = "0.0.0+unknown"
|
|
16
|
+
__version_tuple__ = (0, 0, 0, "+unknown")
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABCMeta
|
|
4
|
+
from enum import Enum, EnumMeta, ReprEnum
|
|
5
|
+
from enum import property as enum_property
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Self
|
|
7
|
+
|
|
8
|
+
from ..exceptions import CustomTypeError, NotFoundEnumValueError
|
|
9
|
+
from ..types import FuncExcept, copy_signature
|
|
10
|
+
|
|
11
|
+
__all__ = ["CustomEnum", "CustomIntEnum", "CustomStrEnum", "EnumABCMeta"]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class EnumABCMeta(EnumMeta, ABCMeta):
|
|
15
|
+
"""Metaclass combining EnumMeta and ABCMeta to support abstract enumerations."""
|
|
16
|
+
|
|
17
|
+
@copy_signature(EnumMeta.__new__)
|
|
18
|
+
def __new__(mcls, *args: Any, **kwargs: Any) -> Any:
|
|
19
|
+
enum_cls = super().__new__(mcls, *args, **kwargs)
|
|
20
|
+
|
|
21
|
+
if len(enum_cls) == 0:
|
|
22
|
+
return enum_cls
|
|
23
|
+
|
|
24
|
+
if enum_cls.__abstractmethods__:
|
|
25
|
+
raise CustomTypeError(
|
|
26
|
+
"Can't instantiate abstract class {cls_name} without an implementation "
|
|
27
|
+
"for abstract method{plural} {methods}.",
|
|
28
|
+
enum_cls,
|
|
29
|
+
cls_name=enum_cls.__name__,
|
|
30
|
+
plural="s" if len(enum_cls.__abstractmethods__) > 1 else "",
|
|
31
|
+
methods=", ".join(f"'{n}'" for n in sorted(enum_cls.__abstractmethods__)),
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
return enum_cls
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class CustomEnum(Enum):
|
|
38
|
+
"""Base class for custom enums."""
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def from_param(cls, value: Any, func_except: FuncExcept | None = None) -> Self:
|
|
42
|
+
"""
|
|
43
|
+
Return the enum value from a parameter.
|
|
44
|
+
|
|
45
|
+
:param value: Value to instantiate the enum class.
|
|
46
|
+
:param func_except: Exception function.
|
|
47
|
+
|
|
48
|
+
:return: Enum value.
|
|
49
|
+
|
|
50
|
+
:raises NotFoundEnumValue: Variable not found in the given enum.
|
|
51
|
+
"""
|
|
52
|
+
func_except = func_except or cls.from_param
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
return cls(value)
|
|
56
|
+
except (ValueError, TypeError):
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
if isinstance(func_except, tuple):
|
|
60
|
+
func_name, var_name = func_except
|
|
61
|
+
else:
|
|
62
|
+
func_name, var_name = func_except, repr(cls)
|
|
63
|
+
|
|
64
|
+
raise NotFoundEnumValueError(
|
|
65
|
+
'The given value for "{var_name}" argument must be a valid {enum_name}, not "{value}"!\n'
|
|
66
|
+
"Valid values are: [{readable_enum}].",
|
|
67
|
+
func_name,
|
|
68
|
+
var_name=var_name,
|
|
69
|
+
enum_name=cls,
|
|
70
|
+
value=value,
|
|
71
|
+
readable_enum=(f"{name} ({value!r})" for name, value in cls.__members__.items()),
|
|
72
|
+
reason=value,
|
|
73
|
+
) from None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class CustomIntEnum(int, CustomEnum, ReprEnum):
|
|
77
|
+
"""Base class for custom int enums."""
|
|
78
|
+
|
|
79
|
+
if TYPE_CHECKING:
|
|
80
|
+
_value_: int
|
|
81
|
+
_value2member_map_: dict[int, Enum]
|
|
82
|
+
|
|
83
|
+
@enum_property
|
|
84
|
+
def value(self) -> int: ...
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class CustomStrEnum(str, CustomEnum, ReprEnum):
|
|
88
|
+
"""Base class for custom str enums."""
|
|
89
|
+
|
|
90
|
+
if TYPE_CHECKING:
|
|
91
|
+
_value_: str
|
|
92
|
+
_value2member_map_: dict[str, Enum]
|
|
93
|
+
|
|
94
|
+
@enum_property
|
|
95
|
+
def value(self) -> str: ...
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Self, overload
|
|
4
|
+
|
|
5
|
+
__all__ = ["Coordinate", "Position", "Size"]
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Coordinate:
|
|
9
|
+
"""
|
|
10
|
+
Positive set of (x, y) coordinates.
|
|
11
|
+
|
|
12
|
+
:raises ValueError: Negative values were passed.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
x: int
|
|
16
|
+
"""Horizontal coordinate."""
|
|
17
|
+
|
|
18
|
+
y: int
|
|
19
|
+
"""Vertical coordinate."""
|
|
20
|
+
|
|
21
|
+
@overload
|
|
22
|
+
def __init__(self, other: tuple[int, int] | Self, /) -> None: ...
|
|
23
|
+
|
|
24
|
+
@overload
|
|
25
|
+
def __init__(self, x: int, y: int, /) -> None: ...
|
|
26
|
+
|
|
27
|
+
def __init__(self, x_or_self: int | tuple[int, int] | Self, y: int | None = None, /) -> None:
|
|
28
|
+
if isinstance(x_or_self, int):
|
|
29
|
+
x = x_or_self
|
|
30
|
+
else:
|
|
31
|
+
x, y = x_or_self if isinstance(x_or_self, tuple) else (x_or_self.x, x_or_self.y)
|
|
32
|
+
|
|
33
|
+
if y is None:
|
|
34
|
+
from ..exceptions import CustomValueError
|
|
35
|
+
|
|
36
|
+
raise CustomValueError("y coordinate must be defined!", self.__class__)
|
|
37
|
+
|
|
38
|
+
if x < 0 or y < 0:
|
|
39
|
+
from ..exceptions import CustomValueError
|
|
40
|
+
|
|
41
|
+
raise CustomValueError("Values can't be negative!", self.__class__)
|
|
42
|
+
|
|
43
|
+
self.x = x
|
|
44
|
+
self.y = y
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class Position(Coordinate):
|
|
48
|
+
"""Positive set of an (x,y) offset relative to the top left corner of an area."""
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class Size(Coordinate):
|
|
52
|
+
"""Positive set of an (x,y), (horizontal,vertical), size of an area."""
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from contextlib import AbstractContextManager
|
|
5
|
+
from types import TracebackType
|
|
6
|
+
from typing import Any, Self
|
|
7
|
+
|
|
8
|
+
from ..types import MISSING, FuncExcept, MissingT, SupportsString
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"CustomError",
|
|
12
|
+
"CustomIndexError",
|
|
13
|
+
"CustomKeyError",
|
|
14
|
+
"CustomNotImplementedError",
|
|
15
|
+
"CustomOverflowError",
|
|
16
|
+
"CustomPermissionError",
|
|
17
|
+
"CustomRuntimeError",
|
|
18
|
+
"CustomTypeError",
|
|
19
|
+
"CustomValueError",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class CustomErrorMeta(type):
|
|
24
|
+
"""Custom base exception meta class."""
|
|
25
|
+
|
|
26
|
+
def __new__[MetaSelf: CustomErrorMeta](
|
|
27
|
+
mcls: type[MetaSelf], name: str, bases: tuple[type, ...], namespace: dict[str, Any], /, **kwds: Any
|
|
28
|
+
) -> MetaSelf:
|
|
29
|
+
cls = super().__new__(mcls, name, bases, namespace, **kwds)
|
|
30
|
+
|
|
31
|
+
if cls.__qualname__.startswith("Custom"):
|
|
32
|
+
cls.__qualname__ = cls.__qualname__[6:]
|
|
33
|
+
|
|
34
|
+
if sys.stdout and sys.stdout.isatty():
|
|
35
|
+
cls.__qualname__ = f"\033[0;31;1m{cls.__qualname__}\033[0m"
|
|
36
|
+
|
|
37
|
+
cls.__module__ = Exception.__module__
|
|
38
|
+
|
|
39
|
+
return cls
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class CustomError(Exception, metaclass=CustomErrorMeta):
|
|
43
|
+
"""Custom base exception class."""
|
|
44
|
+
|
|
45
|
+
def __init__(
|
|
46
|
+
self, message: SupportsString | None = None, func: FuncExcept | None = None, reason: Any = None, **kwargs: Any
|
|
47
|
+
) -> None:
|
|
48
|
+
"""
|
|
49
|
+
Instantiate a new exception with pretty printing and more.
|
|
50
|
+
|
|
51
|
+
:param message: Message of the error.
|
|
52
|
+
:param func: Function this exception was raised from.
|
|
53
|
+
:param reason: Reason of the exception. For example, an optional parameter.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
self.message = message
|
|
57
|
+
self.func = func
|
|
58
|
+
self.reason = reason
|
|
59
|
+
self.kwargs = kwargs
|
|
60
|
+
|
|
61
|
+
super().__init__(message)
|
|
62
|
+
|
|
63
|
+
def __call__(
|
|
64
|
+
self,
|
|
65
|
+
message: SupportsString | None | MissingT = MISSING,
|
|
66
|
+
func: FuncExcept | None | MissingT = MISSING,
|
|
67
|
+
reason: SupportsString | FuncExcept | None | MissingT = MISSING,
|
|
68
|
+
**kwargs: Any,
|
|
69
|
+
) -> Self:
|
|
70
|
+
"""
|
|
71
|
+
Copy an existing exception with defaults and instantiate a new one.
|
|
72
|
+
|
|
73
|
+
:param message: Message of the error.
|
|
74
|
+
:param func: Function this exception was raised from.
|
|
75
|
+
:param reason: Reason of the exception. For example, an optional parameter.
|
|
76
|
+
"""
|
|
77
|
+
from copy import deepcopy
|
|
78
|
+
|
|
79
|
+
err = deepcopy(self)
|
|
80
|
+
|
|
81
|
+
if message is not MISSING:
|
|
82
|
+
err.message = message
|
|
83
|
+
|
|
84
|
+
if func is not MISSING:
|
|
85
|
+
err.func = func
|
|
86
|
+
|
|
87
|
+
if reason is not MISSING:
|
|
88
|
+
err.reason = reason
|
|
89
|
+
|
|
90
|
+
err.kwargs |= kwargs
|
|
91
|
+
|
|
92
|
+
return err
|
|
93
|
+
|
|
94
|
+
def __str__(self) -> str:
|
|
95
|
+
from ..functions import norm_display_name, norm_func_name
|
|
96
|
+
|
|
97
|
+
message = self.message
|
|
98
|
+
|
|
99
|
+
if not message:
|
|
100
|
+
message = "An error occurred!"
|
|
101
|
+
|
|
102
|
+
if self.func:
|
|
103
|
+
func_header = norm_func_name(self.func).strip()
|
|
104
|
+
|
|
105
|
+
if sys.stdout and sys.stdout.isatty():
|
|
106
|
+
func_header = f"\033[0;36m{func_header}\033[0m"
|
|
107
|
+
|
|
108
|
+
func_header = f"({func_header}) "
|
|
109
|
+
else:
|
|
110
|
+
func_header = ""
|
|
111
|
+
|
|
112
|
+
if self.kwargs:
|
|
113
|
+
self.kwargs = {key: norm_display_name(value) for key, value in self.kwargs.items()}
|
|
114
|
+
|
|
115
|
+
if self.reason:
|
|
116
|
+
reason = self.reason = norm_display_name(self.reason)
|
|
117
|
+
|
|
118
|
+
if reason:
|
|
119
|
+
if not isinstance(self.reason, dict):
|
|
120
|
+
reason = f"({reason})"
|
|
121
|
+
|
|
122
|
+
if sys.stdout and sys.stdout.isatty():
|
|
123
|
+
reason = f"\033[0;33m{reason}\033[0m"
|
|
124
|
+
reason = f" {reason}"
|
|
125
|
+
else:
|
|
126
|
+
reason = ""
|
|
127
|
+
|
|
128
|
+
out = f"{func_header}{self.message!s}{reason}".format(**self.kwargs).strip()
|
|
129
|
+
|
|
130
|
+
return out
|
|
131
|
+
|
|
132
|
+
@classmethod
|
|
133
|
+
def catch(cls) -> CatchError[Self]:
|
|
134
|
+
"""
|
|
135
|
+
Create a context manager that catches exceptions of this class type.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
CatchError[Self]: A context manager that will catch and store exceptions of type `cls`
|
|
139
|
+
when used in a `with` block.
|
|
140
|
+
"""
|
|
141
|
+
return CatchError(cls)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class CatchError[CustomErrorT: CustomError](AbstractContextManager["CatchError[CustomErrorT]"]):
|
|
145
|
+
"""
|
|
146
|
+
Context manager for catching a specific exception type.
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
error: CustomErrorT | None
|
|
150
|
+
"""The caught exception instance, if any."""
|
|
151
|
+
tb: TracebackType | None
|
|
152
|
+
"""The traceback object associated with the caught exception."""
|
|
153
|
+
|
|
154
|
+
def __init__(self, error: type[CustomErrorT]) -> None:
|
|
155
|
+
self.error = None
|
|
156
|
+
self.tb = None
|
|
157
|
+
self._to_catch_error = error
|
|
158
|
+
|
|
159
|
+
def __enter__(self) -> Self:
|
|
160
|
+
return self
|
|
161
|
+
|
|
162
|
+
def __exit__(
|
|
163
|
+
self,
|
|
164
|
+
exc_type: type[BaseException] | None,
|
|
165
|
+
exc_value: BaseException | None,
|
|
166
|
+
traceback: TracebackType | None,
|
|
167
|
+
) -> bool | None:
|
|
168
|
+
if isinstance(exc_value, self._to_catch_error):
|
|
169
|
+
self.error = exc_value
|
|
170
|
+
self.tb = traceback
|
|
171
|
+
return True
|
|
172
|
+
|
|
173
|
+
return None
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class CustomValueError(CustomError, ValueError):
|
|
177
|
+
"""Thrown when a specified value is invalid."""
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class CustomIndexError(CustomError, IndexError):
|
|
181
|
+
"""Thrown when an index or generic numeric value is out of bound."""
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class CustomOverflowError(CustomError, OverflowError):
|
|
185
|
+
"""Thrown when a value is out of range. e.g. temporal radius too big."""
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class CustomKeyError(CustomError, KeyError):
|
|
189
|
+
"""Thrown when trying to access an non-existent key."""
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class CustomTypeError(CustomError, TypeError):
|
|
193
|
+
"""Thrown when a passed argument is of wrong type."""
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class CustomRuntimeError(CustomError, RuntimeError):
|
|
197
|
+
"""Thrown when a runtime error occurs."""
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class CustomNotImplementedError(CustomError, NotImplementedError):
|
|
201
|
+
"""Thrown when you encounter a yet not implemented branch of code."""
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class CustomPermissionError(CustomError, PermissionError):
|
|
205
|
+
"""Thrown when the user can't perform an action."""
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .base import CustomKeyError
|
|
4
|
+
|
|
5
|
+
__all__ = ["NotFoundEnumValue", "NotFoundEnumValueError"]
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class NotFoundEnumValueError(CustomKeyError):
|
|
9
|
+
"""Raised when you try to instantiate an Enum with unknown value"""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
NotFoundEnumValue = NotFoundEnumValueError
|