wiederverwendbar 0.8.4__py3-none-any.whl → 0.8.5__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.
@@ -1,4 +1,4 @@
1
- __version__ = "0.8.4"
1
+ __version__ = "0.8.5"
2
2
  TITLE = "wiederverwendbar"
3
3
  VERSION = __version__
4
4
  AUTHOR = "Julius Koenig"
@@ -0,0 +1,9 @@
1
+ from enum import Enum
2
+
3
+
4
+ def get_pretty_str(value):
5
+ if type(value) is str:
6
+ return f"'{value}'"
7
+ elif isinstance(value, Enum):
8
+ return get_pretty_str(value.value)
9
+ return f"{value}"
@@ -1,3 +1,4 @@
1
+ import warnings
1
2
  from typing import Any, Optional, Callable, Union, TYPE_CHECKING
2
3
 
3
4
  from sqlalchemy import inspect
@@ -6,6 +7,7 @@ from sqlalchemy.orm import Session, QueryableAttribute
6
7
  from sqlalchemy.orm.exc import DetachedInstanceError
7
8
 
8
9
  from wiederverwendbar.default import Default
10
+ from wiederverwendbar.functions.get_pretty_str import get_pretty_str
9
11
  from wiederverwendbar.sqlalchemy.raise_has_not_attr import raise_has_not_attr
10
12
 
11
13
  if TYPE_CHECKING:
@@ -141,7 +143,18 @@ class Base:
141
143
  super().__init__(*args, **kwargs)
142
144
 
143
145
  def __str__(self):
144
- return f"{self.__class__.__name__}({', '.join([f'{column_name}={getattr(self, column_name)}' for column_name in self.__str_columns__])})"
146
+ out = f"{self.__class__.__name__}("
147
+ for attr_name in self.__str_columns__:
148
+ if type(attr_name) is tuple:
149
+ attr_view_name = attr_name[0]
150
+ attr_name = attr_name[1]
151
+ else:
152
+ attr_view_name = attr_name
153
+ if not hasattr(self, attr_name):
154
+ warnings.warn(f"Attribute '{attr_name}' is not set for {self}.")
155
+ out += f"{attr_view_name}={get_pretty_str(getattr(self, attr_name))}, "
156
+ out = out[:-2] + ")"
157
+ return out
145
158
 
146
159
  def __repr__(self):
147
160
  return self.__str__()
@@ -42,7 +42,9 @@ class SqlalchemyDb:
42
42
  username: Optional[str] = None,
43
43
  password: Optional[str] = None,
44
44
  echo: Optional[bool] = None,
45
- sqlite_handle_foreign_keys: bool = True,
45
+ test_on_startup: Optional[bool] = None,
46
+ sqlite_check_if_file_exist: Optional[bool] = None,
47
+ sqlite_handle_foreign_keys: Optional[bool] = None,
46
48
  settings: Optional[SqlalchemySettings] = None):
47
49
  """
48
50
  Create a new Sqlalchemy Database
@@ -54,6 +56,8 @@ class SqlalchemyDb:
54
56
  :param username: User to connect to database
55
57
  :param password: Password to connect to database
56
58
  :param echo: Echo SQL queries to console
59
+ :param test_on_startup: Test the database connection on startup.
60
+ :param sqlite_check_if_file_exist: Check if SQLite file exists before connecting to it.
57
61
  :param sqlite_handle_foreign_keys: Enable SQLite Foreign Keys
58
62
  :param settings: Sqlalchemy Settings
59
63
  """
@@ -67,16 +71,24 @@ class SqlalchemyDb:
67
71
  self._username: Optional[str] = username or self.settings.db_username
68
72
  self._password: Optional[str] = password or self.settings.db_password
69
73
  self._echo: bool = echo or self.settings.db_echo
74
+ self._test_on_startup: bool = test_on_startup or self.settings.db_test_on_startup
75
+ self._sqlite_check_if_file_exist: bool = sqlite_check_if_file_exist or self.settings.db_sqlite_check_if_file_exist
70
76
  self._sqlite_handle_foreign_keys: bool = sqlite_handle_foreign_keys or self.settings.db_sqlite_handle_foreign_keys
71
77
 
72
78
  logger.debug(f"Create {self}")
73
79
 
74
80
  self.engine = create_engine(self.connection_string, echo=self.echo)
75
- if self.protocol == "sqlite" and self.sqlite_handle_foreign_keys:
76
- self.listen("connect", self._sqlite_set_handle_foreign_keys)
77
- self.session_maker = sessionmaker(bind=self.engine)
78
- self.Base: DeclarativeMeta = declarative_base(metaclass=DeclarativeMeta)
79
- self.session_maker.configure(binds={self.Base: self.engine})
81
+ if self.protocol == "sqlite":
82
+ if self.sqlite_check_if_file_exist:
83
+ self.listen("connect", self._sqlite_check_if_file_exist_func)
84
+ if self.sqlite_handle_foreign_keys:
85
+ self.listen("connect", self._sqlite_handle_foreign_keys_func)
86
+ self._session_maker = sessionmaker(bind=self.engine)
87
+ self._Base: DeclarativeMeta = declarative_base(metaclass=DeclarativeMeta)
88
+ self.session_maker.configure(binds={self._Base: self.engine})
89
+
90
+ if self.test_on_startup:
91
+ self.test()
80
92
 
81
93
  def __str__(self):
82
94
  return f"{self.__class__.__name__}({self.connection_string_printable})"
@@ -117,10 +129,27 @@ class SqlalchemyDb:
117
129
  def echo(self) -> bool:
118
130
  return self._echo
119
131
 
132
+ @property
133
+ def test_on_startup(self) -> bool:
134
+ return self._test_on_startup
135
+
136
+ @property
137
+ def sqlite_check_if_file_exist(self) -> bool:
138
+ return self._sqlite_check_if_file_exist
139
+
120
140
  @property
121
141
  def sqlite_handle_foreign_keys(self) -> bool:
122
142
  return self._sqlite_handle_foreign_keys
123
143
 
144
+ @property
145
+ def session_maker(self) -> sessionmaker:
146
+ return self._session_maker
147
+
148
+ # noinspection PyPep8Naming
149
+ @property
150
+ def Base(self) -> Any:
151
+ return self._Base
152
+
124
153
  def get_connection_string(self, printable: bool = False) -> str:
125
154
  """
126
155
  Get the Connection String
@@ -173,6 +202,10 @@ class SqlalchemyDb:
173
202
 
174
203
  return self.get_connection_string(printable=True)
175
204
 
205
+ def test(self):
206
+ self.engine.connect()
207
+ print()
208
+
176
209
  def create_all(self,
177
210
  tables: Optional[Sequence[Table]] = None,
178
211
  check_first: bool = True) -> None:
@@ -231,8 +264,13 @@ class SqlalchemyDb:
231
264
 
232
265
  return event.listens_for(self.engine, identifier, *args, **kw)
233
266
 
234
- @classmethod
235
- def _sqlite_set_handle_foreign_keys(cls, connection, _connection_record):
267
+ def _sqlite_check_if_file_exist_func(self, connection, _connection_record):
268
+ if self.file is not None:
269
+ if not self.file.is_file():
270
+ raise FileNotFoundError(f"Database file does not exist: {self.file}")
271
+
272
+ # noinspection PyMethodMayBeStatic
273
+ def _sqlite_handle_foreign_keys_func(self, connection, _connection_record):
236
274
  if not isinstance(connection, sqlite3.Connection):
237
275
  raise RuntimeError(f"Connection is not a sqlite3.Connection: {connection}")
238
276
  cursor = connection.cursor()
@@ -34,6 +34,12 @@ class SqlalchemySettings(PrintableSettings):
34
34
  db_echo: bool = Field(default=False,
35
35
  title="Database echo.",
36
36
  description="Echo SQL queries to console")
37
+ db_test_on_startup: bool = Field(default=True,
38
+ title="Test Database on Startup",
39
+ description="Test database connection on startup")
40
+ db_sqlite_check_if_file_exist: bool = Field(default=True,
41
+ title="Database SQLite Check If File Exist",
42
+ description="Check if file exists in SQLite")
37
43
  db_sqlite_handle_foreign_keys: bool = Field(default=True,
38
44
  title="Database SQLite Handle Foreign Keys",
39
45
  description="Handle foreign keys in SQLite")
@@ -1,49 +1,49 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: wiederverwendbar
3
- Version: 0.8.4
3
+ Version: 0.8.5
4
4
  Summary: A collection of scripts, classes and tools they are \"wiederverwendbar\".
5
5
  Author-Email: Julius Koenig <info@bastelquartier.de>
6
6
  License: GPL-3.0
7
7
  Requires-Python: >=3.9
8
- Requires-Dist: pydantic>=2.11.4
8
+ Requires-Dist: pydantic>=2.11.5
9
9
  Requires-Dist: pydantic-settings>=2.9.1
10
10
  Requires-Dist: devtools>=0.12.2
11
11
  Provides-Extra: full
12
12
  Requires-Dist: rich>=14.0.0; extra == "full"
13
- Requires-Dist: typer>=0.15.3; extra == "full"
13
+ Requires-Dist: typer>=0.16.0; extra == "full"
14
14
  Requires-Dist: pythonping>=1.1.4; extra == "full"
15
15
  Requires-Dist: mongoengine>=0.29.1; extra == "full"
16
- Requires-Dist: nicegui>=2.17.0; extra == "full"
17
- Requires-Dist: uvicorn>=0.34.2; extra == "full"
16
+ Requires-Dist: nicegui>=2.19.0; extra == "full"
17
+ Requires-Dist: uvicorn>=0.34.3; extra == "full"
18
18
  Requires-Dist: fastapi>=0.115.12; extra == "full"
19
- Requires-Dist: starlette-admin[i18n]>=0.14.1; extra == "full"
19
+ Requires-Dist: starlette-admin[i18n]>=0.15.1; extra == "full"
20
20
  Requires-Dist: pillow>=11.2.1; extra == "full"
21
21
  Requires-Dist: blinker>=1.9.0; extra == "full"
22
- Requires-Dist: kombu>=5.5.3; extra == "full"
22
+ Requires-Dist: kombu>=5.5.4; extra == "full"
23
23
  Requires-Dist: nest-asyncio>=1.6.0; extra == "full"
24
- Requires-Dist: sqlalchemy>=2.0.40; extra == "full"
24
+ Requires-Dist: sqlalchemy>=2.0.41; extra == "full"
25
25
  Provides-Extra: rich
26
26
  Requires-Dist: rich>=14.0.0; extra == "rich"
27
27
  Provides-Extra: typer
28
- Requires-Dist: typer>=0.15.3; extra == "typer"
28
+ Requires-Dist: typer>=0.16.0; extra == "typer"
29
29
  Provides-Extra: mongoengine
30
30
  Requires-Dist: mongoengine>=0.29.1; extra == "mongoengine"
31
31
  Requires-Dist: blinker>=1.9.0; extra == "mongoengine"
32
32
  Provides-Extra: uvicorn
33
- Requires-Dist: uvicorn>=0.34.2; extra == "uvicorn"
33
+ Requires-Dist: uvicorn>=0.34.3; extra == "uvicorn"
34
34
  Provides-Extra: fastapi
35
35
  Requires-Dist: fastapi>=0.115.12; extra == "fastapi"
36
36
  Provides-Extra: nicegui
37
- Requires-Dist: nicegui>=2.17.0; extra == "nicegui"
37
+ Requires-Dist: nicegui>=2.19.0; extra == "nicegui"
38
38
  Provides-Extra: starlette-admin
39
- Requires-Dist: starlette-admin[i18n]>=0.14.1; extra == "starlette-admin"
39
+ Requires-Dist: starlette-admin[i18n]>=0.15.1; extra == "starlette-admin"
40
40
  Requires-Dist: pillow>=11.2.1; extra == "starlette-admin"
41
- Requires-Dist: kombu>=5.5.3; extra == "starlette-admin"
41
+ Requires-Dist: kombu>=5.5.4; extra == "starlette-admin"
42
42
  Requires-Dist: nest-asyncio>=1.6.0; extra == "starlette-admin"
43
43
  Provides-Extra: fuctions
44
44
  Requires-Dist: pythonping>=1.1.4; extra == "fuctions"
45
45
  Provides-Extra: sqlalchemy
46
- Requires-Dist: sqlalchemy>=2.0.40; extra == "sqlalchemy"
46
+ Requires-Dist: sqlalchemy>=2.0.41; extra == "sqlalchemy"
47
47
  Description-Content-Type: text/markdown
48
48
 
49
49
  # wiederverwendbar
@@ -1,7 +1,7 @@
1
- wiederverwendbar-0.8.4.dist-info/METADATA,sha256=T6JVzGeHuwJbwBCimkkU-F4uaGLupwebmhfRowesHfw,2012
2
- wiederverwendbar-0.8.4.dist-info/WHEEL,sha256=tSfRZzRHthuv7vxpI4aehrdN9scLjk-dCJkPLzkHxGg,90
3
- wiederverwendbar-0.8.4.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
4
- wiederverwendbar/__init__.py,sha256=7tyTTIvtqm_RTzETKQ6VPMlU1JCLzUzZcHathL1jqvs,156
1
+ wiederverwendbar-0.8.5.dist-info/METADATA,sha256=_Rv2S2NNzn3-vzHy6EQZpS9668alPFZHtDRWdNS2sKU,2012
2
+ wiederverwendbar-0.8.5.dist-info/WHEEL,sha256=tSfRZzRHthuv7vxpI4aehrdN9scLjk-dCJkPLzkHxGg,90
3
+ wiederverwendbar-0.8.5.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
4
+ wiederverwendbar/__init__.py,sha256=rI0baW27B0s4avXRykXayN0cZK4x2ItpEvLEkLDLSm4,156
5
5
  wiederverwendbar/before_after_wrap.py,sha256=8rjyRrDpwbzrsKkY7vbIgtitgLjlF8Lwx8YNvcJWwgY,10425
6
6
  wiederverwendbar/default.py,sha256=MQBBpJMh8Tz4FUwxszVm7M3j6qbW2JH-5jKSjbvsUUk,49
7
7
  wiederverwendbar/examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -46,6 +46,7 @@ wiederverwendbar/functions/datetime.py,sha256=Wf7KJKerb5opdqgm06FjbRBC7ZVvr1WcW0
46
46
  wiederverwendbar/functions/download_file.py,sha256=vNpaNpQ_YL9-D81Leey_RYv_OcPsUEZM5gAgo48tzXQ,5487
47
47
  wiederverwendbar/functions/eval.py,sha256=zINxAfZ2mlNC4m36S2CQT2H4oTHyfBb-MJdZdgMfS0k,4537
48
48
  wiederverwendbar/functions/find_class_method.py,sha256=QAE66bf75gkRrwhiD5spnNG-cX1trdQh6pQhNir79bc,470
49
+ wiederverwendbar/functions/get_pretty_str.py,sha256=pM4itXdiozjD3PQblAxhhkOLO7TY3pDYnmiqlZVJnWw,205
49
50
  wiederverwendbar/functions/run_command.py,sha256=7j9cE0mqNVmu0MOojw7plEMttYZqYMsUgc8kOYS2v5I,1902
50
51
  wiederverwendbar/functions/security/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
52
  wiederverwendbar/functions/security/hashed_password.py,sha256=8qNOzvf4g7Z7YHDfdq6YSq6ZpYkseDogbcU4sxi3GGQ,3470
@@ -96,10 +97,10 @@ wiederverwendbar/pydantic/singleton.py,sha256=4YMBcKiTik2KmuN97CUCmy_ldhfyeDzrsU
96
97
  wiederverwendbar/route.py,sha256=moYlZ5IvV_uDroSggxe80-sR3_nlNsl9Bp69CcCfK8Y,12061
97
98
  wiederverwendbar/singleton.py,sha256=6QqeQpzx4UhBZnvto5_yUs7hpmzt_-dyE169RuwsTa8,6866
98
99
  wiederverwendbar/sqlalchemy/__init__.py,sha256=gkt99fc9wIxaSjrl1xNhmPLrlJNZhrY-sJ5yIezREGM,888
99
- wiederverwendbar/sqlalchemy/base.py,sha256=vocU5IjfyXhgQVTvyfm9pS5resjzceHj_zctGZSDD-Y,14553
100
- wiederverwendbar/sqlalchemy/db.py,sha256=jfjeyMsQu9K92ICS3q5OdBmQILsCGxwmm_QqPSHSSrM,8158
100
+ wiederverwendbar/sqlalchemy/base.py,sha256=q_wiVyiAK4c-z3InFIy4TEN140Rp8MjEQqhxhL2bvaw,15030
101
+ wiederverwendbar/sqlalchemy/db.py,sha256=u3nHJh8pxnOMALldp72sohe1XZ-ZfksZZVpE5qIxHgc,9610
101
102
  wiederverwendbar/sqlalchemy/raise_has_not_attr.py,sha256=GdmbSC8EoBCkpuZQGLp3zzQ0iScsj2XfFZC9OZtWbkA,403
102
- wiederverwendbar/sqlalchemy/settings.py,sha256=jJ6HeetHvofoB2Cppd5yCP94rwX0ltoBRtOrZXqhYCU,2128
103
+ wiederverwendbar/sqlalchemy/settings.py,sha256=RM-bFg_Ga0AUko4JWOvCZLcfoB91kKpn1TqVzgxAm_k,2579
103
104
  wiederverwendbar/sqlalchemy/singleton.py,sha256=LVH1gRl1U1My-cx-Y1M4G-czgCWzAmuVyRz9EYWmGMM,179
104
105
  wiederverwendbar/sqlalchemy/types.py,sha256=1Hb2Y5QacH079iKpN0fV5XALxLYQj7CFYqs5tC3FlFM,1911
105
106
  wiederverwendbar/starlette_admin/__init__.py,sha256=SRuEMG5_2ns78Arw3KJsMMYg4N3RylCJmc5HfhBKAjg,3425
@@ -167,4 +168,4 @@ wiederverwendbar/typer/typer_resolve_defaults.py,sha256=2KD09rxKaur2TbJ3BCcLxbrc
167
168
  wiederverwendbar/uvicorn/__init__.py,sha256=9F7gT-8QyxS41xWKEIHMLUBV9ZTBKZJj8tHPqhMzoHA,126
168
169
  wiederverwendbar/uvicorn/server.py,sha256=04WpB8AUtVkYhoS6qsBVJHOtTNkISQVg_mLVtYmK-1Y,5657
169
170
  wiederverwendbar/uvicorn/settings.py,sha256=IAjlpWR-KH7098jIgH_t46yG17YPbH8MoEmMdn0hgNo,2217
170
- wiederverwendbar-0.8.4.dist-info/RECORD,,
171
+ wiederverwendbar-0.8.5.dist-info/RECORD,,