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