wiederverwendbar 0.8.6__py3-none-any.whl → 0.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.
- wiederverwendbar/__init__.py +8 -6
- wiederverwendbar/branding/__init__.py +1 -0
- wiederverwendbar/branding/settings.py +85 -0
- wiederverwendbar/console/__init__.py +3 -0
- wiederverwendbar/console/console.py +199 -0
- wiederverwendbar/console/out_files.py +19 -0
- wiederverwendbar/console/settings.py +9 -0
- wiederverwendbar/default.py +4 -1
- wiederverwendbar/fastapi/__init__.py +3 -0
- wiederverwendbar/fastapi/app.py +385 -0
- wiederverwendbar/fastapi/dependencies.py +11 -0
- wiederverwendbar/fastapi/settings.py +39 -0
- wiederverwendbar/functions/is_coroutine_function.py +19 -0
- wiederverwendbar/inspect.py +26 -0
- wiederverwendbar/logger/__init__.py +0 -1
- wiederverwendbar/logger/handlers/rich_console_handler.py +15 -6
- wiederverwendbar/logger/handlers/stream_console_handler.py +3 -16
- wiederverwendbar/logger/log_levels.py +2 -3
- wiederverwendbar/logger/settings.py +15 -20
- wiederverwendbar/rich/__init__.py +2 -0
- wiederverwendbar/rich/console.py +215 -0
- wiederverwendbar/rich/settings.py +26 -0
- wiederverwendbar/typer/__init__.py +3 -1
- wiederverwendbar/typer/app.py +172 -0
- wiederverwendbar/typer/settings.py +14 -0
- {wiederverwendbar-0.8.6.dist-info → wiederverwendbar-0.9.0.dist-info}/METADATA +9 -6
- {wiederverwendbar-0.8.6.dist-info → wiederverwendbar-0.9.0.dist-info}/RECORD +29 -45
- wiederverwendbar/examples/__init__.py +0 -0
- wiederverwendbar/examples/before_after_wrap.py +0 -74
- wiederverwendbar/examples/colors.py +0 -16
- wiederverwendbar/examples/extended_thread.py +0 -28
- wiederverwendbar/examples/file_config.py +0 -11
- wiederverwendbar/examples/indexable_model.py +0 -19
- wiederverwendbar/examples/logger.py +0 -31
- wiederverwendbar/examples/logger_context/__init__.py +0 -0
- wiederverwendbar/examples/logger_context/example.py +0 -58
- wiederverwendbar/examples/logger_context/example_module.py +0 -13
- wiederverwendbar/examples/mongoengine/__init__.py +0 -0
- wiederverwendbar/examples/mongoengine/automatic_reference.py +0 -25
- wiederverwendbar/examples/mongoengine/db.py +0 -7
- wiederverwendbar/examples/mongoengine/log_streamer.py +0 -9
- wiederverwendbar/examples/mongoengine/logger.py +0 -25
- wiederverwendbar/examples/post_init.py +0 -29
- wiederverwendbar/examples/route.py +0 -12
- wiederverwendbar/examples/singletons.py +0 -59
- wiederverwendbar/examples/sqlalchemy/__init__.py +0 -0
- wiederverwendbar/examples/sqlalchemy/db.py +0 -89
- wiederverwendbar/examples/starlette_admin/__init__.py +0 -0
- wiederverwendbar/examples/starlette_admin/action_log.py +0 -126
- wiederverwendbar/examples/starlette_admin/action_log_file_download.py +0 -99
- wiederverwendbar/examples/starlette_admin/action_log_form.py +0 -149
- wiederverwendbar/examples/starlette_admin/action_log_thread.py +0 -192
- wiederverwendbar/examples/starlette_admin/automatic_reference_admin.py +0 -47
- wiederverwendbar/examples/starlette_admin/generic_embedded_document_field.py +0 -74
- wiederverwendbar/examples/starlette_admin/multi_path_admin.py +0 -18
- wiederverwendbar/examples/task_manager.py +0 -55
- wiederverwendbar/examples/test_file.py +0 -14
- wiederverwendbar/examples/typer_resolve_defaults.py +0 -15
- wiederverwendbar/examples/uvicorn_server.py +0 -32
- wiederverwendbar/logger/terminal_out_files.py +0 -10
- {wiederverwendbar-0.8.6.dist-info → wiederverwendbar-0.9.0.dist-info}/WHEEL +0 -0
- {wiederverwendbar-0.8.6.dist-info → wiederverwendbar-0.9.0.dist-info}/entry_points.txt +0 -0
wiederverwendbar/__init__.py
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
__title__ = "wiederverwendbar"
|
2
|
+
__description__ = "A collection of scripts, classes and tools they are \\\"wiederverwendbar\\\"."
|
3
|
+
__version__ = "0.9.0"
|
4
|
+
__author__ = "Julius Koenig"
|
5
|
+
__author_email__ = "info@bastelquartier.de"
|
6
|
+
__license__ = "GPL-3.0"
|
7
|
+
__license_url__ = "https://www.gnu.org/licenses/gpl-3.0.md"
|
8
|
+
__terms_of_service__ = "https://bastelquartier.de/terms-of-service"
|
@@ -0,0 +1 @@
|
|
1
|
+
from wiederverwendbar.branding.settings import (BrandingSettings)
|
@@ -0,0 +1,85 @@
|
|
1
|
+
import importlib.util
|
2
|
+
import sys
|
3
|
+
from pathlib import Path
|
4
|
+
from typing import Optional, Any, Union
|
5
|
+
from pydantic import BaseModel, Field, computed_field
|
6
|
+
|
7
|
+
from wiederverwendbar.default import Default
|
8
|
+
|
9
|
+
|
10
|
+
class BrandingSettings(BaseModel):
|
11
|
+
branding_title: Union[None, Default, str] = Field(default=Default(), title="Branding Title", description="Branding title.")
|
12
|
+
branding_description: Union[None, Default, str] = Field(default=Default(), title="Branding Description", description="Branding description.")
|
13
|
+
branding_version: Union[None, Default, str] = Field(default=Default(), title="Branding Version", description="Branding version.")
|
14
|
+
branding_author: Union[None, Default, str] = Field(default=Default(), title="Branding Author", description="Branding author.")
|
15
|
+
branding_author_email: Union[None, Default, str] = Field(default=Default(), title="Branding Author Email", description="Branding author email.")
|
16
|
+
branding_license: Union[None, Default, str] = Field(default=Default(), title="Branding License Name", description="Branding license name.")
|
17
|
+
branding_license_url: Union[None, Default, str] = Field(default=Default(), title="Branding License URL", description="Branding license URL.")
|
18
|
+
branding_terms_of_service: Union[None, Default, str] = Field(default=Default(), title="Branding Terms of Service", description="Branding terms of service.")
|
19
|
+
|
20
|
+
def model_post_init(self, context: Any, /):
|
21
|
+
# get attributes from __main__ module
|
22
|
+
main_module = sys.modules["__main__"]
|
23
|
+
module_data = self.get_attributes(main_module.__dict__)
|
24
|
+
if module_data is None:
|
25
|
+
init_file = Path(main_module.__file__).parent / "__init__.py"
|
26
|
+
if init_file.is_file():
|
27
|
+
init_file_module_spec = importlib.util.spec_from_file_location("__main__.__init__", init_file)
|
28
|
+
init_file_module = importlib.util.module_from_spec(init_file_module_spec)
|
29
|
+
sys.modules["__main__.__init__"] = init_file_module
|
30
|
+
init_file_module_spec.loader.exec_module(init_file_module)
|
31
|
+
module_data = self.get_attributes(init_file_module.__dict__)
|
32
|
+
if module_data is None:
|
33
|
+
module_data = {}
|
34
|
+
|
35
|
+
for key in ["branding_title",
|
36
|
+
"branding_description",
|
37
|
+
"branding_version",
|
38
|
+
"branding_author",
|
39
|
+
"branding_author_email",
|
40
|
+
"branding_license",
|
41
|
+
"branding_license_url",
|
42
|
+
"branding_terms_of_service"]:
|
43
|
+
value = module_data.get(key, None)
|
44
|
+
if type(getattr(self, key)) is Default:
|
45
|
+
setattr(self, key, value)
|
46
|
+
|
47
|
+
super().model_post_init(context)
|
48
|
+
|
49
|
+
@classmethod
|
50
|
+
def get_attributes(cls, ns: dict[str, Any]) -> Optional[dict[str, str]]:
|
51
|
+
found_something = False
|
52
|
+
attributes = {}
|
53
|
+
for key, module_key in {"branding_title": "__title__",
|
54
|
+
"branding_description": "__description__",
|
55
|
+
"branding_version": "__version__",
|
56
|
+
"branding_author": "__author__",
|
57
|
+
"branding_author_email": "__author_email__",
|
58
|
+
"branding_license": "__license__",
|
59
|
+
"branding_license_url": "__license_url__",
|
60
|
+
"branding_terms_of_service": "__terms_of_service__"}.items():
|
61
|
+
if module_key in ns:
|
62
|
+
found_something = True
|
63
|
+
attributes[key] = ns[module_key]
|
64
|
+
return attributes if found_something else None
|
65
|
+
|
66
|
+
@computed_field(title="Branding Version Major", description="Branding version major.")
|
67
|
+
@property
|
68
|
+
def branding_version_major(self) -> Optional[int]:
|
69
|
+
if self.branding_version is None:
|
70
|
+
return None
|
71
|
+
return int(self.branding_version.split(".")[0])
|
72
|
+
|
73
|
+
@computed_field(title="Branding Version Minor", description="Branding version minor.")
|
74
|
+
@property
|
75
|
+
def branding_version_minor(self) -> Optional[int]:
|
76
|
+
if self.branding_version is None:
|
77
|
+
return None
|
78
|
+
return int(self.branding_version.split(".")[1])
|
79
|
+
|
80
|
+
@computed_field(title="Branding Version Patch", description="Branding version patch.")
|
81
|
+
@property
|
82
|
+
def branding_version_patch(self) -> Optional[int]:
|
83
|
+
if self.branding_version is None:
|
84
|
+
return None
|
85
|
+
return int(self.branding_version.split(".")[2])
|
@@ -0,0 +1,199 @@
|
|
1
|
+
from typing import Optional, Any, Literal, Union
|
2
|
+
|
3
|
+
from wiederverwendbar.console.out_files import OutFiles
|
4
|
+
from wiederverwendbar.console.settings import ConsoleSettings
|
5
|
+
|
6
|
+
|
7
|
+
class Console:
|
8
|
+
print_function = print
|
9
|
+
print_function_blacklist_kwargs = ["file"]
|
10
|
+
console_border_styles = {
|
11
|
+
"single_line": ["─", "│", "┌", "┐", "└", "┘", "├", "┤"],
|
12
|
+
"double_line": ["═", "║", "╔", "╗", "╚", "╝", "╠", "╣"]
|
13
|
+
}
|
14
|
+
|
15
|
+
def __init__(self,
|
16
|
+
*,
|
17
|
+
console_file: Optional[OutFiles] = None,
|
18
|
+
console_seperator: Optional[str] = None,
|
19
|
+
console_end: Optional[str] = None,
|
20
|
+
settings: Optional[ConsoleSettings] = None):
|
21
|
+
"""
|
22
|
+
Create a new console.
|
23
|
+
|
24
|
+
:param console_file: Console file. Default is STDOUT.
|
25
|
+
:param console_seperator: Console seperator. Default is a space.
|
26
|
+
:param console_end: Console end. Default is a newline.
|
27
|
+
:param settings: A settings object to use. If None, defaults to ConsoleSettings().
|
28
|
+
"""
|
29
|
+
|
30
|
+
if settings is None:
|
31
|
+
settings = ConsoleSettings()
|
32
|
+
|
33
|
+
if console_file is None:
|
34
|
+
console_file = settings.console_file
|
35
|
+
self._console_file = console_file
|
36
|
+
|
37
|
+
if console_seperator is None:
|
38
|
+
console_seperator = settings.console_seperator
|
39
|
+
self._console_seperator = console_seperator
|
40
|
+
|
41
|
+
if console_end is None:
|
42
|
+
console_end = settings.console_end
|
43
|
+
self._console_end = console_end
|
44
|
+
|
45
|
+
def _print_method_kwargs_filter(self, **kwargs) -> dict[str, Any]:
|
46
|
+
for key in kwargs.copy():
|
47
|
+
if key not in self.print_function_blacklist_kwargs:
|
48
|
+
continue
|
49
|
+
del kwargs[key]
|
50
|
+
return kwargs
|
51
|
+
|
52
|
+
def print(self,
|
53
|
+
*args: Any,
|
54
|
+
sep: Optional[str] = None,
|
55
|
+
end: Optional[str] = None,
|
56
|
+
**kwargs) -> None:
|
57
|
+
"""
|
58
|
+
Prints the values.
|
59
|
+
|
60
|
+
:param args: values to be printed.
|
61
|
+
:param sep: string inserted between values, default a space.
|
62
|
+
:param end: string appended after the last value, default a newline.
|
63
|
+
:param kwargs: Additional parameters.
|
64
|
+
"""
|
65
|
+
|
66
|
+
if sep is None:
|
67
|
+
sep = self._console_seperator
|
68
|
+
if end is None:
|
69
|
+
end = self._console_end
|
70
|
+
self.print_function(*args, **self._print_method_kwargs_filter(sep=sep, end=end, file=OutFiles.STDOUT.get_file(), **kwargs))
|
71
|
+
|
72
|
+
def _card_kwargs(self, mode: Literal["text", "header", "border", "print"], **kwargs) -> dict[str, Any]:
|
73
|
+
return {}
|
74
|
+
|
75
|
+
def _card_get_text(self, text: str, **kwargs) -> str:
|
76
|
+
return text
|
77
|
+
|
78
|
+
def _card_get_header_text(self, text: str, **kwargs) -> str:
|
79
|
+
return text
|
80
|
+
|
81
|
+
def _card_get_border(self,
|
82
|
+
border_style: Literal["single_line", "double_line"],
|
83
|
+
border_part: Literal["horizontal", "vertical", "top_left", "top_right", "bottom_left", "bottom_right", "vertical_left", "vertical_right"],
|
84
|
+
**kwargs):
|
85
|
+
border_style = self.console_border_styles[border_style]
|
86
|
+
if border_part == "horizontal":
|
87
|
+
return border_style[0]
|
88
|
+
elif border_part == "vertical":
|
89
|
+
return border_style[1]
|
90
|
+
elif border_part == "top_left":
|
91
|
+
return border_style[2]
|
92
|
+
elif border_part == "top_right":
|
93
|
+
return border_style[3]
|
94
|
+
elif border_part == "bottom_left":
|
95
|
+
return border_style[4]
|
96
|
+
elif border_part == "bottom_right":
|
97
|
+
return border_style[5]
|
98
|
+
elif border_part == "vertical_left":
|
99
|
+
return border_style[6]
|
100
|
+
elif border_part == "vertical_right":
|
101
|
+
return border_style[7]
|
102
|
+
else:
|
103
|
+
raise ValueError(f"Unknown border part '{border_part}'.")
|
104
|
+
|
105
|
+
def card(self,
|
106
|
+
*sections: Union[str, tuple[str, str]],
|
107
|
+
min_width: Optional[int] = None,
|
108
|
+
max_width: Optional[int] = None,
|
109
|
+
border_style: Literal["single_line", "double_line"] = "single_line",
|
110
|
+
topic_offest: int = 1,
|
111
|
+
padding_left: int = 0,
|
112
|
+
padding_right: int = 0,
|
113
|
+
**kwargs) -> None:
|
114
|
+
if min_width and max_width and min_width > max_width:
|
115
|
+
raise ValueError(f"min_width '{min_width}' is greater than max_width '{max_width}'.")
|
116
|
+
if min_width is not None:
|
117
|
+
min_width -= 2
|
118
|
+
if max_width is not None:
|
119
|
+
if max_width < 10:
|
120
|
+
raise ValueError(f"max_width '{max_width}' is smaller than 10.")
|
121
|
+
max_width -= 2
|
122
|
+
|
123
|
+
# get real width
|
124
|
+
real_width = 0
|
125
|
+
if min_width is not None:
|
126
|
+
real_width = min_width
|
127
|
+
for section in sections:
|
128
|
+
section_topic = ""
|
129
|
+
if isinstance(section, tuple):
|
130
|
+
section_topic = section[0]
|
131
|
+
section = section[1]
|
132
|
+
|
133
|
+
# update real with
|
134
|
+
if len(section_topic) + topic_offest > real_width:
|
135
|
+
real_width = len(section_topic) + topic_offest
|
136
|
+
|
137
|
+
for line in section.splitlines():
|
138
|
+
line = " " * padding_left + line + " " * padding_right # add padding
|
139
|
+
# update real with
|
140
|
+
if len(line) > real_width:
|
141
|
+
real_width = len(line)
|
142
|
+
if max_width is not None:
|
143
|
+
if real_width > max_width:
|
144
|
+
real_width = max_width
|
145
|
+
|
146
|
+
# format sections
|
147
|
+
section_topics: list[str] = []
|
148
|
+
formatted_sections: list[list[str]] = []
|
149
|
+
for section in sections:
|
150
|
+
section_topic = ""
|
151
|
+
if isinstance(section, tuple):
|
152
|
+
section_topic = section[0]
|
153
|
+
section = section[1]
|
154
|
+
if section_topic != "":
|
155
|
+
section_topic = " " + section_topic + " "
|
156
|
+
|
157
|
+
# topic max width
|
158
|
+
if len(section_topic) + topic_offest > real_width:
|
159
|
+
section_topic = section_topic[:real_width - topic_offest - 3] + "..."
|
160
|
+
|
161
|
+
section_topics.append(section_topic)
|
162
|
+
formatted_lines: list[str] = []
|
163
|
+
lines = section.splitlines()
|
164
|
+
while len(lines) > 0:
|
165
|
+
line = lines.pop(0)
|
166
|
+
|
167
|
+
# add topic
|
168
|
+
line = " " * padding_left + line # add padding
|
169
|
+
|
170
|
+
# max width
|
171
|
+
if len(line) + padding_right > real_width:
|
172
|
+
lines.insert(0, line[real_width - padding_right:])
|
173
|
+
line = line[:real_width - padding_right] + " " * padding_right
|
174
|
+
else:
|
175
|
+
line = line.ljust(real_width - padding_right) + " " * padding_right
|
176
|
+
|
177
|
+
formatted_lines.append(line)
|
178
|
+
formatted_sections.append(formatted_lines)
|
179
|
+
card = (f"{self._card_get_border(border_style, 'top_left', **kwargs)}"
|
180
|
+
f"{self._card_get_border(border_style, 'horizontal', **kwargs) * topic_offest}"
|
181
|
+
f"{self._card_get_header_text(section_topics[0], **kwargs)}"
|
182
|
+
f"{self._card_get_border(border_style, 'horizontal', **kwargs) * (real_width - len(section_topics.pop(0)) - topic_offest)}"
|
183
|
+
f"{self._card_get_border(border_style, 'top_right', **kwargs)}\n")
|
184
|
+
while len(formatted_sections) > 0:
|
185
|
+
for line in formatted_sections.pop(0):
|
186
|
+
card += (f"{self._card_get_border(border_style, 'vertical', **kwargs)}"
|
187
|
+
f"{self._card_get_text(line, **kwargs)}"
|
188
|
+
f"{self._card_get_border(border_style, 'vertical', **kwargs)}\n")
|
189
|
+
if len(formatted_sections) > 0:
|
190
|
+
card += (f"{self._card_get_border(border_style, 'vertical_left', **kwargs)}"
|
191
|
+
f"{self._card_get_border(border_style, 'horizontal', **kwargs) * topic_offest}"
|
192
|
+
f"{self._card_get_header_text(section_topics[0], **kwargs)}"
|
193
|
+
f"{self._card_get_border(border_style, 'horizontal', **kwargs) * (real_width - len(section_topics.pop(0)) - topic_offest)}"
|
194
|
+
f"{self._card_get_border(border_style, 'vertical_right', **kwargs)}\n")
|
195
|
+
else:
|
196
|
+
card += (f"{self._card_get_border(border_style, 'bottom_left', **kwargs)}"
|
197
|
+
f"{self._card_get_border(border_style, 'horizontal', **kwargs) * real_width}"
|
198
|
+
f"{self._card_get_border(border_style, 'bottom_right', **kwargs)}")
|
199
|
+
return self.print(card, **kwargs)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import sys
|
2
|
+
from enum import Enum
|
3
|
+
from typing import IO, Any, Union
|
4
|
+
|
5
|
+
|
6
|
+
class OutFiles(str, Enum):
|
7
|
+
"""
|
8
|
+
Output files
|
9
|
+
"""
|
10
|
+
|
11
|
+
STDOUT = "stdout"
|
12
|
+
STDERR = "stderr"
|
13
|
+
|
14
|
+
def get_file(self) -> Union[IO[str], Any]:
|
15
|
+
if self == OutFiles.STDOUT:
|
16
|
+
return sys.stdout
|
17
|
+
elif self == OutFiles.STDERR:
|
18
|
+
return sys.stderr
|
19
|
+
raise ValueError(f"Unknown outfile '{self}'.")
|
@@ -0,0 +1,9 @@
|
|
1
|
+
from pydantic import BaseModel, Field
|
2
|
+
|
3
|
+
from wiederverwendbar.console.out_files import OutFiles
|
4
|
+
|
5
|
+
|
6
|
+
class ConsoleSettings(BaseModel):
|
7
|
+
console_file: OutFiles = Field(default=OutFiles.STDOUT, title="Console File", description="The file to write the console output to.")
|
8
|
+
console_seperator: str = Field(default=" ", title="Console Separator", description="The separator to be used between values.")
|
9
|
+
console_end: str = Field(default="\n", title="Console End", description="The end to be used after the last value.")
|
wiederverwendbar/default.py
CHANGED