python-databases 0.1.5__py3-none-any.whl → 0.1.7__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.
- python_databases/elasticsearch_infrastructure/__init__.py +3 -8
- python_databases/elasticsearch_infrastructure/elasticsearch.py +64 -85
- python_databases/postgresql_infrastructure/__init__.py +1 -1
- python_databases/postgresql_infrastructure/postgresql.py +23 -18
- {python_databases-0.1.5.dist-info → python_databases-0.1.7.dist-info}/METADATA +19 -19
- python_databases-0.1.7.dist-info/RECORD +10 -0
- python_databases-0.1.5.dist-info/RECORD +0 -10
- {python_databases-0.1.5.dist-info → python_databases-0.1.7.dist-info}/WHEEL +0 -0
- {python_databases-0.1.5.dist-info → python_databases-0.1.7.dist-info}/licenses/LICENSE +0 -0
- {python_databases-0.1.5.dist-info → python_databases-0.1.7.dist-info}/top_level.txt +0 -0
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
from dotenv import load_dotenv
|
|
2
2
|
|
|
3
3
|
from python_databases.elasticsearch_infrastructure.elasticsearch import (
|
|
4
|
-
UrlProtocol,
|
|
5
4
|
ElasticSearch,
|
|
5
|
+
ElasticSearchCloud,
|
|
6
6
|
ElasticSearchOnPrem,
|
|
7
|
-
|
|
7
|
+
UrlProtocol,
|
|
8
8
|
)
|
|
9
9
|
|
|
10
10
|
load_dotenv()
|
|
11
11
|
|
|
12
|
-
__all__ = [
|
|
13
|
-
'UrlProtocol',
|
|
14
|
-
'ElasticSearch',
|
|
15
|
-
'ElasticSearchOnPrem',
|
|
16
|
-
'ElasticSearchCloud'
|
|
17
|
-
]
|
|
12
|
+
__all__ = ["UrlProtocol", "ElasticSearch", "ElasticSearchOnPrem", "ElasticSearchCloud"]
|
|
@@ -2,9 +2,10 @@ import logging
|
|
|
2
2
|
import time
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
4
|
from copy import deepcopy
|
|
5
|
-
from datetime import
|
|
5
|
+
from datetime import UTC, datetime
|
|
6
6
|
from enum import Enum
|
|
7
|
-
from typing import
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
8
9
|
import pandas as pd
|
|
9
10
|
from custom_python_logger import get_logger
|
|
10
11
|
from elasticsearch import Elasticsearch, helpers
|
|
@@ -12,20 +13,20 @@ from retrying import retry
|
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
class UrlProtocol(Enum):
|
|
15
|
-
HTTP =
|
|
16
|
-
HTTPS =
|
|
16
|
+
HTTP = "http"
|
|
17
|
+
HTTPS = "https"
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
class ElasticSearch(ABC):
|
|
20
21
|
def __init__(
|
|
21
22
|
self,
|
|
22
23
|
elk_hostname: str,
|
|
23
|
-
elasticsearch_port:
|
|
24
|
-
kibana_port:
|
|
24
|
+
elasticsearch_port: int | None,
|
|
25
|
+
kibana_port: int | None,
|
|
25
26
|
protocol: UrlProtocol,
|
|
26
|
-
username:
|
|
27
|
-
password:
|
|
28
|
-
):
|
|
27
|
+
username: str | None,
|
|
28
|
+
password: str | None,
|
|
29
|
+
) -> None:
|
|
29
30
|
self.logger = get_logger(self.__class__.__name__)
|
|
30
31
|
self._change_elasticsearch_logger()
|
|
31
32
|
|
|
@@ -36,69 +37,65 @@ class ElasticSearch(ABC):
|
|
|
36
37
|
|
|
37
38
|
self.elasticsearch_port = elasticsearch_port
|
|
38
39
|
if not self.elasticsearch_port:
|
|
39
|
-
self.elasticsearch_url = f
|
|
40
|
+
self.elasticsearch_url = f"{self.protocol.value}://{self.elk_hostname}"
|
|
40
41
|
else:
|
|
41
|
-
self.elasticsearch_url = f
|
|
42
|
+
self.elasticsearch_url = f"{self.protocol.value}://{self.elk_hostname}:{self.elasticsearch_port}"
|
|
42
43
|
self.kibana_port = kibana_port
|
|
43
44
|
|
|
44
45
|
self.elk_client = None
|
|
45
46
|
|
|
46
47
|
@abstractmethod
|
|
47
|
-
def connect_to_elasticsearch(self):
|
|
48
|
+
def connect_to_elasticsearch(self) -> None:
|
|
48
49
|
pass
|
|
49
50
|
|
|
50
51
|
@staticmethod
|
|
51
52
|
def _change_elasticsearch_logger(debug_level: int = logging.CRITICAL) -> None:
|
|
52
|
-
tracer = get_logger(
|
|
53
|
+
tracer = get_logger("elasticsearch")
|
|
53
54
|
tracer.setLevel(debug_level)
|
|
54
55
|
|
|
55
56
|
def _set_list_of_docs_quick(self, list_of_docs: list[dict], request_timeout: int = 600) -> None:
|
|
56
57
|
if failed_response := helpers.streaming_bulk(
|
|
57
|
-
|
|
58
|
-
list_of_docs,
|
|
59
|
-
raise_on_error=False,
|
|
60
|
-
request_timeout=request_timeout,
|
|
61
|
-
chunk_size=1000
|
|
58
|
+
self.elk_client, list_of_docs, raise_on_error=False, request_timeout=request_timeout, chunk_size=1000
|
|
62
59
|
):
|
|
63
60
|
for item in failed_response:
|
|
64
|
-
if item[1][
|
|
65
|
-
self.logger.debug(f
|
|
61
|
+
if item[1]["index"]["status"] != 201 or item[1]["index"]["_shards"]["failed"] > 0:
|
|
62
|
+
self.logger.debug(f"Failed document: \n{item}")
|
|
66
63
|
|
|
67
64
|
def _set_list_of_docs_safe(self, list_of_docs: list[dict]) -> None:
|
|
68
65
|
success_response, failed_response = helpers.bulk(self.elk_client, list_of_docs)
|
|
69
66
|
if success_response == len(list_of_docs):
|
|
70
|
-
self.logger.info(
|
|
67
|
+
self.logger.info("All documents were reported successfully")
|
|
71
68
|
else:
|
|
72
69
|
self.logger.error(
|
|
73
|
-
f
|
|
74
|
-
f
|
|
70
|
+
f"Not all documents were reported successfully.\n"
|
|
71
|
+
f"The documents that were not reported successfully: {failed_response}"
|
|
75
72
|
)
|
|
76
73
|
|
|
77
74
|
def post_list_of_docs(self, list_of_docs: list[dict], request_timeout: int = 600, quick: bool = False) -> None:
|
|
78
|
-
self.logger.info(
|
|
75
|
+
self.logger.info("Start to report the documents to ELK")
|
|
79
76
|
|
|
80
77
|
if quick:
|
|
81
78
|
self._set_list_of_docs_quick(list_of_docs=list_of_docs, request_timeout=request_timeout)
|
|
82
79
|
else:
|
|
83
80
|
self._set_list_of_docs_safe(list_of_docs=list_of_docs)
|
|
84
|
-
self.logger.info(
|
|
81
|
+
self.logger.info("Finish to report the documents to ELK")
|
|
85
82
|
|
|
86
83
|
@staticmethod
|
|
87
84
|
def _get_doc_with_basic_info() -> dict:
|
|
88
85
|
doc = {}
|
|
89
86
|
|
|
90
|
-
timestamp = datetime.now(tz=
|
|
87
|
+
timestamp = datetime.now(tz=UTC)
|
|
91
88
|
date_and_time_str = timestamp.isoformat()
|
|
92
|
-
date = date_and_time_str.split(
|
|
93
|
-
time_part = date_and_time_str.split(
|
|
89
|
+
date = date_and_time_str.split("T")[0]
|
|
90
|
+
time_part = date_and_time_str.split("T")[1].split("+")[0]
|
|
94
91
|
|
|
95
92
|
doc.update(
|
|
96
93
|
{
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
94
|
+
"doc_id": timestamp.strftime("%Y%m%dT%H%M%S%f"),
|
|
95
|
+
"timestamp": timestamp,
|
|
96
|
+
"date_and_time_str": date_and_time_str,
|
|
97
|
+
"date_str": date,
|
|
98
|
+
"time_str": time_part,
|
|
102
99
|
}
|
|
103
100
|
)
|
|
104
101
|
return doc
|
|
@@ -106,10 +103,10 @@ class ElasticSearch(ABC):
|
|
|
106
103
|
@staticmethod
|
|
107
104
|
def _add_list_values_as_str(doc: dict, column: str, value: Any) -> None:
|
|
108
105
|
"""Add list values as both list and string representation to the provided doc"""
|
|
109
|
-
if
|
|
110
|
-
filtered_value =
|
|
111
|
-
doc[f
|
|
112
|
-
doc[f
|
|
106
|
+
if isinstance(value, list) and all(isinstance(item, str) for item in value):
|
|
107
|
+
filtered_value = [item for item in value if item is not None]
|
|
108
|
+
doc[f"{column}"] = filtered_value
|
|
109
|
+
doc[f"{column}_str"] = ", ".join(filtered_value)
|
|
113
110
|
|
|
114
111
|
def _build_document(self, row: dict) -> dict:
|
|
115
112
|
doc = self._get_doc_with_basic_info()
|
|
@@ -125,41 +122,27 @@ class ElasticSearch(ABC):
|
|
|
125
122
|
|
|
126
123
|
@staticmethod
|
|
127
124
|
def _fill_list_of_docs(index_name: str, doc: dict[str, Any]) -> dict:
|
|
128
|
-
return {
|
|
129
|
-
"_index": index_name,
|
|
130
|
-
"_source": deepcopy(doc)
|
|
131
|
-
}
|
|
125
|
+
return {"_index": index_name, "_source": deepcopy(doc)}
|
|
132
126
|
|
|
133
127
|
def _prepare_documents_for_bulk(self, data: list[dict], doc_index_name: str) -> list[dict]:
|
|
134
128
|
list_of_docs = []
|
|
135
129
|
for index, row in enumerate(data):
|
|
136
|
-
self.logger.debug(f
|
|
130
|
+
self.logger.debug(f"index: {index + 1}/{len(data)}")
|
|
137
131
|
doc = self._build_document(row=row)
|
|
138
|
-
list_of_docs.append(
|
|
139
|
-
self._fill_list_of_docs(index_name=doc_index_name, doc=doc)
|
|
140
|
-
)
|
|
132
|
+
list_of_docs.append(self._fill_list_of_docs(index_name=doc_index_name, doc=doc))
|
|
141
133
|
return list_of_docs
|
|
142
134
|
|
|
143
135
|
def post_list_of_docs_as_bulk_chunk(
|
|
144
|
-
self,
|
|
145
|
-
list_of_docs: list[dict],
|
|
146
|
-
chunk_size=1000,
|
|
147
|
-
time_sleep: int = 60,
|
|
148
|
-
quick: bool = False
|
|
136
|
+
self, list_of_docs: list[dict], chunk_size: int = 1000, time_sleep: int = 60, quick: bool = False
|
|
149
137
|
) -> None:
|
|
150
138
|
for i in range(0, len(list_of_docs), chunk_size):
|
|
151
|
-
self.logger.info(f
|
|
152
|
-
chunk = list_of_docs[i:i + chunk_size]
|
|
139
|
+
self.logger.info(f"index: {i} - {i + chunk_size} / {len(list_of_docs)}")
|
|
140
|
+
chunk = list_of_docs[i : i + chunk_size]
|
|
153
141
|
self.post_list_of_docs(list_of_docs=chunk, quick=quick)
|
|
154
142
|
time.sleep(time_sleep)
|
|
155
143
|
|
|
156
144
|
def fill_elk_index_as_bulk( # post_list_of_docs_as_bulk
|
|
157
|
-
self,
|
|
158
|
-
data: list[dict],
|
|
159
|
-
doc_index_name: str,
|
|
160
|
-
chunk_size=1000,
|
|
161
|
-
time_sleep: int = 1,
|
|
162
|
-
quick: bool = True
|
|
145
|
+
self, data: list[dict], doc_index_name: str, chunk_size: int = 1000, time_sleep: int = 1, quick: bool = True
|
|
163
146
|
) -> None: # use as the main function to send data to the elastic search
|
|
164
147
|
list_of_docs = self._prepare_documents_for_bulk(
|
|
165
148
|
data=data,
|
|
@@ -177,9 +160,8 @@ class ElasticSearch(ABC):
|
|
|
177
160
|
if self.elk_client.indices.exists(index=index):
|
|
178
161
|
self.logger.info(f"The index '{index}' exists.")
|
|
179
162
|
return True
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
return False
|
|
163
|
+
self.logger.info(f"The index '{index}' does not exist.")
|
|
164
|
+
return False
|
|
183
165
|
|
|
184
166
|
def delete_index(self, index: str) -> None:
|
|
185
167
|
self.elk_client.indices.delete(index=index)
|
|
@@ -189,21 +171,20 @@ class ElasticSearch(ABC):
|
|
|
189
171
|
if self.check_if_index_exists(index=index):
|
|
190
172
|
self.delete_index(index=index)
|
|
191
173
|
return True
|
|
192
|
-
|
|
193
|
-
return False
|
|
174
|
+
return False
|
|
194
175
|
|
|
195
176
|
def get_documents(self, index: str, query: dict) -> list[dict]:
|
|
196
177
|
try:
|
|
197
178
|
res = self.elk_client.search(index=index, body=query)["hits"]["hits"]
|
|
198
179
|
return [doc["_source"] for doc in res]
|
|
199
180
|
except Exception as e:
|
|
200
|
-
raise Exception(f"Failed to get documents from index '{index}': {str(e)}")
|
|
181
|
+
raise Exception(f"Failed to get documents from index '{index}': {str(e)}") from e
|
|
201
182
|
|
|
202
183
|
def convert_dataframes_to_list_of_docs(self, dataframe: pd.DataFrame) -> list:
|
|
203
184
|
data = []
|
|
204
185
|
dataframe_values = dataframe.values.tolist()
|
|
205
186
|
for index, value in enumerate(dataframe_values):
|
|
206
|
-
self.logger.debug(f
|
|
187
|
+
self.logger.debug(f"index: {index}/{len(dataframe_values)}")
|
|
207
188
|
data.append(value)
|
|
208
189
|
return data
|
|
209
190
|
|
|
@@ -212,19 +193,19 @@ class ElasticSearchOnPrem(ElasticSearch):
|
|
|
212
193
|
def __init__(
|
|
213
194
|
self,
|
|
214
195
|
elk_hostname: str,
|
|
215
|
-
elasticsearch_port:
|
|
216
|
-
kibana_port:
|
|
196
|
+
elasticsearch_port: int | None = 9200,
|
|
197
|
+
kibana_port: int | None = 5602,
|
|
217
198
|
protocol: UrlProtocol = UrlProtocol.HTTPS,
|
|
218
|
-
username:
|
|
219
|
-
password:
|
|
220
|
-
):
|
|
199
|
+
username: str | None = None,
|
|
200
|
+
password: str | None = None,
|
|
201
|
+
) -> None:
|
|
221
202
|
super().__init__(
|
|
222
203
|
elk_hostname=elk_hostname,
|
|
223
204
|
elasticsearch_port=elasticsearch_port,
|
|
224
205
|
kibana_port=kibana_port,
|
|
225
206
|
protocol=protocol,
|
|
226
207
|
username=username,
|
|
227
|
-
password=password
|
|
208
|
+
password=password,
|
|
228
209
|
)
|
|
229
210
|
|
|
230
211
|
self.logger = get_logger(self.__class__.__name__)
|
|
@@ -232,42 +213,41 @@ class ElasticSearchOnPrem(ElasticSearch):
|
|
|
232
213
|
self.connect_to_elasticsearch()
|
|
233
214
|
|
|
234
215
|
@retry(stop_max_attempt_number=3, wait_fixed=180000)
|
|
235
|
-
def connect_to_elasticsearch(self):
|
|
216
|
+
def connect_to_elasticsearch(self) -> None:
|
|
236
217
|
if self.username and self.password:
|
|
237
218
|
self.elk_client = Elasticsearch(
|
|
238
219
|
hosts=[self.elasticsearch_url],
|
|
239
220
|
http_auth=(self.username, self.password),
|
|
240
221
|
# ca_certs="/etc/elasticsearch/certs/http_ca.crt",
|
|
241
|
-
verify_certs=False
|
|
222
|
+
verify_certs=False,
|
|
242
223
|
)
|
|
243
224
|
else:
|
|
244
|
-
self.elk_client = Elasticsearch(hosts=[f
|
|
225
|
+
self.elk_client = Elasticsearch(hosts=[f"{self.elasticsearch_url}"])
|
|
245
226
|
|
|
246
227
|
if self.elk_client.ping():
|
|
247
228
|
self.logger.info("Elasticsearch on-prem Connection Successful")
|
|
248
229
|
else:
|
|
249
230
|
self.logger.error("Elasticsearch on-prem Connection Failed")
|
|
250
|
-
raise Exception(
|
|
251
|
-
f'Failed to connect to Elasticsearch on-prem on {self.elasticsearch_url}')
|
|
231
|
+
raise Exception(f"Failed to connect to Elasticsearch on-prem on {self.elasticsearch_url}")
|
|
252
232
|
|
|
253
233
|
|
|
254
234
|
class ElasticSearchCloud(ElasticSearch):
|
|
255
235
|
def __init__(
|
|
256
236
|
self,
|
|
257
237
|
elk_hostname: str,
|
|
258
|
-
elasticsearch_port:
|
|
259
|
-
kibana_port:
|
|
238
|
+
elasticsearch_port: int | None = 9200,
|
|
239
|
+
kibana_port: int | None = 5602,
|
|
260
240
|
protocol: UrlProtocol = UrlProtocol.HTTPS,
|
|
261
|
-
username:
|
|
262
|
-
password:
|
|
263
|
-
):
|
|
241
|
+
username: str | None = None,
|
|
242
|
+
password: str | None = None,
|
|
243
|
+
) -> None:
|
|
264
244
|
super().__init__(
|
|
265
245
|
elk_hostname=elk_hostname,
|
|
266
246
|
elasticsearch_port=elasticsearch_port,
|
|
267
247
|
kibana_port=kibana_port,
|
|
268
248
|
protocol=protocol,
|
|
269
249
|
username=username,
|
|
270
|
-
password=password
|
|
250
|
+
password=password,
|
|
271
251
|
)
|
|
272
252
|
|
|
273
253
|
self.logger = get_logger(self.__class__.__name__)
|
|
@@ -275,12 +255,11 @@ class ElasticSearchCloud(ElasticSearch):
|
|
|
275
255
|
self.connect_to_elasticsearch()
|
|
276
256
|
|
|
277
257
|
@retry(stop_max_attempt_number=3, wait_fixed=180000)
|
|
278
|
-
def connect_to_elasticsearch(self):
|
|
258
|
+
def connect_to_elasticsearch(self) -> None:
|
|
279
259
|
self.elk_client = Elasticsearch(cloud_id=self.elk_hostname, http_auth=(self.username, self.password))
|
|
280
260
|
|
|
281
261
|
if self.elk_client.ping():
|
|
282
262
|
self.logger.info("Elasticsearch cloud Connection Successful")
|
|
283
263
|
else:
|
|
284
264
|
self.logger.error("Elasticsearch cloud Connection Failed")
|
|
285
|
-
raise Exception(
|
|
286
|
-
f'Failed to connect to Elasticsearch cloud on {self.elk_hostname}')
|
|
265
|
+
raise Exception(f"Failed to connect to Elasticsearch cloud on {self.elk_hostname}")
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import logging
|
|
2
1
|
import os
|
|
3
|
-
from
|
|
2
|
+
from types import TracebackType
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
4
5
|
import psycopg2
|
|
5
6
|
from custom_python_logger import get_logger
|
|
6
7
|
from psycopg2 import extras
|
|
7
|
-
from retrying import retry
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class PostgreSQL:
|
|
11
11
|
def __init__(
|
|
12
12
|
self,
|
|
13
|
-
name:
|
|
14
|
-
host:
|
|
15
|
-
port:
|
|
16
|
-
username:
|
|
17
|
-
password:
|
|
18
|
-
):
|
|
13
|
+
name: str | None = None,
|
|
14
|
+
host: str | None = None,
|
|
15
|
+
port: str | None = None,
|
|
16
|
+
username: str | None = None,
|
|
17
|
+
password: str | None = None,
|
|
18
|
+
) -> None:
|
|
19
19
|
self.logger = get_logger(self.__class__.__name__)
|
|
20
20
|
|
|
21
21
|
self.name = name or os.getenv("POSTGRESQL_NAME")
|
|
@@ -30,21 +30,21 @@ class PostgreSQL:
|
|
|
30
30
|
self.create_connection()
|
|
31
31
|
|
|
32
32
|
@property
|
|
33
|
-
def client(self):
|
|
33
|
+
def client(self) -> psycopg2.extensions.connection:
|
|
34
34
|
if not self._client or self._client.closed:
|
|
35
35
|
self.logger.warning("Re-establishing PostgreSQL connection")
|
|
36
36
|
self.create_connection()
|
|
37
37
|
return self._client
|
|
38
38
|
|
|
39
39
|
@property
|
|
40
|
-
def cursor(self):
|
|
40
|
+
def cursor(self) -> psycopg2.extensions.cursor:
|
|
41
41
|
if not self._cursor or self._cursor.closed:
|
|
42
42
|
self.logger.warning("Re-creating PostgreSQL cursor")
|
|
43
43
|
self.create_connection()
|
|
44
44
|
return self._cursor
|
|
45
45
|
|
|
46
46
|
# @retry(stop_max_attempt_number=3, wait_fixed=180000)
|
|
47
|
-
def create_connection(self):
|
|
47
|
+
def create_connection(self) -> None:
|
|
48
48
|
self.logger.info(f"Create connection to {self.host}:{self.port}")
|
|
49
49
|
|
|
50
50
|
self._client = psycopg2.connect(
|
|
@@ -56,15 +56,15 @@ class PostgreSQL:
|
|
|
56
56
|
)
|
|
57
57
|
self._cursor = self._client.cursor(cursor_factory=extras.DictCursor)
|
|
58
58
|
|
|
59
|
-
def execute_query(self, query: str, params:
|
|
59
|
+
def execute_query(self, query: str, params: tuple | dict[str, Any] | None = None) -> None:
|
|
60
60
|
self.logger.debug(f"Fetch rows from DB by query {query} with params {params}")
|
|
61
61
|
try:
|
|
62
62
|
self.cursor.execute(query, params)
|
|
63
63
|
except psycopg2.ProgrammingError as e:
|
|
64
64
|
raise psycopg2.ProgrammingError(f"Error executing query: {query}") from e
|
|
65
|
-
self.logger.debug(
|
|
65
|
+
self.logger.debug("Finished to fetch rows by query")
|
|
66
66
|
|
|
67
|
-
def fetch_all(self, query: str, params:
|
|
67
|
+
def fetch_all(self, query: str, params: tuple | dict[str, Any] | None = None) -> list[dict[str, Any]]:
|
|
68
68
|
self.execute_query(query, params)
|
|
69
69
|
try:
|
|
70
70
|
results = self.cursor.fetchall()
|
|
@@ -73,12 +73,17 @@ class PostgreSQL:
|
|
|
73
73
|
self.logger.debug(f"Fetched {len(results)} rows")
|
|
74
74
|
return results
|
|
75
75
|
|
|
76
|
-
def close(self):
|
|
76
|
+
def close(self) -> None:
|
|
77
77
|
self.cursor.close()
|
|
78
78
|
self.client.close()
|
|
79
79
|
|
|
80
|
-
def __enter__(self):
|
|
80
|
+
def __enter__(self) -> "PostgreSQL":
|
|
81
81
|
return self
|
|
82
82
|
|
|
83
|
-
def __exit__(
|
|
83
|
+
def __exit__(
|
|
84
|
+
self,
|
|
85
|
+
exc_type: type[BaseException] | None,
|
|
86
|
+
exc_val: BaseException | None,
|
|
87
|
+
exc_tb: TracebackType | None,
|
|
88
|
+
) -> None:
|
|
84
89
|
self.close()
|
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-databases
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.7
|
|
4
4
|
Summary: A Python library for managing and interacting with multiple databases seamlessly.
|
|
5
|
-
Home-page: https://github.com/aviz92/python-databases
|
|
6
5
|
Author: Avi Zaguri
|
|
7
|
-
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/aviz92/python-databases
|
|
8
8
|
Project-URL: Repository, https://github.com/aviz92/python-databases
|
|
9
|
+
Project-URL: Issues, https://github.com/aviz92/python-databases/issues
|
|
10
|
+
Keywords: development,databases,ORM,SQL,NoSQL,database management
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Topic :: System :: Logging
|
|
9
16
|
Requires-Python: >=3.11
|
|
10
17
|
Description-Content-Type: text/markdown
|
|
11
18
|
License-File: LICENSE
|
|
12
|
-
Requires-Dist:
|
|
13
|
-
Requires-Dist: wheel
|
|
14
|
-
Requires-Dist: dotenv
|
|
15
|
-
Requires-Dist: custom-python-logger
|
|
16
|
-
Requires-Dist: retrying
|
|
19
|
+
Requires-Dist: custom-python-logger>=2.0.10
|
|
17
20
|
Requires-Dist: elasticsearch<9
|
|
18
|
-
Requires-Dist: pandas
|
|
19
|
-
Requires-Dist:
|
|
20
|
-
Requires-Dist:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
Requires-Dist: pandas>=2.3.3
|
|
22
|
+
Requires-Dist: pre-commit>=4.5.0
|
|
23
|
+
Requires-Dist: psycopg2-binary>=2.9.11
|
|
24
|
+
Requires-Dist: pytest>=9.0.1
|
|
25
|
+
Requires-Dist: python-dotenv>=1.2.1
|
|
26
|
+
Requires-Dist: retrying>=1.4.2
|
|
27
|
+
Requires-Dist: setuptools>=80.9.0
|
|
28
|
+
Requires-Dist: wheel>=0.45.1
|
|
25
29
|
Dynamic: license-file
|
|
26
|
-
Dynamic: project-url
|
|
27
|
-
Dynamic: requires-dist
|
|
28
|
-
Dynamic: requires-python
|
|
29
|
-
Dynamic: summary
|
|
30
30
|
|
|
31
31
|
# Python Databases
|
|
32
32
|
A flexible and extensible Python library that provides a unified interface for interacting with multiple types of databases.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
python_databases/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
python_databases/elasticsearch_infrastructure/__init__.py,sha256=U1orkPi0Pg-1rtY1CPSF4-eVRfrMr36qpT9VNKjBUw4,297
|
|
3
|
+
python_databases/elasticsearch_infrastructure/elasticsearch.py,sha256=qIimoK4ykU38-pDlfqxgEpzsyXwZBP3ld5qXZE5ewb4,9989
|
|
4
|
+
python_databases/postgresql_infrastructure/__init__.py,sha256=yBjq7SLnNgnxW0DCQHEwCi_FrCNtjsJGJXIq7XtkUZg,150
|
|
5
|
+
python_databases/postgresql_infrastructure/postgresql.py,sha256=z1yBG-80Hj67mjDVlPvdIxfT5vl4IlbQE0rk2UEE4wY,3020
|
|
6
|
+
python_databases-0.1.7.dist-info/licenses/LICENSE,sha256=cSikHY6SZFsPZSBizCDAJ0-Bjjzxt-JtX6TVbKxwimo,1067
|
|
7
|
+
python_databases-0.1.7.dist-info/METADATA,sha256=iRGwbOsR4lJX2A5DCpFJqGx0eWMLfTIFhWydWeIC9BM,2087
|
|
8
|
+
python_databases-0.1.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
9
|
+
python_databases-0.1.7.dist-info/top_level.txt,sha256=fXcMUntaYJfKeFS7FjQ6DY4WVXYBsgdrDmhOudYs-vg,17
|
|
10
|
+
python_databases-0.1.7.dist-info/RECORD,,
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
python_databases/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
python_databases/elasticsearch_infrastructure/__init__.py,sha256=cjs95C1yBZpc3kEdkcXEamDvQGsO6ob5r5ttFCJfDaA,314
|
|
3
|
-
python_databases/elasticsearch_infrastructure/elasticsearch.py,sha256=A5NwzqMIcAyuMLhVIlbpoIYO18jQFqWmtaZ7XyeLzzw,10259
|
|
4
|
-
python_databases/postgresql_infrastructure/__init__.py,sha256=h1eJx0ogbtcF6UnN2c7uW5iCuadDab3So1VI15y3kWA,150
|
|
5
|
-
python_databases/postgresql_infrastructure/postgresql.py,sha256=zH8y5w2k2djhHLefDJhlGwa6MtoZKONhJ5vMxIxDdlc,2856
|
|
6
|
-
python_databases-0.1.5.dist-info/licenses/LICENSE,sha256=cSikHY6SZFsPZSBizCDAJ0-Bjjzxt-JtX6TVbKxwimo,1067
|
|
7
|
-
python_databases-0.1.5.dist-info/METADATA,sha256=SccVOpAUU_gPpKNiHgZO2DANGUgjFw7pGcpf59hgUiA,1792
|
|
8
|
-
python_databases-0.1.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
9
|
-
python_databases-0.1.5.dist-info/top_level.txt,sha256=fXcMUntaYJfKeFS7FjQ6DY4WVXYBsgdrDmhOudYs-vg,17
|
|
10
|
-
python_databases-0.1.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|