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

Source Code for Module status

  1  ############################################################################### 
  2  #                                                                             # 
  3  # Copyright (C) 2010-2012 Edward d'Auvergne                                   # 
  4  #                                                                             # 
  5  # This file is part of the program relax.                                     # 
  6  #                                                                             # 
  7  # relax is free software; you can redistribute it and/or modify               # 
  8  # it under the terms of the GNU General Public License as published by        # 
  9  # the Free Software Foundation; either version 2 of the License, or           # 
 10  # (at your option) any later version.                                         # 
 11  #                                                                             # 
 12  # relax is distributed in the hope that it will be useful,                    # 
 13  # but WITHOUT ANY WARRANTY; without even the implied warranty of              # 
 14  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               # 
 15  # GNU General Public License for more details.                                # 
 16  #                                                                             # 
 17  # You should have received a copy of the GNU General Public License           # 
 18  # along with relax; if not, write to the Free Software                        # 
 19  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA   # 
 20  #                                                                             # 
 21  ############################################################################### 
 22   
 23  # Module docstring. 
 24  """Module containing the status singleton object.""" 
 25   
 26  # Python module imports. 
 27  from os import F_OK, access 
 28  from os.path import sep 
 29  import platform 
 30  from Queue import Queue 
 31  from re import search 
 32  from string import split 
 33  import sys 
 34  from threading import Lock, RLock 
 35   
 36  # relax module imports. 
 37  from relax_errors import RelaxError 
 38   
 39   
40 -class Status(object):
41 """The relax status singleton class.""" 42 43 # Class variable for storing the class instance (for the singleton). 44 _instance = None 45
46 - def __new__(self, *args, **kargs):
47 """Replacement method for implementing the singleton design pattern.""" 48 49 # First instantiation. 50 if self._instance is None: 51 # Instantiate. 52 self._instance = object.__new__(self, *args, **kargs) 53 54 # Initialise some variables. 55 self._instance.debug = False 56 self._instance.pedantic = False 57 self._instance.test_mode = False 58 self._instance.uf_intro = False 59 self._instance.show_gui = False 60 self._instance.gui_uf_force_sync = False 61 self._instance.install_path = self._instance._det_install_path() 62 63 # Set up the singleton. 64 self._instance._setup() 65 66 # Already instantiated, so return the instance. 67 return self._instance
68 69
70 - def _det_install_path(self):
71 """Determine, with a bit of magic, the relax installation path. 72 73 @return: The relax installation path. With a Mac OS X app, this will be the path to the 'Resources'. 74 @rtype: str 75 """ 76 77 # The file to search for. 78 file_to_find = 'relax_errors.py' 79 80 # Loop over the system paths, searching for the real path. 81 for path in sys.path: 82 # Found the file, so return the path. 83 if access(path + sep + file_to_find, F_OK): 84 return path 85 86 # Mac OS X application support. 87 for path in sys.path: 88 # Find the Resources folder, where the relax data files are located. 89 if search('Resources', path): 90 # Nasty hack for creating the Resources path. 91 bits = split(path, 'Resources') 92 mac_path = bits[0] + 'Resources' 93 94 # Return the Mac Resources folder path. 95 return mac_path 96 97 # Return the first entry of sys.path as a fallback. 98 return sys.path[0]
99 100
101 - def _setup(self):
102 """Initialise all the status data structures.""" 103 104 # Execution lock object. 105 self.exec_lock = Exec_lock() 106 107 # The data pipe lock object. 108 self.pipe_lock = Relax_lock(name='pipe_lock') 109 110 # The molecule, residue, spin structure lock object. 111 self.spin_lock = Relax_lock(name='spin_lock') 112 113 # The exception queue for handling exceptions in threads. 114 self.exception_queue = Queue() 115 116 # The auto-analysis status containers. 117 self.auto_analysis = {} 118 self.current_analysis = None 119 120 # GUI structures. 121 self.controller_max_entries = 100000 # Scroll back limit in the relax controller. 122 123 # A structure for skipped system and unit tests. 124 self.skipped_tests = [] 125 """The skipped tests list. Each element should be a list of the test case name, the missing Python module, and the name of the test suite category (i.e. 'system' or 'unit').""" 126 127 # Set up the observer objects. 128 self._setup_observers() 129 130 # Text wrapping on different operating systems. 131 if platform.uname()[0] in ['Windows', 'Microsoft']: 132 self.text_width = 79 133 else: 134 self.text_width = 100
135 136
137 - def _setup_observers(self):
138 """Set up all the observer objects.""" 139 140 # A container for all the observers. 141 self.observers = Observer_container() 142 143 # The observer object for status changes in the auto-analyses. 144 self.observers.auto_analyses = Observer('auto_analyses') 145 146 # The observer object for pipe switches. 147 self.observers.pipe_alteration = Observer('pipe_alteration') 148 149 # The observer object for GUI user function completion. 150 self.observers.gui_uf = Observer('gui_uf') 151 152 # The observer object for changes to the GUI analysis tabs. 153 self.observers.gui_analysis = Observer('gui_analysis') 154 155 # The observer object for relax resets. 156 self.observers.reset = Observer('reset') 157 158 # The observer object for the execution lock. 159 self.observers.exec_lock = Observer('exec_lock') 160 161 # The observer object for the creation of results files. 162 self.observers.result_file = Observer('result_file') 163 164 # The observer object for state loading. 165 self.observers.state_load = Observer('state_load')
166 167
168 - def init_auto_analysis(self, name, type):
169 """Initialise a status container for an auto-analysis. 170 171 @param name: The unique name of the auto-analysis. This will act as a key. 172 @type name: str. 173 @param type: The type of auto-analysis. 174 @type type: str 175 """ 176 177 # Add a status container. 178 self.auto_analysis[name] = Auto_analysis(name, type)
179 180
181 - def reset(self):
182 """Reset the status object to its initial state.""" 183 184 # Simply call the setup again. 185 self._setup()
186 187 188
189 -class Auto_analysis:
190 """The auto-analysis status container.""" 191
192 - def __init__(self, name, type):
193 """Initialise the auto-analysis status object. 194 195 @param name: The unique name of the auto-analysis. This will act as a key. 196 @type name: str. 197 @param type: The type of auto-analysis. 198 @type type: str 199 """ 200 201 # The status container. 202 self._status = Status() 203 204 # Store the analysis type. 205 self.__dict__['type'] = type 206 207 # The completion flag. 208 self.__dict__['fin'] = False 209 210 # The Monte Carlo simulation status, if used. 211 self.__dict__['mc_number'] = None
212 213
214 - def __setattr__(self, name, value):
215 """Replacement __setattr__() method. 216 217 @param name: The name of the attribute. 218 @type name: str 219 @param value: The value of the attribute. 220 @type value: anything 221 """ 222 223 # First set the attribute. 224 self.__dict__[name] = value 225 226 # Then notify the observers. 227 self._status.observers.auto_analyses.notify()
228 229 230
231 -class Exec_lock:
232 """A type of locking object for locking execution of relax.""" 233
234 - def __init__(self, fake_lock=False):
235 """Set up the lock-like object. 236 237 @keyword fake_lock: A flag which is True will allow this object to be debugged as the locking mechanism is turned off. 238 @type fake_lock: bool 239 """ 240 241 # Store the arg. 242 self._fake_lock = fake_lock 243 244 # Init a threading.Lock object. 245 self._lock = Lock() 246 247 # The status container. 248 self._status = Status() 249 250 # The name and mode of the locker. 251 self._name = None 252 self._mode = None 253 254 # Script nesting level. 255 self._script_nest = 0 256 257 # Auto-analysis from script launch. 258 self._auto_from_script = False 259 260 # Debugging. 261 if self._fake_lock: 262 self.log = open('lock.log', 'w')
263 264
265 - def acquire(self, name, mode='script'):
266 """Simulate the Lock.acquire() mechanism. 267 268 @param name: The name of the locking code. 269 @type name: str 270 @keyword mode: The mode of the code trying to obtain the lock. This can be one of 'script' for the scripting interface or 'auto-analysis' for the auto-analyses. 271 @type mode: str 272 """ 273 274 # Debugging. 275 if self._status.debug: 276 sys.stdout.write("debug> Execution lock: Acquisition by '%s' ('%s' mode).\n" % (name, mode)) 277 278 # Do not acquire if lunching a script from a script. 279 if mode == 'script' and self._mode == 'script' and self.locked(): 280 # Increment the nesting counter. 281 self._script_nest += 1 282 283 # Debugging. 284 if self._fake_lock: 285 self.log.write("Nested by %s (to level %s)\n" % (name, self._script_nest)) 286 self.log.flush() 287 288 # Return without doing anything. 289 return 290 291 # Skip locking if an auto-analysis is called from a script. 292 if self.locked() and self._mode == 'script' and mode == 'auto-analysis': 293 # Debugging. 294 if self._fake_lock: 295 self.log.write("Skipped unlocking of '%s' lock by '%s'\n" % (self._name, name)) 296 self.log.flush() 297 298 # Switch the flag. 299 self._auto_from_script = True 300 301 # Return without doing anything. 302 return 303 304 # Store the new name and mode. 305 self._name = name 306 self._mode = mode 307 308 # Debugging. 309 if self._fake_lock: 310 self.log.write("Acquired by %s\n" % self._name) 311 self.log.flush() 312 return 313 314 # Acquire the real lock. 315 lock = self._lock.acquire() 316 317 # Notify observers. 318 status = Status() 319 status.observers.exec_lock.notify() 320 321 # Return the real lock. 322 return lock
323 324
325 - def locked(self):
326 """Simulate the Lock.locked() mechanism.""" 327 328 # Debugging (pseudo-locking based on _name). 329 if self._fake_lock: 330 if self._name: 331 return True 332 else: 333 return False 334 335 # Call the real method. 336 return self._lock.locked()
337 338
339 - def release(self):
340 """Simulate the Lock.release() mechanism.""" 341 342 # Debugging. 343 if self._status.debug: 344 sys.stdout.write("debug> Execution lock: Release by '%s' ('%s' mode).\n" % (self._name, self._mode)) 345 346 # Nested scripting. 347 if self._script_nest: 348 # Debugging. 349 if self._fake_lock: 350 self.log.write("Script termination, nest decrement (%s -> %s)\n" % (self._script_nest, self._script_nest-1)) 351 self.log.flush() 352 353 # Decrement. 354 self._script_nest -= 1 355 356 # Return without releasing the lock. 357 return 358 359 # Auto-analysis launched from script. 360 if self._auto_from_script: 361 # Debugging. 362 if self._fake_lock: 363 self.log.write("Auto-analysis launched from script, skipping release.\n") 364 self.log.flush() 365 366 # Unset the flag. 367 self._auto_from_script = False 368 369 # Return without releasing the lock. 370 return 371 372 # Reset the name and mode. 373 self._name = None 374 self._mode = None 375 376 # Debugging. 377 if self._fake_lock: 378 # Main text. 379 text = 'Release' 380 381 # Test suite info. 382 if hasattr(self, 'test_name'): 383 text = text + 'd by %s' % self.test_name 384 385 # Write out, flush, and exit the method. 386 self.log.write("%s\n\n" % text) 387 self.log.flush() 388 return 389 390 # Release the real lock. 391 release = self._lock.release() 392 393 # Notify observers. 394 status = Status() 395 status.observers.exec_lock.notify() 396 397 # Return the status. 398 return release
399 400 401
402 -class Observer(object):
403 """The observer design pattern base class.""" 404
405 - def __init__(self, name='unknown'):
406 """Set up the object. 407 408 @keyword name: The special name for the observer object, used in debugging. 409 @type name: str 410 """ 411 412 # Store the args. 413 self._name = name 414 415 # The dictionary of callback methods. 416 self._callback = {} 417 418 # The list of keys, for ordered execution. 419 self._keys = [] 420 421 # The status container. 422 self._status = Status()
423 424
425 - def notify(self):
426 """Notify all observers of the state change.""" 427 428 # Loop over the callback methods and execute them. 429 for key in self._keys: 430 # Debugging. 431 if self._status.debug: 432 sys.stdout.write("debug> Observer: '%s' notifying '%s'.\n" % (self._name, key)) 433 434 # Call the method. 435 self._callback[key]()
436 437
438 - def register(self, key, method):
439 """Register a method to be called when the state changes. 440 441 @param key: The key to identify the observer's method. 442 @type key: str 443 @param method: The observer's method to be called after a state change. 444 @type method: method 445 """ 446 447 # Already exists. 448 if key in self._keys: 449 raise RelaxError("The observer '%s' already exists." % key) 450 451 # Debugging. 452 if self._status.debug: 453 sys.stdout.write("debug> Observer: '%s' registering '%s'.\n" % (self._name, key)) 454 455 # Add the method to the dictionary of callbacks. 456 self._callback[key] = method 457 458 # Add the key to the ordered list. 459 self._keys.append(key)
460 461
462 - def reset(self):
463 """Reset the object.""" 464 465 # Debugging. 466 if self._status.debug: 467 sys.stdout.write("debug> Resetting observer '%s'.\n" % self._name) 468 469 # Reinitialise the dictionary of callback methods. 470 self._callback = {} 471 472 # Reinitialise the key list. 473 self._keys = []
474 475
476 - def unregister(self, key):
477 """Unregister the method corresponding to the key. 478 479 @param key: The key to identify the observer's method. 480 @type key: str 481 """ 482 483 # Debugging. 484 if self._status.debug: 485 sys.stdout.write("debug> Observer: '%s' unregistering '%s'.\n" % (self._name, key)) 486 487 # Does not exist, so return (allow multiple code paths to unregister methods). 488 if key not in self._keys: 489 if self._status.debug: 490 sys.stdout.write("debug> The key '%s' does not exist.\n" % key) 491 return 492 493 # Remove the method from the dictionary of callbacks. 494 self._callback.pop(key) 495 496 # Remove the key for the ordered key list. 497 self._keys.remove(key)
498 499 500
501 -class Relax_lock:
502 """A type of locking object for relax.""" 503
504 - def __init__(self, name='unknown', fake_lock=False):
505 """Set up the lock-like object. 506 507 @keyword name: The special name for the lock, used in debugging. 508 @type name: str 509 @keyword fake_lock: A flag which is True will allow this object to be debugged as the locking mechanism is turned off. 510 @type fake_lock: bool 511 """ 512 513 # Store the args. 514 self.name = name 515 self._fake_lock = fake_lock 516 517 # Init a reentrant lock object. 518 self._lock = RLock() 519 520 # The status container. 521 self._status = Status() 522 523 # Fake lock. 524 if self._fake_lock: 525 # Track the number of acquires. 526 self._lock_level = 0
527 528
529 - def acquire(self, acquirer='unknown'):
530 """Simulate the RLock.acquire() mechanism. 531 532 @keyword acquirer: The optional name of the acquirer. 533 @type acquirer: str 534 """ 535 536 # Debugging. 537 if self._status.debug: 538 sys.stdout.write("debug> Lock '%s': Acquisition by '%s'.\n" % (self.name, acquirer)) 539 540 # Fake lock. 541 if self._fake_lock: 542 # Increment the lock level. 543 self._lock_level += 1 544 545 # Throw an error. 546 if self._lock_level > 1: 547 raise 548 549 # Return to prevent real locking. 550 return 551 552 # Acquire the real lock. 553 lock = self._lock.acquire() 554 555 # Return the real lock. 556 return lock
557 558
559 - def locked(self):
560 """Simulate the RLock.locked() mechanism.""" 561 562 # Call the real method. 563 return self._lock.locked()
564 565
566 - def release(self, acquirer='unknown'):
567 """Simulate the RLock.release() mechanism. 568 569 @keyword acquirer: The optional name of the acquirer. 570 @type acquirer: str 571 """ 572 573 # Debugging. 574 if self._status.debug: 575 sys.stdout.write("debug> Lock '%s': Release by '%s'.\n" % (self.name, acquirer)) 576 577 # Fake lock. 578 if self._fake_lock: 579 # Decrement the lock level. 580 self._lock_level -= 1 581 582 # Return to prevent real lock release. 583 return 584 585 # Release the real lock. 586 release = self._lock.release() 587 588 # Return the status. 589 return release
590 591 592
593 -class Observer_container:
594 """The container for holding all the observer objects.""" 595
596 - def info(self):
597 """Print out info about all the status objects.""" 598 599 # Blacklisted objects. 600 blacklist = list(self.__class__.__dict__.keys() + dict.__dict__.keys()) 601 602 # Loop over all objects in this container. 603 for name in dir(self): 604 # Skip blacklisted objects. 605 if name in blacklist: 606 continue 607 608 # Get the object. 609 obj = getattr(self, name) 610 611 # An observer object. 612 print("Observer '%s' keys: %s" % (obj._name, obj._keys))
613