hdx-python-database 1.3.9__py3-none-any.whl → 1.4.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.
- hdx/database/_version.py +2 -2
- hdx/database/database.py +9 -1
- hdx/database/dburi.py +5 -5
- hdx/database/no_timezone.py +4 -4
- hdx/database/postgresql.py +6 -2
- hdx/database/views.py +3 -2
- {hdx_python_database-1.3.9.dist-info → hdx_python_database-1.4.0.dist-info}/METADATA +5 -4
- hdx_python_database-1.4.0.dist-info/RECORD +13 -0
- {hdx_python_database-1.3.9.dist-info → hdx_python_database-1.4.0.dist-info}/WHEEL +1 -1
- hdx_python_database-1.3.9.dist-info/RECORD +0 -13
- {hdx_python_database-1.3.9.dist-info → hdx_python_database-1.4.0.dist-info}/licenses/LICENSE +0 -0
hdx/database/_version.py
CHANGED
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '1.
|
|
22
|
-
__version_tuple__ = version_tuple = (1,
|
|
21
|
+
__version__ = version = '1.4.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 4, 0)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
hdx/database/database.py
CHANGED
|
@@ -65,6 +65,7 @@ class Database:
|
|
|
65
65
|
reflect (bool): Whether to reflect existing tables. Defaults to False.
|
|
66
66
|
**kwargs: See below
|
|
67
67
|
pg_restore_file (Path | str): Restore database from pg_restore file
|
|
68
|
+
pg_timeout (int): Seconds to wait for PostgreSQL. Defaults to None (wait forever).
|
|
68
69
|
recreate_schema (bool): Whether to recreate schema
|
|
69
70
|
schema_name (str): Database schema name. Defaults to "public".
|
|
70
71
|
prepare_fn (Callable[[], None]]): Function to call before Base.metadata.create_all.
|
|
@@ -73,6 +74,7 @@ class Database:
|
|
|
73
74
|
ssh_username (str): SSH username
|
|
74
75
|
ssh_password (str): SSH password
|
|
75
76
|
ssh_private_key: Location of SSH private key (instead of ssh_password)
|
|
77
|
+
ssh_host_key (str): SSH server host key to verify against. Strongly recommended.
|
|
76
78
|
For more advanced usage, see SSHTunnelForwarder documentation.
|
|
77
79
|
|
|
78
80
|
"""
|
|
@@ -98,10 +100,12 @@ class Database:
|
|
|
98
100
|
schema_name = None
|
|
99
101
|
if len(kwargs) == 0:
|
|
100
102
|
pg_restore_file = None
|
|
103
|
+
pg_timeout = None
|
|
101
104
|
recreate_schema = False
|
|
102
105
|
prepare_fn = do_nothing_fn
|
|
103
106
|
else:
|
|
104
107
|
pg_restore_file = kwargs.pop("pg_restore_file", None)
|
|
108
|
+
pg_timeout = kwargs.pop("pg_timeout", None)
|
|
105
109
|
recreate_schema = kwargs.pop("recreate_schema", False)
|
|
106
110
|
schema_name = kwargs.pop("schema", "public")
|
|
107
111
|
prepare_fn = kwargs.pop("prepare_fn", do_nothing_fn)
|
|
@@ -116,6 +120,10 @@ class Database:
|
|
|
116
120
|
raise
|
|
117
121
|
ssh_host = kwargs["ssh_host"]
|
|
118
122
|
del kwargs["ssh_host"]
|
|
123
|
+
if "ssh_host_key" not in kwargs:
|
|
124
|
+
logger.warning(
|
|
125
|
+
"ssh_host_key not supplied: SSH server identity will not be verified."
|
|
126
|
+
)
|
|
119
127
|
ssh_port = kwargs.get("ssh_port")
|
|
120
128
|
if ssh_port is not None:
|
|
121
129
|
ssh_port = int(ssh_port)
|
|
@@ -147,7 +155,7 @@ class Database:
|
|
|
147
155
|
"No engine, database uri or connection parameters provided!"
|
|
148
156
|
)
|
|
149
157
|
if not engine and dialect == "postgresql":
|
|
150
|
-
wait_for_postgresql(db_uri)
|
|
158
|
+
wait_for_postgresql(db_uri, pg_timeout)
|
|
151
159
|
if pg_restore_file and db_uri:
|
|
152
160
|
restore_from_pgfile(db_uri, pg_restore_file)
|
|
153
161
|
if not table_base:
|
hdx/database/dburi.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Connection URI utilities"""
|
|
2
2
|
|
|
3
3
|
from typing import Any
|
|
4
|
-
from urllib.parse import urlsplit
|
|
4
|
+
from urllib.parse import quote_plus, unquote, urlsplit
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
def get_params_from_connection_uri(
|
|
@@ -28,8 +28,8 @@ def get_params_from_connection_uri(
|
|
|
28
28
|
"database": result.path[1:],
|
|
29
29
|
"host": result.hostname,
|
|
30
30
|
"port": result.port,
|
|
31
|
-
"username": result.username,
|
|
32
|
-
"password": result.password,
|
|
31
|
+
"username": unquote(result.username) if result.username else None,
|
|
32
|
+
"password": unquote(result.password) if result.password else None,
|
|
33
33
|
"dialect": dialect,
|
|
34
34
|
}
|
|
35
35
|
if include_driver:
|
|
@@ -72,9 +72,9 @@ def get_connection_uri(
|
|
|
72
72
|
strings.append(f"+{driver}")
|
|
73
73
|
strings.append("://")
|
|
74
74
|
if username:
|
|
75
|
-
strings.append(username)
|
|
75
|
+
strings.append(quote_plus(username))
|
|
76
76
|
if password:
|
|
77
|
-
strings.append(f":{password}@")
|
|
77
|
+
strings.append(f":{quote_plus(password)}@")
|
|
78
78
|
else:
|
|
79
79
|
strings.append("@")
|
|
80
80
|
if host:
|
hdx/database/no_timezone.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Utilities for database datetime columns without timezone"""
|
|
2
2
|
|
|
3
|
-
from datetime import
|
|
3
|
+
from datetime import UTC, datetime
|
|
4
4
|
|
|
5
5
|
from sqlalchemy import DateTime, TypeDecorator
|
|
6
6
|
from sqlalchemy.orm import DeclarativeBase, declared_attr
|
|
@@ -19,7 +19,7 @@ class ConversionNoTZ(TypeDecorator):
|
|
|
19
19
|
if value is None:
|
|
20
20
|
return value
|
|
21
21
|
if value.tzinfo:
|
|
22
|
-
value = value.astimezone(
|
|
22
|
+
value = value.astimezone(UTC)
|
|
23
23
|
|
|
24
24
|
return value.replace(tzinfo=None)
|
|
25
25
|
|
|
@@ -27,9 +27,9 @@ class ConversionNoTZ(TypeDecorator):
|
|
|
27
27
|
if value is None:
|
|
28
28
|
return value
|
|
29
29
|
if value.tzinfo is None:
|
|
30
|
-
return value.replace(tzinfo=
|
|
30
|
+
return value.replace(tzinfo=UTC)
|
|
31
31
|
|
|
32
|
-
return value.astimezone(
|
|
32
|
+
return value.astimezone(UTC)
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
class Base(DeclarativeBase):
|
hdx/database/postgresql.py
CHANGED
|
@@ -26,17 +26,19 @@ class PostgresError(Exception):
|
|
|
26
26
|
pass
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
def wait_for_postgresql(db_uri: str) -> None:
|
|
29
|
+
def wait_for_postgresql(db_uri: str, timeout: int | None = None) -> None:
|
|
30
30
|
"""Waits for PostgreSQL database to be up
|
|
31
31
|
|
|
32
32
|
Args:
|
|
33
33
|
db_uri (str): Connection URI
|
|
34
|
+
timeout (Optional[int]): Seconds to wait. Defaults to None (wait forever).
|
|
34
35
|
|
|
35
36
|
Returns:
|
|
36
37
|
None
|
|
37
38
|
"""
|
|
38
39
|
connecting_string = "Checking for PostgreSQL..."
|
|
39
40
|
db_uri_nd = remove_driver_from_uri(db_uri)
|
|
41
|
+
start = time.monotonic()
|
|
40
42
|
while True:
|
|
41
43
|
try:
|
|
42
44
|
logger.info(connecting_string)
|
|
@@ -45,6 +47,8 @@ def wait_for_postgresql(db_uri: str) -> None:
|
|
|
45
47
|
logger.info("PostgreSQL is running!")
|
|
46
48
|
break
|
|
47
49
|
except psycopg.OperationalError:
|
|
50
|
+
if timeout is not None and time.monotonic() - start > timeout:
|
|
51
|
+
raise PostgresError(f"PostgreSQL not available after {timeout}s")
|
|
48
52
|
time.sleep(1)
|
|
49
53
|
|
|
50
54
|
|
|
@@ -59,7 +63,7 @@ def restore_from_pgfile(db_uri: str, pg_restore_file: Path | str) -> str:
|
|
|
59
63
|
Returns:
|
|
60
64
|
str: Output from the pg_restore command
|
|
61
65
|
"""
|
|
62
|
-
pg_restore_file = Path(pg_restore_file)
|
|
66
|
+
pg_restore_file = Path(pg_restore_file).absolute()
|
|
63
67
|
db_params = get_params_from_connection_uri(db_uri)
|
|
64
68
|
if pg_restore_file.suffix == ".xz":
|
|
65
69
|
decompress = subprocess.Popen(
|
hdx/database/views.py
CHANGED
|
@@ -24,12 +24,13 @@ class DropView(DDLElement):
|
|
|
24
24
|
|
|
25
25
|
@compiler.compiles(CreateView)
|
|
26
26
|
def _create_view(element, compiler, **kw):
|
|
27
|
-
|
|
27
|
+
name = compiler.preparer.quote(element.name)
|
|
28
|
+
return f"CREATE VIEW {name} AS {compiler.sql_compiler.process(element.selectable, literal_binds=True)}"
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
@compiler.compiles(DropView)
|
|
31
32
|
def _drop_view(element, compiler, **kw):
|
|
32
|
-
return f"DROP VIEW {element.name}"
|
|
33
|
+
return f"DROP VIEW {compiler.preparer.quote(element.name)}"
|
|
33
34
|
|
|
34
35
|
|
|
35
36
|
def view_exists(ddl, target, connection, **kw):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: hdx-python-database
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: HDX Python database utilities
|
|
5
5
|
Project-URL: Homepage, https://github.com/OCHA-DAP/hdx-python-database
|
|
6
6
|
Author-email: Michael Rans <rans@un.org>
|
|
@@ -17,14 +17,15 @@ Classifier: Operating System :: POSIX :: Linux
|
|
|
17
17
|
Classifier: Operating System :: Unix
|
|
18
18
|
Classifier: Programming Language :: Python
|
|
19
19
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
-
Requires-Python: >=3.
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
21
|
Requires-Dist: sqlalchemy>=2
|
|
22
22
|
Provides-Extra: docs
|
|
23
23
|
Requires-Dist: mkapi; extra == 'docs'
|
|
24
24
|
Provides-Extra: postgresql
|
|
25
25
|
Requires-Dist: psycopg[binary]; extra == 'postgresql'
|
|
26
26
|
Provides-Extra: sshtunnel
|
|
27
|
-
Requires-Dist:
|
|
27
|
+
Requires-Dist: cryptography>=48.0.1; extra == 'sshtunnel'
|
|
28
|
+
Requires-Dist: sshtunnel>=0.5.1; extra == 'sshtunnel'
|
|
28
29
|
Description-Content-Type: text/markdown
|
|
29
30
|
|
|
30
31
|
[](https://github.com/OCHA-DAP/hdx-python-database/actions/workflows/run-python-tests.yaml)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
hdx/database/__init__.py,sha256=sG2OYV5LHskgiDkB4IeVeQyPHL22Ta1kuxf-xN92-5E,155
|
|
2
|
+
hdx/database/_version.py,sha256=u2oUj6dCZIqQ271Pm6INHvXKeiux4BSapl6g0c_tllg,520
|
|
3
|
+
hdx/database/database.py,sha256=SA3XwmWMg7VIrUaWWRXVLMxXtKV4F9gz-jK5CJEPI18,13320
|
|
4
|
+
hdx/database/dburi.py,sha256=5PbvVe56MglpHxflAFeRY0TXli3XThZrDphPWmBp4ho,3055
|
|
5
|
+
hdx/database/no_timezone.py,sha256=MG4Z2t7SwjPKDnwDD0CVftoIZC0hxpSg-C-1FyEy0R0,1067
|
|
6
|
+
hdx/database/postgresql.py,sha256=JSLWQrAYps7Dxkb0En0kndr-9lNQhPpaY5UvxydTmb8,3321
|
|
7
|
+
hdx/database/utils.py,sha256=nzChgDj2_Mq8GS6xWaNmqOgpAiwK9tGYV_skKbErEVA,401
|
|
8
|
+
hdx/database/views.py,sha256=3ZQT5jS-q3w30zKypnyuy8-2BKwkyYC0It9om9hxmCs,1669
|
|
9
|
+
hdx/database/with_timezone.py,sha256=GUUylmTwQHp22yedmCVKpVJzvjWgdS9lg9KeqciNJkY,235
|
|
10
|
+
hdx_python_database-1.4.0.dist-info/METADATA,sha256=ezN2GRZ808XVFheZ6d7DFaWqsX-am5kQ8jLvbrVAegQ,3985
|
|
11
|
+
hdx_python_database-1.4.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
12
|
+
hdx_python_database-1.4.0.dist-info/licenses/LICENSE,sha256=wc-4GpMn-ODs-U_bTe1YCiPVgvcjzrpYOx2wPuyAeII,1079
|
|
13
|
+
hdx_python_database-1.4.0.dist-info/RECORD,,
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
hdx/database/__init__.py,sha256=sG2OYV5LHskgiDkB4IeVeQyPHL22Ta1kuxf-xN92-5E,155
|
|
2
|
-
hdx/database/_version.py,sha256=g62BHujLgGdP6cB1oucUZ89PYnKUKXkd5IR17viSOVM,520
|
|
3
|
-
hdx/database/database.py,sha256=ZjI8roQM0x56RlywG5xLIVZ4pVqVqiREilbTfzYxwNU,12856
|
|
4
|
-
hdx/database/dburi.py,sha256=Kkg9eyS0Dia63vTTWPu4K1nqhn9igpZYMw-66wPn2ZU,2934
|
|
5
|
-
hdx/database/no_timezone.py,sha256=WuiSijzzUox6mpvPMveklA2oqKQcaudr2zz-U78Lq6A,1099
|
|
6
|
-
hdx/database/postgresql.py,sha256=sBvuBQNoT_iJvy3W2lFupDKbx-g25UhlRYv2LlQmpVs,3013
|
|
7
|
-
hdx/database/utils.py,sha256=nzChgDj2_Mq8GS6xWaNmqOgpAiwK9tGYV_skKbErEVA,401
|
|
8
|
-
hdx/database/views.py,sha256=zkRTsD1cE_afJaxdYd72EoUv3qdnqBL-ekYPkv-DeD4,1603
|
|
9
|
-
hdx/database/with_timezone.py,sha256=GUUylmTwQHp22yedmCVKpVJzvjWgdS9lg9KeqciNJkY,235
|
|
10
|
-
hdx_python_database-1.3.9.dist-info/METADATA,sha256=YIP17SHZ_JMjbUlKBYmKmN0LAUI1nPEYgMmmI2XNH0g,3920
|
|
11
|
-
hdx_python_database-1.3.9.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
12
|
-
hdx_python_database-1.3.9.dist-info/licenses/LICENSE,sha256=wc-4GpMn-ODs-U_bTe1YCiPVgvcjzrpYOx2wPuyAeII,1079
|
|
13
|
-
hdx_python_database-1.3.9.dist-info/RECORD,,
|
{hdx_python_database-1.3.9.dist-info → hdx_python_database-1.4.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|