Package Gnumed :: Package pycommon :: Module gmCfg
[frames] | no frames]

Source Code for Module Gnumed.pycommon.gmCfg

  1  """GNUmed configuration handling. 
  2   
  3  Two sources of configuration information are supported: 
  4   
  5   - database tables 
  6   
  7  Theory of operation: 
  8   
  9  It is helpful to have a solid log target set up before importing this 
 10  module in your code. This way you will be able to see even those log 
 11  messages generated during module import. 
 12   
 13  Once your software has established database connectivity you can 
 14  set up a config source from the database. You can limit the option 
 15  applicability by the constraints "workplace", "user", and "cookie". 
 16   
 17  The basic API for handling items is get()/set(). 
 18  The database config objects auto-syncs with the backend. 
 19   
 20  @copyright: GPL 
 21  """ 
 22  # TODO: 
 23  # - optional arg for set -> type 
 24  #================================================================== 
 25  # $Source: /cvsroot/gnumed/gnumed/gnumed/client/pycommon/gmCfg.py,v $ 
 26  __version__ = "$Revision: 1.60 $" 
 27  __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>" 
 28   
 29  # standard modules 
 30  import sys, types, cPickle, decimal, logging, re as regex 
 31   
 32   
 33  # gnumed modules 
 34  if __name__ == '__main__': 
 35          sys.path.insert(0, '../../') 
 36  from Gnumed.pycommon import gmPG2, gmTools 
 37   
 38   
 39  _log = logging.getLogger('gm.cfg') 
 40  _log.info(__version__) 
 41   
 42  # don't change this without knowing what you do as 
 43  # it will already be in many databases 
 44  cfg_DEFAULT = "xxxDEFAULTxxx" 
 45  #================================================================== 
 46  # FIXME: make a cBorg around this 
47 -class cCfgSQL:
48 - def __init__(self):
49 self.ro_conn = gmPG2.get_connection()
50 #----------------------------------------------- 51 # external API 52 #-----------------------------------------------
53 - def get2(self, option=None, workplace=None, cookie=None, bias=None, default=None, sql_return_type=None):
54 """Retrieve configuration option from backend. 55 56 @param bias: Determine the direction into which to look for config options. 57 58 'user': When no value is found for "current_user/workplace" look for a value 59 for "current_user" regardless of workspace. The corresponding concept is: 60 61 "Did *I* set this option anywhere on this site ? If so, reuse the value." 62 63 'workplace': When no value is found for "current_user/workplace" look for a value 64 for "workplace" regardless of user. The corresponding concept is: 65 66 "Did anyone set this option for *this workplace* ? If so, reuse that value." 67 @param default: if no value is found for the option this value is returned 68 instead, also the option is set to this value in the backend, if <None> 69 a missing option will NOT be created in the backend 70 @param sql_return_type: a PostgreSQL type the value of the option is to be 71 cast to before returning, if None no cast will be applied, you will 72 want to make sure that sql_return_type and type(default) are compatible 73 """ 74 75 # this is the one to use (Sa 17 Feb 2007 12:16:56 CET) 76 77 if None in [option, workplace]: 78 raise ValueError, 'neither <option> (%s) nor <workplace> (%s) may be [None]' % (option, workplace) 79 if bias not in ['user', 'workplace']: 80 raise ValueError, '<bias> must be "user" or "workplace"' 81 82 # does this option exist ? 83 cmd = u"select type from cfg.cfg_template where name=%(opt)s" 84 rows, idx = gmPG2.run_ro_queries(link_obj=self.ro_conn, queries = [{'cmd': cmd, 'args': {'opt': option}}]) 85 if len(rows) == 0: 86 # not found ... 87 if default is None: 88 # ... and no default either 89 return None 90 _log.info('creating option [%s] with default [%s]' % (option, default)) 91 success = self.set(workplace = workplace, cookie = cookie, option = option, value = default) 92 if not success: 93 # ... but cannot create option with default value either 94 _log.error('creating option failed') 95 return default 96 97 cfg_table_type_suffix = rows[0][0] 98 args = { 99 'opt': option, 100 'wp': workplace, 101 'cookie': cookie, 102 'def': cfg_DEFAULT 103 } 104 105 if cfg_table_type_suffix == u'data': 106 sql_return_type = u'' 107 else: 108 sql_return_type = gmTools.coalesce ( 109 initial = sql_return_type, 110 instead = u'', 111 template_initial = u'::%s' 112 ) 113 114 # 1) search value with explicit workplace and current user 115 where_parts = [ 116 u'vco.owner = CURRENT_USER', 117 u'vco.workplace = %(wp)s', 118 u'vco.option = %(opt)s' 119 ] 120 where_parts.append(gmTools.coalesce ( 121 initial = cookie, 122 instead = u'vco.cookie is null', 123 template_initial = u'vco.cookie = %(cookie)s' 124 )) 125 cmd = u"select vco.value%s from cfg.v_cfg_opts_%s vco where %s limit 1" % ( 126 sql_return_type, 127 cfg_table_type_suffix, 128 u' and '.join(where_parts) 129 ) 130 rows, idx = gmPG2.run_ro_queries(link_obj=self.ro_conn, queries = [{'cmd': cmd, 'args': args}]) 131 if len(rows) > 0: 132 if cfg_table_type_suffix == u'data': 133 return cPickle.loads(str(rows[0][0])) 134 return rows[0][0] 135 136 _log.warning('no user AND workplace specific value for option [%s] in config database' % option) 137 138 # 2) search value with biased query 139 if bias == 'user': 140 # did *I* set this option on *any* workplace ? 141 where_parts = [ 142 u'vco.option = %(opt)s', 143 u'vco.owner = CURRENT_USER', 144 ] 145 else: 146 # did *anyone* set this option on *this* workplace ? 147 where_parts = [ 148 u'vco.option = %(opt)s', 149 u'vco.workplace = %(wp)s' 150 ] 151 where_parts.append(gmTools.coalesce ( 152 initial = cookie, 153 instead = u'vco.cookie is null', 154 template_initial = u'vco.cookie = %(cookie)s' 155 )) 156 cmd = u"select vco.value%s from cfg.v_cfg_opts_%s vco where %s" % ( 157 sql_return_type, 158 cfg_table_type_suffix, 159 u' and '.join(where_parts) 160 ) 161 rows, idx = gmPG2.run_ro_queries(link_obj=self.ro_conn, queries = [{'cmd': cmd, 'args': args}]) 162 if len(rows) > 0: 163 # set explicitely for user/workplace 164 self.set ( 165 workplace = workplace, 166 cookie = cookie, 167 option = option, 168 value = rows[0][0] 169 ) 170 if cfg_table_type_suffix == u'data': 171 return cPickle.loads(str(rows[0][0])) 172 return rows[0][0] 173 174 _log.warning('no user OR workplace specific value for option [%s] in config database' % option) 175 176 # 3) search value within default site policy 177 where_parts = [ 178 u'vco.owner = %(def)s', 179 u'vco.workplace = %(def)s', 180 u'vco.option = %(opt)s' 181 ] 182 cmd = u"select vco.value%s from cfg.v_cfg_opts_%s vco where %s" % ( 183 sql_return_type, 184 cfg_table_type_suffix, 185 u' and '.join(where_parts) 186 ) 187 rows, idx = gmPG2.run_ro_queries(link_obj=self.ro_conn, queries = [{'cmd': cmd, 'args': args}]) 188 if len(rows) > 0: 189 # set explicitely for user/workplace 190 self.set ( 191 workplace = workplace, 192 cookie = cookie, 193 option = option, 194 value = rows[0]['value'] 195 ) 196 if cfg_table_type_suffix == u'data': 197 return cPickle.loads(str(rows[0]['value'])) 198 return rows[0]['value'] 199 200 _log.warning('no default site policy value for option [%s] in config database' % option) 201 202 # 4) not found, set default ? 203 if default is None: 204 _log.warning('no default value for option [%s] supplied by caller' % option) 205 return None 206 _log.info('setting option [%s] to default [%s]' % (option, default)) 207 success = self.set ( 208 workplace = workplace, 209 cookie = cookie, 210 option = option, 211 value = default 212 ) 213 if not success: 214 return None 215 216 return default
217 #-----------------------------------------------
218 - def getID(self, workplace = None, cookie = None, option = None):
219 """Get config value from database. 220 221 - unset arguments are assumed to mean database defaults except for <cookie> 222 """ 223 # sanity checks 224 if option is None: 225 _log.error("Need to know which option to retrieve.") 226 return None 227 228 alias = self.__make_alias(workplace, 'CURRENT_USER', cookie, option) 229 230 # construct query 231 where_parts = [ 232 'vco.option=%(opt)s', 233 'vco.workplace=%(wplace)s' 234 ] 235 where_args = { 236 'opt': option, 237 'wplace': workplace 238 } 239 if workplace is None: 240 where_args['wplace'] = cfg_DEFAULT 241 242 where_parts.append('vco.owner=CURRENT_USER') 243 244 if cookie is not None: 245 where_parts.append('vco.cookie=%(cookie)s') 246 where_args['cookie'] = cookie 247 where_clause = ' and '.join(where_parts) 248 cmd = u""" 249 select vco.pk_cfg_item 250 from cfg.v_cfg_options vco 251 where %s 252 limit 1""" % where_clause 253 254 rows, idx = gmPG2.run_ro_queries(link_obj=self.ro_conn, queries = [{'cmd': cmd, 'args': where_args}], return_data=True) 255 if len(rows) == 0: 256 _log.warning('option definition for [%s] not in config database' % alias) 257 return None 258 return rows[0][0]
259 #----------------------------
260 - def set(self, workplace = None, cookie = None, option = None, value = None):
261 """Set (insert or update) option value in database. 262 263 Any parameter that is None will be set to the database default. 264 265 Note: you can't change the type of a parameter once it has been 266 created in the backend. If you want to change the type you will 267 have to delete the parameter and recreate it using the new type. 268 """ 269 # sanity checks 270 if None in [option, value]: 271 raise ValueError('invalid arguments (option=<%s>, value=<%s>)' % (option, value)) 272 273 rw_conn = gmPG2.get_connection(readonly=False) 274 275 alias = self.__make_alias(workplace, 'CURRENT_USER', cookie, option) 276 277 opt_value = value 278 sql_type_cast = u'' 279 if isinstance(value, basestring): 280 sql_type_cast = u'::text' 281 elif isinstance(value, types.BooleanType): 282 opt_value = int(opt_value) 283 elif isinstance(value, (types.FloatType, types.IntType, types.LongType, decimal.Decimal, types.BooleanType)): 284 sql_type_cast = u'::numeric' 285 elif isinstance(value, types.ListType): 286 # there can be different syntaxes for list types so don't try to cast them 287 pass 288 elif isinstance(value, types.BufferType): 289 # can go directly into bytea 290 pass 291 else: 292 try: 293 opt_value = gmPG2.dbapi.Binary(cPickle.dumps(value)) 294 sql_type_cast = '::bytea' 295 except cPickle.PicklingError: 296 _log.error("cannot pickle option of type [%s] (key: %s, value: %s)", type(value), alias, str(value)) 297 raise 298 except: 299 _log.error("don't know how to store option of type [%s] (key: %s, value: %s)", type(value), alias, str(value)) 300 raise 301 302 cmd = u'select cfg.set_option(%%(opt)s, %%(val)s%s, %%(wp)s, %%(cookie)s, NULL)' % sql_type_cast 303 args = { 304 'opt': option, 305 'val': opt_value, 306 'wp': workplace, 307 'cookie': cookie 308 } 309 try: 310 rows, idx = gmPG2.run_rw_queries(link_obj=rw_conn, queries=[{'cmd': cmd, 'args': args}], return_data=True) 311 result = rows[0][0] 312 except: 313 _log.exception('cannot set option') 314 result = False 315 316 rw_conn.commit() # will rollback if transaction failed 317 rw_conn.close() 318 319 return result
320 #-------------------------------------------
321 - def getAllParams(self, user = None, workplace = cfg_DEFAULT):
322 """Get names of all stored parameters for a given workplace/(user)/cookie-key. 323 This will be used by the ConfigEditor object to create a parameter tree. 324 """ 325 # if no workplace given: any workplace (= cfg_DEFAULT) 326 where_snippets = [ 327 u'cfg_template.pk=cfg_item.fk_template', 328 u'cfg_item.workplace=%(wplace)s' 329 ] 330 where_args = {'wplace': workplace} 331 332 # if no user given: current db user 333 if user is None: 334 where_snippets.append(u'cfg_item.owner=CURRENT_USER') 335 else: 336 where_snippets.append(u'cfg_item.owner=%(usr)s') 337 where_args['usr'] = user 338 339 where_clause = u' and '.join(where_snippets) 340 341 cmd = u""" 342 select name, cookie, owner, type, description 343 from cfg.cfg_template, cfg.cfg_item 344 where %s""" % where_clause 345 346 # retrieve option definition 347 rows, idx = gmPG2.run_ro_queries(link_obj=self.ro_conn, queries = [{'cmd': cmd, 'args': where_args}], return_data=True) 348 return rows
349 #----------------------------
350 - def delete(self, workplace = None, cookie = None, option = None):
351 """ 352 Deletes an option or a whole group. 353 Note you have to call store() in order to save 354 the changes. 355 """ 356 if option is None: 357 raise ValueError('<option> cannot be None') 358 359 if cookie is None: 360 cmd = u""" 361 delete from cfg.cfg_item where 362 fk_template=(select pk from cfg.cfg_template where name = %(opt)s) and 363 owner = CURRENT_USER and 364 workplace = %(wp)s and 365 cookie is Null 366 """ 367 else: 368 cmd = u""" 369 delete from cfg.cfg_item where 370 fk_template=(select pk from cfg.cfg_template where name = %(opt)s) and 371 owner = CURRENT_USER and 372 workplace = %(wp)s and 373 cookie = %(cookie)s 374 """ 375 args = {'opt': option, 'wp': workplace, 'cookie': cookie} 376 gmPG2.run_rw_queries(queries=[{'cmd': cmd, 'args': args}]) 377 return True
378 #----------------------------
379 - def __make_alias(self, workplace, user, cookie, option):
380 return '%s-%s-%s-%s' % (workplace, user, cookie, option)
381 #===================================================================
382 -def getDBParam(workplace = None, cookie = None, option = None):
383 """Convenience function to get config value from database. 384 385 will search for context dependant match in this order: 386 - CURRENT_USER_CURRENT_WORKPLACE 387 - CURRENT_USER_DEFAULT_WORKPLACE 388 - DEFAULT_USER_CURRENT_WORKPLACE 389 - DEFAULT_USER_DEFAULT_WORKPLACE 390 391 We assume that the config tables are found on service "default". 392 That way we can handle the db connection inside this function. 393 394 Returns (value, set) of first match. 395 """ 396 397 # FIXME: depending on set store for user ... 398 399 if option is None: 400 return (None, None) 401 402 # connect to database (imports gmPG2 if need be) 403 dbcfg = cCfgSQL() 404 405 # (set_name, user, workplace) 406 sets2search = [] 407 if workplace is not None: 408 sets2search.append(['CURRENT_USER_CURRENT_WORKPLACE', None, workplace]) 409 sets2search.append(['CURRENT_USER_DEFAULT_WORKPLACE', None, None]) 410 if workplace is not None: 411 sets2search.append(['DEFAULT_USER_CURRENT_WORKPLACE', cfg_DEFAULT, workplace]) 412 sets2search.append(['DEFAULT_USER_DEFAULT_WORKPLACE', cfg_DEFAULT, None]) 413 # loop over sets 414 matchingSet = None 415 result = None 416 for set in sets2search: 417 result = dbcfg.get( 418 workplace = set[2], 419 user = set[1], 420 option = option, 421 cookie = cookie 422 ) 423 if result is not None: 424 matchingSet = set[0] 425 break 426 _log.debug('[%s] not found for [%s@%s]' % (option, set[1], set[2])) 427 428 # cleanup 429 if matchingSet is None: 430 _log.warning('no config data for [%s]' % option) 431 return (result, matchingSet)
432 #-------------------------------------------------------------
433 -def setDBParam(workplace = None, user = None, cookie = None, option = None, value = None):
434 """Convenience function to store config values in database. 435 436 We assume that the config tables are found on service "default". 437 That way we can handle the db connection inside this function. 438 439 Omitting any parameter (or setting to None) will store database defaults for it. 440 441 - returns True/False 442 """ 443 # connect to database 444 dbcfg = cCfgSQL() 445 # set value 446 success = dbcfg.set( 447 workplace = workplace, 448 user = user, 449 option = option, 450 value = value 451 ) 452 453 if not success: 454 return False 455 return True
456 #============================================================= 457 # main 458 #============================================================= 459 if __name__ == "__main__": 460 461 root = logging.getLogger() 462 root.setLevel(logging.DEBUG) 463 464 #---------------------------------------------------------
465 - def test_db_cfg():
466 print "testing database config" 467 print "=======================" 468 469 myDBCfg = cCfgSQL() 470 471 print "delete() works:", myDBCfg.delete(option='font name', workplace = 'test workplace') 472 print "font is initially:", myDBCfg.get2(option = 'font name', workplace = 'test workplace', bias = 'user') 473 print "set() works:", myDBCfg.set(option='font name', value="Times New Roman", workplace = 'test workplace') 474 print "font after set():", myDBCfg.get2(option = 'font name', workplace = 'test workplace', bias = 'user') 475 print "delete() works:", myDBCfg.delete(option='font name', workplace = 'test workplace') 476 print "font after delete():", myDBCfg.get2(option = 'font name', workplace = 'test workplace', bias = 'user') 477 print "font after get() with default:", myDBCfg.get2(option = 'font name', workplace = 'test workplace', bias = 'user', default = 'WingDings') 478 print "font right after get() with another default:", myDBCfg.get2(option = 'font name', workplace = 'test workplace', bias = 'user', default = 'default: Courier') 479 print "set() works:", myDBCfg.set(option='font name', value="Times New Roman", workplace = 'test workplace') 480 print "font after set() on existing option:", myDBCfg.get2(option = 'font name', workplace = 'test workplace', bias = 'user') 481 482 print "setting array option" 483 print "array now:", myDBCfg.get2(option = 'test array', workplace = 'test workplace', bias = 'user') 484 aList = ['val 1', 'val 2'] 485 print "set():", myDBCfg.set(option='test array', value = aList, workplace = 'test workplace') 486 print "array now:", myDBCfg.get2(option = 'test array', workplace = 'test workplace', bias = 'user') 487 aList = ['val 11', 'val 12'] 488 print "set():", myDBCfg.set(option='test array', value = aList, workplace = 'test workplace') 489 print "array now:", myDBCfg.get2(option = 'test array', workplace = 'test workplace', bias = 'user') 490 print "delete() works:", myDBCfg.delete(option='test array', workplace='test workplace') 491 print "array now:", myDBCfg.get2(option = 'test array', workplace = 'test workplace', bias = 'user') 492 493 print "setting complex option" 494 data = {1: 'line 1', 2: 'line2', 3: {1: 'line3.1', 2: 'line3.2'}, 4: 1234} 495 print "set():", myDBCfg.set(option = "complex option test", value = data, workplace = 'test workplace') 496 print "complex option now:", myDBCfg.get2(workplace = 'test workplace', option = "complex option test", bias = 'user') 497 print "delete() works:", myDBCfg.delete(option = "complex option test", workplace = 'test workplace') 498 print "complex option now:", myDBCfg.get2(workplace = 'test workplace', option = "complex option test", bias = 'user')
499 500 #--------------------------------------------------------- 501 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'): 502 try: 503 test_db_cfg() 504 except: 505 _log.exception('test suite failed') 506 raise 507 508 #============================================================= 509 # $Log: gmCfg.py,v $ 510 # Revision 1.60 2008/08/20 13:53:45 ncq 511 # - robustify type handling in get2/set: 512 # - explicitely support buffer() 513 # - properly handle _data table 514 # - unpickle retrieved bytea 515 # 516 # Revision 1.59 2008/04/13 13:57:32 ncq 517 # - lay to rest the venerable cCfgFile, it 518 # has served us well 519 # 520 # Revision 1.58 2008/01/30 14:04:32 ncq 521 # - disable support from gmCfgFile() 522 # 523 # Revision 1.57 2007/12/26 18:34:02 ncq 524 # - no more old CLI lib 525 # 526 # Revision 1.56 2007/12/23 11:57:14 ncq 527 # - cleanup 528 # 529 # Revision 1.55 2007/12/11 15:35:28 ncq 530 # - use std lib logging 531 # 532 # Revision 1.54 2007/02/22 17:41:13 ncq 533 # - adjust to gmPerson changes 534 # 535 # Revision 1.53 2007/02/17 14:11:56 ncq 536 # - better get2 docs 537 # - allow custom cast on get2() return value 538 # 539 # Revision 1.52 2007/01/30 17:38:06 ncq 540 # - cleanup and a comment 541 # 542 # Revision 1.51 2006/12/22 15:20:12 ncq 543 # - do not fail hard if config file not found, after all, user 544 # may want to set it later, but still do not hide exceptions 545 # 546 # Revision 1.50 2006/12/21 10:49:38 ncq 547 # - do not hide exceptiosn 548 # 549 # Revision 1.49 2006/12/13 14:55:56 ncq 550 # - remove get() from SQL source 551 # 552 # Revision 1.48 2006/12/05 13:54:02 ncq 553 # - better error messages 554 # - u''ify some query parts 555 # - simplify some code 556 # 557 # Revision 1.47 2006/11/14 16:27:36 ncq 558 # - improved test suite 559 # 560 # Revision 1.46 2006/11/07 00:27:45 ncq 561 # - psycopg2 knows how to adapt lists/tuples to ARRAY syntax, at 562 # least when SQL_IN is loaded, so we can't use explicit casts with 563 # <datatype>[] anymore in our SQL 564 # 565 # Revision 1.45 2006/11/05 16:00:17 ncq 566 # - unicode is text so don't pickle it 567 # 568 # Revision 1.44 2006/10/25 07:19:03 ncq 569 # - no more gmPG 570 # 571 # Revision 1.43 2006/10/08 11:02:02 ncq 572 # - support decimal type, too 573 # - make queries unicode 574 # 575 # Revision 1.42 2006/09/21 19:41:21 ncq 576 # - convert to use gmPG2 577 # - axe cCfgBase 578 # - massive cCfgSQL cleanup 579 # - axe get() 580 # - axe get_by_workplace() 581 # - pump up get2(), set() and delete() 582 # - getID() still pending review 583 # - get2(bytea) still pending fixes 584 # - fix up database config test suite 585 # 586 # Revision 1.41 2006/09/12 17:20:36 ncq 587 # - mark up the deprecated sql get()ters 588 # 589 # Revision 1.40 2006/07/24 14:16:56 ncq 590 # - get_by_user() never worked so axe it 591 # 592 # Revision 1.39 2006/05/16 15:50:07 ncq 593 # - several small fixes in get2() regarding 594 # less travelled codepathes 595 # 596 # Revision 1.38 2006/05/01 18:44:43 ncq 597 # - fix gmPG -> gmPG_ usage 598 # 599 # Revision 1.37 2006/02/27 15:39:06 ncq 600 # - add cCfg_SQL.get2() 601 # 602 # Revision 1.36 2006/01/13 14:57:14 ncq 603 # - really fix get_by_workplace() - still needs set() functionality 604 # 605 # Revision 1.35 2006/01/01 17:22:08 ncq 606 # - get_by_workplace always returns default value in case of 607 # errors/option not found except when there is not default given 608 # in which case it will return None on error 609 # 610 # Revision 1.34 2005/12/30 16:51:03 ncq 611 # - slightly improved method documentation 612 # 613 # Revision 1.33 2005/12/14 16:56:09 ncq 614 # - enhance get_by_user() and get_by_workplace() with a default 615 # which if set will enable to store the option even if there's 616 # no template in the database 617 # - fix unit test 618 # 619 # Revision 1.32 2005/12/14 10:41:11 ncq 620 # - allow cCfgSQL to set up its own connection if none given 621 # - add cCfgSQL.get_by_user() 622 # - smarten up cCfgSQL.get() 623 # 624 # Revision 1.31 2005/11/19 08:47:56 ihaywood 625 # tiny bugfixes 626 # 627 # Revision 1.30 2005/11/18 15:48:44 ncq 628 # - config tables now in cfg.* schema so adjust to that 629 # - also some id -> pk changes 630 # 631 # Revision 1.29 2005/10/10 18:05:46 ncq 632 # - ignore error on failing to delete non-existant backup config 633 # file as that was way over the top behaviour 634 # 635 # Revision 1.28 2005/10/08 09:24:09 ihaywood 636 # lack of a backup config file is now an warning only. 637 # 638 # Revision 1.27 2005/08/14 15:35:31 ncq 639 # - cleanup 640 # 641 # Revision 1.26 2005/02/05 10:58:09 ihaywood 642 # fixed patient picture problem (gratutious use of a named parameter) 643 # more rationalisation of loggin in gmCfg 644 # 645 # Revision 1.24 2005/01/10 11:46:51 ncq 646 # - make cCfgSQL also support arbitrary option values in cfg_data 647 # 648 # Revision 1.23 2004/09/06 22:18:12 ncq 649 # - eventually fix the get/setDBParam(), at least it appears to work 650 # 651 # Revision 1.22 2004/09/02 00:39:27 ncq 652 # - use new v_cfg_options 653 # - remove personalize argument from getDBParam() in favour of clarity 654 # 655 # Revision 1.21 2004/08/24 13:40:43 ncq 656 # - when cleaning up cfgSQL.set() I screwed up, fixed 657 # 658 # Revision 1.20 2004/08/23 10:24:10 ncq 659 # - made setdbparam saner re default params, eg. param=None will set to 660 # database default, eg if anything else wanted user needs to explicitely 661 # set 662 # - cleanup 663 # 664 # Revision 1.19 2004/08/20 13:22:13 ncq 665 # - cleanup 666 # - getFirstMatchingDBSet() -> getDBParam() 667 # - argument personalize default true in getDBParam() stores 668 # option value if found for other that current user/current workspace 669 # 670 # Revision 1.18 2004/08/16 12:15:20 ncq 671 # - don't hide module global gmDefCfgFile inside "if __name__ == '__main__'" so 672 # that epydoc picks it up properly for documentation 673 # 674 # Revision 1.17 2004/08/16 12:06:50 ncq 675 # - hopefully improve docstring by including import example 676 # 677 # Revision 1.16 2004/08/11 11:07:33 ncq 678 # - needless args on cfg queries removed 679 # 680 # Revision 1.15 2004/08/11 08:00:05 ncq 681 # - improve log prefix 682 # - cleanup SQL cfg/remove old use of _USER 683 # 684 # Revision 1.14 2004/07/24 17:10:09 ncq 685 # - fix getAllParams() 686 # 687 # Revision 1.13 2004/07/19 13:53:35 ncq 688 # - some cleanup re setDBParam()/getFirstMatchingDBset() 689 # 690 # Revision 1.12 2004/07/19 11:50:42 ncq 691 # - cfg: what used to be called "machine" really is "workplace", so fix 692 # 693 # Revision 1.11 2004/07/17 21:08:51 ncq 694 # - gmPG.run_query() now has a verbosity parameter, so use it 695 # 696 # Revision 1.10 2004/07/12 13:49:39 ncq 697 # - log version 698 # 699 # Revision 1.9 2004/07/12 02:48:40 ihaywood 700 # same again 701 # 702 # Revision 1.8 2004/07/12 02:44:12 ihaywood 703 # it should not be neccessary to specify the full path when 704 # importing from the same package. 705 # It makes the file gratutiously dependent on being in the gnumed 706 # directory structure. 707 # 708 # Revision 1.7 2004/07/06 00:25:17 ncq 709 # - assign Null design pattern instance if no default cfg file found 710 # 711 # Revision 1.6 2004/06/28 22:36:33 hinnef 712 # added lazy loading of gmPG to gmCfgSQL:getAllParams 713 # 714 # Revision 1.5 2004/06/22 07:58:47 ihaywood 715 # minor bugfixes 716 # let gmCfg cope with config files that are not real files 717 # 718 # Revision 1.4 2004/06/19 18:55:44 shilbert 719 # - fixes for various import statements 720 # 721 # Revision 1.3 2004/02/26 14:32:46 ncq 722 # - fixed and lazied even more 723 # 724 # Revision 1.2 2004/02/25 22:56:38 sjtan 725 # 726 # probably a typo ; temp fix until authors see it. 727 # 728 # Revision 1.1 2004/02/25 09:30:13 ncq 729 # - moved here from python-common 730 # 731 # Revision 1.71 2004/02/25 08:46:12 ncq 732 # - hopefully lazyied the import of gmCLI, too 733 # 734 # Revision 1.70 2004/02/25 08:39:04 ncq 735 # - I think I removed the dependancy on gmPG as long as cCfgSQL isn't used 736 # 737 # Revision 1.69 2004/01/06 23:44:40 ncq 738 # - __default__ -> xxxDEFAULTxxx 739 # 740 # Revision 1.68 2003/11/22 02:03:48 ihaywood 741 # reverted to version 1.66 742 # 743 # Revision 1.66 2003/10/22 22:05:18 ncq 744 # - cleanup, coding style 745 # 746 # Revision 1.65 2003/10/22 21:37:04 hinnef 747 # added convenience function setDBParam() to reduce redundant code on setting backend parameters 748 # 749 # Revision 1.64 2003/10/02 20:01:15 hinnef 750 # fixed selection of user in gmcfgSQL.get/getID/getAllParams so that _user will be found, too 751 # 752 # Revision 1.63 2003/09/26 19:35:21 hinnef 753 # - added delete() methods in cCfgFile and cCfgSQL, small fixes in cfgSQL.set() 754 # 755 # Revision 1.62 2003/09/24 10:32:13 ncq 756 # - in _get_conf_name() we need to make std_dirs when aName is None, 757 # not aDir, also init base_name/base_dir to a known state 758 # 759 # Revision 1.61 2003/09/21 08:37:47 ihaywood 760 # database code now properly escaped 761 # 762 # Revision 1.60 2003/08/24 13:36:39 hinnef 763 # added getFirstMatchingDBSet() for convenient config data retrieval 764 # 765 # Revision 1.59 2003/08/24 08:01:44 ncq 766 # - removed some dead code, cleanup 767 # 768 # Revision 1.58 2003/08/23 18:33:50 hinnef 769 # added small comment in __get_conf_name(), commented out two verbose debug messages 770 # 771 # Revision 1.57 2003/08/10 00:53:09 ncq 772 # - cleanup to Hilmars nice additions 773 # 774 # Revision 1.56 2003/08/07 23:31:04 hinnef 775 # changed CfgFile.__get_conf_name so that files can be searched in more than one location 776 # 777 # Revision 1.55 2003/07/21 20:53:50 ncq 778 # - fix string screwup 779 # 780 # Revision 1.54 2003/07/21 19:18:06 ncq 781 # - use gmPG.run_query(), not home-brew 782 # - kill gmPG.esc() use 783 # - cleanup/comments 784 # 785 # Revision 1.53 2003/06/26 21:29:58 ncq 786 # - (cmd, arg) style, fatal->verbose 787 # 788 # Revision 1.52 2003/06/26 04:18:40 ihaywood 789 # Fixes to gmCfg for commas 790 # 791 # Revision 1.51 2003/06/21 10:44:09 ncq 792 # - handle read-only media better when modifying config file 793 # 794 # Revision 1.50 2003/06/17 22:21:53 ncq 795 # - improve __get_conf_name() 796 # 797 # Revision 1.49 2003/06/03 21:52:23 hinnef 798 # - fixed a bug in cfgSQL.set() when updating a value 799 # 800 # Revision 1.48 2003/05/12 09:12:48 ncq 801 # - minor cleanups 802 # 803 # Revision 1.47 2003/05/10 18:45:52 hinnef 804 # - added getAllParams for use in gmConfigRegistry 805 # 806 # Revision 1.46 2003/04/14 07:45:47 ncq 807 # - better temp names in cfgFile.store() 808 # 809 # Revision 1.45 2003/03/31 00:26:46 ncq 810 # - forgot "\n" 811 # 812 # Revision 1.44 2003/03/30 21:38:28 ncq 813 # - put some blurb in new, empty config files 814 # 815 # Revision 1.43 2003/03/27 21:10:12 ncq 816 # - move '__default__' to cfg_DEFAULT constant 817 # 818 # Revision 1.42 2003/03/23 10:32:50 ncq 819 # - improve console messages a bit 820 # 821 # Revision 1.41 2003/02/21 08:58:51 ncq 822 # - improve PgArray detection even more 823 # 824 # Revision 1.40 2003/02/21 08:51:57 ncq 825 # - catch exception on missing PgArray 826 # 827 # Revision 1.39 2003/02/15 08:51:05 ncq 828 # - don't remove empty lines in lists 829 # 830 # Revision 1.38 2003/02/11 16:52:36 ncq 831 # - log one more failing corner case 832 # 833 # Revision 1.37 2003/02/09 09:48:28 ncq 834 # - revert breakage created by sjtan 835 # 836 # Revision 1.36 2003/02/09 02:02:30 sjtan 837 # 838 # allows first time run of gmGuiMain without a conf file. A Default conf file called gnumed.conf is created. 839 # 840 # Revision 1.35 2003/01/28 10:53:09 ncq 841 # - clarification to test code 842 # 843 # Revision 1.34 2003/01/12 11:53:58 ncq 844 # - fixed subtle bug resulting from ro/rw connections: 845 # - set() would request a rw conn thus CURRENT_USER = "_user" 846 # - get() would use a ro conn, thus CURRENT_USER == "user" 847 # - there'd never be a match and the items would keep proliferating 848 # 849 # Revision 1.33 2003/01/05 09:56:58 ncq 850 # - ironed out some bugs in the array handling 851 # - streamlined code 852 # - have cfg.set() explicitely use rw conn to DB only when needed 853 # 854 # Revision 1.32 2003/01/04 12:19:04 ncq 855 # - better comment 856 # 857 # Revision 1.31 2003/01/04 12:17:05 ncq 858 # - backup old config file before overwriting 859 # 860 # Revision 1.30 2002/12/26 15:49:10 ncq 861 # - better comments 862 # 863 # Revision 1.29 2002/12/26 15:21:18 ncq 864 # - database config now works even with string lists 865 # 866 # Revision 1.28 2002/12/01 01:11:42 ncq 867 # - log config file line number on parse errors 868 # 869 # Revision 1.27 2002/11/28 11:40:12 ncq 870 # - added database config 871 # - reorganized self test 872 # 873 # Revision 1.26 2002/11/18 09:41:25 ncq 874 # - removed magic #! interpreter incantation line to make Debian happy 875 # 876 # Revision 1.25 2002/11/17 20:09:10 ncq 877 # - always display __doc__ when called standalone 878 # 879 # Revision 1.24 2002/11/05 18:15:03 ncq 880 # - new helper getOptions() 881 # - modified example code to show real use 882 # 883 # Revision 1.23 2002/11/04 15:38:28 ncq 884 # - moved empty config file creation to helper function 885 # 886 # Revision 1.22 2002/11/03 14:11:19 ncq 887 # - autocreate log file on failing to find one 888 # 889 # Revision 1.21 2002/11/03 13:21:05 ncq 890 # - phase 1: error levels more suitable 891 # 892 # Revision 1.20 2002/10/22 21:11:44 ncq 893 # - throwing exception ImportError on failing to load the 894 # default config file wasn't such a good idea after all 895 # since we might _actually_ only be interested in a different 896 # file ... 897 # 898 # Revision 1.19 2002/10/22 15:30:16 ncq 899 # - added getGroups() 900 # 901 # Revision 1.18 2002/10/19 19:30:13 ncq 902 # - on being imported always raise ImportError on failing to use default config file 903 # 904 # Revision 1.17 2002/10/19 19:24:37 ncq 905 # - fixed some whitespace breakage 906 # - raise error on failing to parse default config file, if you really want 907 # to override this you should handle that exception in your own code 908 # 909 # Revision 1.16 2002/10/18 19:57:09 hinnef 910 # fixed problems when a invalid filename is given, static class variables and 911 # changed the initialization of gmDefCfgFile so that it can be imported from 912 # standalone modules 913 # 914 # Revision 1.15 2002/09/30 10:58:27 ncq 915 # - consistently spell GnuMed 916 # 917 # Revision 1.14 2002/09/26 13:21:37 ncq 918 # - log version 919 # 920 # Revision 1.13 2002/09/12 23:11:14 ncq 921 # - fixed one nasty overwriting bug in store() 922 # 923 # Revision 1.12 2002/09/12 10:07:29 ncq 924 # - properly normalize : -> = 925 # 926 # Revision 1.11 2002/09/12 09:17:11 ncq 927 # - windows unsucked 928 # 929 # Revision 1.10 2002/09/10 18:43:02 ncq 930 # - windows sucks ! 931 # 932 # Revision 1.9 2002/09/10 18:31:45 ncq 933 # - windows is strange: os.rename -> shutil.copyfile + os.remove 934 # 935 # Revision 1.8 2002/09/10 18:15:28 ncq 936 # - os.rename() over existing files fails on non-UNIX 937 # 938 # Revision 1.7 2002/09/10 17:51:33 ncq 939 # - more sensible log levels for some data 940 # 941 # Revision 1.6 2002/09/08 15:55:47 ncq 942 # - added log cvs keyword 943 # 944