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

Source Code for Module status

  1  ############################################################################### 
  2  #                                                                             # 
  3  # Copyright (C) 2010-2013 Edward d'Auvergne                                   # 
  4  # Copyright (C) 2014 Troels E. Linnet                                         # 
  5  #                                                                             # 
  6  # This file is part of the program relax (http://www.nmr-relax.com).          # 
  7  #                                                                             # 
  8  # This program is free software: you can redistribute it and/or modify        # 
  9  # it under the terms of the GNU General Public License as published by        # 
 10  # the Free Software Foundation, either version 3 of the License, or           # 
 11  # (at your option) any later version.                                         # 
 12  #                                                                             # 
 13  # This program is distributed in the hope that it will be useful,             # 
 14  # but WITHOUT ANY WARRANTY; without even the implied warranty of              # 
 15  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               # 
 16  # GNU General Public License for more details.                                # 
 17  #                                                                             # 
 18  # You should have received a copy of the GNU General Public License           # 
 19  # along with this program.  If not, see <http://www.gnu.org/licenses/>.       # 
 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 re import search 
 31  import sys 
 32  from threading import Lock, RLock 
 33   
 34  # relax module imports. 
 35  from compat import Queue 
 36  from lib.errors import RelaxError 
 37   
 38   
39 -class Status(object):
40 """The relax status singleton class.""" 41 42 # Class variable for storing the class instance (for the singleton). 43 _instance = None 44
45 - def __new__(self, *args, **kargs):
46 """Replacement method for implementing the singleton design pattern.""" 47 48 # First instantiation. 49 if self._instance is None: 50 # Instantiate. 51 self._instance = object.__new__(self, *args, **kargs) 52 53 # Initialise some variables. 54 self._instance.debug = False 55 self._instance.escalate = False 56 self._instance.prompt = 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 = 'compat.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 = path.split('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 = [] 252 self._mode = [] 253 254 # Script nesting level. 255 self._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 # Store the new name and mode. 279 self._name.append(name) 280 self._mode.append(mode) 281 282 # Nested locking. 283 if self.locked(): 284 # Increment the nesting counter. 285 self._nest += 1 286 287 # Debugging. 288 if self._fake_lock: 289 self.log.write("Nested by %s (to level %s)\n" % (name, self._nest)) 290 self.log.flush() 291 292 # Return without doing anything. 293 return 294 295 # Debugging. 296 if self._fake_lock: 297 self.log.write("Acquired by %s\n" % self._name[-1]) 298 self.log.flush() 299 return 300 301 # Acquire the real lock. 302 lock = self._lock.acquire() 303 304 # Notify observers. 305 status = Status() 306 status.observers.exec_lock.notify() 307 308 # Return the real lock. 309 return lock
310 311
312 - def locked(self):
313 """Simulate the Lock.locked() mechanism.""" 314 315 # Debugging (pseudo-locking based on _name). 316 if self._fake_lock: 317 if len(self._name): 318 return True 319 else: 320 return False 321 322 # Call the real method. 323 return self._lock.locked()
324 325
326 - def release(self):
327 """Simulate the Lock.release() mechanism.""" 328 329 # Debugging. 330 if self._status.debug: 331 sys.stdout.write("debug> Execution lock: Release by '%s' ('%s' mode).\n" % (self._name[-1], self._mode[-1])) 332 333 # Pop the name and mode. 334 self._name.pop(-1) 335 self._mode.pop(-1) 336 337 # Nested locking. 338 if self._nest: 339 # Debugging. 340 if self._fake_lock: 341 self.log.write("Nested locking decrement (%s -> %s)\n" % (self._nest, self._nest-1)) 342 self.log.flush() 343 344 # Decrement. 345 self._nest -= 1 346 347 # Return without releasing the lock. 348 return 349 350 # Debugging. 351 if self._fake_lock: 352 # Main text. 353 text = 'Release' 354 355 # Test suite info. 356 if hasattr(self, 'test_name'): 357 text = text + 'd by %s' % self.test_name 358 359 # Write out, flush, and exit the method. 360 self.log.write("%s\n\n" % text) 361 self.log.flush() 362 return 363 364 # Release the real lock. 365 release = self._lock.release() 366 367 # Notify observers. 368 status = Status() 369 status.observers.exec_lock.notify() 370 371 # Return the status. 372 return release
373 374 375
376 -class Observer(object):
377 """The observer design pattern base class.""" 378
379 - def __init__(self, name='unknown'):
380 """Set up the object. 381 382 @keyword name: The special name for the observer object, used in debugging. 383 @type name: str 384 """ 385 386 # Store the args. 387 self._name = name 388 389 # The dictionary of callback methods (and their names). 390 self._callback = {} 391 self._method_names = {} 392 393 # The list of keys, for ordered execution. 394 self._keys = [] 395 396 # The status container. 397 self._status = Status()
398 399
400 - def notify(self):
401 """Notify all observers of the state change.""" 402 403 # Loop over the callback methods and execute them. 404 for key in self._keys: 405 # Debugging. 406 if self._status.debug: 407 if self._method_names[key]: 408 sys.stdout.write("debug> Observer: '%s' notifying the '%s' method %s().\n" % (self._name, key, self._method_names[key])) 409 else: 410 sys.stdout.write("debug> Observer: '%s' notifying '%s'.\n" % (self._name, key)) 411 412 # Call the method. 413 self._callback[key]()
414 415
416 - def register(self, key, method, method_name=None):
417 """Register a method to be called when the state changes. 418 419 @param key: The key to identify the observer's method. 420 @type key: str 421 @param method: The observer's method to be called after a state change. 422 @type method: method 423 @keyword method_name: The optional method name used in debugging printouts. 424 @type method_name: str or None 425 """ 426 427 # Already exists. 428 if key in self._keys: 429 raise RelaxError("The observer '%s' already exists." % key) 430 431 # Blank key. 432 if key == None: 433 raise RelaxError("The observer key must be supplied.") 434 435 # Debugging. 436 if self._status.debug: 437 if method_name: 438 sys.stdout.write("debug> Observer: '%s' registering the '%s' method %s().\n" % (self._name, key, method_name)) 439 else: 440 sys.stdout.write("debug> Observer: '%s' registering '%s'.\n" % (self._name, key)) 441 442 # Add the method to the dictionary of callbacks. 443 self._callback[key] = method 444 445 # Add the method name. 446 self._method_names[key] = method_name 447 448 # Add the key to the ordered list. 449 self._keys.append(key)
450 451
452 - def reset(self):
453 """Reset the object.""" 454 455 # Debugging. 456 if self._status.debug: 457 sys.stdout.write("debug> Resetting observer '%s'.\n" % self._name) 458 459 # Reinitialise the dictionary of callback methods. 460 self._callback = {} 461 462 # Reinitialise the key list. 463 self._keys = []
464 465
466 - def unregister(self, key):
467 """Unregister the method corresponding to the key. 468 469 @param key: The key to identify the observer's method. 470 @type key: str 471 """ 472 473 # Debugging. 474 if self._status.debug: 475 sys.stdout.write("debug> Observer: '%s' unregistering '%s'.\n" % (self._name, key)) 476 477 # Does not exist, so return (allow multiple code paths to unregister methods). 478 if key not in self._keys: 479 if self._status.debug: 480 sys.stdout.write("debug> The key '%s' does not exist.\n" % key) 481 return 482 483 # Remove the method from the dictionary of callbacks. 484 self._callback.pop(key) 485 486 # Remove the name. 487 self._method_names.pop(key) 488 489 # Remove the key for the ordered key list. 490 self._keys.remove(key)
491 492 493
494 -class Relax_lock:
495 """A type of locking object for relax.""" 496
497 - def __init__(self, name='unknown', fake_lock=False):
498 """Set up the lock-like object. 499 500 @keyword name: The special name for the lock, used in debugging. 501 @type name: str 502 @keyword fake_lock: A flag which is True will allow this object to be debugged as the locking mechanism is turned off. 503 @type fake_lock: bool 504 """ 505 506 # Store the args. 507 self.name = name 508 self._fake_lock = fake_lock 509 510 # Init a reentrant lock object. 511 self._lock = RLock() 512 513 # The status container. 514 self._status = Status() 515 516 # Fake lock. 517 if self._fake_lock: 518 # Track the number of acquires. 519 self._lock_level = 0
520 521
522 - def acquire(self, acquirer='unknown'):
523 """Simulate the RLock.acquire() mechanism. 524 525 @keyword acquirer: The optional name of the acquirer. 526 @type acquirer: str 527 """ 528 529 # Debugging. 530 if self._status.debug: 531 sys.stdout.write("debug> Lock '%s': Acquisition by '%s'.\n" % (self.name, acquirer)) 532 533 # Fake lock. 534 if self._fake_lock: 535 # Increment the lock level. 536 self._lock_level += 1 537 538 # Throw an error. 539 if self._lock_level > 1: 540 raise 541 542 # Return to prevent real locking. 543 return 544 545 # Acquire the real lock. 546 lock = self._lock.acquire() 547 548 # Return the real lock. 549 return lock
550 551
552 - def locked(self):
553 """Simulate the RLock.locked() mechanism.""" 554 555 # Call the real method. 556 return self._lock.locked()
557 558
559 - def release(self, acquirer='unknown'):
560 """Simulate the RLock.release() mechanism. 561 562 @keyword acquirer: The optional name of the acquirer. 563 @type acquirer: str 564 """ 565 566 # Debugging. 567 if self._status.debug: 568 sys.stdout.write("debug> Lock '%s': Release by '%s'.\n" % (self.name, acquirer)) 569 570 # Fake lock. 571 if self._fake_lock: 572 # Decrement the lock level. 573 self._lock_level -= 1 574 575 # Return to prevent real lock release. 576 return 577 578 # Release the real lock. 579 release = self._lock.release() 580 581 # Return the status. 582 return release
583 584 585
586 -class Observer_container:
587 """The container for holding all the observer objects.""" 588
589 - def info(self):
590 """Print out info about all the status objects.""" 591 592 # Blacklisted objects. 593 blacklist = list(self.__class__.__dict__.keys() + dict.__dict__.keys()) 594 595 # Loop over all objects in this container. 596 for name in dir(self): 597 # Skip blacklisted objects. 598 if name in blacklist: 599 continue 600 601 # Get the object. 602 obj = getattr(self, name) 603 604 # An observer object. 605 print("Observer '%s' keys: %s" % (obj._name, obj._keys))
606