Package AccessEngine :: Module AERegistrar
[hide private]
[frames] | no frames]

Source Code for Module AccessEngine.AERegistrar

   1  ''' 
   2  Defines the registrar functions responsible for managing an on-disk repository  
   3  of User Interface Elements (UIEs) that define the L{AEScript}s, Devices, Choosers, 
   4  and Monitors available to the user and the system. Defines a helper class  
   5  L{UIESet} that is used internally by the registrar to represent collections  
   6  of UIEs that are to be loaded when SUE starts, when any L{AETier} starts, or when  
   7  a particular L{AETier} starts for a particular user. 
   8   
   9  Functions to install, uninstall, associate, disassociate, and list UIEs are 
  10  primarily of interest to third-party UIE developers and expert users. These 
  11  methods allow extensions to be added to SUE and associated with profiles so 
  12  they are loaded automatically at startup or when L{AETier}s are created. The 
  13  L{SUEMain} script defines a command line interface for accessing these 
  14  methods. See the documentation in that module or the SUE man page for details. 
  15     
  16  Functions to load UIEs are of interest to SUE developers. Given a UIE name, the 
  17  L{loadOne} method will import the Python module containing the named UIE and 
  18  return an instance of the class in that module of the same name (if the UIE 
  19  module is properly installed). Given a UIE kind and name along with the name 
  20  of a , the L{loadAssociated} method will import all Python modules containing 
  21  the UIEs to be loaded and return a list of UIE objects sorted in the desired 
  22  load order. Objects, not classes, are returned by this method implying any 
  23  initialization requiring data from an external source must be done outside the 
  24  constructor. 
  25   
  26   
  27  @var LOCAL_PATH: Location of the set of installed UIEs for this user 
  28  @type LOCAL_PATH: string 
  29  @var GLOBAL_PATH: Location of the set of installed UIEs for all users of the 
  30    system 
  31  @type GLOBAL_PATH: string 
  32  @var SCRIPT: Kind of UIE responsible for registering tasks that handle  
  33    L{AEEvent}s and L{AEInput.Gesture}s 
  34  @type SCRIPT: string 
  35  @var DEVICE: Kind of UIE responsible for managing a method of input  
  36      (e.g keyboard), managing a method of output (e.g speech), or both (e.g.  
  37      Braille device) 
  38  @type DEVICE: string 
  39  @var CHOOSER: Kind of UIE responsible for interacting with the user via more  
  40      than the usual SUE key combos (e.g. configuration, help, search) 
  41  @type CHOOSER: string 
  42  @var MONITOR: Kind of UIE responsible for creating a human-readable  
  43      representation (e.g. GUI dialog, log file) of some AccessEngine data (e.g.  
  44      input, output, events) for the purposes of development and debugging 
  45  @type MONITOR: string 
  46  @var ALL_KINDS: List of all known kinds of UIEs 
  47  @type ALL_KINDS: list 
  48  @var STARTUP_KINDS: Subset of L{ALL_KINDS} that should be loaded at startup 
  49  @type STARTUP_KINDS: list 
  50   
  51  @author: Peter Parente 
  52  @organization: IBM Corporation 
  53  @copyright: Copyright (c) 2005, 2007 IBM Corporation 
  54  @license: The BSD License 
  55   
  56  @author: Frank Zenker 
  57  @organization: IT Science Center Ruegen gGmbH, Germany 
  58  @copyright: Copyright (c) 2007, 2008 ITSC Ruegen 
  59  @license: The BSD License 
  60   
  61  All rights reserved. This program and the accompanying materials are made  
  62  available under the terms of the BSD license which accompanies 
  63  this distribution, and is available at 
  64  U{http://www.opensource.org/licenses/bsd-license.php} 
  65  ''' 
  66  import os, glob, imp, cPickle, sys, logging, shutil, traceback 
  67  import AccessEngine 
  68  from Tools.i18n import _ 
  69  from SUEConstants import BUILTIN_PROFILES, PROG_VERSION, PROG_DATE, HOME_USER, \ 
  70       HOME_DIR 
  71   
  72  # create a logger for AERegistrar 
  73  log = logging.getLogger('AERegistrar') 
  74   
  75  # paths to the pickled listings of installed UIEs, global to the system and  
  76  # local to this user 
  77  LOCAL_PATH = os.path.join(HOME_USER, os.path.basename(sys.argv[0]) + '.installed') 
  78  GLOBAL_PATH = os.path.join(HOME_DIR, 'global.installed') 
  79   
  80  # constants representing kinds of UIEs and the folders where they are stored  
  81  # under the INSTALL_PATH 
  82  SCRIPT = 'script' 
  83  DEVICE = 'device' 
  84  CHOOSER = 'chooser' 
  85  MONITOR = 'monitor' 
  86   
  87  # list of all supports types of UIEs 
  88  ALL_KINDS = [DEVICE, CHOOSER, MONITOR, SCRIPT] 
  89  STARTUP_KINDS = [DEVICE, MONITOR] 
  90   
91 -class Metadata(object):
92 ''' 93 Bag class for specifying UIE metadata. 94 95 @ivar name: Name of the UIE which this metadata describes 96 @type name: string 97 @ivar path: Absolute path to the UIE on disk 98 @type path: string 99 @ivar kind: Kind of UIE 100 @type kind: string 101 @ivar tier: Name of the L{AETier} to which a L{AEScript} should be applied 102 @type tier: string 103 @ivar all_tiers: Apply a L{AEScript} to all L{AETier}s? 104 @type all_tiers: boolean 105 @ivar profiles: Suggested names of profiles that should be updated to include 106 the UIE 107 @type profiles: list of string 108 '''
109 - def __init__(self, name, path, data):
110 ''' 111 Checks and stores values from the data dictionary retrieved from a __uie__ 112 module variable in a UIE. 113 114 @param name: Name of the UIE 115 @type name: string 116 @param path: Absolute path to the UIE on disk 117 @type path: string 118 @param data: Dictionary containing the __uie__ metadata 119 @type data: dictionary 120 ''' 121 self.name = name 122 self.path = os.path.realpath(path) 123 try: 124 self.kind = data['kind'] 125 except KeyError: 126 raise AttributeError(_('Missing kind metadata for %s') % name) 127 self.tier = data.get('tier') 128 self.all_tiers = bool(data.get('all_tiers', False)) 129 if self.kind == SCRIPT and (not data.has_key('tier') and 130 not data.has_key('all_tiers')): 131 raise AttributeError(_('Missing AETier metadata for %s') % name) 132 self.profiles = data.get('profiles', BUILTIN_PROFILES)
133
134 -class UIESet(object):
135 ''' 136 Associates the names of installed UIEs with times when they should be 137 automatically loaded by SUE under a particular user profile. Supported times 138 for automatically loading UIEs include when SUE starts, when any new L{AETier} 139 is created, or when a L{AETier} with a particular process name is created. 140 141 @ivar name: Name of this UIESet 142 @type name: string 143 @ivar on_startup: Lists of UIE names keyed by UIE kind 144 @type on_startup: dictionary 145 @ivar on_tier: Lists of UIE names keyed by UIE kind in dictionaries keyed by 146 L{AETier} name 147 @type on_tier: dictionary 148 @ivar on_any_tier: Lists of UIE names keyed by UIE kind 149 @type on_any_tier: dictionary 150 '''
151 - def __init__(self, name):
152 ''' 153 Stores the name of the UIESet. Initializes the instance dictionaries. 154 155 @param name: Name of the UIESet 156 @type name: string 157 ''' 158 self.name = name 159 self.on_startup = {} 160 self.on_tier = {} 161 self.on_any_tier = {}
162
163 - def addToStartup(self, kind, name, index):
164 ''' 165 Adds the name of a UIE of the given kind to the L{on_startup} dictionary so 166 that it is loaded automatically when SUE starts. The index determines when 167 in the list of all L{on_startup} UIEs of the given kind the named UIE is 168 loaded. 169 170 @param kind: Kind of UIE, one of L{ALL_KINDS} 171 @type kind: string 172 @param name: Name of the UIE, unique across all UIEs of the same kind 173 @type name: string 174 @param index: Load order of the UIE when multiple UIEs are to be loaded at 175 the given time. A value of None meaning after all other UIEs of the same 176 kind. 177 @type index: integer or None 178 ''' 179 uies = self.on_startup.setdefault(kind, []) 180 if name in uies: 181 uies.remove(name) 182 if index is None: 183 index = len(uies) 184 uies.insert(index, name)
185
186 - def removeFromStartup(self, kind, name):
187 ''' 188 Removes the name of a UIE of the given kind from the L{on_startup} 189 dictionary so that it is no longer loaded automatically on SUE startup. 190 191 @param kind: Kind of UIE, one of L{ALL_KINDS} 192 @type kind: string 193 @param name: Name of the UIE, unique across all UIEs of the same kind 194 @type name: string 195 @raise ValueError: When the UIE of the given name and kind is not associated 196 with this profile to be loaded on startup 197 ''' 198 uies = self.on_startup.get(kind, []) 199 try: 200 uies.remove(name) 201 except ValueError: 202 raise ValueError(_('%s not associated with %s') % (name, self.name))
203
204 - def addToAETier(self, kind, name, tier_name, index):
205 ''' 206 Adds the name of a UIE of the given kind to the L{on_tier} dictionary so 207 that it is loaded automatically when a L{AETier} with the given name is 208 created. The index determines when in the list of all L{on_tier} UIEs of the 209 given kind the named UIE is loaded. 210 211 @param kind: Kind of UIE, one of L{ALL_KINDS} 212 @type kind: string 213 @param name: Name of the UIE, unique across all UIEs of the same kind 214 @type name: string 215 @param tier_name: Name of the L{AETier} 216 @type tier_name: string 217 @param index: Load order of the UIE when multiple UIEs are to be loaded at 218 the given time. A value of None meaning after all other UIEs of the same 219 kind. 220 @type index: integer or None 221 ''' 222 kinds = self.on_tier.setdefault(tier_name, {}) 223 uies = kinds.setdefault(kind, []) 224 if name in uies: 225 uies.remove(name) 226 if index is None: 227 index = len(uies) 228 uies.insert(index, name)
229
230 - def removeFromAETier(self, kind, name, tier_name):
231 ''' 232 Removes the name of a UIE of the given kind from the L{on_tier} 233 dictionary so that it is no longer loaded automatically on when a L{AETier} 234 with the given name is created. 235 236 @param kind: Kind of UIE, one of L{ALL_KINDS} 237 @type kind: string 238 @param name: Name of the UIE, unique across all UIEs 239 @type name: string 240 @param tier_name: Name of the L{AETier} 241 @type tier_name: string 242 @raise ValueError: When the UIE of the given name and kind is not associated 243 with this profile to be loaded when the given L{AETier} starts 244 ''' 245 kinds = self.on_tier.get(tier_name, {}) 246 uies = kinds.get(kind, []) 247 try: 248 uies.remove(name) 249 except ValueError: 250 raise ValueError(_('%s not associated with %s' % (name, self.name))) 251 if not len(uies): 252 del kinds[kind] 253 if not len(kinds): 254 del self.on_tier[tier_name]
255
256 - def addToAnyAETier(self, kind, name, index):
257 ''' 258 Adds the name of a UIE of the given kind to the L{on_any_tier} dictionary 259 so that it is loaded automatically when any L{AETier} starts. The index 260 determines when in the list of all L{on_any_tier} UIEs of the given kind 261 the named UIE is loaded. 262 263 @param kind: Kind of UIE, one of L{ALL_KINDS} 264 @type kind: string 265 @param name: Name of the UIE, unique across all UIEs of the same kind 266 @type name: string 267 @param index: Load order of the UIE when multiple UIEs are to be loaded at 268 the given time. A value of None meaning after all other UIEs of the same 269 kind. 270 @type index: integer or None 271 ''' 272 uies = self.on_any_tier.setdefault(kind, []) 273 if name in uies: 274 uies.remove(name) 275 if index is None: 276 index = len(uies) 277 uies.insert(index, name)
278
279 - def removeFromAnyAETier(self, kind, name):
280 ''' 281 Removes the name of a UIE of the given kind from the L{on_any_tier} 282 dictionary so that it is no longer loaded automatically when any L{AETier} is 283 created. 284 285 @param kind: Kind of UIE, one of L{ALL_KINDS} 286 @type kind: string 287 @param name: Name of the UIE, unique across all UIEs of the same kind 288 @type name: string 289 @raise ValueError: When the UIE of the given name and kind is not associated 290 with this profile to be loaded when any L{AETier} is created 291 ''' 292 uies = self.on_any_tier.get(kind, []) 293 try: 294 uies.remove(name) 295 except ValueError: 296 raise ValueError(_('%s not associated with %s' % (name, self.name)))
297
298 - def removeFromAll(self, name, kind=None):
299 ''' 300 Removes all references to the UIE with the given name and kind from the 301 L{on_startup}, L{on_tier}, and L{on_any_tier} dictionaries. Ignores all 302 exceptions. 303 304 @param name: Name of the UIE, unique across all UIEs of the same kind 305 @type name: string 306 @param kind: Kind of UIE, one of L{ALL_KINDS}, or None to indicate the kind 307 is unknown and all kinds should be searched 308 @type kind: string 309 ''' 310 for kind in ALL_KINDS: 311 try: 312 self.removeFromStartup(kind, name) 313 except ValueError, e: 314 pass 315 try: 316 self.removeFromAnyAETier(kind, name) 317 except ValueError, e: 318 pass 319 for tier_name in self.on_tier.keys(): 320 try: 321 self.removeFromAETier(kind, name, tier_name) 322 except ValueError, e: 323 pass
324
325 - def listStartup(self, kind):
326 ''' 327 Gets a list of all UIE names of the given kind that are to be loaded at SUE 328 startup. 329 330 @param kind: Kind of UIE, one of L{ALL_KINDS} 331 @type kind: string 332 @return: Names of all UIEs to be loaded at SUE startup 333 @rtype: list of string 334 ''' 335 return self.on_startup.get(kind, [])
336
337 - def listAETierMap(self, kind):
338 ''' 339 Gets a list of all UIE names of the given kind paired with the names of the 340 L{AETier}s on which they will load. 341 342 @param kind: Kind of UIE, one of L{ALL_KINDS} 343 @type kind: string 344 @return: Lists of L{AEScript} names associated with L{AETier} names 345 @rtype: dictionary of string : list 346 ''' 347 d = {} 348 for name, kinds in self.on_tier.items(): 349 try: 350 d[name] = kinds[kind] 351 except KeyError: 352 pass 353 return d
354
355 - def listAETier(self, kind, tier=None):
356 ''' 357 Gets a list of all UIE names of the given kind that are to be loaded when 358 a L{AETier} with the given name is created. If the given L{AETier} name is 359 None, then list all UIEs to be loaded for particular L{AETier}s. 360 361 @param kind: Kind of UIE, one of L{ALL_KINDS} 362 @type kind: string 363 @param tier: Name of the L{AETier} 364 @type tier: string 365 @return: Names of all UIEs to be loaded at L{AETier} creation time 366 @rtype: list of string 367 ''' 368 # get all UIEs for all named tiers 369 if tier is None: 370 l = [] 371 for name, kinds in self.on_tier.items(): 372 try: 373 l.extend(kinds[kind]) 374 except KeyError: 375 pass 376 return l 377 else: 378 # get UIEs for the named tier only 379 kinds = self.on_tier.get(tier, {}) 380 return kinds.get(kind, [])
381
382 - def listAnyAETier(self, kind):
383 ''' 384 Gets a list of all UIE names of the given kind that are to be loaded when 385 any L{AETier} is created. 386 387 @param kind: Kind of UIE, one of L{ALL_KINDS} 388 @type kind: string 389 @return: Names of all UIEs to be loaded at L{AETier} creation time 390 @rtype: list of string 391 ''' 392 return self.on_any_tier.get(kind, [])
393
394 -def _ensureUIEValid(real_path):
395 ''' 396 Ensures the UIE module at the given path contains a class matching the 397 stated name. Ensure L{Metadata} is specified in the UIE to aid in 398 installation and association. Returns the UIE metadata. 399 400 @note: No longer checks if a class fits the definition of the given kind of 401 UIE. This validation is difficult when the module imports dependencies and 402 actually provides little useful information about whether or not the UIE is 403 implemented correctly. It can only tell us if it provides the proper 404 interface. We have gracefully degredation at runtime, so why bother "almost 405 ensuring" it's valid at install time? 406 407 @param real_path: Real path to the UIE module located on disk 408 @type real_path: string 409 @return: Metadata from the UIE 410 @rtype: L{Metadata} 411 @raise AttributeError: When __uie__ dictionary is missing from the UIE 412 @raise IOError: When the UIE module cannot be read 413 ''' 414 # compute the name of the UIE 415 name, ext = os.path.basename(real_path).split(os.extsep) 416 # read the UIE file as text 417 text = file(real_path).read() 418 # make sure it contains a class of the same name 419 if text.find('class %s' % name) < 0: 420 raise AttributeError( 421 _('Module %s must have class with name %s') % (name, name)) 422 # check for and get the metadata 423 return _parseUIEMetadata(name, real_path, text)
424
425 -def _parseUIEMetadata(name, path, text):
426 ''' 427 Gets the metadata from a UIE module without importing it. 428 429 @param name: Name of the installed UIE to fetch metadata from 430 @type name: string 431 @param text: Pre-fetched contents of the UIE 432 @type text: string 433 @return: Metadata from the UIE 434 @rtype: L{Metadata} 435 @raise AttributeError: When __uie__ dictionary is missing from the UIE 436 @raise KeyError: When the named UIE is not installed 437 @raise IOError: When the UIE module cannot be read 438 ''' 439 # find the __uie__ line 440 s = text.find('__uie__') 441 if s < 0 or (s != 0 and text[s-1] != '\n'): 442 raise AttributeError( 443 _('Module %s does not have __uie__ metadata') % name) 444 e = text.find('\n', s) 445 # turn the __uie__ var into a dictionary 446 exec(text[s:e]) 447 # and wrap it in a convenience class 448 return Metadata(name, path, __uie__)
449
450 -def _getUIEInstance(name):
451 ''' 452 Gets an instance of the UIE class having the same name as the module 453 indicated by the given name. 454 455 @param name: Name of the UIE, unique across all UIEs of the same kind 456 @type name: string 457 @return: UIE instance 458 @rtype: object 459 @raise KeyError: When the UIE is not installed globally or locally 460 @raise AttributeError: When the module is missing a class of the given name 461 @raise ImportError: When the UIE module cannot be imported 462 @raise IOError: When the UIE module cannot be read 463 ''' 464 mod = _getUIEModule(name) 465 # now get the class and instantiate it 466 cls = getattr(mod, name) 467 return cls()
468
469 -def _getUIEModule(name):
470 ''' 471 Loads the UIE module of the given name. Returns a reference to the module 472 for use by L{_getUIEInstance} by querying the L{InstallCache}. 473 474 @param name: Name of the UIE, unique across all UIEs 475 @type name: string 476 @return: Reference to the UIE module 477 @rtype: module 478 @raise KeyError: When the UIE is not installed globally or locally 479 @raise IOError: When the UIE module cannot be read 480 @raise ImportError: When the UIE module cannot be imported 481 ''' 482 # get the path without the filename of the UIE module 483 metadata = InstallCache.get(name) 484 path = os.path.dirname(metadata.path) 485 # get the metadata for the module to import 486 f, pathname, description = imp.find_module(name, [path]) 487 # add the foldering containing the UIE to the Python search path temporarily 488 sys.path.append(os.path.dirname(pathname)) 489 try: 490 # load the module from disk 491 m = imp.load_module(name, f, pathname, description) 492 finally: 493 # make sure we close the file 494 f.close() 495 # remove the last added search path 496 sys.path.pop() 497 # get the module 498 return m
499
500 -def install(real_path, local=True, overwrite=False):
501 ''' 502 Installs a new UIE module in either the local or global repository. 503 Installing in the global repository requires write permissions on the SUE 504 home directory at its install location. Checks if the UIE to be installed is 505 valid as determined by L{_ensureUIEValid} and retrieves its metadata. 506 507 @param real_path: Relative or absolute location of the UIE module on disk 508 @type real_path: string 509 @param local: Install this UIE for the current user only (True) or for all 510 users (False)? 511 @type local: boolean 512 @param overwrite: Ignore errors if a UIE is already installed and overwrite? 513 @type overwrite: boolean 514 @raise AttributeError: When __uie__ dictionary is missing from the UIE 515 @raise KeyError: When the named UIE is already installed 516 @raise IOError: When the UIE module cannot be read 517 ''' 518 # make sure the UIE is valid 519 metadata = _ensureUIEValid(real_path) 520 if local: 521 InstallCache.addLocal(metadata, overwrite) 522 else: 523 InstallCache.addGlobal(metadata, overwrite)
524
525 -def uninstall(name, local=True):
526 ''' 527 Removes a UIE module from the global or local repository. Removes all 528 references to a UIE uninstalled from the local repository from existing 529 profiles. 530 531 @param name: Name of the UIE, unique across all UIEs 532 @type name: string 533 @param local: Uninstall this UIE for the current user only (True) or for all 534 users (False)? 535 @type local: boolean 536 @raise AttributeError: When __uie__ dictionary is missing from the UIE 537 @raise KeyError: When the UIE is not already installed 538 @raise IOError: When the local on-disk cache cannot be updated 539 ''' 540 # remove the install cache entry 541 if local: 542 metadata = InstallCache.removeLocal(name) 543 else: 544 metadata = InstallCache.removeGlobal(name) 545 kind = metadata.kind 546 # remove all references in the available profiles 547 for pn in AccessEngine.AESettingsManager.listProfiles(): 548 AccessEngine.AESettingsManager.init(profile=pn) 549 uieset = AccessEngine.AESettingsManager.load(__name__) 550 uieset.removeFromAll(name, kind) 551 AccessEngine.AESettingsManager.save(__name__, uieset)
552
553 -def associate(name, profiles=None, tier=None, all_tiers=False, 554 index=None):
555 ''' 556 Associates an installed UIE of the given name with a profile so that it is 557 loaded automatically by SUE. 558 559 @param name: Name of the UIE, unique across all UIEs 560 @type name: string 561 @param profiles: Names of the profiles in which UIE of the given name will 562 be loaded automatically. Defaults to None meaning L{BUILTIN_PROFILES} 563 will be used. 564 @type profiles: list of string 565 @param tier: Name of the L{AETier} which will cause the loading of the 566 given UIE. Defaults to None. 567 @type tier: string 568 @param all_tiers: Load this UIE on all L{AETier}s? 569 @type all_tiers: boolean 570 @param index: Load order of the UIE when multiple UIEs are to be loaded at 571 the given time. Defaults to None meaning after all other associated UIEs 572 of the same kind. 573 @type index: integer 574 @return: Names of profiles with which the UIE was associated 575 @rtype: list of string 576 @raise KeyError: When the UIE is not already installed 577 ''' 578 # get the UIE metadata 579 metadata = InstallCache.get(name) 580 profiles = profiles or BUILTIN_PROFILES 581 all_tiers = all_tiers or metadata.all_tiers 582 tier = tier or metadata.tier 583 associated = [] 584 for profile in profiles: 585 # load the UIESet 586 AccessEngine.AESettingsManager.init(profile) 587 try: 588 uieset = AccessEngine.AESettingsManager.load(__name__) 589 except KeyError: 590 # create a new UIESet if it does not exist for this profile 591 uieset = UIESet(profile) 592 # decide when to load the give UIE 593 if metadata.kind in STARTUP_KINDS: 594 uieset.addToStartup(metadata.kind, name, index) 595 elif all_tiers: 596 uieset.addToAnyAETier(metadata.kind, name, index) 597 elif tier is not None: 598 uieset.addToAETier(metadata.kind, name, tier, index) 599 else: 600 # not an automatically loaded UIE 601 continue 602 associated.append(profile) 603 # write the profile back to disk 604 AccessEngine.AESettingsManager.save(__name__, uieset) 605 return associated
606
607 -def disassociate(name, profiles=None, tier=None, all_tiers=False):
608 ''' 609 Disassociates an installed UIE of the given name from a profile so 610 that it is no longer loaded automatically by SUE. 611 612 @param name: Name of the UIE, unique across all UIEs 613 @type name: string 614 @param profiles: Names of the profiles in which UIE of the given name will 615 no longer be loaded automatically. Defaults to None meaning 616 L{BUILTIN_PROFILES} will be used. 617 @type profiles: list of string 618 @param tier: Name of the L{AETier} which will no longer cause the loading 619 of the given UIE. Defaults to None. 620 @type tier: string 621 @param all_tiers: Disassociate this UIE from loading in all L{AETier}s? 622 @type all_tiers: boolean 623 @return: Names of profiles from which the UIE was disassociated 624 @rtype: list of string 625 ''' 626 try: 627 # get the UIE metadata 628 metadata = InstallCache.get(name) 629 all_tiers = all_tiers or metadata.all_tiers 630 tier = tier or metadata.tier 631 except KeyError: 632 # the UIE might be missing already 633 metadata = None 634 profiles = profiles or BUILTIN_PROFILES 635 disassociated = [] 636 for profile in profiles: 637 # load the UIESet 638 AccessEngine.AESettingsManager.init(profile) 639 try: 640 uieset = AccessEngine.AESettingsManager.load(__name__) 641 except KeyError: 642 # create a new UIESet if it does not exist for this profile 643 uieset = UIESet(profile) 644 disassociated.append(profile) 645 try: 646 # remove from all if the metadata is no longer available 647 if metadata is None: 648 uieset.removeFromAll(name) 649 # decide when not to load the given UIE 650 elif metadata.kind in STARTUP_KINDS: 651 uieset.removeFromStartup(metadata.kind, name) 652 elif all_tiers: 653 uieset.removeFromAnyAETier(metadata.kind, name) 654 elif tier is not None: 655 uieset.removeFromAETier(metadata.kind, name, tier) 656 except ValueError: 657 pass 658 # write the profile back to disk 659 AccessEngine.AESettingsManager.save(__name__, uieset) 660 return disassociated
661
662 -def listInstalled(kind=None):
663 ''' 664 Gets a list of all installed UIEs of the given kind, both local and global. 665 666 @param kind: Kind of UIE, one of L{ALL_KINDS} or None to indicate all kinds 667 @type kind: string 668 @return: Names of installed UIEs 669 @rtype: list of string 670 ''' 671 # gets lists of local and global installed UIEs 672 uies = InstallCache.list() 673 if kind is None: 674 # return all if no kind specified 675 return uies 676 # only get the kind indicated 677 return [metadata.name for metadata in 678 (InstallCache.get(name) for name in uies) 679 if (metadata.kind == kind and os.path.exists(metadata.path))]
680
681 -def hasAETierAssociated(kind, profile, tier):
682 ''' 683 Gets if at least one UIE of the given kind is associated with the given 684 L{AETier} in the profile. 685 686 @param kind: Kind of UIE, one of L{ALL_KINDS} 687 @type kind: string 688 @param profile: Name of the profile 689 @type profile: string 690 @param tier: Name of the L{AETier} potentially associated with UIEs 691 @type tier: string 692 @return: At least one L{AETier} specific UIE? 693 @rtype: boolean 694 @raise ValueError: When the named profile does not exist 695 ''' 696 # load the profile 697 AccessEngine.AESettingsManager.init(profile) 698 AccessEngine.AESettingsManager.ensureProfile() 699 try: 700 uieset = AccessEngine.AESettingsManager.load(__name__) 701 except KeyError: 702 # no information stored, so create a new UIESet 703 uieset = UIESet(profile) 704 AccessEngine.AESettingsManager.save(__name__, uieset) 705 return len(uieset.listAETier(kind, tier)) > 0
706
707 -def listAssociatedMap(kind, profile):
708 ''' 709 Gets a map associating L{AETier} names with UIEs of the given kind in this 710 profile. 711 712 @param kind: Kind of UIE, one of L{ALL_KINDS} 713 @type kind: string 714 @param profile: Name of the profile 715 @type profile: string 716 @return: Dictionary mapping L{AETier} names to lists of UIE names 717 @rtype: dictionary of string : list 718 @raise ValueError: When the named profile does not exist 719 ''' 720 # load the profile 721 AccessEngine.AESettingsManager.init(profile) 722 AccessEngine.AESettingsManager.ensureProfile() 723 try: 724 uieset = AccessEngine.AESettingsManager.load(__name__) 725 except KeyError: 726 # no information stored, so create a new UIESet 727 uieset = UIESet(profile) 728 AccessEngine.AESettingsManager.save(__name__, uieset) 729 return uieset.listAETierMap(kind)
730
731 -def listAssociatedAny(kind, profile):
732 ''' 733 Gets a list of UIEs of the given kind associated with any L{AETier} in this 734 profile. 735 736 @param kind: Kind of UIE, one of L{ALL_KINDS} 737 @type kind: string 738 @param profile: Name of the profile 739 @type profile: string 740 @return: List of UIEs that load on any L{AETier} 741 @rtype: list of string 742 @raise ValueError: When the named profile does not exist 743 ''' 744 # load the profile 745 AccessEngine.AESettingsManager.init(profile) 746 AccessEngine.AESettingsManager.ensureProfile() 747 try: 748 uieset = AccessEngine.AESettingsManager.load(__name__) 749 except KeyError: 750 # no information stored, so create a new UIESet 751 uieset = UIESet(profile) 752 AccessEngine.AESettingsManager.save(__name__, uieset) 753 return uieset.listAnyAETier(kind)
754
755 -def listAssociated(kind, profile, tier=None):
756 ''' 757 Gets a list of the names of all UIEs of the given kind associated with the 758 given profile. The tier may be specified for L{AEScript <AEScript.AEScript>}s. If it is, only UIEs 759 associated with that L{AETier} are returned. 760 761 @param kind: Kind of UIE, one of L{ALL_KINDS} 762 @type kind: string 763 @param profile: Name of the profile 764 @type profile: string 765 @param tier: Name of the L{AETier} associated with the UIEs. If None, fetches 766 UIE names for all L{AETier}s. 767 @type tier: string 768 @return: Names of the UIEs 769 @rtype: list of string or 2-tuple 770 @raise ValueError: When the named profile does not exist 771 ''' 772 # load the profile 773 AccessEngine.AESettingsManager.init(profile) 774 AccessEngine.AESettingsManager.ensureProfile() 775 try: 776 uieset = AccessEngine.AESettingsManager.load(__name__) 777 except KeyError: 778 # no information stored, so create a new UIESet 779 uieset = UIESet(profile) 780 AccessEngine.AESettingsManager.save(__name__, uieset) 781 # decide what list to check 782 if kind in STARTUP_KINDS: 783 # kind of UIE that loads on startup 784 return uieset.listStartup(kind) 785 else: 786 # Scripts that load on a particular AETier 787 return uieset.listAnyAETier(kind) + uieset.listAETier(kind, tier)
788
789 -def getPath(name):
790 ''' 791 Gets the absolute path where an installed UIE is located on disk. Does not 792 include the filename of the UIE. 793 794 @param name: Name of the UIE, unique across all UIEs 795 @type name: string 796 @return: Absolute parent directory of the UIE module on disk 797 @rtype: string 798 @raise KeyError: When a UIE with the given name is not installed 799 ''' 800 path = InstallCache.get(name).path 801 return os.path.dirname(path)
802
803 -def loadOne(name):
804 ''' 805 Gets the class representing the installed UIE of the given kind and name. 806 Checks if the UIE is installed. Fails to load the UIE if any of the 807 dependencies are missing. 808 809 @param name: Name of the UIE, unique across all UIEs of the same kind 810 @type name: string 811 @return: Instantiated UIE object or None if the object could not be loaded 812 @rtype: object 813 ''' 814 try: 815 return _getUIEInstance(name) 816 except SyntaxError, e: 817 # on any exception, pass on loading this UIE, but log the error 818 log.error('syntax error in %s: %s', name, str(e)) 819 return None 820 except Exception, e: 821 # on any exception, pass on loading this UIE, but log the error 822 info = traceback.extract_tb(sys.exc_info()[2]) 823 log.debug('cannot load %s (%d): %s', name, info[-1][1], str(e)) 824 return None
825
826 -def loadAssociated(kind, profile, tier=None):
827 ''' 828 Gets all UIE classes in the given profile to be loaded at the given time. 829 830 @param kind: Kind of UIE, one of L{ALL_KINDS} 831 @type kind: string 832 @param profile: Name of the profile 833 @type profile: string 834 @param tier: Name of the L{AETier} which causes the loading of the listed UIEs 835 @type tier: string 836 @return: All UIE instances to be loaded sorted in proper load order 837 @rtype: list of object 838 ''' 839 names = listAssociated(kind, profile, tier) 840 return [uie for uie in (loadOne(name) for name in names) if uie is not None]
841
842 -class InstallCache(object):
843 ''' 844 Manages an in-memory cache of installed UIEs with persistent storage of the 845 cache on disk. The in-memory cache is brought up-to-date each time data is 846 read on the cache. The on-disk cache is updated each time new data is written 847 to the cache. 848 849 @ivar l_timestamp: Timestamp of the local cache file on disk 850 @type l_timestamp: integer 851 @ivar g_timestamp: Timestamp of the global cache file on disk 852 @type g_timestamp: integer 853 @ivar local: In-memory cache of UIEs installed for the local user pairing UIE 854 names with their absolute install paths 855 @type local: dictionary 856 @ivar local: In-memory cache of UIEs installed globally to be shared across 857 all users of the system user pairing UIE names with their absolute install 858 paths 859 @type local: dictionary 860 '''
861 - def __init__(self):
862 ''' 863 Initialize timestamps and try to load UIEs from disk. 864 ''' 865 # initialize the time stamps 866 self.l_timestamp = 0 867 self.g_timestamp = 0 868 # initialize the local and global install dictionaries in memory 869 self.local = {} 870 self.globl = {}
871
872 - def init(self):
873 ''' 874 Try to read the local and global contents from disk. 875 ''' 876 self._refreshLocal() 877 self._refreshGlobal()
878
879 - def _write(self, path, data):
880 ''' 881 Persist the in-memory cache on disk. Include the current version and 882 revision information. 883 884 @param path: Path to the on-disk cache 885 @type path: string 886 @param data: Dictionary of name/value pairs 887 @type data: dictionary 888 @raise IOError: When the cache file cannot be written 889 ''' 890 try: 891 f = file(path, 'wb') 892 cPickle.dump((data, PROG_VERSION+PROG_DATE), f, protocol=-1) 893 except cPickle.PickleError: 894 f.close() 895 raise IOError
896
897 - def _read(self, path):
898 ''' 899 Read the on-disk cache into memory. Loads the version and revision 900 information persisted when the cache was written also. 901 902 @param path: Path to the on-disk cache 903 @type path: string 904 @return: Dictionary of name/value pairs and version/revision informatoin 905 @rtype: tuple of dictionary and string 906 @raise IOError: When the cache file cannot be read 907 ''' 908 try: 909 f = file(path, 'rb') 910 return cPickle.load(f) 911 except cPickle.PickleError: 912 f.close() 913 raise IOError
914
915 - def _mtime(self, path):
916 ''' 917 @return: Modified time on the file at path 918 @rtype: integer 919 ''' 920 try: 921 return os.stat(path).st_mtime 922 except OSError: 923 return 0
924
925 - def _refreshLocal(self):
926 ''' 927 Refreshes the in-memory local cache if the on-disk cache is newer. 928 929 @raise IOError: When the cache file cannot be read 930 ''' 931 mtime = self._mtime(LOCAL_PATH) 932 if mtime > self.l_timestamp: 933 self.local, version = self._read(LOCAL_PATH) 934 self.l_timestamp = mtime
935
936 - def _refreshGlobal(self):
937 ''' 938 Refreshes the in-memory global cache if the on-disk cache is newer. 939 940 @raise IOError: When the cache file cannot be read 941 ''' 942 mtime = self._mtime(GLOBAL_PATH) 943 if mtime > self.g_timestamp: 944 self.globl, version = self._read(GLOBAL_PATH) 945 self.g_timestamp = mtime
946 #self._updateDefaultProfiles() 947 948 #def _updateDefaultProfiles(self): 949 #''' 950 #Updates the L{BUILTIN_PROFILES} by associating newly installed global UIEs 951 #and removing uninstalled global UIEs. 952 953 #@note: Not currently used. May be supported in future versions. 954 #''' 955 #for profile in BUILTIN_PROFILES: 956 #sm = SettingsManager(profile) 957 ## load the UIESet for the profile 958 #try: 959 #uieset = sm.load(__name__) 960 ## get the UIEs already installed in the profile 961 #wl = uieset.getWhitelist() 962 ## and those that were once installed but now removed 963 #bl = uieset.getBlacklist() 964 #except KeyError: 965 #wl = bl = set() 966 ## finally, get the set of globally installed UIEs 967 #il = set(self.globl) 968 ## UIEs to associate: (il - wl) - bl 969 #for name in (il - wl) - bl: 970 #associate(name, [profile]) 971 ## UIEs to disassociate: (wl - il) 972 #for name in wl - il: 973 #disassociate(name, [profile]) 974
975 - def list(self):
976 ''' 977 Lists all installed UIEs, regardless of local or global status. 978 979 @return: List of UIE names 980 @rtype: list 981 ''' 982 self._refreshLocal() 983 self._refreshGlobal() 984 return self.local.keys() + self.globl.keys()
985
986 - def addLocal(self, metadata, overwrite):
987 ''' 988 Adds a new UIE to the list of UIEs installed locally. 989 990 @param metadata: Metadata describing the UIE 991 @type metadata: L{Metadata} 992 @param overwrite: Ignore errors if a UIE is already installed and 993 overwrite? 994 @type overwrite: boolean 995 @raise KeyError: When a UIE of the given name is already installed locally 996 or globally 997 @raise IOError: When the local on-disk cache cannot be updated 998 ''' 999 name = metadata.name 1000 if not overwrite: 1001 if self.local.has_key(name): 1002 raise KeyError(_('%s is already installed locally') % name) 1003 if self.globl.has_key(name): 1004 raise KeyError(_('%s is already installed globally') % name) 1005 self.local[name] = metadata 1006 self._write(LOCAL_PATH, self.local)
1007
1008 - def addGlobal(self, metadata, overwrite):
1009 ''' 1010 Adds a new UIE to the list of UIEs installed globally. 1011 1012 @param metadata: Metadata describing the UIE 1013 @type metadata: L{Metadata} 1014 @param overwrite: Ignore errors if a UIE is already installed and 1015 overwrite? 1016 @type overwrite: boolean 1017 @raise KeyError: When a UIE of the given name is already installed globally 1018 @raise IOError: When the global on-disk cache cannot be updated 1019 ''' 1020 name = metadata.name 1021 if not overwrite: 1022 if self.globl.has_key(name): 1023 raise KeyError(_('%s is already installed globally') % name) 1024 self.globl[name] = metadata 1025 self._write(GLOBAL_PATH, self.globl)
1026
1027 - def removeLocal(self, name):
1028 ''' 1029 Uninstalls an existing UIE from the local cache. 1030 1031 @param name: Name of the UIE 1032 @type name: string 1033 @return: Metadata for the removed UIE 1034 @rtype: L{Metadata} 1035 @raise KeyError: When the UIE is not already installed 1036 @raise IOError: When the local on-disk cache cannot be updated 1037 ''' 1038 try: 1039 metadata = self.local[name] 1040 except KeyError: 1041 raise KeyError(_('%s is not installed locally') % name) 1042 del self.local[name] 1043 self._write(LOCAL_PATH, self.local) 1044 return metadata
1045
1046 - def removeGlobal(self, name):
1047 ''' 1048 Uninstalls an existing UIE from the global cache. 1049 1050 @param name: Name of the UIE 1051 @type name: string 1052 @return: Metadata for the removed UIE 1053 @rtype: L{Metadata} 1054 @raise KeyError: When the UIE is not already installed 1055 @raise IOError: When the global on-disk cache cannot be updated 1056 ''' 1057 try: 1058 metadata = self.globl[name] 1059 except KeyError: 1060 raise KeyError(_('%s is not installed globally') % name) 1061 del self.globl[name] 1062 self._write(GLOBAL_PATH, self.globl) 1063 return metadata
1064
1065 - def get(self, name):
1066 ''' 1067 Gets the absolute path to an installed UIE whether it is local or global. 1068 1069 @param name: Name of the UIE 1070 @type name: string 1071 @param metadata: Metadata describing the UIE 1072 @type metadata: L{Metadata} 1073 @raise KeyError: When the UIE isn't installed locally or globally 1074 ''' 1075 try: 1076 self._refreshLocal() 1077 return self.local[name] 1078 except KeyError: 1079 pass 1080 self._refreshGlobal() 1081 try: 1082 return self.globl[name] 1083 except KeyError: 1084 raise KeyError(_('%s not installed') % name)
1085 1086 # make the install cache a singleton 1087 InstallCache = InstallCache() 1088 InstallCache.init() 1089