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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: dbobj3
3
- Version: 0.7.0
3
+ Version: 0.8.0
4
4
  Summary: High-level MySQL/MSSQL/Oracle access module
5
5
  Requires-Python: >=3.6
6
6
  Requires-Dist: pymysql
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "dbobj3"
7
- version = "0.7.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.7.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.6
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.7.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
- self.connHandle = MySQLdb.connect(host=prmHost,db=prmDb,user=prmUser,passwd=prmPass,port=prmPort)
474
- self.connHandle.autocommit(True)
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 args:
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 "Can't connect to" in str(e):
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.mySqlExecute( prmSql, *args )
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.mySqlExecute( prmSql, *args )
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