diracx-db 0.0.1a21__py3-none-any.whl → 0.0.6__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 diracx-db might be problematic. Click here for more details.
- diracx/db/__main__.py +1 -1
- diracx/db/exceptions.py +4 -1
- diracx/db/os/job_parameters.py +25 -7
- diracx/db/os/utils.py +18 -11
- diracx/db/sql/auth/db.py +113 -78
- diracx/db/sql/auth/schema.py +32 -24
- diracx/db/sql/dummy/db.py +5 -17
- diracx/db/sql/dummy/schema.py +8 -6
- diracx/db/sql/job/db.py +155 -205
- diracx/db/sql/job/schema.py +115 -59
- diracx/db/sql/job_logging/db.py +60 -143
- diracx/db/sql/job_logging/schema.py +54 -15
- diracx/db/sql/pilot_agents/db.py +0 -1
- diracx/db/sql/pilot_agents/schema.py +26 -23
- diracx/db/sql/sandbox_metadata/db.py +164 -57
- diracx/db/sql/sandbox_metadata/schema.py +9 -4
- diracx/db/sql/task_queue/db.py +44 -125
- diracx/db/sql/task_queue/schema.py +2 -0
- diracx/db/sql/utils/__init__.py +29 -451
- diracx/db/sql/utils/base.py +461 -0
- diracx/db/sql/utils/functions.py +142 -0
- diracx/db/sql/utils/types.py +137 -0
- {diracx_db-0.0.1a21.dist-info → diracx_db-0.0.6.dist-info}/METADATA +8 -6
- diracx_db-0.0.6.dist-info/RECORD +37 -0
- {diracx_db-0.0.1a21.dist-info → diracx_db-0.0.6.dist-info}/WHEEL +1 -2
- {diracx_db-0.0.1a21.dist-info → diracx_db-0.0.6.dist-info}/entry_points.txt +2 -2
- diracx/db/sql/utils/job.py +0 -574
- diracx_db-0.0.1a21.dist-info/RECORD +0 -36
- diracx_db-0.0.1a21.dist-info/top_level.txt +0 -1
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from functools import partial
|
|
5
|
+
from zoneinfo import ZoneInfo
|
|
6
|
+
|
|
7
|
+
import sqlalchemy.types as types
|
|
8
|
+
from sqlalchemy import Column as RawColumn
|
|
9
|
+
from sqlalchemy import DateTime, Enum
|
|
10
|
+
|
|
11
|
+
from .functions import utcnow
|
|
12
|
+
|
|
13
|
+
Column: partial[RawColumn] = partial(RawColumn, nullable=False)
|
|
14
|
+
NullColumn: partial[RawColumn] = partial(RawColumn, nullable=True)
|
|
15
|
+
DateNowColumn = partial(Column, type_=DateTime(timezone=True), server_default=utcnow())
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def EnumColumn(name, enum_type, **kwargs): # noqa: N802
|
|
19
|
+
return Column(name, Enum(enum_type, native_enum=False, length=16), **kwargs)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class EnumBackedBool(types.TypeDecorator):
|
|
23
|
+
"""Maps a ``EnumBackedBool()`` column to True/False in Python."""
|
|
24
|
+
|
|
25
|
+
impl = types.Enum("True", "False", name="enum_backed_bool")
|
|
26
|
+
cache_ok = True
|
|
27
|
+
|
|
28
|
+
def process_bind_param(self, value, dialect) -> str:
|
|
29
|
+
if value is True:
|
|
30
|
+
return "True"
|
|
31
|
+
elif value is False:
|
|
32
|
+
return "False"
|
|
33
|
+
else:
|
|
34
|
+
raise NotImplementedError(value, dialect)
|
|
35
|
+
|
|
36
|
+
def process_result_value(self, value, dialect) -> bool:
|
|
37
|
+
if value == "True":
|
|
38
|
+
return True
|
|
39
|
+
elif value == "False":
|
|
40
|
+
return False
|
|
41
|
+
else:
|
|
42
|
+
raise NotImplementedError(f"Unknown {value=}")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class SmarterDateTime(types.TypeDecorator):
|
|
46
|
+
"""A DateTime type that also accepts ISO8601 strings.
|
|
47
|
+
|
|
48
|
+
Takes into account converting timezone aware datetime objects into
|
|
49
|
+
naive form and back when needed.
|
|
50
|
+
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
impl = DateTime()
|
|
54
|
+
cache_ok = True
|
|
55
|
+
|
|
56
|
+
def __init__(
|
|
57
|
+
self,
|
|
58
|
+
stored_tz: ZoneInfo | None = ZoneInfo("UTC"),
|
|
59
|
+
returned_tz: ZoneInfo = ZoneInfo("UTC"),
|
|
60
|
+
stored_naive_sqlite=True,
|
|
61
|
+
stored_naive_mysql=True,
|
|
62
|
+
stored_naive_postgres=False, # Forces timezone-awareness
|
|
63
|
+
):
|
|
64
|
+
self._stored_naive_dialect = {
|
|
65
|
+
"sqlite": stored_naive_sqlite,
|
|
66
|
+
"mysql": stored_naive_mysql,
|
|
67
|
+
"postgres": stored_naive_postgres,
|
|
68
|
+
}
|
|
69
|
+
self._stored_tz: ZoneInfo | None = stored_tz # None = Local timezone
|
|
70
|
+
self._returned_tz: ZoneInfo = returned_tz
|
|
71
|
+
|
|
72
|
+
def _stored_naive(self, dialect):
|
|
73
|
+
if dialect.name not in self._stored_naive_dialect:
|
|
74
|
+
raise NotImplementedError(dialect.name)
|
|
75
|
+
return self._stored_naive_dialect.get(dialect.name)
|
|
76
|
+
|
|
77
|
+
def process_bind_param(self, value, dialect):
|
|
78
|
+
if value is None:
|
|
79
|
+
return None
|
|
80
|
+
|
|
81
|
+
if isinstance(value, str):
|
|
82
|
+
try:
|
|
83
|
+
value: datetime = datetime.fromisoformat(value)
|
|
84
|
+
except ValueError as err:
|
|
85
|
+
raise ValueError(f"Unable to parse datetime string: {value}") from err
|
|
86
|
+
|
|
87
|
+
if not isinstance(value, datetime):
|
|
88
|
+
raise ValueError(f"Expected datetime or ISO8601 string, but got {value!r}")
|
|
89
|
+
|
|
90
|
+
if not value.tzinfo:
|
|
91
|
+
raise ValueError(
|
|
92
|
+
f"Provided timestamp {value=} has no tzinfo -"
|
|
93
|
+
" this is problematic and may cause inconsistencies in stored timestamps.\n"
|
|
94
|
+
" Please always work with tz-aware datetimes / attach tzinfo to your datetime objects:"
|
|
95
|
+
" e.g. datetime.now(tz=timezone.utc) or use datetime_obj.astimezone() with no arguments if you need to "
|
|
96
|
+
"attach the local timezone to a local naive timestamp."
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Check that we need to convert the timezone to match self._stored_tz timezone:
|
|
100
|
+
if self._stored_naive(dialect):
|
|
101
|
+
# if self._stored_tz is None, we use our local/system timezone.
|
|
102
|
+
stored_tz = self._stored_tz
|
|
103
|
+
|
|
104
|
+
# astimezone converts to the stored timezone (local timezone if None)
|
|
105
|
+
# replace strips the TZ info --> naive datetime object
|
|
106
|
+
value = value.astimezone(tz=stored_tz).replace(tzinfo=None)
|
|
107
|
+
|
|
108
|
+
return value
|
|
109
|
+
|
|
110
|
+
def process_result_value(self, value, dialect):
|
|
111
|
+
if value is None:
|
|
112
|
+
return None
|
|
113
|
+
if not isinstance(value, datetime):
|
|
114
|
+
raise NotImplementedError(f"{value=} not a datetime object")
|
|
115
|
+
|
|
116
|
+
if self._stored_naive(dialect):
|
|
117
|
+
# Here we add back the tzinfo to the naive timestamp
|
|
118
|
+
# from the DB to make it aware again.
|
|
119
|
+
if value.tzinfo is None:
|
|
120
|
+
# we are definitely given a naive timestamp, so handle it.
|
|
121
|
+
# add back the timezone info if stored_tz is set
|
|
122
|
+
if self._stored_tz:
|
|
123
|
+
value = value.replace(tzinfo=self._stored_tz)
|
|
124
|
+
else:
|
|
125
|
+
# if stored as a local time, add back the system timezone info...
|
|
126
|
+
value = value.astimezone()
|
|
127
|
+
else:
|
|
128
|
+
raise ValueError(
|
|
129
|
+
f"stored_naive is True for {dialect.name=}, but the database engine returned "
|
|
130
|
+
"a tz-aware datetime. You need to check the SQLAlchemy model is consistent with the DB schema."
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# finally, convert the datetime according to the "returned_tz"
|
|
134
|
+
value = value.astimezone(self._returned_tz)
|
|
135
|
+
|
|
136
|
+
# phew...
|
|
137
|
+
return value
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: diracx-db
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.6
|
|
4
4
|
Summary: TODO
|
|
5
5
|
License: GPL-3.0-only
|
|
6
6
|
Classifier: Intended Audience :: Science/Research
|
|
@@ -9,12 +9,14 @@ Classifier: Programming Language :: Python :: 3
|
|
|
9
9
|
Classifier: Topic :: Scientific/Engineering
|
|
10
10
|
Classifier: Topic :: System :: Distributed Computing
|
|
11
11
|
Requires-Python: >=3.11
|
|
12
|
-
Description-Content-Type: text/markdown
|
|
13
|
-
Requires-Dist: dirac
|
|
14
12
|
Requires-Dist: diracx-core
|
|
15
|
-
Requires-Dist: fastapi
|
|
16
13
|
Requires-Dist: opensearch-py[async]
|
|
17
14
|
Requires-Dist: pydantic>=2.10
|
|
15
|
+
Requires-Dist: python-dateutil
|
|
18
16
|
Requires-Dist: sqlalchemy[aiomysql,aiosqlite]>=2
|
|
17
|
+
Requires-Dist: uuid-utils
|
|
19
18
|
Provides-Extra: testing
|
|
20
|
-
Requires-Dist: diracx-testing; extra ==
|
|
19
|
+
Requires-Dist: diracx-testing; extra == 'testing'
|
|
20
|
+
Requires-Dist: freezegun; extra == 'testing'
|
|
21
|
+
Provides-Extra: types
|
|
22
|
+
Requires-Dist: types-python-dateutil; extra == 'types'
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
diracx/db/__init__.py,sha256=2oeUeVwZq53bo_ZOflEYZsBn7tcR5Tzb2AIu0TAWELM,109
|
|
2
|
+
diracx/db/__main__.py,sha256=6YlmpiU1cLLHjKLy1DfdEOQUyvSla-MbJsJ7aQwAOVs,1757
|
|
3
|
+
diracx/db/exceptions.py,sha256=1nn-SZLG-nQwkxbvHjZqXhE5ouzWj1f3qhSda2B4ZEg,83
|
|
4
|
+
diracx/db/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
diracx/db/os/__init__.py,sha256=IZr6z6SefrRvuC8sTC4RmB3_wwOyEt1GzpDuwSMH8O4,112
|
|
6
|
+
diracx/db/os/job_parameters.py,sha256=3w_CeA2z-cY5pWwXkGu-Fod27FobbUXuwVKK-jN037U,1479
|
|
7
|
+
diracx/db/os/utils.py,sha256=V4T-taos64SFNcorfIr7mq5l5y88K6TzyCj1YqWk8VI,11562
|
|
8
|
+
diracx/db/sql/__init__.py,sha256=JYu0b0IVhoXy3lX2m2r2dmAjsRS7IbECBUMEDvX0Te4,391
|
|
9
|
+
diracx/db/sql/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
diracx/db/sql/auth/db.py,sha256=F9s05K-9C6kL2nUZY7P8zD79fiuo2biREMhfI7oCjh4,11979
|
|
11
|
+
diracx/db/sql/auth/schema.py,sha256=9fUV7taDPnoAcoiwRAmQraOmF2Ytoizjs2TFvN7zsVs,3132
|
|
12
|
+
diracx/db/sql/dummy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
+
diracx/db/sql/dummy/db.py,sha256=MKSUSJI1BlRgK08tjCfkCkOz02asvJAeBw60pAdiGV8,1212
|
|
14
|
+
diracx/db/sql/dummy/schema.py,sha256=9zI53pKlzc6qBezsyjkatOQrNZdGCjwgjQ8Iz_pyAXs,789
|
|
15
|
+
diracx/db/sql/job/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
diracx/db/sql/job/db.py,sha256=bX-4OMyW4h9tqeTE3OvonxTXlL6j_Qvv9uEtK5SthN8,10120
|
|
17
|
+
diracx/db/sql/job/schema.py,sha256=fJdmiLp6psdAjo_CoBfSAGSYk2NJkSBwvik9tznESD0,5740
|
|
18
|
+
diracx/db/sql/job_logging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
|
+
diracx/db/sql/job_logging/db.py,sha256=hyklARuEj3R1sSJ8UaObRprmsRx7RjbKAcbfgT9BwRg,5496
|
|
20
|
+
diracx/db/sql/job_logging/schema.py,sha256=k6uBw-RHAcJ5GEleNpiWoXEJBhCiNG-y4xAgBKHZjjM,2524
|
|
21
|
+
diracx/db/sql/pilot_agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
|
+
diracx/db/sql/pilot_agents/db.py,sha256=6CQ0QGV4NhsGKVCygEtE4kmIjT89xJwrIMuYZTslWFE,1231
|
|
23
|
+
diracx/db/sql/pilot_agents/schema.py,sha256=BTFLuiwcxAvAtTvTP9C7DbGtXoM-IHVDG9k7HMx62AA,2211
|
|
24
|
+
diracx/db/sql/sandbox_metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
|
+
diracx/db/sql/sandbox_metadata/db.py,sha256=FtyPx6GAGJAH-lmuw8PQj6_KGHG6t3AC3-E9uWf-JNs,10236
|
|
26
|
+
diracx/db/sql/sandbox_metadata/schema.py,sha256=V5gV2PHwzTbBz_th9ribLfE7Lqk8YGemDmvqq4jWQJ4,1530
|
|
27
|
+
diracx/db/sql/task_queue/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
|
+
diracx/db/sql/task_queue/db.py,sha256=2qul1D2tX2uCI92N591WK5xWHakG0pNibzDwKQ7W-I8,6246
|
|
29
|
+
diracx/db/sql/task_queue/schema.py,sha256=5efAgvNYRkLlaJ2NzRInRfmVa3tyIzQu2l0oRPy4Kzw,3258
|
|
30
|
+
diracx/db/sql/utils/__init__.py,sha256=k1DI4Idlqv36pXn2BhQysb947Peio9DnYaePslkTpUQ,685
|
|
31
|
+
diracx/db/sql/utils/base.py,sha256=DqW-JYgjqvqkwLFqou5uzg73lZ83C0jHCgkt9qR1NTg,17255
|
|
32
|
+
diracx/db/sql/utils/functions.py,sha256=_E4tc9Gti6LuSh7QEyoqPJSvCuByVqvRenOXCzxsulE,4014
|
|
33
|
+
diracx/db/sql/utils/types.py,sha256=KNZWJfpvHTjfIPg6Nn7zY-rS0q3ybnirHcTcLAYSYbE,5118
|
|
34
|
+
diracx_db-0.0.6.dist-info/METADATA,sha256=Lu8x2pR3BfnKGgHYz4w5Z4CTCf7tPi9p9tlldVFJiLo,780
|
|
35
|
+
diracx_db-0.0.6.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
36
|
+
diracx_db-0.0.6.dist-info/entry_points.txt,sha256=UPqhLvb9gui0kOyWeI_edtefcrHToZmQt1p76vIwujo,317
|
|
37
|
+
diracx_db-0.0.6.dist-info/RECORD,,
|