dbobj3 0.7.0__tar.gz → 0.8.0__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.
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "dbobj3"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.8.0"
|
|
8
8
|
description = "High-level MySQL/MSSQL/Oracle access module"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.6"
|
|
@@ -16,7 +16,7 @@ dependencies = [
|
|
|
16
16
|
packages = ["src/dbobj3.py"]
|
|
17
17
|
|
|
18
18
|
[tool.bumpversion]
|
|
19
|
-
current_version = "0.
|
|
19
|
+
current_version = "0.8.0"
|
|
20
20
|
commit = true
|
|
21
21
|
tag = true
|
|
22
22
|
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env python3.
|
|
1
|
+
#!/usr/bin/env python3.9
|
|
2
2
|
|
|
3
3
|
"""
|
|
4
4
|
High-Level Access to MySQL, MS SQL Server, Oracle Module and more.
|
|
@@ -21,9 +21,10 @@ Example:
|
|
|
21
21
|
rsServices.next()
|
|
22
22
|
|
|
23
23
|
"""
|
|
24
|
-
__version__ = "0.
|
|
24
|
+
__version__ = "0.8.0"
|
|
25
25
|
|
|
26
26
|
from sys import version as sys_version, exit
|
|
27
|
+
from datetime import datetime, date, time
|
|
27
28
|
pyversion = sys_version[0:sys_version.find(' ')]
|
|
28
29
|
using_mysqlclient, using_pymysql = False, False
|
|
29
30
|
|
|
@@ -32,7 +33,7 @@ try:
|
|
|
32
33
|
#raise ImportError
|
|
33
34
|
import MySQLdb
|
|
34
35
|
import MySQLdb.cursors
|
|
35
|
-
import MySQLdb.converters
|
|
36
|
+
#import MySQLdb.converters
|
|
36
37
|
# MySQLdb dropped Python3.x support
|
|
37
38
|
# - For Centos/Python2.x use
|
|
38
39
|
# - yum install mysql-devel, MySQL-python
|
|
@@ -49,8 +50,8 @@ if not using_mysqlclient:
|
|
|
49
50
|
# pymysql is a pure python implementation of MySQLdb
|
|
50
51
|
# Linux:
|
|
51
52
|
import pymysql as MySQLdb
|
|
52
|
-
import MySQLdb.cursors
|
|
53
|
-
import MySQLdb.converters
|
|
53
|
+
#import MySQLdb.cursors
|
|
54
|
+
#import MySQLdb.converters
|
|
54
55
|
# Windows:
|
|
55
56
|
#import pymysql
|
|
56
57
|
#pymysql.install_as_MySQLdb()
|
|
@@ -63,6 +64,43 @@ if not using_mysqlclient:
|
|
|
63
64
|
lstLoadedDrivers.append({"pymysql": "Not Loaded (MySQL)"})
|
|
64
65
|
#'''
|
|
65
66
|
|
|
67
|
+
def _build_pymysql_conv():
|
|
68
|
+
"""
|
|
69
|
+
Return a PyMySQL conversion dict that parses DATETIME / TIMESTAMP / DATE
|
|
70
|
+
into real Python datetime/date objects (mimicking mysqlclient behavior).
|
|
71
|
+
"""
|
|
72
|
+
if not using_pymysql:
|
|
73
|
+
return None
|
|
74
|
+
from pymysql.constants import FIELD_TYPE
|
|
75
|
+
from pymysql.converters import conversions
|
|
76
|
+
conv = conversions.copy()
|
|
77
|
+
|
|
78
|
+
def _parse_dt(val):
|
|
79
|
+
if val is None:
|
|
80
|
+
return None
|
|
81
|
+
v = val.strip()
|
|
82
|
+
# Try common formats (truncate fractional seconds)
|
|
83
|
+
base = v.split('.')[0]
|
|
84
|
+
for fmt in ("%Y-%m-%d %H:%M:%S",
|
|
85
|
+
"%Y-%m-%d %H:%M",
|
|
86
|
+
"%Y-%m-%dT%H:%M:%S",
|
|
87
|
+
"%Y-%m-%d"):
|
|
88
|
+
try:
|
|
89
|
+
return datetime.strptime(base, fmt)
|
|
90
|
+
except Exception:
|
|
91
|
+
pass
|
|
92
|
+
# ISO fallback
|
|
93
|
+
try:
|
|
94
|
+
return datetime.fromisoformat(v.replace('Z',''))
|
|
95
|
+
except Exception:
|
|
96
|
+
return v # leave as original string if unparseable
|
|
97
|
+
|
|
98
|
+
conv[FIELD_TYPE.DATETIME] = _parse_dt
|
|
99
|
+
conv[FIELD_TYPE.TIMESTAMP] = _parse_dt
|
|
100
|
+
conv[FIELD_TYPE.DATE] = (lambda v: date.fromisoformat(v) if v else None)
|
|
101
|
+
return conv
|
|
102
|
+
|
|
103
|
+
|
|
66
104
|
try:
|
|
67
105
|
import pymssql
|
|
68
106
|
lstLoadedDrivers.append({"pymssql": "Loaded (Microsoft SQL Server)"})
|
|
@@ -470,8 +508,23 @@ class dbObjConnection:
|
|
|
470
508
|
|
|
471
509
|
def mySqlConnect(self, prmHost, prmDb, prmUser, prmPass, prmPort=3306):
|
|
472
510
|
try:
|
|
473
|
-
|
|
474
|
-
|
|
511
|
+
kwargs = dict(host=prmHost, db=prmDb, user=prmUser, passwd=prmPass, port=prmPort)
|
|
512
|
+
if using_pymysql:
|
|
513
|
+
conv = _build_pymysql_conv()
|
|
514
|
+
if conv:
|
|
515
|
+
kwargs["conv"] = conv
|
|
516
|
+
# Ensure utf8mb4 for PyMySQL
|
|
517
|
+
kwargs.setdefault("charset", "utf8mb4")
|
|
518
|
+
kwargs.setdefault("autocommit", True)
|
|
519
|
+
self.connHandle = MySQLdb.connect(**kwargs)
|
|
520
|
+
# Uniform autocommit enabling (mysqlclient vs pymysql)
|
|
521
|
+
try:
|
|
522
|
+
self.connHandle.autocommit(True)
|
|
523
|
+
except Exception:
|
|
524
|
+
try:
|
|
525
|
+
self.connHandle.autocommit = True
|
|
526
|
+
except Exception:
|
|
527
|
+
pass
|
|
475
528
|
if(self.connHandle):
|
|
476
529
|
self.setConnectSuccess()
|
|
477
530
|
else:
|
|
@@ -744,10 +797,10 @@ class dbObjConnection:
|
|
|
744
797
|
self.connHandle.close()
|
|
745
798
|
self.connHandle = None
|
|
746
799
|
|
|
747
|
-
def execute(self, prmSql, *args):
|
|
800
|
+
def execute(self, prmSql, *args, **kw):
|
|
748
801
|
"""Execute the SQL and return a record set if applicable"""
|
|
749
802
|
if(self.dbType == "MYSQL"):
|
|
750
|
-
return self.mySqlExecute(prmSql, *args)
|
|
803
|
+
return self.mySqlExecute(prmSql, *args, **kw)
|
|
751
804
|
if(self.dbType == "MSSQL"):
|
|
752
805
|
return self.msSqlExecute(prmSql)
|
|
753
806
|
if(self.dbType == "ORACLE"):
|
|
@@ -900,7 +953,7 @@ class dbObjConnection:
|
|
|
900
953
|
else:
|
|
901
954
|
raise Exception
|
|
902
955
|
|
|
903
|
-
def setMySqlExecuteFail(self, mySqlCursor, prmSql, e, *args):
|
|
956
|
+
def setMySqlExecuteFail(self, mySqlCursor, prmSql, e, *args, **kw):
|
|
904
957
|
self.msgObj.success = False
|
|
905
958
|
prmSql += '' if not args else ' ({0}) '.format( repr(args) )
|
|
906
959
|
self.msgObj.message = "SQL Failed: {0} - {1}".format( prmSql, e )
|
|
@@ -914,9 +967,9 @@ class dbObjConnection:
|
|
|
914
967
|
finally:
|
|
915
968
|
return mySqlCursor
|
|
916
969
|
|
|
917
|
-
def setMySqlExecuteSuccess(self, mySqlCursor, prmSql, *args):
|
|
970
|
+
def setMySqlExecuteSuccess(self, mySqlCursor, prmSql, *args, **kw):
|
|
918
971
|
self.msgObj.success = True
|
|
919
|
-
self.msgObj.message = "SQL Executed: {0} {1}".format(prmSql,'' if not args else repr(args))
|
|
972
|
+
self.msgObj.message = "SQL Executed: {0} {1}".format(prmSql,'' if not args else repr(args)+ ' ' + repr(kw))
|
|
920
973
|
try:
|
|
921
974
|
mySqlCursor.numFields = len(mySqlCursor.cursor.description)
|
|
922
975
|
if(mySqlCursor.numRows):
|
|
@@ -929,10 +982,14 @@ class dbObjConnection:
|
|
|
929
982
|
finally:
|
|
930
983
|
return mySqlCursor
|
|
931
984
|
|
|
932
|
-
def mySqlExecute(self, prmSql, *args):
|
|
985
|
+
def mySqlExecute(self, prmSql, *args, **kw):
|
|
933
986
|
mySqlCursor = self.getCursor()
|
|
934
987
|
try:
|
|
935
|
-
if
|
|
988
|
+
if not hasattr(self.connHandle, 'open') or self.connHandle.open==0 :
|
|
989
|
+
self.reconnect()
|
|
990
|
+
if kw:
|
|
991
|
+
mySqlCursor.cursor.execute(prmSql, kw)
|
|
992
|
+
elif args:
|
|
936
993
|
mySqlCursor.cursor.execute(prmSql, args)
|
|
937
994
|
else:
|
|
938
995
|
mySqlCursor.cursor.execute(prmSql)
|
|
@@ -950,22 +1007,23 @@ class dbObjConnection:
|
|
|
950
1007
|
self.msgObj.message += ' or killed process/connection.'
|
|
951
1008
|
self.msgObj.message += ' connHandle={0}, connHandle.open={1}'.format(self.connHandle,self.connHandle.open)
|
|
952
1009
|
self.reconnect()
|
|
953
|
-
elif self.connHandle.open==0 or "
|
|
1010
|
+
elif not hasattr(self.connHandle, 'open') or self.connHandle.open==0 or "t connect t" in str(e).lower():
|
|
954
1011
|
self.msgObj.message += ' - Database Connection Closed or Never Established.'
|
|
955
1012
|
self.msgObj.message += ' connHandle={0}, connHandle.open={1}'.format(self.connHandle,self.connHandle.open)
|
|
956
1013
|
self.reconnect()
|
|
957
|
-
return mySqlCursor if not self.msgObj.success else self.mySqlExecute( prmSql, *args )
|
|
1014
|
+
return mySqlCursor if not self.msgObj.success else self.mySqlExecute( prmSql, *args, **kw )
|
|
958
1015
|
except Exception as e:
|
|
959
1016
|
mySqlCursor = self.setMySqlExecuteFail(mySqlCursor, prmSql, e, *args)
|
|
960
|
-
if self.connHandle.open==0 or "Can't connect to" in str(e):
|
|
1017
|
+
#if self.connHandle.open==0 or "Can't connect to" in str(e): # This line was failing (self.connHandle was an "int") and so not reconnecting
|
|
1018
|
+
if not hasattr(self.connHandle, 'open') or self.connHandle.open==0 or "t connect t" in str(e).lower():
|
|
961
1019
|
self.msgObj.message += ' - Database Connection Closed or Never Established.'
|
|
962
1020
|
self.msgObj.message += ' connHandle={0}, connHandle.open={1}'.format(self.connHandle,self.connHandle.open)
|
|
963
1021
|
self.reconnect()
|
|
964
|
-
return mySqlCursor if not self.msgObj.success else self.mySqlExecute( prmSql, *args )
|
|
1022
|
+
return mySqlCursor if not self.msgObj.success else self.mySqlExecute( prmSql, *args, **kw )
|
|
965
1023
|
print(self.msgObj.message)
|
|
966
1024
|
return mySqlCursor
|
|
967
1025
|
else:
|
|
968
|
-
mySqlCursor = self.setMySqlExecuteSuccess(mySqlCursor, prmSql, *args)
|
|
1026
|
+
mySqlCursor = self.setMySqlExecuteSuccess(mySqlCursor, prmSql, *args, **kw)
|
|
969
1027
|
return mySqlCursor
|
|
970
1028
|
|
|
971
1029
|
def msSqlExecute(self, prmSql):
|
|
@@ -1133,14 +1191,14 @@ class dbObjConnection:
|
|
|
1133
1191
|
self.msgObj.message += ' - Database Connection Closed or Never Established.'
|
|
1134
1192
|
self.msgObj.message += ' connHandle={0}, connHandle.open={1}'.format(self.connHandle,self.connHandle.open)
|
|
1135
1193
|
self.reconnect()
|
|
1136
|
-
return mySqlCursor if not self.msgObj.success else self.
|
|
1194
|
+
return mySqlCursor if not self.msgObj.success else self.mySqlExecuteMany( prmSql, *args )
|
|
1137
1195
|
except Exception as e:
|
|
1138
1196
|
mySqlCursor = self.setMySqlExecuteFail(mySqlCursor, prmSql, e, *args)
|
|
1139
1197
|
if self.connHandle.open==0 or "Can't connect to" in str(e):
|
|
1140
1198
|
self.msgObj.message += ' - Database Connection Closed or Never Established.'
|
|
1141
1199
|
self.msgObj.message += ' connHandle={0}, connHandle.open={1}'.format(self.connHandle,self.connHandle.open)
|
|
1142
1200
|
self.reconnect()
|
|
1143
|
-
return mySqlCursor if not self.msgObj.success else self.
|
|
1201
|
+
return mySqlCursor if not self.msgObj.success else self.mySqlExecuteMany( prmSql, *args )
|
|
1144
1202
|
print(self.msgObj.message)
|
|
1145
1203
|
return mySqlCursor
|
|
1146
1204
|
else:
|
|
File without changes
|