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

Source Code for Module status

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