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.

@@ -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
1
+ Metadata-Version: 2.4
2
2
  Name: diracx-db
3
- Version: 0.0.1a21
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 == "testing"
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,,
@@ -1,5 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.6.0)
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
-
@@ -1,7 +1,7 @@
1
- [diracx.db.os]
1
+ [diracx.dbs.os]
2
2
  JobParametersDB = diracx.db.os:JobParametersDB
3
3
 
4
- [diracx.db.sql]
4
+ [diracx.dbs.sql]
5
5
  AuthDB = diracx.db.sql:AuthDB
6
6
  JobDB = diracx.db.sql:JobDB
7
7
  JobLoggingDB = diracx.db.sql:JobLoggingDB