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
73 log = logging.getLogger('AERegistrar')
74
75
76
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
81
82 SCRIPT = 'script'
83 DEVICE = 'device'
84 CHOOSER = 'chooser'
85 MONITOR = 'monitor'
86
87
88 ALL_KINDS = [DEVICE, CHOOSER, MONITOR, SCRIPT]
89 STARTUP_KINDS = [DEVICE, MONITOR]
90
133
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 '''
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
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
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
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
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
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
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
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
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
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
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
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
379 kinds = self.on_tier.get(tier, {})
380 return kinds.get(kind, [])
381
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
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
415 name, ext = os.path.basename(real_path).split(os.extsep)
416
417 text = file(real_path).read()
418
419 if text.find('class %s' % name) < 0:
420 raise AttributeError(
421 _('Module %s must have class with name %s') % (name, name))
422
423 return _parseUIEMetadata(name, real_path, text)
424
449
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
466 cls = getattr(mod, name)
467 return cls()
468
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
483 metadata = InstallCache.get(name)
484 path = os.path.dirname(metadata.path)
485
486 f, pathname, description = imp.find_module(name, [path])
487
488 sys.path.append(os.path.dirname(pathname))
489 try:
490
491 m = imp.load_module(name, f, pathname, description)
492 finally:
493
494 f.close()
495
496 sys.path.pop()
497
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
519 metadata = _ensureUIEValid(real_path)
520 if local:
521 InstallCache.addLocal(metadata, overwrite)
522 else:
523 InstallCache.addGlobal(metadata, overwrite)
524
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
541 if local:
542 metadata = InstallCache.removeLocal(name)
543 else:
544 metadata = InstallCache.removeGlobal(name)
545 kind = metadata.kind
546
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
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
586 AccessEngine.AESettingsManager.init(profile)
587 try:
588 uieset = AccessEngine.AESettingsManager.load(__name__)
589 except KeyError:
590
591 uieset = UIESet(profile)
592
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
601 continue
602 associated.append(profile)
603
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
628 metadata = InstallCache.get(name)
629 all_tiers = all_tiers or metadata.all_tiers
630 tier = tier or metadata.tier
631 except KeyError:
632
633 metadata = None
634 profiles = profiles or BUILTIN_PROFILES
635 disassociated = []
636 for profile in profiles:
637
638 AccessEngine.AESettingsManager.init(profile)
639 try:
640 uieset = AccessEngine.AESettingsManager.load(__name__)
641 except KeyError:
642
643 uieset = UIESet(profile)
644 disassociated.append(profile)
645 try:
646
647 if metadata is None:
648 uieset.removeFromAll(name)
649
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
659 AccessEngine.AESettingsManager.save(__name__, uieset)
660 return disassociated
661
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
672 uies = InstallCache.list()
673 if kind is None:
674
675 return uies
676
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
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
697 AccessEngine.AESettingsManager.init(profile)
698 AccessEngine.AESettingsManager.ensureProfile()
699 try:
700 uieset = AccessEngine.AESettingsManager.load(__name__)
701 except KeyError:
702
703 uieset = UIESet(profile)
704 AccessEngine.AESettingsManager.save(__name__, uieset)
705 return len(uieset.listAETier(kind, tier)) > 0
706
730
754
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
773 AccessEngine.AESettingsManager.init(profile)
774 AccessEngine.AESettingsManager.ensureProfile()
775 try:
776 uieset = AccessEngine.AESettingsManager.load(__name__)
777 except KeyError:
778
779 uieset = UIESet(profile)
780 AccessEngine.AESettingsManager.save(__name__, uieset)
781
782 if kind in STARTUP_KINDS:
783
784 return uieset.listStartup(kind)
785 else:
786
787 return uieset.listAnyAETier(kind) + uieset.listAETier(kind, tier)
788
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
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
818 log.error('syntax error in %s: %s', name, str(e))
819 return None
820 except Exception, e:
821
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
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
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 '''
862 '''
863 Initialize timestamps and try to load UIEs from disk.
864 '''
865
866 self.l_timestamp = 0
867 self.g_timestamp = 0
868
869 self.local = {}
870 self.globl = {}
871
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
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
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
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
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
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
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
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
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
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
1087 InstallCache = InstallCache()
1088 InstallCache.init()
1089