hdx-python-database 1.2.0__py2.py3-none-any.whl → 1.2.2__py2.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/__init__.py +42 -80
- hdx/database/_version.py +2 -2
- hdx/database/dburi.py +99 -0
- hdx/database/no_timezone.py +40 -0
- hdx/database/postgresql.py +10 -26
- {hdx_python_database-1.2.0.dist-info → hdx_python_database-1.2.2.dist-info}/METADATA +4 -5
- hdx_python_database-1.2.2.dist-info/RECORD +11 -0
- hdx_python_database-1.2.0.dist-info/RECORD +0 -9
- {hdx_python_database-1.2.0.dist-info → hdx_python_database-1.2.2.dist-info}/LICENSE +0 -0
- {hdx_python_database-1.2.0.dist-info → hdx_python_database-1.2.2.dist-info}/WHEEL +0 -0
- {hdx_python_database-1.2.0.dist-info → hdx_python_database-1.2.2.dist-info}/top_level.txt +0 -0
- {hdx_python_database-1.2.0.dist-info → hdx_python_database-1.2.2.dist-info}/zip-safe +0 -0
hdx/database/__init__.py
CHANGED
|
@@ -1,32 +1,34 @@
|
|
|
1
1
|
from ._version import version as __version__ # noqa: F401
|
|
2
|
+
from .dburi import get_connection_uri
|
|
2
3
|
|
|
3
4
|
"""Database utilities"""
|
|
4
5
|
import logging
|
|
5
|
-
from typing import Any,
|
|
6
|
-
from urllib.parse import urlsplit
|
|
6
|
+
from typing import Any, Optional, Type, Union
|
|
7
7
|
|
|
8
8
|
from sqlalchemy import create_engine
|
|
9
|
-
from sqlalchemy.ext.declarative import
|
|
10
|
-
from sqlalchemy.orm import Session, sessionmaker
|
|
9
|
+
from sqlalchemy.ext.declarative import declared_attr
|
|
10
|
+
from sqlalchemy.orm import DeclarativeBase, Session, sessionmaker
|
|
11
11
|
from sqlalchemy.pool import NullPool
|
|
12
12
|
from sshtunnel import SSHTunnelForwarder
|
|
13
13
|
|
|
14
|
-
from
|
|
14
|
+
from .no_timezone import Base as NoTZBase
|
|
15
|
+
from .postgresql import wait_for_postgresql
|
|
15
16
|
|
|
16
17
|
logger = logging.getLogger(__name__)
|
|
17
18
|
|
|
18
19
|
|
|
19
|
-
class
|
|
20
|
+
class Base(DeclarativeBase):
|
|
20
21
|
@declared_attr.directive
|
|
21
22
|
def __tablename__(cls):
|
|
22
23
|
return f"{cls.__name__.lower()}s"
|
|
23
24
|
|
|
24
25
|
|
|
25
|
-
Base = declarative_base(cls=HDXBase)
|
|
26
|
-
|
|
27
|
-
|
|
28
26
|
class Database:
|
|
29
|
-
"""Database helper class to handle ssh tunnels, waiting for PostgreSQL to
|
|
27
|
+
"""Database helper class to handle ssh tunnels, waiting for PostgreSQL to
|
|
28
|
+
be up etc. Can be used in a with statement returning a Session object.
|
|
29
|
+
db_has_tz which defaults to False indicates whether database datetime
|
|
30
|
+
columns have timezones. If not, conversion occurs between Python datetimes
|
|
31
|
+
with timezones to timezoneless database columns.
|
|
30
32
|
|
|
31
33
|
Args:
|
|
32
34
|
database (Optional[str]): Database name
|
|
@@ -34,7 +36,9 @@ class Database:
|
|
|
34
36
|
port (Union[int, str, None]): Database port
|
|
35
37
|
username (Optional[str]): Username to log into database
|
|
36
38
|
password (Optional[str]): Password to log into database
|
|
37
|
-
|
|
39
|
+
dialect (str): Database dialect. Defaults to "postgresql".
|
|
40
|
+
driver (Optional[str]): Database driver. Defaults to None (psycopg if postgresql or None)
|
|
41
|
+
db_has_tz (bool): True if db datetime columns have timezone. Defaults to False.
|
|
38
42
|
**kwargs: See below
|
|
39
43
|
ssh_host (str): SSH host (the server to connect to)
|
|
40
44
|
ssh_port (int): SSH port. Defaults to 22.
|
|
@@ -52,7 +56,9 @@ class Database:
|
|
|
52
56
|
port: Union[int, str, None] = None,
|
|
53
57
|
username: Optional[str] = None,
|
|
54
58
|
password: Optional[str] = None,
|
|
55
|
-
|
|
59
|
+
dialect: str = "postgresql",
|
|
60
|
+
driver: Optional[str] = None,
|
|
61
|
+
db_has_tz: bool = False,
|
|
56
62
|
**kwargs: Any,
|
|
57
63
|
) -> None:
|
|
58
64
|
if port is not None:
|
|
@@ -76,12 +82,22 @@ class Database:
|
|
|
76
82
|
port = self.server.local_bind_port
|
|
77
83
|
else:
|
|
78
84
|
self.server = None
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
85
|
+
db_uri = get_connection_uri(
|
|
86
|
+
database,
|
|
87
|
+
host,
|
|
88
|
+
port,
|
|
89
|
+
username,
|
|
90
|
+
password,
|
|
91
|
+
dialect=dialect,
|
|
92
|
+
driver=driver,
|
|
83
93
|
)
|
|
84
|
-
|
|
94
|
+
if dialect == "postgresql":
|
|
95
|
+
wait_for_postgresql(db_uri)
|
|
96
|
+
if db_has_tz:
|
|
97
|
+
table_base = Base
|
|
98
|
+
else:
|
|
99
|
+
table_base = NoTZBase
|
|
100
|
+
self.session = self.get_session(db_uri, table_base=table_base)
|
|
85
101
|
|
|
86
102
|
def __enter__(self) -> Session:
|
|
87
103
|
return self.session
|
|
@@ -92,74 +108,20 @@ class Database:
|
|
|
92
108
|
self.server.stop()
|
|
93
109
|
|
|
94
110
|
@staticmethod
|
|
95
|
-
def get_session(
|
|
96
|
-
|
|
97
|
-
|
|
111
|
+
def get_session(
|
|
112
|
+
db_uri: str, table_base: Type[DeclarativeBase] = NoTZBase
|
|
113
|
+
) -> Session:
|
|
114
|
+
"""Gets SQLAlchemy session given url. Tables must inherit from Base in
|
|
115
|
+
hdx.utilities.database unless base is defined.
|
|
98
116
|
|
|
99
117
|
Args:
|
|
100
|
-
|
|
118
|
+
db_uri (str): Connection URI
|
|
119
|
+
table_base (Type[DeclarativeBase]): Base database table class. Defaults to NoTZBase.
|
|
101
120
|
|
|
102
121
|
Returns:
|
|
103
122
|
sqlalchemy.orm.Session: SQLAlchemy session
|
|
104
123
|
"""
|
|
105
|
-
engine = create_engine(
|
|
124
|
+
engine = create_engine(db_uri, poolclass=NullPool, echo=False)
|
|
106
125
|
Session = sessionmaker(bind=engine)
|
|
107
|
-
|
|
126
|
+
table_base.metadata.create_all(engine)
|
|
108
127
|
return Session()
|
|
109
|
-
|
|
110
|
-
@staticmethod
|
|
111
|
-
def get_params_from_sqlalchemy_url(db_url: str) -> Dict[str, Any]:
|
|
112
|
-
"""Gets PostgreSQL database connection parameters from SQLAlchemy url
|
|
113
|
-
|
|
114
|
-
Args:
|
|
115
|
-
db_url (str): SQLAlchemy url
|
|
116
|
-
|
|
117
|
-
Returns:
|
|
118
|
-
Dict[str,Any]: Dictionary of database connection parameters
|
|
119
|
-
"""
|
|
120
|
-
result = urlsplit(db_url)
|
|
121
|
-
return {
|
|
122
|
-
"database": result.path[1:],
|
|
123
|
-
"host": result.hostname,
|
|
124
|
-
"port": result.port,
|
|
125
|
-
"username": result.username,
|
|
126
|
-
"password": result.password,
|
|
127
|
-
"driver": result.scheme,
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
@staticmethod
|
|
131
|
-
def get_sqlalchemy_url(
|
|
132
|
-
database: Optional[str] = None,
|
|
133
|
-
host: Optional[str] = None,
|
|
134
|
-
port: Union[int, str, None] = None,
|
|
135
|
-
username: Optional[str] = None,
|
|
136
|
-
password: Optional[str] = None,
|
|
137
|
-
driver: str = "postgresql",
|
|
138
|
-
) -> str:
|
|
139
|
-
"""Gets SQLAlchemy url from database connection parameters
|
|
140
|
-
|
|
141
|
-
Args:
|
|
142
|
-
database (Optional[str]): Database name
|
|
143
|
-
host (Optional[str]): Host where database is located
|
|
144
|
-
port (Union[int, str, None]): Database port
|
|
145
|
-
username (Optional[str]): Username to log into database
|
|
146
|
-
password (Optional[str]): Password to log into database
|
|
147
|
-
driver (str): Database driver. Defaults to "postgresql".
|
|
148
|
-
|
|
149
|
-
Returns:
|
|
150
|
-
db_url (str): SQLAlchemy url
|
|
151
|
-
"""
|
|
152
|
-
strings = [f"{driver}://"]
|
|
153
|
-
if username:
|
|
154
|
-
strings.append(username)
|
|
155
|
-
if password:
|
|
156
|
-
strings.append(f":{password}@")
|
|
157
|
-
else:
|
|
158
|
-
strings.append("@")
|
|
159
|
-
if host:
|
|
160
|
-
strings.append(host)
|
|
161
|
-
if port is not None:
|
|
162
|
-
strings.append(f":{port}")
|
|
163
|
-
if database:
|
|
164
|
-
strings.append(f"/{database}")
|
|
165
|
-
return "".join(strings)
|
hdx/database/_version.py
CHANGED
hdx/database/dburi.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""Connection URI utilities"""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, Optional, Union
|
|
4
|
+
from urllib.parse import urlsplit
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_params_from_connection_uri(
|
|
8
|
+
db_uri: str,
|
|
9
|
+
include_driver: bool = True,
|
|
10
|
+
) -> Dict[str, Any]:
|
|
11
|
+
"""Gets PostgreSQL database connection parameters from connection URI
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
db_uri (str): Connection URI
|
|
15
|
+
include_driver (bool): Whether to include driver in params. Defaults to True.
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
Dict[str,Any]: Dictionary of database connection parameters
|
|
19
|
+
"""
|
|
20
|
+
result = urlsplit(db_uri)
|
|
21
|
+
dialectdriver = result.scheme.split("+")
|
|
22
|
+
if len(dialectdriver) == 1:
|
|
23
|
+
dialect = dialectdriver[0]
|
|
24
|
+
driver = None
|
|
25
|
+
else:
|
|
26
|
+
dialect, driver = dialectdriver
|
|
27
|
+
result = {
|
|
28
|
+
"database": result.path[1:],
|
|
29
|
+
"host": result.hostname,
|
|
30
|
+
"port": result.port,
|
|
31
|
+
"username": result.username,
|
|
32
|
+
"password": result.password,
|
|
33
|
+
"dialect": dialect,
|
|
34
|
+
}
|
|
35
|
+
if include_driver:
|
|
36
|
+
result["driver"] = driver
|
|
37
|
+
return result
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def get_connection_uri(
|
|
41
|
+
database: Optional[str] = None,
|
|
42
|
+
host: Optional[str] = None,
|
|
43
|
+
port: Union[int, str, None] = None,
|
|
44
|
+
username: Optional[str] = None,
|
|
45
|
+
password: Optional[str] = None,
|
|
46
|
+
dialect: str = "postgresql",
|
|
47
|
+
driver: Optional[str] = None,
|
|
48
|
+
include_driver: bool = True,
|
|
49
|
+
) -> str:
|
|
50
|
+
"""Gets connection uri from database connection parameters
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
database (Optional[str]): Database name
|
|
54
|
+
host (Optional[str]): Host where database is located
|
|
55
|
+
port (Union[int, str, None]): Database port
|
|
56
|
+
username (Optional[str]): Username to log into database
|
|
57
|
+
password (Optional[str]): Password to log into database
|
|
58
|
+
dialect (str): Database dialect. Defaults to "postgresql".
|
|
59
|
+
driver (Optional[str]): Database driver. Defaults to None (psycopg if postgresql or None)
|
|
60
|
+
include_driver (bool): Whether to include driver in uri. Defaults to True.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
str: Connection URI
|
|
64
|
+
"""
|
|
65
|
+
strings = [dialect]
|
|
66
|
+
if include_driver:
|
|
67
|
+
if dialect == "postgresql" and driver is None:
|
|
68
|
+
driver = "psycopg"
|
|
69
|
+
if driver:
|
|
70
|
+
strings.append(f"+{driver}")
|
|
71
|
+
strings.append("://")
|
|
72
|
+
if username:
|
|
73
|
+
strings.append(username)
|
|
74
|
+
if password:
|
|
75
|
+
strings.append(f":{password}@")
|
|
76
|
+
else:
|
|
77
|
+
strings.append("@")
|
|
78
|
+
if host:
|
|
79
|
+
strings.append(host)
|
|
80
|
+
if port is not None:
|
|
81
|
+
strings.append(f":{port}")
|
|
82
|
+
if database:
|
|
83
|
+
strings.append(f"/{database}")
|
|
84
|
+
return "".join(strings)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def remove_driver_from_uri(db_uri: str) -> str:
|
|
88
|
+
"""Remove driver from connection URI
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
db_uri (str): Connection URI
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
str: Connection URI with driver removed
|
|
95
|
+
"""
|
|
96
|
+
result = urlsplit(db_uri)
|
|
97
|
+
dialectdriver = result.scheme
|
|
98
|
+
dialect = dialectdriver.split("+")[0]
|
|
99
|
+
return db_uri.replace(dialectdriver, dialect)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Utilities for database datetime columns without timezone"""
|
|
2
|
+
from datetime import datetime, timezone
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from sqlalchemy import DateTime, TypeDecorator
|
|
6
|
+
from sqlalchemy.orm import DeclarativeBase, declared_attr
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ConversionNoTZ(TypeDecorator):
|
|
10
|
+
"""Convert from/to datetime with timezone from database columns that don't
|
|
11
|
+
use timezone"""
|
|
12
|
+
|
|
13
|
+
impl = DateTime
|
|
14
|
+
cache_ok = True
|
|
15
|
+
|
|
16
|
+
def process_bind_param(self, value: Optional[datetime], _):
|
|
17
|
+
if value is None:
|
|
18
|
+
return value
|
|
19
|
+
if value.tzinfo:
|
|
20
|
+
value = value.astimezone(timezone.utc)
|
|
21
|
+
|
|
22
|
+
return value.replace(tzinfo=None)
|
|
23
|
+
|
|
24
|
+
def process_result_value(self, value, _):
|
|
25
|
+
if value is None:
|
|
26
|
+
return value
|
|
27
|
+
if value.tzinfo is None:
|
|
28
|
+
return value.replace(tzinfo=timezone.utc)
|
|
29
|
+
|
|
30
|
+
return value.astimezone(timezone.utc)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Base(DeclarativeBase):
|
|
34
|
+
type_annotation_map = {
|
|
35
|
+
datetime: ConversionNoTZ,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@declared_attr.directive
|
|
39
|
+
def __tablename__(cls):
|
|
40
|
+
return f"{cls.__name__.lower()}s"
|
hdx/database/postgresql.py
CHANGED
|
@@ -1,56 +1,40 @@
|
|
|
1
1
|
"""PostgreSQL specific utilities"""
|
|
2
2
|
import logging
|
|
3
3
|
import time
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
from .dburi import remove_driver_from_uri
|
|
5
6
|
|
|
6
7
|
try:
|
|
7
|
-
import
|
|
8
|
+
import psycopg
|
|
8
9
|
except ImportError:
|
|
10
|
+
psycopg = None
|
|
9
11
|
# dependency missing, issue a warning
|
|
10
12
|
import warnings
|
|
11
13
|
|
|
12
14
|
warnings.warn(
|
|
13
|
-
"
|
|
15
|
+
"psycopg not found! Please install hdx-python-database[postgresql] to enable."
|
|
14
16
|
)
|
|
15
17
|
|
|
16
18
|
logger = logging.getLogger(__name__)
|
|
17
19
|
|
|
18
20
|
|
|
19
|
-
def wait_for_postgresql(
|
|
20
|
-
database: Optional[str],
|
|
21
|
-
host: Optional[str],
|
|
22
|
-
port: Union[int, str, None],
|
|
23
|
-
username: Optional[str],
|
|
24
|
-
password: Optional[str],
|
|
25
|
-
) -> None:
|
|
21
|
+
def wait_for_postgresql(db_uri: str) -> None:
|
|
26
22
|
"""Waits for PostgreSQL database to be up
|
|
27
23
|
|
|
28
24
|
Args:
|
|
29
|
-
|
|
30
|
-
host (Optional[str]): Host where database is located
|
|
31
|
-
port (Union[int, str, None]): Database port
|
|
32
|
-
username (Optional[str]): Username to log into database
|
|
33
|
-
password (Optional[str]): Password to log into database
|
|
25
|
+
db_uri (str): Connection URI
|
|
34
26
|
|
|
35
27
|
Returns:
|
|
36
28
|
None
|
|
37
29
|
"""
|
|
38
30
|
connecting_string = "Checking for PostgreSQL..."
|
|
39
|
-
|
|
40
|
-
port = int(port)
|
|
31
|
+
db_uri_nd = remove_driver_from_uri(db_uri)
|
|
41
32
|
while True:
|
|
42
33
|
try:
|
|
43
34
|
logger.info(connecting_string)
|
|
44
|
-
connection =
|
|
45
|
-
database=database,
|
|
46
|
-
host=host,
|
|
47
|
-
port=port,
|
|
48
|
-
user=username,
|
|
49
|
-
password=password,
|
|
50
|
-
connect_timeout=3,
|
|
51
|
-
)
|
|
35
|
+
connection = psycopg.connect(db_uri_nd, connect_timeout=3)
|
|
52
36
|
connection.close()
|
|
53
37
|
logger.info("PostgreSQL is running!")
|
|
54
38
|
break
|
|
55
|
-
except
|
|
39
|
+
except psycopg.OperationalError:
|
|
56
40
|
time.sleep(1)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: hdx-python-database
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.2
|
|
4
4
|
Summary: HDX Python database utilities
|
|
5
5
|
Home-page: https://github.com/OCHA-DAP/hdx-python-database
|
|
6
6
|
Author: Michael Rans
|
|
@@ -13,7 +13,6 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
13
13
|
Classifier: Programming Language :: Python
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
15
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.8
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.9
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.10
|
|
@@ -26,15 +25,15 @@ Classifier: Operating System :: POSIX :: Linux
|
|
|
26
25
|
Classifier: Operating System :: Unix
|
|
27
26
|
Classifier: Operating System :: MacOS
|
|
28
27
|
Classifier: Operating System :: Microsoft :: Windows
|
|
29
|
-
Requires-Python: >=3.
|
|
28
|
+
Requires-Python: >=3.8
|
|
30
29
|
Description-Content-Type: text/markdown; charset=UTF-8
|
|
31
30
|
License-File: LICENSE
|
|
32
31
|
Requires-Dist: sqlalchemy (>=2)
|
|
33
32
|
Requires-Dist: sshtunnel
|
|
34
33
|
Provides-Extra: postgresql
|
|
35
|
-
Requires-Dist:
|
|
34
|
+
Requires-Dist: psycopg[binary] ; extra == 'postgresql'
|
|
36
35
|
|
|
37
|
-
[](https://github.com/OCHA-DAP/hdx-python-database/actions/workflows/run-python-tests.yml)
|
|
38
37
|
[](https://codecov.io/gh/OCHA-DAP/hdx-python-database)
|
|
39
38
|
[](https://github.com/psf/black)
|
|
40
39
|
[](https://pycqa.github.io/isort/)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
hdx/database/__init__.py,sha256=HPKyvzWk-Gqd63sE3kcoyEO9n5h_9l3IeuzhzFnMWfQ,4458
|
|
2
|
+
hdx/database/_version.py,sha256=O2qpyKb1ilx0S5H2I5jnx1k8SSt3R0rHyv19gH5I8Ws,160
|
|
3
|
+
hdx/database/dburi.py,sha256=hNksrjaCC7AtD_M5ef0DCjkS0rJCofQqdXVD2BHBIGc,2938
|
|
4
|
+
hdx/database/no_timezone.py,sha256=7Gnidmu317AWAWx4aPY4oIWBio9dqqSsiUTndEi5hMY,1082
|
|
5
|
+
hdx/database/postgresql.py,sha256=9VlcdHhBURZPDaazj1kEq8gA4hUFJY-5hIN9uzibOlM,969
|
|
6
|
+
hdx_python_database-1.2.2.dist-info/LICENSE,sha256=wc-4GpMn-ODs-U_bTe1YCiPVgvcjzrpYOx2wPuyAeII,1079
|
|
7
|
+
hdx_python_database-1.2.2.dist-info/METADATA,sha256=ykW44dWeZ6DVkzUerqYbitQNoJE5WiBOkhBZXo3Rtzg,2464
|
|
8
|
+
hdx_python_database-1.2.2.dist-info/WHEEL,sha256=bb2Ot9scclHKMOLDEHY6B2sicWOgugjFKaJsT7vwMQo,110
|
|
9
|
+
hdx_python_database-1.2.2.dist-info/top_level.txt,sha256=cfo3TyDjnu-B1jb_itPPFQCH00c5VT5GuZ30eNrGmlI,4
|
|
10
|
+
hdx_python_database-1.2.2.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
11
|
+
hdx_python_database-1.2.2.dist-info/RECORD,,
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
hdx/database/__init__.py,sha256=-X6o1GRxotnDS36Av4Ek9-9qwsH38auCgMYYPw0N2DM,5443
|
|
2
|
-
hdx/database/_version.py,sha256=CilGHycSRzmSEnCMQvTRJ5J_LWxMs---UqkVbOmQ2rA,160
|
|
3
|
-
hdx/database/postgresql.py,sha256=mvekzE3OE_LgoUyoJSGXwYh9K4DwzgSNi-6MsaHQ9RE,1511
|
|
4
|
-
hdx_python_database-1.2.0.dist-info/LICENSE,sha256=wc-4GpMn-ODs-U_bTe1YCiPVgvcjzrpYOx2wPuyAeII,1079
|
|
5
|
-
hdx_python_database-1.2.0.dist-info/METADATA,sha256=TS90b_YLxudhzOc3wTPyBj0voEpFA-uZRls-nZcqxHY,2483
|
|
6
|
-
hdx_python_database-1.2.0.dist-info/WHEEL,sha256=bb2Ot9scclHKMOLDEHY6B2sicWOgugjFKaJsT7vwMQo,110
|
|
7
|
-
hdx_python_database-1.2.0.dist-info/top_level.txt,sha256=cfo3TyDjnu-B1jb_itPPFQCH00c5VT5GuZ30eNrGmlI,4
|
|
8
|
-
hdx_python_database-1.2.0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
9
|
-
hdx_python_database-1.2.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|