wcp-library 1.5.7__tar.gz → 1.6.1__tar.gz

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.
Files changed (25) hide show
  1. {wcp_library-1.5.7 → wcp_library-1.6.1}/PKG-INFO +1 -1
  2. {wcp_library-1.5.7 → wcp_library-1.6.1}/pyproject.toml +1 -1
  3. {wcp_library-1.5.7 → wcp_library-1.6.1}/wcp_library/sql/__init__.py +8 -8
  4. {wcp_library-1.5.7 → wcp_library-1.6.1}/wcp_library/sql/oracle.py +166 -81
  5. {wcp_library-1.5.7 → wcp_library-1.6.1}/wcp_library/sql/postgres.py +163 -47
  6. {wcp_library-1.5.7 → wcp_library-1.6.1}/README.md +0 -0
  7. {wcp_library-1.5.7 → wcp_library-1.6.1}/wcp_library/__init__.py +0 -0
  8. {wcp_library-1.5.7 → wcp_library-1.6.1}/wcp_library/credentials/__init__.py +0 -0
  9. {wcp_library-1.5.7 → wcp_library-1.6.1}/wcp_library/credentials/_credential_manager_asynchronous.py +0 -0
  10. {wcp_library-1.5.7 → wcp_library-1.6.1}/wcp_library/credentials/_credential_manager_synchronous.py +0 -0
  11. {wcp_library-1.5.7 → wcp_library-1.6.1}/wcp_library/credentials/api.py +0 -0
  12. {wcp_library-1.5.7 → wcp_library-1.6.1}/wcp_library/credentials/ftp.py +0 -0
  13. {wcp_library-1.5.7 → wcp_library-1.6.1}/wcp_library/credentials/internet.py +0 -0
  14. {wcp_library-1.5.7 → wcp_library-1.6.1}/wcp_library/credentials/oracle.py +0 -0
  15. {wcp_library-1.5.7 → wcp_library-1.6.1}/wcp_library/credentials/postgres.py +0 -0
  16. {wcp_library-1.5.7 → wcp_library-1.6.1}/wcp_library/emailing.py +0 -0
  17. {wcp_library-1.5.7 → wcp_library-1.6.1}/wcp_library/ftp/__init__.py +0 -0
  18. {wcp_library-1.5.7 → wcp_library-1.6.1}/wcp_library/ftp/ftp.py +0 -0
  19. {wcp_library-1.5.7 → wcp_library-1.6.1}/wcp_library/ftp/sftp.py +0 -0
  20. {wcp_library-1.5.7 → wcp_library-1.6.1}/wcp_library/informatica.py +0 -0
  21. {wcp_library-1.5.7 → wcp_library-1.6.1}/wcp_library/logging.py +0 -0
  22. {wcp_library-1.5.7 → wcp_library-1.6.1}/wcp_library/selenium/__init__.py +0 -0
  23. {wcp_library-1.5.7 → wcp_library-1.6.1}/wcp_library/selenium/_selenium_driver.py +0 -0
  24. {wcp_library-1.5.7 → wcp_library-1.6.1}/wcp_library/selenium/selenium_helper.py +0 -0
  25. {wcp_library-1.5.7 → wcp_library-1.6.1}/wcp_library/time.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: wcp-library
3
- Version: 1.5.7
3
+ Version: 1.6.1
4
4
  Summary: Common utilites for internal development at WCP
5
5
  Author: Mitch-Petersen
6
6
  Author-email: mitch.petersen@wcap.ca
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "wcp-library"
3
- version = "1.5.7"
3
+ version = "1.6.1"
4
4
  description = "Common utilites for internal development at WCP"
5
5
  authors = [{name="Mitch-Petersen", email="mitch.petersen@wcap.ca"}]
6
6
  readme = "README.md"
@@ -9,20 +9,20 @@ import psycopg
9
9
  logger = logging.getLogger(__name__)
10
10
 
11
11
 
12
- def retry(f: callable) -> callable:
12
+ def retry(func: callable) -> callable:
13
13
  """
14
14
  Decorator to retry a function
15
15
 
16
- :param f: function
16
+ :param func: function
17
17
  :return: function
18
18
  """
19
19
 
20
- @wraps(f)
20
+ @wraps(func)
21
21
  def wrapper(self, *args, **kwargs):
22
22
  self._retry_count = 0
23
23
  while True:
24
24
  try:
25
- return f(self, *args, **kwargs)
25
+ return func(self, *args, **kwargs)
26
26
  except (oracledb.OperationalError, oracledb.DatabaseError, psycopg.OperationalError) as e:
27
27
  if isinstance(e, (oracledb.OperationalError, oracledb.DatabaseError, psycopg.OperationalError, psycopg.DatabaseError)):
28
28
  error_obj, = e.args
@@ -38,20 +38,20 @@ def retry(f: callable) -> callable:
38
38
  return wrapper
39
39
 
40
40
 
41
- def async_retry(f: callable) -> callable:
41
+ def async_retry(func: callable) -> callable:
42
42
  """
43
43
  Decorator to retry a function
44
44
 
45
- :param f: function
45
+ :param func: function
46
46
  :return: function
47
47
  """
48
48
 
49
- @wraps(f)
49
+ @wraps(func)
50
50
  async def wrapper(self, *args, **kwargs):
51
51
  self._retry_count = 0
52
52
  while True:
53
53
  try:
54
- return await f(self, *args, **kwargs)
54
+ return await func(self, *args, **kwargs)
55
55
  except (oracledb.OperationalError, oracledb.DatabaseError, psycopg.OperationalError) as e:
56
56
  if isinstance(e, (oracledb.OperationalError, oracledb.DatabaseError, psycopg.OperationalError, psycopg.DatabaseError)):
57
57
  error_obj, = e.args
@@ -4,7 +4,7 @@ from typing import Optional
4
4
  import numpy as np
5
5
  import pandas as pd
6
6
  import oracledb
7
- from oracledb import ConnectionPool, AsyncConnectionPool
7
+ from oracledb import ConnectionPool, AsyncConnectionPool, Connection, AsyncConnection
8
8
 
9
9
  from wcp_library.sql import retry, async_retry
10
10
 
@@ -12,7 +12,7 @@ logger = logging.getLogger(__name__)
12
12
 
13
13
 
14
14
  def _connect_warehouse(username: str, password: str, hostname: str, port: int, database: str, min_connections: int,
15
- max_connections: int) -> ConnectionPool:
15
+ max_connections: int, use_pool: bool) -> ConnectionPool | Connection:
16
16
  """
17
17
  Create Warehouse Connection
18
18
 
@@ -23,23 +23,34 @@ def _connect_warehouse(username: str, password: str, hostname: str, port: int, d
23
23
  :param database: database
24
24
  :param min_connections:
25
25
  :param max_connections:
26
- :return: session_pool
26
+ :param use_pool: use connection pool
27
+ :return: session_pool | connection
27
28
  """
28
29
 
29
- dsn = oracledb.makedsn(hostname, port, sid=database)
30
- session_pool = oracledb.create_pool(
31
- user=username,
32
- password=password,
33
- dsn=dsn,
34
- min=min_connections,
35
- max=max_connections,
36
- increment=1,
37
- )
38
- return session_pool
30
+ if use_pool:
31
+ logger.debug(f"Creating connection pool with min size {min_connections} and max size {max_connections}")
32
+ dsn = oracledb.makedsn(hostname, port, sid=database)
33
+ session_pool = oracledb.create_pool(
34
+ user=username,
35
+ password=password,
36
+ dsn=dsn,
37
+ min=min_connections,
38
+ max=max_connections,
39
+ increment=1,
40
+ )
41
+ return session_pool
42
+ else:
43
+ logger.debug("Creating single connection")
44
+ connection = oracledb.connect(
45
+ user=username,
46
+ password=password,
47
+ dsn=oracledb.makedsn(hostname, port, sid=database)
48
+ )
49
+ return connection
39
50
 
40
51
 
41
52
  async def _async_connect_warehouse(username: str, password: str, hostname: str, port: int, database: str,
42
- min_connections: int, max_connections: int) -> AsyncConnectionPool:
53
+ min_connections: int, max_connections: int, use_pool: bool) -> AsyncConnectionPool | AsyncConnection:
43
54
  """
44
55
  Create Warehouse Connection
45
56
 
@@ -50,19 +61,30 @@ async def _async_connect_warehouse(username: str, password: str, hostname: str,
50
61
  :param database: database
51
62
  :param min_connections:
52
63
  :param max_connections:
53
- :return: session_pool
64
+ :param use_pool: use connection pool
65
+ :return: session_pool | connection
54
66
  """
55
67
 
56
- dsn = oracledb.makedsn(hostname, port, sid=database)
57
- session_pool = oracledb.create_pool_async(
58
- user=username,
59
- password=password,
60
- dsn=dsn,
61
- min=min_connections,
62
- max=max_connections,
63
- increment=1
64
- )
65
- return session_pool
68
+ if use_pool:
69
+ logger.debug(f"Creating async connection pool with min size {min_connections} and max size {max_connections}")
70
+ dsn = oracledb.makedsn(hostname, port, sid=database)
71
+ session_pool = oracledb.create_pool_async(
72
+ user=username,
73
+ password=password,
74
+ dsn=dsn,
75
+ min=min_connections,
76
+ max=max_connections,
77
+ increment=1
78
+ )
79
+ return session_pool
80
+ else:
81
+ logger.debug("Creating single async connection")
82
+ connection = await oracledb.connect_async(
83
+ user=username,
84
+ password=password,
85
+ dsn=oracledb.makedsn(hostname, port, sid=database)
86
+ )
87
+ return connection
66
88
 
67
89
 
68
90
  """~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"""
@@ -75,15 +97,17 @@ class OracleConnection(object):
75
97
  :return: None
76
98
  """
77
99
 
78
- def __init__(self, min_connections: int = 2, max_connections: int = 5):
100
+ def __init__(self, use_pool: bool = False, min_connections: int = 2, max_connections: int = 5):
79
101
  self._username: Optional[str] = None
80
102
  self._password: Optional[str] = None
81
103
  self._hostname: Optional[str] = None
82
104
  self._port: Optional[int] = None
83
105
  self._database: Optional[str] = None
84
106
  self._sid: Optional[str] = None
107
+ self._connection: Optional[Connection] = None
85
108
  self._session_pool: Optional[ConnectionPool] = None
86
109
 
110
+ self.use_pool = use_pool
87
111
  self.min_connections = min_connections
88
112
  self.max_connections = max_connections
89
113
 
@@ -101,8 +125,27 @@ class OracleConnection(object):
101
125
 
102
126
  sid_or_service = self._database if self._database else self._sid
103
127
 
104
- self._session_pool = _connect_warehouse(self._username, self._password, self._hostname, self._port,
105
- sid_or_service, self.min_connections, self.max_connections)
128
+ connection = _connect_warehouse(self._username, self._password, self._hostname, self._port,
129
+ sid_or_service, self.min_connections, self.max_connections, self.use_pool)
130
+
131
+ if self.use_pool:
132
+ self._session_pool = connection
133
+ else:
134
+ self._connection = connection
135
+
136
+ def _get_connection(self) -> Connection:
137
+ """
138
+ Get the connection, either from the pool or create a new one
139
+
140
+ :return: Connection
141
+ """
142
+
143
+ if self.use_pool:
144
+ return self._session_pool.acquire()
145
+ else:
146
+ if not self._connection or not self._connection.is_healthy():
147
+ self._connect()
148
+ return self._connection
106
149
 
107
150
  def set_user(self, credentials_dict: dict) -> None:
108
151
  """
@@ -131,7 +174,12 @@ class OracleConnection(object):
131
174
  :return: None
132
175
  """
133
176
 
134
- self._session_pool.close()
177
+ if self.use_pool:
178
+ self._session_pool.close()
179
+ else:
180
+ if self._connection and self._connection.is_healthy():
181
+ self._connection.close()
182
+ self._connection = None
135
183
 
136
184
  @retry
137
185
  def execute(self, query: str) -> None:
@@ -142,11 +190,11 @@ class OracleConnection(object):
142
190
  :return: None
143
191
  """
144
192
 
145
- connection = self._session_pool.acquire()
146
- cursor = connection.cursor()
147
- cursor.execute(query)
148
- connection.commit()
149
- self._session_pool.release(connection)
193
+ connection = self._get_connection()
194
+ with connection:
195
+ cursor = connection.cursor()
196
+ cursor.execute(query)
197
+ connection.commit()
150
198
 
151
199
  @retry
152
200
  def safe_execute(self, query: str, packed_values: dict) -> None:
@@ -158,14 +206,14 @@ class OracleConnection(object):
158
206
  :return: None
159
207
  """
160
208
 
161
- connection = self._session_pool.acquire()
162
- cursor = connection.cursor()
163
- cursor.execute(query, packed_values)
164
- connection.commit()
165
- self._session_pool.release(connection)
209
+ connection = self._get_connection()
210
+ with connection:
211
+ cursor = connection.cursor()
212
+ cursor.execute(query, packed_values)
213
+ connection.commit()
166
214
 
167
215
  @retry
168
- def execute_multiple(self, queries: list[list[str, dict]]) -> None:
216
+ def execute_multiple(self, queries: list[tuple[str, dict]]) -> None:
169
217
  """
170
218
  Execute multiple queries
171
219
 
@@ -173,17 +221,17 @@ class OracleConnection(object):
173
221
  :return: None
174
222
  """
175
223
 
176
- connection = self._session_pool.acquire()
177
- cursor = connection.cursor()
178
- for item in queries:
179
- query = item[0]
180
- packed_values = item[1]
181
- if packed_values:
182
- cursor.execute(query, packed_values)
183
- else:
184
- cursor.execute(query)
185
- connection.commit()
186
- self._session_pool.release(connection)
224
+ connection = self._get_connection()
225
+ with connection:
226
+ cursor = connection.cursor()
227
+ for item in queries:
228
+ query = item[0]
229
+ packed_values = item[1]
230
+ if packed_values:
231
+ cursor.execute(query, packed_values)
232
+ else:
233
+ cursor.execute(query)
234
+ connection.commit()
187
235
 
188
236
  @retry
189
237
  def execute_many(self, query: str, dictionary: list[dict]) -> None:
@@ -195,11 +243,11 @@ class OracleConnection(object):
195
243
  :return: None
196
244
  """
197
245
 
198
- connection = self._session_pool.acquire()
199
- cursor = connection.cursor()
200
- cursor.executemany(query, dictionary)
201
- connection.commit()
202
- self._session_pool.release(connection)
246
+ connection = self._get_connection()
247
+ with connection:
248
+ cursor = connection.cursor()
249
+ cursor.executemany(query, dictionary)
250
+ connection.commit()
203
251
 
204
252
  @retry
205
253
  def fetch_data(self, query: str, packed_data=None) -> list:
@@ -211,14 +259,14 @@ class OracleConnection(object):
211
259
  :return: rows
212
260
  """
213
261
 
214
- connection = self._session_pool.acquire()
215
- cursor = connection.cursor()
216
- if packed_data:
217
- cursor.execute(query, packed_data)
218
- else:
219
- cursor.execute(query)
220
- rows = cursor.fetchall()
221
- self._session_pool.release(connection)
262
+ connection = self._get_connection()
263
+ with connection:
264
+ cursor = connection.cursor()
265
+ if packed_data:
266
+ cursor.execute(query, packed_data)
267
+ else:
268
+ cursor.execute(query)
269
+ rows = cursor.fetchall()
222
270
  return rows
223
271
 
224
272
  @retry
@@ -267,7 +315,7 @@ class OracleConnection(object):
267
315
  dfObj = dfObj.replace({np.nan: None})
268
316
  main_dict = dfObj.to_dict('records')
269
317
 
270
- query = """INSERT INTO {} ({}) VALUES ({})""".format(outputTableName, col, bind)
318
+ query = f"""INSERT INTO {outputTableName} ({col}) VALUES ({bind})"""
271
319
  self.execute_many(query, main_dict)
272
320
 
273
321
  @retry
@@ -279,7 +327,7 @@ class OracleConnection(object):
279
327
  :return: None
280
328
  """
281
329
 
282
- truncateQuery = """TRUNCATE TABLE {}""".format(tableName)
330
+ truncateQuery = f"""TRUNCATE TABLE {tableName}"""
283
331
  self.execute(truncateQuery)
284
332
 
285
333
  @retry
@@ -291,7 +339,7 @@ class OracleConnection(object):
291
339
  :return: None
292
340
  """
293
341
 
294
- deleteQuery = """DELETE FROM {}""".format(tableName)
342
+ deleteQuery = f"""DELETE FROM {tableName}"""
295
343
  self.execute(deleteQuery)
296
344
 
297
345
  def __del__(self) -> None:
@@ -301,7 +349,12 @@ class OracleConnection(object):
301
349
  :return: None
302
350
  """
303
351
 
304
- self._session_pool.close()
352
+ if self.use_pool:
353
+ self._session_pool.close()
354
+ else:
355
+ if self._connection and self._connection.is_healthy():
356
+ self._connection.close()
357
+ self._connection = None
305
358
 
306
359
 
307
360
  class AsyncOracleConnection(object):
@@ -311,7 +364,7 @@ class AsyncOracleConnection(object):
311
364
  :return: None
312
365
  """
313
366
 
314
- def __init__(self, min_connections: int = 2, max_connections: int = 5):
367
+ def __init__(self, use_pool: bool = False, min_connections: int = 2, max_connections: int = 5):
315
368
  self._db_service: str = "Oracle"
316
369
  self._username: Optional[str] = None
317
370
  self._password: Optional[str] = None
@@ -319,8 +372,10 @@ class AsyncOracleConnection(object):
319
372
  self._port: Optional[int] = None
320
373
  self._database: Optional[str] = None
321
374
  self._sid: Optional[str] = None
375
+ self._connection: Optional[AsyncConnection] = None
322
376
  self._session_pool: Optional[AsyncConnectionPool] = None
323
377
 
378
+ self.use_pool = use_pool
324
379
  self.min_connections = min_connections
325
380
  self.max_connections = max_connections
326
381
 
@@ -338,8 +393,28 @@ class AsyncOracleConnection(object):
338
393
 
339
394
  sid_or_service = self._database if self._database else self._sid
340
395
 
341
- self._session_pool = await _async_connect_warehouse(self._username, self._password, self._hostname, self._port,
342
- sid_or_service, self.min_connections, self.max_connections)
396
+ connection = await _async_connect_warehouse(self._username, self._password, self._hostname, self._port,
397
+ sid_or_service, self.min_connections, self.max_connections,
398
+ self.use_pool)
399
+
400
+ if self.use_pool:
401
+ self._session_pool = connection
402
+ else:
403
+ self._connection = connection
404
+
405
+ async def _get_connection(self) -> AsyncConnection:
406
+ """
407
+ Get the connection, either from the pool or create a new one
408
+
409
+ :return: AsyncConnection
410
+ """
411
+
412
+ if self.use_pool:
413
+ return await self._session_pool.acquire()
414
+ else:
415
+ if not self._connection or not self._connection.is_healthy():
416
+ await self._connect()
417
+ return self._connection
343
418
 
344
419
  async def set_user(self, credentials_dict: dict) -> None:
345
420
  """
@@ -368,7 +443,12 @@ class AsyncOracleConnection(object):
368
443
  :return: None
369
444
  """
370
445
 
371
- await self._session_pool.close()
446
+ if self.use_pool:
447
+ await self._session_pool.close()
448
+ else:
449
+ if self._connection and self._connection.is_healthy():
450
+ await self._connection.close()
451
+ self._connection = None
372
452
 
373
453
  @async_retry
374
454
  async def execute(self, query: str) -> None:
@@ -379,7 +459,8 @@ class AsyncOracleConnection(object):
379
459
  :return: None
380
460
  """
381
461
 
382
- async with self._session_pool.acquire() as connection:
462
+ connection = await self._get_connection()
463
+ async with connection:
383
464
  with connection.cursor() as cursor:
384
465
  await cursor.execute(query)
385
466
  await connection.commit()
@@ -394,13 +475,14 @@ class AsyncOracleConnection(object):
394
475
  :return: None
395
476
  """
396
477
 
397
- async with self._session_pool.acquire() as connection:
478
+ connection = await self._get_connection()
479
+ async with connection:
398
480
  with connection.cursor() as cursor:
399
481
  await cursor.execute(query, packed_values)
400
482
  await connection.commit()
401
483
 
402
484
  @async_retry
403
- async def execute_multiple(self, queries: list[list[str, dict]]) -> None:
485
+ async def execute_multiple(self, queries: list[tuple[str, dict]]) -> None:
404
486
  """
405
487
  Execute multiple queries
406
488
 
@@ -408,7 +490,8 @@ class AsyncOracleConnection(object):
408
490
  :return: None
409
491
  """
410
492
 
411
- async with self._session_pool.acquire() as connection:
493
+ connection = await self._get_connection()
494
+ async with connection:
412
495
  with connection.cursor() as cursor:
413
496
  for item in queries:
414
497
  query = item[0]
@@ -429,7 +512,8 @@ class AsyncOracleConnection(object):
429
512
  :return: None
430
513
  """
431
514
 
432
- async with self._session_pool.acquire() as connection:
515
+ connection = await self._get_connection()
516
+ async with connection:
433
517
  with connection.cursor() as cursor:
434
518
  await cursor.executemany(query, dictionary)
435
519
  await connection.commit()
@@ -444,7 +528,8 @@ class AsyncOracleConnection(object):
444
528
  :return: rows
445
529
  """
446
530
 
447
- async with self._session_pool.acquire() as connection:
531
+ connection = await self._get_connection()
532
+ async with connection:
448
533
  with connection.cursor() as cursor:
449
534
  if packed_data:
450
535
  await cursor.execute(query, packed_data)
@@ -499,7 +584,7 @@ class AsyncOracleConnection(object):
499
584
  dfObj = dfObj.replace({np.nan: None})
500
585
  main_dict = dfObj.to_dict('records')
501
586
 
502
- query = """INSERT INTO {} ({}) VALUES ({})""".format(outputTableName, col, bind)
587
+ query = f"""INSERT INTO {outputTableName} ({col}) VALUES ({bind})"""
503
588
  await self.execute_many(query, main_dict)
504
589
 
505
590
  @async_retry
@@ -511,7 +596,7 @@ class AsyncOracleConnection(object):
511
596
  :return: None
512
597
  """
513
598
 
514
- truncateQuery = """TRUNCATE TABLE {}""".format(tableName)
599
+ truncateQuery = f"""TRUNCATE TABLE {tableName}"""
515
600
  await self.execute(truncateQuery)
516
601
 
517
602
  @async_retry
@@ -523,5 +608,5 @@ class AsyncOracleConnection(object):
523
608
  :return: None
524
609
  """
525
610
 
526
- deleteQuery = """DELETE FROM {}""".format(tableName)
611
+ deleteQuery = f"""DELETE FROM {tableName}"""
527
612
  await self.execute(deleteQuery)
@@ -3,6 +3,8 @@ from typing import Optional
3
3
 
4
4
  import numpy as np
5
5
  import pandas as pd
6
+ import psycopg
7
+ from psycopg import AsyncConnection, Connection
6
8
  from psycopg.conninfo import make_conninfo
7
9
  from psycopg.sql import SQL
8
10
  from psycopg_pool import AsyncConnectionPool, ConnectionPool
@@ -13,7 +15,7 @@ logger = logging.getLogger(__name__)
13
15
 
14
16
 
15
17
  def _connect_warehouse(username: str, password: str, hostname: str, port: int, database: str, min_connections: int,
16
- max_connections: int) -> ConnectionPool:
18
+ max_connections: int, use_pool: bool) -> Connection | ConnectionPool:
17
19
  """
18
20
  Create Warehouse Connection
19
21
 
@@ -30,18 +32,24 @@ def _connect_warehouse(username: str, password: str, hostname: str, port: int, d
30
32
  conn_string = f"dbname={database} user={username} password={password} host={hostname} port={port}"
31
33
  conninfo = make_conninfo(conn_string)
32
34
 
33
- session_pool = ConnectionPool(
34
- conninfo=conninfo,
35
- min_size=min_connections,
36
- max_size=max_connections,
37
- kwargs={'options': '-c datestyle=ISO,YMD'},
38
- open=True
39
- )
40
- return session_pool
35
+ if use_pool:
36
+ logger.debug(f"Creating connection pool with min size {min_connections} and max size {max_connections}")
37
+ session_pool = ConnectionPool(
38
+ conninfo=conninfo,
39
+ min_size=min_connections,
40
+ max_size=max_connections,
41
+ kwargs={'options': '-c datestyle=ISO,YMD'},
42
+ open=True
43
+ )
44
+ return session_pool
45
+ else:
46
+ logger.debug("Creating single connection")
47
+ connection = psycopg.connect(conninfo=conninfo, options='-c datestyle=ISO,YMD')
48
+ return connection
41
49
 
42
50
 
43
51
  async def _async_connect_warehouse(username: str, password: str, hostname: str, port: int, database: str, min_connections: int,
44
- max_connections: int) -> AsyncConnectionPool:
52
+ max_connections: int, use_pool: bool) -> AsyncConnection | AsyncConnectionPool:
45
53
  """
46
54
  Create Warehouse Connection
47
55
 
@@ -58,14 +66,20 @@ async def _async_connect_warehouse(username: str, password: str, hostname: str,
58
66
  conn_string = f"dbname={database} user={username} password={password} host={hostname} port={port}"
59
67
  conninfo = make_conninfo(conn_string)
60
68
 
61
- session_pool = AsyncConnectionPool(
62
- conninfo=conninfo,
63
- min_size=min_connections,
64
- max_size=max_connections,
65
- kwargs={"options": "-c datestyle=ISO,YMD"},
66
- open=False
67
- )
68
- return session_pool
69
+ if use_pool:
70
+ logger.debug(f"Creating async connection pool with min size {min_connections} and max size {max_connections}")
71
+ session_pool = AsyncConnectionPool(
72
+ conninfo=conninfo,
73
+ min_size=min_connections,
74
+ max_size=max_connections,
75
+ kwargs={"options": "-c datestyle=ISO,YMD"},
76
+ open=False
77
+ )
78
+ return session_pool
79
+ else:
80
+ logger.debug("Creating single async connection")
81
+ connection = await AsyncConnection.connect(conninfo=conninfo, options='-c datestyle=ISO,YMD')
82
+ return connection
69
83
 
70
84
 
71
85
  """~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"""
@@ -78,14 +92,16 @@ class PostgresConnection(object):
78
92
  :return: None
79
93
  """
80
94
 
81
- def __init__(self, min_connections: int = 2, max_connections: int = 5):
95
+ def __init__(self, use_pool: bool = False, min_connections: int = 2, max_connections: int = 5):
82
96
  self._username: Optional[str] = None
83
97
  self._password: Optional[str] = None
84
98
  self._hostname: Optional[str] = None
85
99
  self._port: Optional[int] = None
86
100
  self._database: Optional[str] = None
101
+ self._connection: Optional[Connection] = None
87
102
  self._session_pool: Optional[ConnectionPool] = None
88
103
 
104
+ self.use_pool = use_pool
89
105
  self.min_connections = min_connections
90
106
  self.max_connections = max_connections
91
107
 
@@ -101,8 +117,30 @@ class PostgresConnection(object):
101
117
  :return: None
102
118
  """
103
119
 
104
- self._session_pool = _connect_warehouse(self._username, self._password, self._hostname, self._port,
105
- self._database, self.min_connections, self.max_connections)
120
+
121
+ self.connection = _connect_warehouse(self._username, self._password, self._hostname, self._port,
122
+ self._database, self.min_connections, self.max_connections, self.use_pool)
123
+
124
+ if self.use_pool:
125
+ self._session_pool = self.connection
126
+ self._session_pool.open()
127
+ else:
128
+ self._connection = self.connection
129
+
130
+ def _get_connection(self) -> Connection:
131
+ """
132
+ Get the connection object
133
+
134
+ :return: connection
135
+ """
136
+
137
+ if self.use_pool:
138
+ connection = self._session_pool.getconn()
139
+ return connection
140
+ else:
141
+ if self._connection is None or self._connection.closed:
142
+ self._connect()
143
+ return self._connection
106
144
 
107
145
  def set_user(self, credentials_dict: dict) -> None:
108
146
  """
@@ -127,7 +165,12 @@ class PostgresConnection(object):
127
165
  :return: None
128
166
  """
129
167
 
130
- self._session_pool.close()
168
+ if self.use_pool:
169
+ self._session_pool.close()
170
+ else:
171
+ if self._connection is not None and not self._connection.closed:
172
+ self._connection.close()
173
+ self._connection = None
131
174
 
132
175
  @retry
133
176
  def execute(self, query: SQL | str) -> None:
@@ -138,9 +181,13 @@ class PostgresConnection(object):
138
181
  :return: None
139
182
  """
140
183
 
141
- with self._session_pool.connection() as connection:
184
+ connection = self._get_connection()
185
+ with connection:
142
186
  connection.execute(query)
143
187
 
188
+ if self.use_pool:
189
+ self._session_pool.putconn(connection)
190
+
144
191
  @retry
145
192
  def safe_execute(self, query: SQL | str, packed_values: dict) -> None:
146
193
  """
@@ -151,11 +198,15 @@ class PostgresConnection(object):
151
198
  :return: None
152
199
  """
153
200
 
154
- with self._session_pool.connection() as connection:
201
+ connection = self._get_connection()
202
+ with connection:
155
203
  connection.execute(query, packed_values)
156
204
 
205
+ if self.use_pool:
206
+ self._session_pool.putconn(connection)
207
+
157
208
  @retry
158
- def execute_multiple(self, queries: list[list[SQL | str, dict]]) -> None:
209
+ def execute_multiple(self, queries: list[tuple[SQL | str, dict]]) -> None:
159
210
  """
160
211
  Execute multiple queries
161
212
 
@@ -163,7 +214,8 @@ class PostgresConnection(object):
163
214
  :return: None
164
215
  """
165
216
 
166
- with self._session_pool.connection() as connection:
217
+ connection = self._get_connection()
218
+ with connection:
167
219
  for item in queries:
168
220
  query = item[0]
169
221
  packed_values = item[1]
@@ -172,6 +224,9 @@ class PostgresConnection(object):
172
224
  else:
173
225
  connection.execute(query)
174
226
 
227
+ if self.use_pool:
228
+ self._session_pool.putconn(connection)
229
+
175
230
  @retry
176
231
  def execute_many(self, query: SQL | str, dictionary: list[dict]) -> None:
177
232
  """
@@ -182,10 +237,14 @@ class PostgresConnection(object):
182
237
  :return: None
183
238
  """
184
239
 
185
- with self._session_pool.connection() as connection:
240
+ connection = self._get_connection()
241
+ with connection:
186
242
  cursor = connection.cursor()
187
243
  cursor.executemany(query, dictionary)
188
244
 
245
+ if self.use_pool:
246
+ self._session_pool.putconn(connection)
247
+
189
248
  @retry
190
249
  def fetch_data(self, query: SQL | str, packed_data=None) -> list[tuple]:
191
250
  """
@@ -196,13 +255,17 @@ class PostgresConnection(object):
196
255
  :return: rows
197
256
  """
198
257
 
199
- with self._session_pool.connection() as connection:
258
+ connection = self._get_connection()
259
+ with connection:
200
260
  cursor = connection.cursor()
201
261
  if packed_data:
202
262
  cursor.execute(query, packed_data)
203
263
  else:
204
264
  cursor.execute(query)
205
265
  rows = cursor.fetchall()
266
+
267
+ if self.use_pool:
268
+ self._session_pool.putconn(connection)
206
269
  return rows
207
270
 
208
271
  @retry
@@ -255,7 +318,7 @@ class PostgresConnection(object):
255
318
  if record[key] == '':
256
319
  record[key] = None
257
320
 
258
- query = """INSERT INTO {} ({}) VALUES ({})""".format(outputTableName, col, params)
321
+ query = f"INSERT INTO {outputTableName} ({col}) VALUES ({params})"
259
322
  self.execute_many(query, main_dict)
260
323
 
261
324
  @retry
@@ -267,7 +330,7 @@ class PostgresConnection(object):
267
330
  :return: None
268
331
  """
269
332
 
270
- truncateQuery = """TRUNCATE TABLE {}""".format(tableName)
333
+ truncateQuery = f"TRUNCATE TABLE {tableName}"
271
334
  self.execute(truncateQuery)
272
335
 
273
336
  @retry
@@ -279,7 +342,7 @@ class PostgresConnection(object):
279
342
  :return: None
280
343
  """
281
344
 
282
- deleteQuery = """DELETE FROM {}""".format(tableName)
345
+ deleteQuery = f"DELETE FROM {tableName}"
283
346
  self.execute(deleteQuery)
284
347
 
285
348
  def __del__(self) -> None:
@@ -289,7 +352,12 @@ class PostgresConnection(object):
289
352
  :return: None
290
353
  """
291
354
 
292
- self._session_pool.close()
355
+ if self._session_pool is not None:
356
+ self._session_pool.close()
357
+ else:
358
+ if self._connection is not None and not self._connection.closed:
359
+ self._connection.close()
360
+ self._connection = None
293
361
 
294
362
 
295
363
  class AsyncPostgresConnection(object):
@@ -299,14 +367,16 @@ class AsyncPostgresConnection(object):
299
367
  :return: None
300
368
  """
301
369
 
302
- def __init__(self, min_connections: int = 2, max_connections: int = 5):
370
+ def __init__(self, use_pool: bool = False, min_connections: int = 2, max_connections: int = 5):
303
371
  self._username: Optional[str] = None
304
372
  self._password: Optional[str] = None
305
373
  self._hostname: Optional[str] = None
306
374
  self._port: Optional[int] = None
307
375
  self._database: Optional[str] = None
376
+ self._connection: Optional[AsyncConnection] = None
308
377
  self._session_pool: Optional[AsyncConnectionPool] = None
309
378
 
379
+ self.use_pool = use_pool
310
380
  self.min_connections = min_connections
311
381
  self.max_connections = max_connections
312
382
 
@@ -322,9 +392,30 @@ class AsyncPostgresConnection(object):
322
392
  :return: None
323
393
  """
324
394
 
325
- self._session_pool = await _async_connect_warehouse(self._username, self._password, self._hostname, self._port,
326
- self._database, self.min_connections, self.max_connections)
327
- await self._session_pool.open()
395
+ connection = await _async_connect_warehouse(self._username, self._password, self._hostname, self._port,
396
+ self._database, self.min_connections, self.max_connections,
397
+ self.use_pool)
398
+ if self.use_pool:
399
+ self._session_pool = connection
400
+ await self._session_pool.open()
401
+ else:
402
+ self._connection = connection
403
+
404
+ async def _get_connection(self) -> AsyncConnection:
405
+ """
406
+ Get the connection object
407
+
408
+ :return: connection
409
+ """
410
+
411
+ if self.use_pool:
412
+ connection = await self._session_pool.getconn()
413
+ return connection
414
+ else:
415
+ if self._connection is None or self._connection.closed:
416
+ await self._connect()
417
+ return self._connection
418
+
328
419
 
329
420
  async def set_user(self, credentials_dict: dict) -> None:
330
421
  """
@@ -349,7 +440,12 @@ class AsyncPostgresConnection(object):
349
440
  :return: None
350
441
  """
351
442
 
352
- await self._session_pool.close()
443
+ if self.use_pool:
444
+ await self._session_pool.close()
445
+ else:
446
+ if self._connection is not None and not self._connection.closed:
447
+ await self._connection.close()
448
+ self._connection = None
353
449
 
354
450
  @async_retry
355
451
  async def execute(self, query: SQL | str) -> None:
@@ -360,9 +456,13 @@ class AsyncPostgresConnection(object):
360
456
  :return: None
361
457
  """
362
458
 
363
- async with self._session_pool.connection() as connection:
459
+ connection = await self._get_connection()
460
+ async with connection:
364
461
  await connection.execute(query)
365
462
 
463
+ if self.use_pool:
464
+ await self._session_pool.putconn(connection)
465
+
366
466
  @async_retry
367
467
  async def safe_execute(self, query: SQL | str, packed_values: dict) -> None:
368
468
  """
@@ -373,11 +473,15 @@ class AsyncPostgresConnection(object):
373
473
  :return: None
374
474
  """
375
475
 
376
- async with self._session_pool.connection() as connection:
476
+ connection = await self._get_connection()
477
+ async with connection:
377
478
  await connection.execute(query, packed_values)
378
479
 
480
+ if self.use_pool:
481
+ await self._session_pool.putconn(connection)
482
+
379
483
  @async_retry
380
- async def execute_multiple(self, queries: list[list[SQL | str, dict]]) -> None:
484
+ async def execute_multiple(self, queries: list[tuple[SQL | str, dict]]) -> None:
381
485
  """
382
486
  Execute multiple queries
383
487
 
@@ -385,7 +489,8 @@ class AsyncPostgresConnection(object):
385
489
  :return: None
386
490
  """
387
491
 
388
- async with self._session_pool.connection() as connection:
492
+ connection = await self._get_connection()
493
+ async with connection:
389
494
  for item in queries:
390
495
  query = item[0]
391
496
  packed_values = item[1]
@@ -394,6 +499,9 @@ class AsyncPostgresConnection(object):
394
499
  else:
395
500
  await connection.execute(query)
396
501
 
502
+ if self.use_pool:
503
+ await self._session_pool.putconn(connection)
504
+
397
505
  @async_retry
398
506
  async def execute_many(self, query: SQL | str, dictionary: list[dict]) -> None:
399
507
  """
@@ -404,10 +512,14 @@ class AsyncPostgresConnection(object):
404
512
  :return: None
405
513
  """
406
514
 
407
- async with self._session_pool.connection() as connection:
515
+ connection = await self._get_connection()
516
+ async with connection:
408
517
  cursor = connection.cursor()
409
518
  await cursor.executemany(query, dictionary)
410
519
 
520
+ if self.use_pool:
521
+ await self._session_pool.putconn(connection)
522
+
411
523
  @async_retry
412
524
  async def fetch_data(self, query: SQL | str, packed_data=None) -> list[tuple]:
413
525
  """
@@ -418,13 +530,17 @@ class AsyncPostgresConnection(object):
418
530
  :return: rows
419
531
  """
420
532
 
421
- async with self._session_pool.connection() as connection:
533
+ connection = await self._get_connection()
534
+ async with connection:
422
535
  cursor = connection.cursor()
423
536
  if packed_data:
424
537
  await cursor.execute(query, packed_data)
425
538
  else:
426
539
  await cursor.execute(query)
427
540
  rows = await cursor.fetchall()
541
+
542
+ if self.use_pool:
543
+ await self._session_pool.putconn(connection)
428
544
  return rows
429
545
 
430
546
  @async_retry
@@ -448,7 +564,7 @@ class AsyncPostgresConnection(object):
448
564
  params = param_list[0]
449
565
 
450
566
  main_dict = df.to_dict('records')
451
- query = """DELETE FROM {} WHERE {}""".format(outputTableName, params)
567
+ query = f"DELETE FROM {outputTableName} WHERE {params}"
452
568
  await self.execute_many(query, main_dict)
453
569
 
454
570
  @async_retry
@@ -477,7 +593,7 @@ class AsyncPostgresConnection(object):
477
593
  if record[key] == '':
478
594
  record[key] = None
479
595
 
480
- query = """INSERT INTO {} ({}) VALUES ({})""".format(outputTableName, col, params)
596
+ query = f"INSERT INTO {outputTableName} ({col}) VALUES ({params})"
481
597
  await self.execute_many(query, main_dict)
482
598
 
483
599
  @async_retry
@@ -489,7 +605,7 @@ class AsyncPostgresConnection(object):
489
605
  :return: None
490
606
  """
491
607
 
492
- truncateQuery = """TRUNCATE TABLE {}""".format(tableName)
608
+ truncateQuery = f"TRUNCATE TABLE {tableName}"
493
609
  await self.execute(truncateQuery)
494
610
 
495
611
  @async_retry
@@ -501,5 +617,5 @@ class AsyncPostgresConnection(object):
501
617
  :return: None
502
618
  """
503
619
 
504
- deleteQuery = """DELETE FROM {}""".format(tableName)
620
+ deleteQuery = f"DELETE FROM {tableName}"
505
621
  await self.execute(deleteQuery)
File without changes