@@ -97,10 +97,11 @@ def __init__(self, databaseURL, usePassword=True, writePermission=False):
9797
9898 self .database = None
9999 self .port = None
100+ self .mysqlPassword = None
100101 self .usePassword = usePassword
101102 self .server = None
102103
103- self .databaseEngine , self .sshUser , self .sshHost , self .mysqlHost , self .port , self .mysqlUser , self .database = self .parseURL (databaseURL )
104+ self .databaseEngine , self .sshUser , self .sshHost , self .mysqlHost , self .port , self .mysqlUser , self .mysqlPassword , self . database = self .parseURL (databaseURL )
104105
105106 self .connect ()
106107
@@ -148,41 +149,44 @@ class color:
148149 print ("\n \n " )
149150
150151 def parseURL (self , url ):
151- #mysql://sshusername@cafeine2.crulrg.ulaval.ca/mysqlusername:mysqlpassword@questions
152- match = re .search ("mysql://([^@]+?):([^@]+?)/(.*?)@(.+)" , url )
153- if match is not None :
152+ # Standard URL formats:
153+ # mysql://user[:password]@host[:port]/database
154+ # mysql+ssh://sshuser@sshhost/mysqluser[:password]@mysqlhost[:port]/database
155+ # sqlite3://path or file://path
156+ parsed = parse .urlparse (url )
157+
158+ if parsed .scheme == 'mysql' :
154159 engine = Engine .mysql
155- sshUser = None
156- sshHost = None
157- mysqlHost = match . group ( 1 )
158- mysqlPort = match . group ( 2 )
159- mysqluser = match . group ( 3 )
160- database = match . group ( 4 )
161- return ( engine , sshUser , sshHost , mysqlHost , mysqlPort , mysqluser , database )
162-
163- match = re . search ( "mysql.ssh://(.+?)@([^@]+?):([^@]+?)/(.*?)@(.+)" , url )
164- if match is not None :
160+ mysqlUser = parsed . username
161+ mysqlPassword = parsed . password
162+ mysqlHost = parsed . hostname
163+ mysqlPort = parsed . port or 3306
164+ database = parsed . path . lstrip ( '/' )
165+ if not mysqlUser or not mysqlHost or not database :
166+ raise ValueError ( "Incomplete mysql URL: {0}. Use mysql://user@host[:port]/ database" . format ( url ) )
167+ return ( engine , None , None , mysqlHost , mysqlPort , mysqlUser , mysqlPassword , database )
168+
169+ if parsed . scheme == 'mysql+ssh' :
165170 engine = Engine .mysql
166- sshUser = match .group (1 )
167- sshHost = match .group (2 )
171+ sshUser = parsed .username
172+ sshHost = parsed .hostname
173+ # Path contains: /mysqluser[:password]@mysqlhost[:port]/database
174+ match = re .match (r'^/([^:@]+)(?::([^@]*))?@([^:/]+)(?::(\d+))?/(.+)$' , parsed .path )
175+ if match is None :
176+ raise ValueError ("Incomplete mysql+ssh URL: {0}. Use mysql+ssh://sshuser@sshhost/mysqluser@mysqlhost/database" .format (url ))
177+ mysqlUser = match .group (1 )
178+ mysqlPassword = match .group (2 )
168179 mysqlHost = match .group (3 )
169- mysqlPort = 3306
170- mysqlUser = match .group (4 )
180+ mysqlPort = int (match .group (4 )) if match .group (4 ) else 3306
171181 database = match .group (5 )
172- return (engine , sshUser , sshHost , mysqlHost , mysqlPort , mysqlUser , database )
182+ return (engine , sshUser , sshHost , mysqlHost , mysqlPort , mysqlUser , mysqlPassword , database )
173183
174- match = re .search (r"(sqlite\d?|file)://(.*?)" , url )
175- if match is not None :
184+ if parsed .scheme in ('sqlite3' , 'sqlite' , 'file' ):
176185 engine = Engine .sqlite3
177- sshUser = None
178- sshHost = None
179- mysqlHost = "127.0.0.1"
180- mysqlPort = 3306
181- mysqlUser = None
182- database = match .group (2 )
183- return (engine , sshUser , sshHost , mysqlHost , mysqlPort , mysqlUser , database )
186+ database = url .split ('://' , 1 )[1 ]
187+ return (engine , None , None , "127.0.0.1" , 3306 , None , None , database )
184188
185- raise ValueError ("Unrecognized or incomplete URL : {0 }. Use mysql://host/mysqlusername@questions , mysql+ssh://sshusername @sshhost:mysql_host/mysqlusername@questions , or simply sqlite ://filename " .format (url ))
189+ raise ValueError ("Unrecognized URL scheme '{0}' in : {1 }. Use mysql://user@ host[:port]/database , mysql+ssh://sshuser @sshhost/mysqluser@mysqlhost/database , or sqlite3 ://path " .format (parsed . scheme , url ))
186190
187191 def __enter__ (self ):
188192 return self
@@ -209,9 +213,12 @@ def connect(self):
209213 self .cursor = self .connection .cursor ()
210214 else :
211215 import mysql .connector as mysql
212- import keyring
213216
214- if self .usePassword is True :
217+ if self .mysqlPassword is not None :
218+ pwd = self .mysqlPassword
219+ elif self .usePassword is True :
220+ import keyring
221+
215222 if self .sshHost is not None :
216223 serviceName = "mysql-{0}-ssh-{1}" .format (self .mysqlHost , self .sshHost )
217224 else :
@@ -220,7 +227,8 @@ def connect(self):
220227 pwd = keyring .get_password (serviceName , self .mysqlUser )
221228 if pwd is None :
222229 raise Exception (""" Set the password in the system password manager on the command line with:
223- {2} -m keyring set {0} {1}""" .format (serviceName , self .mysqlUser , sys .executable ))
230+ {2} -m keyring set {0} {1}
231+ Or provide the password in the URL: mysql://user:password@host/database""" .format (serviceName , self .mysqlUser , sys .executable ))
224232 else :
225233 pwd = None
226234
@@ -230,8 +238,6 @@ def connect(self):
230238 self .server = Cafeine (username = self .sshUser )
231239 self .port = self .server .startMySQLTunnel (ssh_host = self .sshHost , remote_bind_address = self .mysqlHost )
232240 actualMysqlHost = "127.0.0.1"
233- else :
234- self .port = 3306
235241
236242 self .connection = mysql .connect (host = actualMysqlHost ,
237243 port = self .port ,
@@ -478,62 +484,50 @@ def __init__(self, databaseURL, usePassword=True, writePermission=False):
478484
479485 self .database = None
480486 self .port = None
487+ self .mysqlPassword = None
481488 self .usePassword = usePassword
482489 self .server = None
483490
484- self .databaseEngine , self .sshUser , self .sshHost , self .mysqlHost , self .port , self .mysqlUser , self .database = self .parseURL (databaseURL )
491+ self .databaseEngine , self .sshUser , self .sshHost , self .mysqlHost , self .port , self .mysqlUser , self .mysqlPassword , self . database = self .parseURL (databaseURL )
485492
486493 self .connect ()
487494
488495 def showDatabaseInfo (self ):
489496 pass
490497
491498 def parseURL (self , url ):
492- match = re .search ("mysql://([^@]+?):([^@]+?)/(.*?)@(.+)" , url )
493- if match is not None :
499+ # Standard URL formats:
500+ # mysql://user[:password]@host[:port]/database
501+ # mysql+ssh://sshuser@sshhost/mysqluser[:password]@mysqlhost[:port]/database
502+ parsed = parse .urlparse (url )
503+
504+ if parsed .scheme == 'mysql' :
494505 engine = Engine .mysql
495- sshUser = None
496- sshHost = None
497- mysqlHost = match . group ( 1 )
498- mysqlPort = match . group ( 2 )
499- mysqluser = match . group ( 3 )
500- database = match . group ( 4 )
501- return ( engine , sshUser , sshHost , mysqlHost , mysqlPort , mysqluser , database )
502-
503- match = re . search ( "mysql.ssh://(.+?)@([^@]+?):([^@]+?)/(.*?)@(.+)" , url )
504- if match is not None :
506+ mysqlUser = parsed . username
507+ mysqlPassword = parsed . password
508+ mysqlHost = parsed . hostname
509+ mysqlPort = parsed . port or 3306
510+ database = parsed . path . lstrip ( '/' )
511+ if not mysqlUser or not mysqlHost or not database :
512+ raise ValueError ( "Incomplete mysql URL: {0}. Use mysql://user@host[:port]/ database" . format ( url ) )
513+ return ( engine , None , None , mysqlHost , mysqlPort , mysqlUser , mysqlPassword , database )
514+
515+ if parsed . scheme == 'mysql+ssh' :
505516 engine = Engine .mysql
506-
507- sshUser = match .group (1 )
508- sshHost = match .group (2 )
517+ sshUser = parsed .username
518+ sshHost = parsed .hostname
519+ # Path contains: /mysqluser[:password]@mysqlhost[:port]/database
520+ match = re .match (r'^/([^:@]+)(?::([^@]*))?@([^:/]+)(?::(\d+))?/(.+)$' , parsed .path )
521+ if match is None :
522+ raise ValueError ("Incomplete mysql+ssh URL: {0}. Use mysql+ssh://sshuser@sshhost/mysqluser@mysqlhost/database" .format (url ))
523+ mysqlUser = match .group (1 )
524+ mysqlPassword = match .group (2 )
509525 mysqlHost = match .group (3 )
510- mysqlPort = 3306
511- mysqlUser = match .group (4 )
526+ mysqlPort = int (match .group (4 )) if match .group (4 ) else 3306
512527 database = match .group (5 )
513- return (engine , sshUser , sshHost , mysqlHost , mysqlPort , mysqlUser , database )
514-
515- # #mysql://sshusername@cafeine2.crulrg.ulaval.ca/mysqlusername:mysqlpassword@questions
516- # match = re.search("mysql://([^@]+?)/(.*?)@(.+)", url)
517- # if match is not None:
518- # engine = Engine.mysql
519- # sshUser = None
520- # sshHost = None
521- # mysqlHost = match.group(1)
522- # mysqluser = match.group(2)
523- # database = match.group(3)
524- # return (engine, sshUser, sshHost, mysqlHost, mysqluser, database)
525-
526- # match = re.search("mysql.ssh://(.+?)@([^@]+?):([^@]+?)/(.*?)@(.+)", url)
527- # if match is not None:
528- # engine = Engine.mysql
529- # sshUser = match.group(1)
530- # sshHost = match.group(2)
531- # mysqlHost = match.group(3)
532- # mysqlUser = match.group(4)
533- # database = match.group(5)
534- # return (engine, sshUser, sshHost, mysqlHost, mysqlUser, database)
535-
536- raise ValueError ("Unrecognized or incomplete URL: {0}. Use mysql://host/mysqlusername@questions, mysql+ssh://sshusername@sshhost:mysql_host/mysqlusername@questions, or simply sqlite://filename" .format (url ))
528+ return (engine , sshUser , sshHost , mysqlHost , mysqlPort , mysqlUser , mysqlPassword , database )
529+
530+ raise ValueError ("Unrecognized URL scheme '{0}' in: {1}. Use mysql://user@host[:port]/database or mysql+ssh://sshuser@sshhost/mysqluser@mysqlhost/database" .format (parsed .scheme , url ))
537531
538532 def __enter__ (self ):
539533 return self
@@ -546,9 +540,12 @@ def connect(self):
546540 try :
547541 if not self .isConnected :
548542 import mysql .connector as mysql
549- import keyring
550543
551- if self .usePassword is True :
544+ if self .mysqlPassword is not None :
545+ pwd = self .mysqlPassword
546+ elif self .usePassword is True :
547+ import keyring
548+
552549 if self .sshHost is not None :
553550 serviceName = "mysql-{0}-ssh-{1}" .format (self .mysqlHost , self .sshHost )
554551 else :
@@ -559,7 +556,8 @@ def connect(self):
559556 pwd = keyring .get_password ("mysql-{0}" .format (self .mysqlHost ), self .mysqlUser )
560557 if pwd is None :
561558 raise Exception (""" Set the password in the system password manager on the command line with:
562- {2} -m keyring set {0} {1}""" .format (serviceName , self .mysqlUser , sys .executable ))
559+ {2} -m keyring set {0} {1}
560+ Or provide the password in the URL: mysql://user:password@host/database""" .format (serviceName , self .mysqlUser , sys .executable ))
563561 else :
564562 pwd = None
565563
@@ -777,7 +775,7 @@ def insert(self, table: str, entry: dict):
777775
778776if __name__ == "__main__" :
779777 sys .path .insert (0 , os .path .abspath (os .path .join (os .path .dirname (__file__ ), '..' , '..' )))
780- db = Database ("mysql+ssh://dcclab@cafeine3.crulrg.ulaval.ca/dcclab@labdata" )
778+ db = Database ("mysql+ssh://dcclab@cafeine3.crulrg.ulaval.ca/dcclab@cafeine3.crulrg.ulaval.ca/ labdata" )
781779 db .execute ('select * from files' )
782780 print (db .fetchAll ())
783781 # db = Database("/Users/dccote/GitHub/PyVino/raman.db")
0 commit comments