Module UIRegistrar
[hide private]
[frames] | no frames]

Source Code for Module UIRegistrar

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