hdx-python-database 1.3.8__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 +8 -18
- hdx/database/database.py +28 -20
- hdx/database/dburi.py +13 -13
- hdx/database/no_timezone.py +5 -6
- hdx/database/postgresql.py +11 -5
- hdx/database/views.py +3 -5
- {hdx_python_database-1.3.8.dist-info → hdx_python_database-1.4.0.dist-info}/METADATA +81 -19
- hdx_python_database-1.4.0.dist-info/RECORD +13 -0
- {hdx_python_database-1.3.8.dist-info → hdx_python_database-1.4.0.dist-info}/WHEEL +1 -1
- hdx_python_database-1.3.8.dist-info/RECORD +0 -13
- {hdx_python_database-1.3.8.dist-info → hdx_python_database-1.4.0.dist-info}/licenses/LICENSE +0 -0
hdx/database/_version.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
# file generated by
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
2
|
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
3
4
|
|
|
4
5
|
__all__ = [
|
|
5
6
|
"__version__",
|
|
@@ -10,25 +11,14 @@ __all__ = [
|
|
|
10
11
|
"commit_id",
|
|
11
12
|
]
|
|
12
13
|
|
|
13
|
-
TYPE_CHECKING = False
|
|
14
|
-
if TYPE_CHECKING:
|
|
15
|
-
from typing import Tuple
|
|
16
|
-
from typing import Union
|
|
17
|
-
|
|
18
|
-
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
19
|
-
COMMIT_ID = Union[str, None]
|
|
20
|
-
else:
|
|
21
|
-
VERSION_TUPLE = object
|
|
22
|
-
COMMIT_ID = object
|
|
23
|
-
|
|
24
14
|
version: str
|
|
25
15
|
__version__: str
|
|
26
|
-
__version_tuple__:
|
|
27
|
-
version_tuple:
|
|
28
|
-
commit_id:
|
|
29
|
-
__commit_id__:
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
30
20
|
|
|
31
|
-
__version__ = version = '1.
|
|
32
|
-
__version_tuple__ = version_tuple = (1,
|
|
21
|
+
__version__ = version = '1.4.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 4, 0)
|
|
33
23
|
|
|
34
24
|
__commit_id__ = commit_id = None
|
hdx/database/database.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Database utilities"""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
from typing import Any
|
|
4
|
+
from typing import Any
|
|
5
5
|
|
|
6
6
|
from sqlalchemy import Engine, TableClause, create_engine, insert
|
|
7
7
|
from sqlalchemy.exc import SQLAlchemyError
|
|
@@ -64,7 +64,8 @@ class Database:
|
|
|
64
64
|
table_base (Optional[Type[DeclarativeBase]]): Override table base. Defaults to None.
|
|
65
65
|
reflect (bool): Whether to reflect existing tables. Defaults to False.
|
|
66
66
|
**kwargs: See below
|
|
67
|
-
pg_restore_file (str): Restore database from pg_restore file
|
|
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,23 +74,24 @@ 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
|
"""
|
|
79
81
|
|
|
80
82
|
def __init__(
|
|
81
83
|
self,
|
|
82
|
-
engine:
|
|
83
|
-
db_uri:
|
|
84
|
-
database:
|
|
85
|
-
host:
|
|
86
|
-
port:
|
|
87
|
-
username:
|
|
88
|
-
password:
|
|
84
|
+
engine: Engine | None = None,
|
|
85
|
+
db_uri: str | None = None,
|
|
86
|
+
database: str | None = None,
|
|
87
|
+
host: str | None = None,
|
|
88
|
+
port: int | str | None = None,
|
|
89
|
+
username: str | None = None,
|
|
90
|
+
password: str | None = None,
|
|
89
91
|
dialect: str = "postgresql",
|
|
90
|
-
driver:
|
|
92
|
+
driver: str | None = None,
|
|
91
93
|
db_has_tz: bool = False,
|
|
92
|
-
table_base:
|
|
94
|
+
table_base: type[DeclarativeBase] | None = None,
|
|
93
95
|
reflect: bool = False,
|
|
94
96
|
**kwargs: Any,
|
|
95
97
|
) -> None:
|
|
@@ -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:
|
|
@@ -230,8 +238,8 @@ class Database:
|
|
|
230
238
|
|
|
231
239
|
def batch_populate(
|
|
232
240
|
self,
|
|
233
|
-
rows:
|
|
234
|
-
dbtable:
|
|
241
|
+
rows: list[dict],
|
|
242
|
+
dbtable: type[DeclarativeBase],
|
|
235
243
|
batch_size: int = 1000,
|
|
236
244
|
) -> None:
|
|
237
245
|
"""Batch populate database table.
|
|
@@ -252,11 +260,11 @@ class Database:
|
|
|
252
260
|
|
|
253
261
|
@staticmethod
|
|
254
262
|
def create_session(
|
|
255
|
-
engine:
|
|
256
|
-
db_uri:
|
|
257
|
-
table_base:
|
|
263
|
+
engine: Engine | None = None,
|
|
264
|
+
db_uri: str | None = None,
|
|
265
|
+
table_base: type[DeclarativeBase] = NoTZBase,
|
|
258
266
|
reflect: bool = False,
|
|
259
|
-
) ->
|
|
267
|
+
) -> tuple[Session, Any]:
|
|
260
268
|
"""Creates SQLAlchemy session given SQLAlchemy engine or database uri
|
|
261
269
|
(one of which must be supplied). Tables must inherit from Base in
|
|
262
270
|
hdx.utilities.database unless base is defined. If reflect is True,
|
|
@@ -332,7 +340,7 @@ class Database:
|
|
|
332
340
|
return False
|
|
333
341
|
|
|
334
342
|
@staticmethod
|
|
335
|
-
def prepare_view(view_params:
|
|
343
|
+
def prepare_view(view_params: dict) -> TableClause:
|
|
336
344
|
"""Prepare SQLAlchemy view from dictionary with keys: name, metadata and
|
|
337
345
|
selectable. Must be run before Base.metadata.create_all.
|
|
338
346
|
|
|
@@ -345,7 +353,7 @@ class Database:
|
|
|
345
353
|
return view(**view_params)
|
|
346
354
|
|
|
347
355
|
@classmethod
|
|
348
|
-
def prepare_views(cls, view_params_list:
|
|
356
|
+
def prepare_views(cls, view_params_list: list[dict]) -> list[TableClause]:
|
|
349
357
|
"""Prepare SQLAlchemy views from a list of dictionaries with keys: name,
|
|
350
358
|
metadata and selectable. Must be run before Base.metadata.create_all.
|
|
351
359
|
|
hdx/database/dburi.py
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"""Connection URI utilities"""
|
|
2
2
|
|
|
3
|
-
from typing import Any
|
|
4
|
-
from urllib.parse import urlsplit
|
|
3
|
+
from typing import Any
|
|
4
|
+
from urllib.parse import quote_plus, unquote, urlsplit
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
def get_params_from_connection_uri(
|
|
8
8
|
db_uri: str,
|
|
9
9
|
include_driver: bool = True,
|
|
10
|
-
) ->
|
|
10
|
+
) -> dict[str, Any]:
|
|
11
11
|
"""Gets PostgreSQL database connection parameters from connection URI
|
|
12
12
|
|
|
13
13
|
Args:
|
|
@@ -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:
|
|
@@ -38,13 +38,13 @@ def get_params_from_connection_uri(
|
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
def get_connection_uri(
|
|
41
|
-
database:
|
|
42
|
-
host:
|
|
43
|
-
port:
|
|
44
|
-
username:
|
|
45
|
-
password:
|
|
41
|
+
database: str | None = None,
|
|
42
|
+
host: str | None = None,
|
|
43
|
+
port: int | str | None = None,
|
|
44
|
+
username: str | None = None,
|
|
45
|
+
password: str | None = None,
|
|
46
46
|
dialect: str = "postgresql",
|
|
47
|
-
driver:
|
|
47
|
+
driver: str | None = None,
|
|
48
48
|
include_driver: bool = True,
|
|
49
49
|
) -> str:
|
|
50
50
|
"""Gets connection uri from database connection parameters
|
|
@@ -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,7 +1,6 @@
|
|
|
1
1
|
"""Utilities for database datetime columns without timezone"""
|
|
2
2
|
|
|
3
|
-
from datetime import
|
|
4
|
-
from typing import Optional
|
|
3
|
+
from datetime import UTC, datetime
|
|
5
4
|
|
|
6
5
|
from sqlalchemy import DateTime, TypeDecorator
|
|
7
6
|
from sqlalchemy.orm import DeclarativeBase, declared_attr
|
|
@@ -16,11 +15,11 @@ class ConversionNoTZ(TypeDecorator):
|
|
|
16
15
|
impl = DateTime
|
|
17
16
|
cache_ok = True
|
|
18
17
|
|
|
19
|
-
def process_bind_param(self, value:
|
|
18
|
+
def process_bind_param(self, value: datetime | None, _):
|
|
20
19
|
if value is None:
|
|
21
20
|
return value
|
|
22
21
|
if value.tzinfo:
|
|
23
|
-
value = value.astimezone(
|
|
22
|
+
value = value.astimezone(UTC)
|
|
24
23
|
|
|
25
24
|
return value.replace(tzinfo=None)
|
|
26
25
|
|
|
@@ -28,9 +27,9 @@ class ConversionNoTZ(TypeDecorator):
|
|
|
28
27
|
if value is None:
|
|
29
28
|
return value
|
|
30
29
|
if value.tzinfo is None:
|
|
31
|
-
return value.replace(tzinfo=
|
|
30
|
+
return value.replace(tzinfo=UTC)
|
|
32
31
|
|
|
33
|
-
return value.astimezone(
|
|
32
|
+
return value.astimezone(UTC)
|
|
34
33
|
|
|
35
34
|
|
|
36
35
|
class Base(DeclarativeBase):
|
hdx/database/postgresql.py
CHANGED
|
@@ -4,6 +4,7 @@ import logging
|
|
|
4
4
|
import subprocess
|
|
5
5
|
import time
|
|
6
6
|
from os import environ
|
|
7
|
+
from pathlib import Path
|
|
7
8
|
|
|
8
9
|
from .dburi import get_params_from_connection_uri, remove_driver_from_uri
|
|
9
10
|
|
|
@@ -25,17 +26,19 @@ class PostgresError(Exception):
|
|
|
25
26
|
pass
|
|
26
27
|
|
|
27
28
|
|
|
28
|
-
def wait_for_postgresql(db_uri: str) -> None:
|
|
29
|
+
def wait_for_postgresql(db_uri: str, timeout: int | None = None) -> None:
|
|
29
30
|
"""Waits for PostgreSQL database to be up
|
|
30
31
|
|
|
31
32
|
Args:
|
|
32
33
|
db_uri (str): Connection URI
|
|
34
|
+
timeout (Optional[int]): Seconds to wait. Defaults to None (wait forever).
|
|
33
35
|
|
|
34
36
|
Returns:
|
|
35
37
|
None
|
|
36
38
|
"""
|
|
37
39
|
connecting_string = "Checking for PostgreSQL..."
|
|
38
40
|
db_uri_nd = remove_driver_from_uri(db_uri)
|
|
41
|
+
start = time.monotonic()
|
|
39
42
|
while True:
|
|
40
43
|
try:
|
|
41
44
|
logger.info(connecting_string)
|
|
@@ -44,22 +47,25 @@ def wait_for_postgresql(db_uri: str) -> None:
|
|
|
44
47
|
logger.info("PostgreSQL is running!")
|
|
45
48
|
break
|
|
46
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")
|
|
47
52
|
time.sleep(1)
|
|
48
53
|
|
|
49
54
|
|
|
50
|
-
def restore_from_pgfile(db_uri: str, pg_restore_file: str) -> str:
|
|
55
|
+
def restore_from_pgfile(db_uri: str, pg_restore_file: Path | str) -> str:
|
|
51
56
|
"""Restore database from a pg_restore file created by pg_backup using the
|
|
52
57
|
pg_restore command.
|
|
53
58
|
|
|
54
59
|
Args:
|
|
55
60
|
db_uri (str): Connection URI
|
|
56
|
-
pg_restore_file (str): Path to the pg_restore database file
|
|
61
|
+
pg_restore_file (Path | str): Path to the pg_restore database file
|
|
57
62
|
|
|
58
63
|
Returns:
|
|
59
64
|
str: Output from the pg_restore command
|
|
60
65
|
"""
|
|
66
|
+
pg_restore_file = Path(pg_restore_file).absolute()
|
|
61
67
|
db_params = get_params_from_connection_uri(db_uri)
|
|
62
|
-
if pg_restore_file
|
|
68
|
+
if pg_restore_file.suffix == ".xz":
|
|
63
69
|
decompress = subprocess.Popen(
|
|
64
70
|
("unxz", "-c", "-d", pg_restore_file), stdout=subprocess.PIPE
|
|
65
71
|
).stdout
|
|
@@ -81,7 +87,7 @@ def restore_from_pgfile(db_uri: str, pg_restore_file: str) -> str:
|
|
|
81
87
|
subprocess_params.append(f"{value}")
|
|
82
88
|
|
|
83
89
|
if not decompress:
|
|
84
|
-
subprocess_params.append(pg_restore_file)
|
|
90
|
+
subprocess_params.append(str(pg_restore_file))
|
|
85
91
|
env = environ.copy()
|
|
86
92
|
password = db_params.get("password")
|
|
87
93
|
if password:
|
hdx/database/views.py
CHANGED
|
@@ -24,15 +24,13 @@ class DropView(DDLElement):
|
|
|
24
24
|
|
|
25
25
|
@compiler.compiles(CreateView)
|
|
26
26
|
def _create_view(element, compiler, **kw):
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
compiler.sql_compiler.process(element.selectable, literal_binds=True),
|
|
30
|
-
)
|
|
27
|
+
name = compiler.preparer.quote(element.name)
|
|
28
|
+
return f"CREATE VIEW {name} AS {compiler.sql_compiler.process(element.selectable, literal_binds=True)}"
|
|
31
29
|
|
|
32
30
|
|
|
33
31
|
@compiler.compiles(DropView)
|
|
34
32
|
def _drop_view(element, compiler, **kw):
|
|
35
|
-
return "DROP VIEW
|
|
33
|
+
return f"DROP VIEW {compiler.preparer.quote(element.name)}"
|
|
36
34
|
|
|
37
35
|
|
|
38
36
|
def view_exists(ddl, target, connection, **kw):
|
|
@@ -1,9 +1,9 @@
|
|
|
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
|
-
Author-email: Michael Rans <rans@
|
|
6
|
+
Author-email: Michael Rans <rans@un.org>
|
|
7
7
|
License: MIT
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
Keywords: HDX,database,postgresql
|
|
@@ -16,29 +16,16 @@ Classifier: Operating System :: Microsoft :: Windows
|
|
|
16
16
|
Classifier: Operating System :: POSIX :: Linux
|
|
17
17
|
Classifier: Operating System :: Unix
|
|
18
18
|
Classifier: Programming Language :: Python
|
|
19
|
-
Classifier: Programming Language :: Python :: 3
|
|
20
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
22
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
23
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
24
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
25
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
26
19
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
27
|
-
Requires-Python: >=3.
|
|
20
|
+
Requires-Python: >=3.11
|
|
28
21
|
Requires-Dist: sqlalchemy>=2
|
|
29
|
-
Provides-Extra: dev
|
|
30
|
-
Requires-Dist: pre-commit; extra == 'dev'
|
|
31
22
|
Provides-Extra: docs
|
|
32
23
|
Requires-Dist: mkapi; extra == 'docs'
|
|
33
24
|
Provides-Extra: postgresql
|
|
34
25
|
Requires-Dist: psycopg[binary]; extra == 'postgresql'
|
|
35
26
|
Provides-Extra: sshtunnel
|
|
36
|
-
Requires-Dist:
|
|
37
|
-
|
|
38
|
-
Requires-Dist: psycopg[binary]; extra == 'test'
|
|
39
|
-
Requires-Dist: pytest; extra == 'test'
|
|
40
|
-
Requires-Dist: pytest-cov; extra == 'test'
|
|
41
|
-
Requires-Dist: sshtunnel; extra == 'test'
|
|
27
|
+
Requires-Dist: cryptography>=48.0.1; extra == 'sshtunnel'
|
|
28
|
+
Requires-Dist: sshtunnel>=0.5.1; extra == 'sshtunnel'
|
|
42
29
|
Description-Content-Type: text/markdown
|
|
43
30
|
|
|
44
31
|
[](https://github.com/OCHA-DAP/hdx-python-database/actions/workflows/run-python-tests.yaml)
|
|
@@ -53,3 +40,78 @@ For more information, please read the [documentation](https://hdx-python-databas
|
|
|
53
40
|
|
|
54
41
|
This library is part of the [Humanitarian Data Exchange](https://data.humdata.org/) (HDX) project. If you have
|
|
55
42
|
humanitarian related data, please upload your datasets to HDX.
|
|
43
|
+
|
|
44
|
+
# Development
|
|
45
|
+
|
|
46
|
+
## Environment
|
|
47
|
+
|
|
48
|
+
Development is currently done using Python 3.13. The environment can be created with:
|
|
49
|
+
|
|
50
|
+
```shell
|
|
51
|
+
uv sync
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
This creates a .venv folder with the versions specified in the project's uv.lock file.
|
|
55
|
+
|
|
56
|
+
### Pre-commit
|
|
57
|
+
|
|
58
|
+
pre-commit will be installed when syncing uv. It is run every time you make a git
|
|
59
|
+
commit if you call it like this:
|
|
60
|
+
|
|
61
|
+
```shell
|
|
62
|
+
pre-commit install
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
With pre-commit, all code is formatted according to
|
|
66
|
+
[ruff](https://docs.astral.sh/ruff/) guidelines.
|
|
67
|
+
|
|
68
|
+
To check if your changes pass pre-commit without committing, run:
|
|
69
|
+
|
|
70
|
+
```shell
|
|
71
|
+
pre-commit run --all-files
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Packages
|
|
75
|
+
|
|
76
|
+
[uv](https://github.com/astral-sh/uv) is used for package management. If
|
|
77
|
+
you've introduced a new package to the source code (i.e. anywhere in `src/`),
|
|
78
|
+
please add it to the `project.dependencies` section of `pyproject.toml` with
|
|
79
|
+
any known version constraints.
|
|
80
|
+
|
|
81
|
+
To add packages required only for testing, add them to the
|
|
82
|
+
`[dependency-groups]`.
|
|
83
|
+
|
|
84
|
+
Any changes to the dependencies will be automatically reflected in
|
|
85
|
+
`uv.lock` with `pre-commit`, but you can re-generate the files without committing by
|
|
86
|
+
executing:
|
|
87
|
+
|
|
88
|
+
```shell
|
|
89
|
+
uv lock --upgrade
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Project
|
|
93
|
+
|
|
94
|
+
[uv](https://github.com/astral-sh/uv) is used for project management. The project can be
|
|
95
|
+
built using:
|
|
96
|
+
|
|
97
|
+
```shell
|
|
98
|
+
uv build
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Linting and syntax checking can be run with:
|
|
102
|
+
|
|
103
|
+
```shell
|
|
104
|
+
uv run ruff check
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
To run the tests and view coverage, execute:
|
|
108
|
+
|
|
109
|
+
```shell
|
|
110
|
+
uv run pytest
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Documentation
|
|
114
|
+
|
|
115
|
+
The documentation, including API documentation, is generated using ReadtheDocs and
|
|
116
|
+
MkDocs with Material. As you change the source code, remember to update the
|
|
117
|
+
documentation at `documentation/index.md`.
|
|
@@ -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=_hPMnsWQOeGw9lqV0fueN3y4Gw7vLcBaoT0Bio9pJWo,704
|
|
3
|
-
hdx/database/database.py,sha256=pOmshEfhkvlVN-cWJ9LX8ZL99TyZnwPAHOC-v0nF1pM,12926
|
|
4
|
-
hdx/database/dburi.py,sha256=rw8RuLpU85vxYl5-Zk1SdZRAz-tqre_ot5aEnkjhxAQ,2977
|
|
5
|
-
hdx/database/no_timezone.py,sha256=Lw087G3qOf8H70o8Hq_VvOMZ3ANei-lIA_sUlpFU_Uo,1130
|
|
6
|
-
hdx/database/postgresql.py,sha256=5xmGFbzjTPTr9clQw4rOD1ZQEzYuY25Ho2hhz-WdCC8,2923
|
|
7
|
-
hdx/database/utils.py,sha256=nzChgDj2_Mq8GS6xWaNmqOgpAiwK9tGYV_skKbErEVA,401
|
|
8
|
-
hdx/database/views.py,sha256=U4BN9tbjlr93qKkeNbZNo_PCIwGsng09mQ2xFqh5CH0,1636
|
|
9
|
-
hdx/database/with_timezone.py,sha256=GUUylmTwQHp22yedmCVKpVJzvjWgdS9lg9KeqciNJkY,235
|
|
10
|
-
hdx_python_database-1.3.8.dist-info/METADATA,sha256=fC99IRhtjoty5SpmkVDobyMMYS3SYhs68ftDUoxOrZM,2841
|
|
11
|
-
hdx_python_database-1.3.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
12
|
-
hdx_python_database-1.3.8.dist-info/licenses/LICENSE,sha256=wc-4GpMn-ODs-U_bTe1YCiPVgvcjzrpYOx2wPuyAeII,1079
|
|
13
|
-
hdx_python_database-1.3.8.dist-info/RECORD,,
|
{hdx_python_database-1.3.8.dist-info → hdx_python_database-1.4.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|