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

Source Code for Module AccessEngine.AETier

   1  ''' 
   2  Defines a class for managing the L{AEScript <AEScript.AEScript>}s loaded for a single instance of an 
   3  application. 
   4   
   5  @author: Peter Parente 
   6  @author: Pete Brunet 
   7  @author: Larry Weiss 
   8  @author: Scott Haeger 
   9  @organization: IBM Corporation 
  10  @copyright: Copyright (c) 2005, 2007 IBM Corporation 
  11  @license: The BSD License 
  12   
  13  @author: Frank Zenker 
  14  @author: Ramona Bunk 
  15  @author: Nicole Anacker 
  16  @organization: IT Science Center Ruegen gGmbH, Germany 
  17  @copyright: Copyright (c) 2007, 2008 ITSC Ruegen 
  18  @license: The BSD License 
  19   
  20  All rights reserved. This program and the accompanying materials are made  
  21  available under the terms of the BSD license which accompanies 
  22  this distribution, and is available at 
  23  U{http://www.opensource.org/licenses/bsd-license.php} 
  24  ''' 
  25   
  26  import logging, weakref, traceback, sys, os 
  27  import types 
  28  import AccessEngine 
  29  from AccessEngine import AEState, AEInput 
  30  import AEScript, AEConstants, AccessEngineAPI 
  31  #from AccessEngine.AEPor import AEPor 
  32   
  33  log = logging.getLogger('AETier') 
  34   
35 -class AETier(object):
36 ''' 37 Manages the L{AEScript <AEScript.AEScript>}s for a single instance of a running program (a process). 38 Supports the addition and removal of L{AEScript <AEScript.AEScript>}s from its stack. Executes 39 script tasks registered in each L{AEScript <AEScript.AEScript>} in response to L{AEEvent}s. Provides 40 methods to assist L{AEScript <AEScript.AEScript>}s in finding registered tasks throughout the 41 L{AETier}. 42 43 @ivar aid: ID uniquely identifying this L{AETier} from all other L{AETier}s 44 @type aid: opaque 45 @ivar name: Name of this L{AETier} 46 @type name: string 47 @ivar pointer_por: Pointer of regard for the user focus 48 @type pointer_por: L{AEPor} 49 @ivar focus_por: Point of regard for the application focus 50 @type focus_por: L{AEPor} 51 @ivar virtual_por: review pointer 52 @type virtual_por: L{AEPor} 53 @ivar last_key: Stores the key code, key sym, and modifiers for the last key 54 pressed 55 @type last_key: 3-tuple of integer, string, integer 56 57 @ivar scripts: List of L{AEScript <AEScript.AEScript>}s treated as a stack (last in, first executed) 58 @type scripts: list [L{AEScript <AEScript.AEScript>}] 59 60 @ivar wanted_events: Lookup table for what L{AEEvent}s are desired by any 61 task in any L{AEScript <AEScript.AEScript>} in this L{AETier}. Used to optimize event dispatch and 62 processing. Unwanted events are ignored. :: 63 Dictionary { 64 key: <class 'AEEvent-Class-Type'>, 65 value: integer 66 } 67 68 @type wanted_events: dictionary 69 @ivar script_refs: References to L{AEScript <AEScript.AEScript>}s keyed by task names, commands, 70 identifiers, etc. Many to one mapping. See L{addTaskRef} and 71 L{removeTaskRef}.:: 72 WeakValueDictionary { 73 key: ('Task Identity', script.getClassName()), value: Script; 74 key: id(Chooser), value: Script; 75 key: L{AEInput.GestureList}, value: Script 76 } 77 78 @type script_refs: weakref.WeakValueDictionary 79 @ivar chain_refs: References to L{AEScript <AEScript.AEScript>}s keyed by task names. Many to many 80 mapping. See L{addChainRef} and L{removeChainRef}. :: 81 Dictionary { 82 key: ('Task Identity', script.getClassName()), 83 value: { key: Script, value: None} 84 } 85 86 @type chain_refs: dictionary 87 @ivar chain_stack: Stack of task names currently executing in one or more 88 chains before, after, or around one or more anchors :: 89 Tuple ('Task Identity', script.getClassName()) 90 91 @type chain_stack: list of tuple 92 @ivar temp_data: Arbitrary name/value pairs stored by tasks executing in 93 this L{AETier} in response to a single event. The data is persistent until 94 the event has been handled by all tasks at which time it is cleared. :: 95 Dictionary { 96 key: keyName immutable, 97 value: object 98 } 99 100 @type temp_data: dictionary 101 @ivar task_history: Stack of task and L{AEScript <AEScript.AEScript>} names having already 102 executed in response to an L{AEEvent}, including all chained tasks and 103 all tasks executed from within other tasks :: 104 Tuple ('Task Identity', script.getClassName()) 105 106 @type task_history: list of tuple 107 108 @ivar show: Call show* methods on L{AETierManager} to log events? Set by the 109 L{AETierManager} based on whether monitors are loaded or not. Optimization. 110 @type show: boolean 111 @ivar event_layer: 112 @type event_layer: integer 113 '''
114 - def __init__(self, name, aid, por):
115 ''' 116 Stores a reference to the L{AETierManager} that created this L{AETier}. Creates 117 an empty list of L{AEScript <AEScript.AEScript>}s. Initializes the stored focus L{AEPor} to None and 118 the pointer L{AEPor} to the application. Creates the weak dictionary for 119 L{AEScript <AEScript.AEScript>} tasks and stores other information about the L{AETier}. 120 121 @param name: Name of this L{AETier} 122 @type name: string 123 @param aid: ID uniquely identifying this L{AETier} from all other L{AETier}s 124 @type aid: opaque 125 @param por: First point of regard to the application represented by 126 this L{AETier}. Typically, the top most accessible. 127 @type por: L{AEPor} 128 ''' 129 self.name = name 130 self.aid = aid 131 self.pointer_por = por 132 self.focus_por = None 133 self.virtual_por = None 134 self.last_key = (None, None, None) 135 self.scripts = [] 136 self.wanted_events = {} 137 self.event_layer = None 138 self.script_refs = weakref.WeakValueDictionary() 139 self.chain_refs = {} 140 # fields to be cleared after each event 141 self.clearState()
142
143 - def close(self):
144 ''' 145 Clears all L{AEScript <AEScript.AEScript>}s and closes them. 146 ''' 147 self.clearScripts()
148
149 - def clearState(self):
150 ''' 151 Clears all stored L{temp_data}, the L{chain_stack}, L{task_history}, and 152 the L{show} flag. 153 ''' 154 self.temp_data = {} 155 self.task_history = [] 156 self.chain_stack = [] 157 self.show = False
158
159 - def getName(self):
160 ''' 161 @return: Name of this L{AETier} (i.e. name of the app with which it is 162 associated) 163 @rtype: string 164 ''' 165 return self.name
166
167 - def getIdentity(self):
168 ''' 169 @return: Unique identifier for this L{AETier} 170 @rtype: opaque 171 ''' 172 return self.aid
173
174 - def getPointer(self):
175 ''' 176 Gets the user L{AEPor} for this L{AETier}. 177 178 @return: Point of regard of user attention 179 @rtype: L{AEPor} 180 ''' 181 return self.pointer_por
182
183 - def setPointer(self, por):
184 ''' 185 Sets the user L{AEPor} for this L{AETier}. 186 187 @param por: Point of regard of user attention 188 @type por: L{AEPor} 189 ''' 190 self.pointer_por = por
191
192 - def getFocus(self):
193 ''' 194 Gets the application focus L{AEPor} for this L{AETier}. 195 196 @return: Point of regard of application focus 197 @rtype: L{AEPor} 198 ''' 199 return self.focus_por
200
201 - def getLastKey(self):
202 ''' 203 Gets the last key code, key sym, and key modifiers. Useful for dealing with 204 applications that do a poor job of synthesizing caret and text events. 205 206 @return: The key code, key sym, and modifiers for the last key pressed 207 @rtype: 3-tuple of integer, string, integer 208 ''' 209 return self.last_key
210
211 - def getTempVal(self, name):
212 ''' 213 Gets data from L{temp_data}. 214 215 @param name: Name of the stored data 216 @type name: immutable 217 @return: Value stored under the given name 218 @rtype: object 219 @raise KeyError: When no data is stored under the given name 220 ''' 221 return self.temp_data[name]
222
223 - def setTempVal(self, name, value):
224 ''' 225 Stores data for the duration of the execution of an L{AEEvent}. 226 227 @param name: Name to associate with the value 228 @type name: immutable 229 @param value: Any value to store 230 @type value: object 231 ''' 232 self.temp_data[name] = value
233
234 - def setShow(self, show):
235 ''' 236 Sets whether show methods should be called on the L{AETierManager} when 237 handling events or not based on the presence of L{AEMonitor}s in the 238 manager. 239 240 @param show: Call show methods to notify monitors? 241 @type show: boolean 242 ''' 243 self.show = show
244
245 - def getEventLayer(self):
246 ''' 247 ''' 248 # TODO: is the var event_layer needed? 249 return self.event_layer
250
251 - def pushScript(self, *scripts):
252 ''' 253 Adds one or more L{AEScript <AEScript.AEScript>}s to the top of the stack. If more than one L{AEScript <AEScript.AEScript>} 254 is specified, the last specified L{AEScript <AEScript.AEScript>} will be at the top of the stack 255 when this method completes. That is, the behavior of pushing more than one 256 L{AEScript <AEScript.AEScript>} at a time is the same as if each L{AEScript <AEScript.AEScript>} were pushed individually. 257 258 @param scripts: L{AEScript <AEScript.AEScript>}s to add 259 @type scripts: list of L{AEScript <AEScript.AEScript>} 260 ''' 261 self.insertScript(0, *scripts)
262
263 - def insertScript(self, index, *scripts):
264 ''' 265 Adds one L{AEScript <AEScript.AEScript>} to the stack at the insertion index. Negative indices are 266 valid per Python list convention. 267 268 @param index: Index at which the L{AEScript <AEScript.AEScript>} should be inserted in the stack 269 @type index: integer 270 @param scripts: L{AEScript <AEScript.AEScript>}s to add 271 @type scripts: list of L{AEScript <AEScript.AEScript>} 272 ''' 273 for script in scripts: 274 # put the Script somewhere in the stack 275 self.scripts.insert(index, script) 276 # initialize the Script base classes with important, persistent references 277 script.preInit(self) 278 self.event_layer = AEConstants.LAYER_FOCUS 279 try: 280 # now let Script specific code run 281 script.init() 282 except Exception, e: 283 # pop the script from the stack if it fails to initialize 284 self.scripts.pop(index) 285 # let it finalize 286 try: 287 script.close() 288 except Exception: 289 # ignore errors while finalizing 290 pass 291 script.postClose() 292 # warn about the error 293 info = traceback.extract_tb(sys.exc_info()[2]) 294 log.debug('cannot initialize %s (%s, %d): %s', script.getClassName(), 295 os.path.basename(info[-1][0]), 296 info[-1][1], str(e))
297
298 - def popScript(self, *indices):
299 ''' 300 Removes one or more L{AEScript <AEScript.AEScript>}s from the stack given 301 their indices. Negative indices are valid per Python list convention. Calls 302 L{AEScript.AEScript.close} on each to ensure they have a chance to persist 303 state. 304 305 @param indices: Indices of L{AEScript <AEScript.AEScript>}s to remove 306 @type indices: list of integer 307 @raise IndexError: When the stack is empty 308 @raise ValueError: When a specific index is out of the bounds of the stack 309 ''' 310 indices = list(indices) 311 # have to pop in reverse order to avoid corrupting original indices 312 indices.sort(reverse=True) 313 scripts = map(self.scripts.pop, indices) 314 for script in scripts: 315 try: 316 script.close() 317 except Exception: 318 # ignore errors during close 319 pass 320 script.postClose()
321
322 - def clearScripts(self):
323 ''' 324 Removes all L{AEScript <AEScript.AEScript>}s from the stack. Calls 325 L{AEScript.AEScript.close} on each to ensure they have a chance to persist 326 state. 327 ''' 328 for script in self.scripts: 329 try: 330 script.close() 331 except Exception: 332 # ignore errors during close 333 pass 334 script.postClose() 335 self.scripts = [] # TODO: loescht nicht alle Referenzen in script_refs 336 self.script_refs.clear() # TODO:
337
338 - def getScripts(self):
339 ''' 340 Gets all L{AEScript <AEScript.AEScript>}s pushed onto this L{AETier} in execution order. 341 342 @note: This method does not return a copy of the stack. Modifications will 343 affect this L{AETier}! 344 @return: All L{AEScript <AEScript.AEScript>}s pushed onto this L{AETier} 345 @rtype: list of L{AEScript <AEScript.AEScript>} 346 ''' 347 return self.scripts
348
349 - def setEventInterest(self, kind, wants):
350 ''' 351 Updates the L{wanted_events} dictionary to indicate that a task in a 352 L{AEScript <AEScript.AEScript>} in this L{AETier} has been registered for a particular kind of 353 L{AEEvent}. This information is used for optimization purposes such that no 354 processing will occur on the event unless at least one task is 355 registered that will use it. 356 357 @param kind: Kind of L{AEEvent} of interest to a task 358 @type kind: L{AEEvent} class 359 @param wants: Does a L{AEScript <AEScript.AEScript>} want an event (i.e. a task is registering 360 for it or no longer want an event (i.e. a task is unregistering for 361 it)? 362 @type wants: boolean 363 ''' 364 count = self.wanted_events.setdefault(kind, 0) 365 if wants: 366 count += 1 367 else: 368 count -= 1 369 if count <= 0: 370 del self.wanted_events[kind] 371 else: 372 self.wanted_events[kind] = count 373 # inform the AETierManager of the new event interest 374 AccessEngine.AETierManager.setEventInterest(kind, wants)
375
376 - def addTaskRef(self, key, script):
377 ''' 378 Adds a key that can be used later to quickly look up a reference to a 379 L{AEScript <AEScript.AEScript>} in this L{AETier}. Optimization for the 380 L{getChooserTask}, L{getCommandTask}, and L{getRegisteredTasks} methods. 381 382 @param key: Key under which to hash the L{AEScript <AEScript.AEScript>} 383 @type key: immutable 384 @param script: Script to store in the hash 385 @type script: L{AEScript <AEScript.AEScript>} 386 ''' 387 self.script_refs[key] = script
388
389 - def removeTaskRef(self, task_key):
390 ''' 391 Removes a key used to quickly look up a reference to a L{AEScript <AEScript.AEScript>} in this 392 L{AETier}. Keys are cleaned up automatically when L{AEScript <AEScript.AEScript>}s are destroyed. 393 However, this method may be used to manually remove L{AEScript <AEScript.AEScript>}s at any time. 394 395 @param task_key: Key under which to hash the L{AEScript <AEScript.AEScript>} 396 @type task_key: immutable 397 ''' 398 try: 399 del self.script_refs[task_key] 400 except KeyError: 401 pass
402
403 - def getRegisteredTasks(self):
404 ''' 405 Gets the keys of all script tasks currently registered in this L{AETier}. 406 407 @return: List of all registered script task keys 408 @rtype: list of tuple ('task name', 'script name') 409 ''' 410 names = [] 411 for script in self.scripts: 412 names.extend(script.getTaskKeys()) 413 return names
414
415 - def findRegisteredTasks(self, task_name):
416 ''' 417 Finds all tasks with the given name over all scripts in this tier. None is 418 returned if not found. 419 420 @param task_name: the name of the wanted tasks. 421 @type task_name: string 422 @return: list of the found keys or None. 423 @rtype: list of tuples ('Task Identitiy', script.getClassName()) 424 ''' 425 keys = self.script_refs.keys() 426 found = [] 427 for key in keys: 428 if type(key) is types.TupleType and key[0] == task_name: 429 found.append(key) 430 if len(found) > 0: 431 return found 432 else: 433 return None
434
435 - def getRegisteredTaskFunctions(self, task_key):
436 ''' 437 Gets a task with the given key. None is returned if not found. 438 439 @param task_key: Key to the task to find 440 @type task_key: tuple of ('task name', 'script name') 441 @return: the execute and update functions to the task 442 @rtype: list of functions [exec_function, update_function] 443 ''' 444 try: 445 script = self.script_refs[task_key] 446 except KeyError: 447 return None 448 return script.getRegisteredTask(task_key)
449
450 - def isTaskRegistered(self, task_key):
451 ''' 452 Looks whether a task is already registered. 453 454 @param task_key: Key to the task to find 455 @type task_key: tuple of ('task name', 'script name') 456 @return: True when the task is already registered, otherwise False 457 @rtype: boolean 458 ''' 459 try: 460 script = self.script_refs[task_key] 461 return True 462 except KeyError: 463 return False
464
465 - def getAnchor(self):
466 ''' 467 Gets the identity of the task to which the current task is chained 468 and its corresponding L{AEScript <AEScript.AEScript>}. If there is no anchor for the executing 469 task, this method returns None. 470 471 @return: task identity and L{AEScript <AEScript.AEScript>} class name or None 472 @rtype: 2-tuple of string 473 ''' 474 try: 475 return self.chain_stack[-1] 476 except IndexError: 477 return None
478
479 - def addChainRef(self, target, script):
480 ''' 481 Adds a weak reference to a L{AEScript <AEScript.AEScript>} as one to check for tasks which are 482 part of a the chain for the target. Optimization for the L{manageChain} 483 method. 484 485 @param target: Name of the task to link to 486 @type target: string or tuple of ('task name', 'script name') 487 @param script: Script have at least one link in the chain for the target 488 @type script: L{AEScript <AEScript.AEScript>} 489 ''' 490 # build the right key: ('task name', 'script name') 491 if type(target) is not types.TupleType: 492 target = (target, script.getClassName()) 493 chain = self.chain_refs.setdefault(target, weakref.WeakKeyDictionary()) 494 chain[script] = None
495
496 - def removeChainRef(self, target, script):
497 ''' 498 Removes a weak reference to a L{AEScript <AEScript.AEScript>} as one to no longer check for 499 tasks which are part of the chain for the target. Optimization for the 500 L{manageChain} method. 501 502 @param target: Name of the task to unlink from 503 @type target: string or tuple of ('task name', 'script name') 504 @param script: Script have at least one link in the chain for the target 505 @type script: L{AEScript <AEScript.AEScript>} 506 ''' 507 # build the right key: ('task name', 'script name') 508 if type(target) is not types.TupleType: 509 target = (target, script.getClassName()) 510 try: 511 chain = self.chain_refs[target] 512 del chain[script] 513 if len(chain) == 0: 514 del self.chain_refs[target] 515 except (KeyError, ValueError): 516 pass
517
518 - def wantsEvent(self, event):
519 ''' 520 Gets if this L{AETier} wants a particular kind of L{AEEvent} given that one of 521 its L{AEScript <AEScript.AEScript>}s has a task that wants to handle it. 522 523 @param event: Event to be tested 524 @type event: L{AEEvent} 525 @rtype: boolean 526 ''' 527 return self.wanted_events.has_key(type(event))
528
529 - def getChooserTask(self, task_key):
530 ''' 531 Gets a task registered under a particular key set to execute in response 532 to events. None is returned if not found. 533 534 @param task_key: Unique ID for locating a registered task 535 @type task_key: integer 536 @return: task functions to execute in response to the event or None 537 @rtype: function 538 ''' 539 try: 540 script = self.script_refs[task_key] 541 except KeyError: 542 return None 543 return script.getChooserTask(task_key)
544
545 - def getCommandTask(self, gesture_list):
546 ''' 547 Gets a script task registered to execute in response to the L{AEInput.Gesture}. 548 None is returned if not found. 549 550 @param gesture_list: Gestures and device expressed as a list of virtual key 551 codes 552 @type gesture_list: L{AEInput.Gesture} 553 @return: Name of the task to execute in response to the input gesture 554 or None if no task registered to execute 555 @rtype: tuple of ('task name', 'script name') 556 ''' 557 try: 558 script = self.script_refs[gesture_list] 559 except KeyError: 560 return None 561 return script.getCommandTask(gesture_list)
562
563 - def getEventTasks(self, event_type, task_layer):
564 ''' 565 Gets all tasks registered to handle the given type of event on the 566 given layer by iterating through the registered {Script}s. The tasks are 567 returned in the order they will be executed both within and across 568 L{AEScript <AEScript.AEScript>}s in this L{AETier}. 569 570 @param event_type: Desired type of L{AEEvent} 571 @type event_type: L{AEEvent} class 572 @param task_layer: Layer on which the desired tasks are registered 573 @type task_layer: integer 574 @return: List of all tasks registered to handle the given type of event 575 on the given layer 576 @rtype: list of tuple ('task name', 'script name') 577 ''' 578 tasks = [] 579 map(tasks.extend, [s.getEventTasks(event_type, task_layer) for s in 580 self.scripts]) 581 return tasks
582
583 - def getChainedTasks(self, target_key):
584 ''' 585 Gets all tasks linked to the target by iterating through L{AEScript <AEScript.AEScript>} 586 references established by L{addChainRef}. The tasks are returned in the 587 order in which they were registered within a L{AEScript <AEScript.AEScript>}. Ordering is undefined 588 across L{AEScript <AEScript.AEScript>}s within a chain segment. 589 590 @param target_key: Name and script of the task to link to 591 @type target_key: tuple of string ('task name', 'script name') 592 @return: task keys for before, around, and after segments 593 @rtype: 3-tuple of list, task key, list 594 @raise KeyError: When the target has no chained tasks 595 ''' 596 befores = [] 597 afters = [] 598 scripts = self.chain_refs[target_key].keys() 599 arounds = [s.mergeChain(target_key, befores, afters) for s in scripts] 600 return befores, arounds[0], afters
601
602 - def getHistory(self):
603 ''' 604 Gets the entire linear history of tasks that have executed and the 605 L{AEScript <AEScript.AEScript>}s they are in. 606 607 @return: List of task identities and L{AEScript <AEScript.AEScript>} class names 608 @rtype: list of 2-tuple of string 609 ''' 610 return self.task_history
611
612 - def getTaskHistory(self):
613 ''' 614 Gets the identities of all tasks that have executed in response to the 615 event currently being processed. This includes all chained tasks and all 616 tasks executed using L{AEScript.doTask <AEScript.AEScript.doTask>} flattened 617 into a linear history. 618 619 If any task that executed did not have an identity, None is substituted 620 in its place. 621 622 The history is ordered from the first task to respond to an event to 623 the most recent task to respond, including the current task. 624 625 @return: Identities of the tasks 626 @rtype: list of string 627 ''' 628 return [names[0] for names in self.task_history]
629
630 - def getScriptHistory(self):
631 ''' 632 Gets the class names of all L{AEScript <AEScript.AEScript>}s that have executed in response to the 633 event currently being processed. The contents of the list returned by this 634 method have a one-to-one pairing with the list contents from 635 L{getTaskHistory}. 636 637 The history is ordered from the first L{AEScript <AEScript.AEScript>} to respond to an event to 638 the most recent L{AEScript <AEScript.AEScript>} to respond, including the current L{AEScript <AEScript.AEScript>}. 639 640 @return: Class names of the L{AEScript <AEScript.AEScript>}s 641 @rtype: list of string 642 ''' 643 return [names[1] for names in self.task_history]
644
645 - def _executeTask(self, por, layer, task_key, task_params, propagate, 646 script = None):
647 ''' 648 Executes the given task in response to the given L{AEEvent}. 649 650 @param por: Point of regard where the event occurred 651 @type por: L{AEPor} 652 @param layer: Layer on which the L{AEEvent} occurred, one of 653 L{AEConstants.Event.LAYER_FOCUS}, L{AEConstants.Event.LAYER_TIER}, 654 L{AEConstants.Event.LAYER_BACKGROUND} 655 @type layer: integer 656 @param task_key: A task registered to handle the given event 657 @type task_key: tuple of string ('task name', 'script name') 658 @param task_params: Keyword arguments to be provided to the task function as 659 parameters 660 @type task_params: dictionary 661 @param propagate: Should this event be propagated to the tasks execute 662 function or should we call the tasks update function instead for 663 housekeeping? 664 @type propagate: boolean 665 @param script: The L{AEScript <AEScript.AEScript>} containing the task 666 @type script: L{AEScript <AEScript.AEScript>} 667 668 @return: Should the next registered task be executed or only updated? 669 @rtype: boolean 670 ''' 671 reset_pointer_por = True 672 673 # if params has no 'por', set 'por' to the pointer_por 674 task_params.setdefault('por', por or self.pointer_por) 675 task_params['task_name'] = task_key[0] 676 task_params.setdefault('layer', layer) 677 678 # get the script that contains the task 679 if script is None: 680 try: 681 script = self.script_refs[task_key] # NIC 682 except KeyError: 683 return 684 685 # look for the right functions to execute: 686 task_functions = script.getRegisteredTask(task_key) 687 if task_functions: 688 execute = task_functions[0] 689 update = task_functions[1] 690 try: 691 if propagate: 692 # unpack the dictionary and execute the task 693 rv = execute(**task_params) 694 else: 695 # unpack the dictionary and update the task 696 if update: 697 update(**task_params) 698 rv = False 699 except AccessEngineAPI.AEApiError.AEApiError, ex: 700 # output the AccessEngineAPIError if state.Trap is True 701 kwargs=task_params 702 kwargs['text'] = str(ex) 703 if AccessEngine.AETierManager.getState().Trap: 704 AccessEngineAPI.sayError(script, cap='audio', role='output', **kwargs) 705 else: 706 log.exception(ex) 707 rv = True 708 except Exception: 709 # log any other Script exception 710 log.exception('%s: %s %s', task_key, layer, task_params) 711 # continue processing 712 rv = True 713 if rv is None: 714 # continue processing if no return value is specified 715 rv = True 716 elif not rv: 717 # inhibit automatic updating of the pointer if propogation is blocked 718 reset_pointer_por = False 719 # look, whether the pointer_por needs to be reset 720 if reset_pointer_por and AEConstants.LAYER_FOCUS == layer: 721 self.pointer_por = task_params['por'] 722 return rv
723
724 - def manageChain(self, por, layer, task_key, task_params, propagate, chain):
725 ''' 726 Executes all script tasks chained to the given task, recursively if 727 chain=True. 728 729 @note: This method is purposely long because much code that could be 730 refactored into other methods is inlined for performance reasons. 731 732 @param por: 733 @type por: L{AEPor} 734 @param layer: Layer in which to execute the task, one of 735 L{AEConstants.Event.LAYER_FOCUS}, L{AEConstants.Event.LAYER_TIER}, 736 L{AEConstants.Event.LAYER_BACKGROUND} 737 @type layer: integer 738 @param task_key: Key of the task to execute 739 @type task_key: tuple of string ('task name', 'script name') 740 @param task_params: Keyword data to pass to the task function as parameters 741 on execution or update 742 @type task_params: dictionary 743 @param propagate: Should chained tasks be executed or updated? 744 @type propagate: boolean 745 @param chain: Execute all tasks chained to the one named or not? 746 @type chain: boolean 747 ''' 748 # pull show into local vars 749 show = self.show 750 751 if not chain: 752 # no chaining, just execute, duplicate execute code for performance 753 # add to the linear history before executing so that it's available 754 # to changed and doTask tasks 755 self.task_history.append(task_key) 756 if show: 757 # execute this Task and log information 758 # TODO: showTask ueberarbeiten 759 AccessEngine.AETierManager.showTask(task_key[0], self.script_refs[task_key]) 760 propagate = self._executeTask(por, layer, task_key, task_params, propagate) 761 AccessEngine.AETierManager.showPropagate(propagate) 762 else: 763 # execute only, duplicated for performance 764 propagate = self._executeTask(por, layer, task_key, task_params, propagate) 765 # do not push the task back on the stack because we're done anyways 766 else: 767 # fetch all chain segments at once 768 try: 769 befores, around, afters = self.getChainedTasks(task_key) 770 except KeyError: 771 # no chaining, just execute, duplicate execute code for performance 772 # add to the linear history before executing so that it's available 773 # to changed and doTask tasks 774 self.task_history.append(task_key) 775 if show: 776 # execute this Task and log information 777 AccessEngine.AETierManager.showTask(task_key[0], self.script_refs[task_key]) 778 propagate = self._executeTask(por, layer, task_key, task_params, 779 propagate) 780 AccessEngine.AETierManager.showPropagate(propagate) 781 else: 782 # execute only, duplicated for performance 783 propagate = self._executeTask(por, layer, task_key, task_params, 784 propagate) 785 # do not push the task back on the stack because we're done anyways 786 else: 787 # put the name of this task and its script onto the stack 788 self.chain_stack.append(task_key) 789 790 # get the chain of tasks to run before this one 791 for before in befores: 792 if show: 793 AccessEngine.AETierManager.showChain(AEConstants.CHAIN_BEFORE, task_key[0]) 794 # recurse with before tasks 795 if before[0] is not None: 796 propagate = self.manageChain(por, layer, before, task_params, 797 propagate, chain) 798 799 # check for task to run instead of this one 800 if around is None: 801 # pop this task 802 cache = self.chain_stack.pop() 803 # add to the linear history before executing so that it's available 804 # to changed and doTask tasks 805 self.task_history.append(task_key) 806 if show: 807 # execute this Task and log information 808 AccessEngine.AETierManager.showTask(task_key[0], self.script_refs[task_key]) 809 propagate = self._executeTask(por, layer, task_key, task_params, 810 propagate) 811 AccessEngine.AETierManager.showPropagate(propagate) 812 else: 813 # execute only, duplicated for performance 814 propagate = self._executeTask(por, layer, task_key, task_params, 815 propagate) 816 # get the pointer as the starting POR for the next task execution 817 por = self.getPointer() 818 # push this task back onto the stack 819 self.chain_stack.append(cache) 820 else: 821 if show: 822 AccessEngine.AETierManager.showChain(AEConstants.CHAIN_AROUND, task_key[0]) 823 # look for a Script with a Task with the given name 824 task_key = around 825 # bail immediately if it doesn't exist 826 if task_key[0] is not None: 827 # recurse with around tasks 828 propagate = self.manageChain(por, layer, around, task_params, 829 propagate, chain) 830 # get the pointer after the recursive step 831 por = self.getPointer() 832 833 # get the chain of tasks to run after this one 834 for after in afters: 835 if show: 836 AccessEngine.AETierManager.showChain(AEConstants.CHAIN_AFTER, task_key[0]) 837 # look for a Script with a Task with the given name 838 if after[0] is not None: 839 propagate = self.manageChain(por, layer, after, task_params, 840 propagate, chain) 841 # pop the name of this task off the stack permanently 842 self.chain_stack.pop() 843 # return the current value of propagate 844 return propagate
845
846 - def manageEvent(self, event):
847 ''' 848 Manages an event by iterating through the L{AEScript <AEScript.AEScript>} stack (top to bottom) and 849 checking for registered tasks of the given type. Executes the registered 850 tasks (last registered, first executed) in each L{AEScript <AEScript.AEScript>} until one of the 851 following conditions is met: 852 - All tasks have executed 853 - A task returns False 854 - A task raises an exception 855 856 In the latter two cases, no additional tasks in the current L{AEScript <AEScript.AEScript>} or 857 additional L{AEScript <AEScript.AEScript>}s in this L{AETier} are executed. Instead the 858 update methods are called to allow housekeeping operations (e.g updating 859 state) to be performed. 860 861 If a task returns neither True or False (e.g. it returns None) a 862 warning is logged and the return value is treated as if it were True. This 863 likely means the task forgot to specify a return value. 864 865 @param event: Event to process 866 @type event: L{AEEvent.Base.AEEvent} 867 ''' 868 try: 869 # grab POR from any focus type event 870 self.focus_por = event.getFocusPOR() 871 except AttributeError: 872 pass 873 874 if not self.wantsEvent(event): 875 # quit immediately if no Task wants this event 876 return 877 878 # pull show into local vars 879 show = self.show 880 881 # show the event in registered monitors 882 if show: 883 AccessEngine.AETierManager.showEvent(event, self.name) 884 885 # get these values once to initialize, most are constant for all Tasks 886 event_por = event.getPOR() 887 task_layer = event.getLayer() 888 task_params = event.getDataForTask() 889 event_type = type(event) 890 891 # run through all the registered Scripts 892 propagate = True 893 for script in self.scripts: 894 # run through all Tasks of the given type in this Script 895 for task in script.getEventTasks(event_type, task_layer): 896 # add to linear history before executing so it's available to chained 897 # and doTask tasks 898 self.task_history.append(task) 899 900 if show: 901 # execute task and log information 902 AccessEngine.AETierManager.showTask(task[0], script) 903 propagate = self.manageChain(event_por, task_layer, task, task_params, 904 propagate, True) 905 AccessEngine.AETierManager.showPropagate(propagate) 906 else: 907 # execute only, duplicated for performance 908 propagate = self.manageChain(event_por, task_layer, task, task_params, 909 propagate, True)
910
911 - def manageGesture(self, event, count):
912 ''' 913 Manages an event by getting the L{AEInput.Gesture} that triggered it and 914 locating a task registered to execute in response to it. If a task 915 could not be found for the given event, the task registered for invalid 916 gestures is executed instead. 917 918 @param event: Event to process 919 @type event: L{AEEvent.InputGesture} 920 @param count: Number of times this gesture has been issued without 921 interruption 922 @type count: integer 923 ''' 924 # look for a Script with a Task registered to respond to this input command 925 task_key = self.getCommandTask(event.getTaskKey()) 926 if task_key is None: return 927 928 # show the event in registered monitors 929 if self.show: 930 AccessEngine.AETierManager.showEvent(event, self.name) 931 # extract layer and data for task 932 layer = event.getLayer() 933 task_params = event.getDataForTask() 934 # insert the count into the data for the task 935 task_params['cycle_count'] = count 936 # look for a Script with a Task with the given name 937 938 # account for chains on the task 939 self.manageChain(None, layer, task_key, task_params, True, True)
940
941 - def manageTimerTask(self, event):
942 ''' 943 Manages a TimerAlert event by locating a task registered to execute in 944 response to it. 945 946 @param event: Event to process 947 @type event: L{AEEvent} 948 ''' 949 task_key = event.getTaskKey() 950 # pull show into local namespace 951 show = self.show 952 # show the event in registered monitors 953 layer = event.getLayer() 954 task_params = event.getDataForTask() 955 if show: 956 AccessEngine.AETierManager.showEvent(event, self.name) 957 script = self.script_refs[task_key] 958 # execute that Task and log its information 959 AccessEngine.AETierManager.showTask(task_key[0], script) 960 rv = self._executeTask(None, layer, task_key, task_params, True, script) 961 AccessEngine.AETierManager.showPropagate(rv) 962 pass 963 else: 964 self._executeTask(None, layer, task_key, task_params, True)
965
966 - def manageChooserTask(self, event):
967 ''' 968 Manages an event by locating a task under a particular key (not keyboard 969 key, but some immutable identifier) registered to execute in response to 970 it. 971 972 @param event: Event to process 973 @type event: L{AEEvent} 974 ''' 975 chooser_id = event.getTaskKey() 976 # look for a Script with a Task registered to respond to this event 977 task_key = self.getChooserTask(chooser_id) 978 # pull show into local namespace 979 show = self.show 980 # show the event in registered monitors 981 if show: 982 AccessEngine.AETierManager.showEvent(event, self.name) 983 if task_key is not None: 984 layer = event.getLayer() 985 #script = task.getScript() 986 script = self.script_refs[chooser_id] 987 task_params = event.getDataForTask() 988 if show: 989 # execute that Task and log its information 990 AccessEngine.AETierManager.showTask(task_key[0], script) 991 rv = self._executeTask(None, layer, task_key, task_params, True, script) 992 AccessEngine.AETierManager.showPropagate(rv) 993 else: 994 self._executeTask(None, layer, task_key, task_params, True, script)
995
996 - def managePrivate(self, event):
997 ''' 998 Pulls private data from an event and stores it in appropriate instance 999 variables. Does not forward the event to L{AEScript <AEScript.AEScript>}s and tasks. Rather, 1000 the stored information is made public through L{AccessEngineAPI} methods. 1001 1002 @param event: Event to process 1003 @type event: L{AEEvent} 1004 ''' 1005 data = event.getDataForTask() 1006 kind = data['kind'] 1007 if kind == event.KEY_PRESS: 1008 # store last scan code and key sym string 1009 self.last_key = data['code'], data['sym'], data['mod']
1010