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

Source Code for Module status

  1  ############################################################################### 
  2  #                                                                             # 
  3  # Copyright (C) 2010-2013 Edward d'Auvergne                                   # 
  4  #                                                                             # 
  5  # This file is part of the program relax (http://www.nmr-relax.com).          # 
  6  #                                                                             # 
  7  # This program 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 3 of the License, or           # 
 10  # (at your option) any later version.                                         # 
 11  #                                                                             # 
 12  # This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.       # 
 19  #                                                                             # 
 20  ############################################################################### 
 21   
 22  # Module docstring. 
 23  """Module containing the status singleton object.""" 
 24   
 25  # Python module imports. 
 26  from os import F_OK, access 
 27  from os.path import sep 
 28  import platform 
 29  from re import search 
 30  import sys 
 31  from threading import Lock, RLock 
 32   
 33  # relax module imports. 
 34  from compat import Queue 
 35  from lib.errors import RelaxError 
 36   
 37   
38 -class Status(object):
39 """The relax status singleton class.""" 40 41 # Class variable for storing the class instance (for the singleton). 42 _instance = None 43
44 - def __new__(self, *args, **kargs):
45 """Replacement method for implementing the singleton design pattern.""" 46 47 # First instantiation. 48 if self._instance is None: 49 # Instantiate. 50 self._instance = object.__new__(self, *args, **kargs) 51 52 # Initialise some variables. 53 self._instance.debug = False 54 self._instance.pedantic = False 55 self._instance.test_mode = False 56 self._instance.uf_intro = False 57 self._instance.show_gui = False 58 self._instance.gui_uf_force_sync = 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 = 'compat.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 = path.split('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 # The observer object for state loading. 163 self.observers.state_load = Observer('state_load')
164 165
166 - def init_auto_analysis(self, name, type):
167 """Initialise a status container for an auto-analysis. 168 169 @param name: The unique name of the auto-analysis. This will act as a key. 170 @type name: str. 171 @param type: The type of auto-analysis. 172 @type type: str 173 """ 174 175 # Add a status container. 176 self.auto_analysis[name] = Auto_analysis(name, type)
177 178
179 - def reset(self):
180 """Reset the status object to its initial state.""" 181 182 # Simply call the setup again. 183 self._setup()
184 185 186
187 -class Auto_analysis:
188 """The auto-analysis status container.""" 189
190 - def __init__(self, name, type):
191 """Initialise the auto-analysis status object. 192 193 @param name: The unique name of the auto-analysis. This will act as a key. 194 @type name: str. 195 @param type: The type of auto-analysis. 196 @type type: str 197 """ 198 199 # The status container. 200 self._status = Status() 201 202 # Store the analysis type. 203 self.__dict__['type'] = type 204 205 # The completion flag. 206 self.__dict__['fin'] = False 207 208 # The Monte Carlo simulation status, if used. 209 self.__dict__['mc_number'] = None
210 211
212 - def __setattr__(self, name, value):
213 """Replacement __setattr__() method. 214 215 @param name: The name of the attribute. 216 @type name: str 217 @param value: The value of the attribute. 218 @type value: anything 219 """ 220 221 # First set the attribute. 222 self.__dict__[name] = value 223 224 # Then notify the observers. 225 self._status.observers.auto_analyses.notify()
226 227 228
229 -class Exec_lock:
230 """A type of locking object for locking execution of relax.""" 231
232 - def __init__(self, fake_lock=False):
233 """Set up the lock-like object. 234 235 @keyword fake_lock: A flag which is True will allow this object to be debugged as the locking mechanism is turned off. 236 @type fake_lock: bool 237 """ 238 239 # Store the arg. 240 self._fake_lock = fake_lock 241 242 # Init a threading.Lock object. 243 self._lock = Lock() 244 245 # The status container. 246 self._status = Status() 247 248 # The name and mode of the locker. 249 self._name = [] 250 self._mode = [] 251 252 # Script nesting level. 253 self._nest = 0 254 255 # Auto-analysis from script launch. 256 self._auto_from_script = False 257 258 # Debugging. 259 if self._fake_lock: 260 self.log = open('lock.log', 'w')
261 262
263 - def acquire(self, name, mode='script'):
264 """Simulate the Lock.acquire() mechanism. 265 266 @param name: The name of the locking code. 267 @type name: str 268 @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. 269 @type mode: str 270 """ 271 272 # Debugging. 273 if self._status.debug: 274 sys.stdout.write("debug> Execution lock: Acquisition by '%s' ('%s' mode).\n" % (name, mode)) 275 276 # Store the new name and mode. 277 self._name.append(name) 278 self._mode.append(mode) 279 280 # Nested locking. 281 if self.locked(): 282 # Increment the nesting counter. 283 self._nest += 1 284 285 # Debugging. 286 if self._fake_lock: 287 self.log.write("Nested by %s (to level %s)\n" % (name, self._nest)) 288 self.log.flush() 289 290 # Return without doing anything. 291 return 292 293 # Debugging. 294 if self._fake_lock: 295 self.log.write("Acquired by %s\n" % self._name[-1]) 296 self.log.flush() 297 return 298 299 # Acquire the real lock. 300 lock = self._lock.acquire() 301 302 # Notify observers. 303 status = Status() 304 status.observers.exec_lock.notify() 305 306 # Return the real lock. 307 return lock
308 309
310 - def locked(self):
311 """Simulate the Lock.locked() mechanism.""" 312 313 # Debugging (pseudo-locking based on _name). 314 if self._fake_lock: 315 if len(self._name): 316 return True 317 else: 318 return False 319 320 # Call the real method. 321 return self._lock.locked()
322 323
324 - def release(self):
325 """Simulate the Lock.release() mechanism.""" 326 327 # Debugging. 328 if self._status.debug: 329 sys.stdout.write("debug> Execution lock: Release by '%s' ('%s' mode).\n" % (self._name[-1], self._mode[-1])) 330 331 # Pop the name and mode. 332 self._name.pop(-1) 333 self._mode.pop(-1) 334 335 # Nested locking. 336 if self._nest: 337 # Debugging. 338 if self._fake_lock: 339 self.log.write("Nested locking decrement (%s -> %s)\n" % (self._nest, self._nest-1)) 340 self.log.flush() 341 342 # Decrement. 343 self._nest -= 1 344 345 # Return without releasing the lock. 346 return 347 348 # Debugging. 349 if self._fake_lock: 350 # Main text. 351 text = 'Release' 352 353 # Test suite info. 354 if hasattr(self, 'test_name'): 355 text = text + 'd by %s' % self.test_name 356 357 # Write out, flush, and exit the method. 358 self.log.write("%s\n\n" % text) 359 self.log.flush() 360 return 361 362 # Release the real lock. 363 release = self._lock.release() 364 365 # Notify observers. 366 status = Status() 367 status.observers.exec_lock.notify() 368 369 # Return the status. 370 return release
371 372 373
374 -class Observer(object):
375 """The observer design pattern base class.""" 376
377 - def __init__(self, name='unknown'):
378 """Set up the object. 379 380 @keyword name: The special name for the observer object, used in debugging. 381 @type name: str 382 """ 383 384 # Store the args. 385 self._name = name 386 387 # The dictionary of callback methods (and their names). 388 self._callback = {} 389 self._method_names = {} 390 391 # The list of keys, for ordered execution. 392 self._keys = [] 393 394 # The status container. 395 self._status = Status()
396 397
398 - def notify(self):
399 """Notify all observers of the state change.""" 400 401 # Loop over the callback methods and execute them. 402 for key in self._keys: 403 # Debugging. 404 if self._status.debug: 405 if self._method_names[key]: 406 sys.stdout.write("debug> Observer: '%s' notifying the '%s' method %s().\n" % (self._name, key, self._method_names[key])) 407 else: 408 sys.stdout.write("debug> Observer: '%s' notifying '%s'.\n" % (self._name, key)) 409 410 # Call the method. 411 self._callback[key]()
412 413
414 - def register(self, key, method, method_name=None):
415 """Register a method to be called when the state changes. 416 417 @param key: The key to identify the observer's method. 418 @type key: str 419 @param method: The observer's method to be called after a state change. 420 @type method: method 421 @keyword method_name: The optional method name used in debugging printouts. 422 @type method_name: str or None 423 """ 424 425 # Already exists. 426 if key in self._keys: 427 raise RelaxError("The observer '%s' already exists." % key) 428 429 # Blank key. 430 if key == None: 431 raise RelaxError("The observer key must be supplied.") 432 433 # Debugging. 434 if self._status.debug: 435 if method_name: 436 sys.stdout.write("debug> Observer: '%s' registering the '%s' method %s().\n" % (self._name, key, method_name)) 437 else: 438 sys.stdout.write("debug> Observer: '%s' registering '%s'.\n" % (self._name, key)) 439 440 # Add the method to the dictionary of callbacks. 441 self._callback[key] = method 442 443 # Add the method name. 444 self._method_names[key] = method_name 445 446 # Add the key to the ordered list. 447 self._keys.append(key)
448 449
450 - def reset(self):
451 """Reset the object.""" 452 453 # Debugging. 454 if self._status.debug: 455 sys.stdout.write("debug> Resetting observer '%s'.\n" % self._name) 456 457 # Reinitialise the dictionary of callback methods. 458 self._callback = {} 459 460 # Reinitialise the key list. 461 self._keys = []
462 463
464 - def unregister(self, key):
465 """Unregister the method corresponding to the key. 466 467 @param key: The key to identify the observer's method. 468 @type key: str 469 """ 470 471 # Debugging. 472 if self._status.debug: 473 sys.stdout.write("debug> Observer: '%s' unregistering '%s'.\n" % (self._name, key)) 474 475 # Does not exist, so return (allow multiple code paths to unregister methods). 476 if key not in self._keys: 477 if self._status.debug: 478 sys.stdout.write("debug> The key '%s' does not exist.\n" % key) 479 return 480 481 # Remove the method from the dictionary of callbacks. 482 self._callback.pop(key) 483 484 # Remove the name. 485 self._method_names.pop(key) 486 487 # Remove the key for the ordered key list. 488 self._keys.remove(key)
489 490 491
492 -class Relax_lock:
493 """A type of locking object for relax.""" 494
495 - def __init__(self, name='unknown', fake_lock=False):
496 """Set up the lock-like object. 497 498 @keyword name: The special name for the lock, used in debugging. 499 @type name: str 500 @keyword fake_lock: A flag which is True will allow this object to be debugged as the locking mechanism is turned off. 501 @type fake_lock: bool 502 """ 503 504 # Store the args. 505 self.name = name 506 self._fake_lock = fake_lock 507 508 # Init a reentrant lock object. 509 self._lock = RLock() 510 511 # The status container. 512 self._status = Status() 513 514 # Fake lock. 515 if self._fake_lock: 516 # Track the number of acquires. 517 self._lock_level = 0
518 519
520 - def acquire(self, acquirer='unknown'):
521 """Simulate the RLock.acquire() mechanism. 522 523 @keyword acquirer: The optional name of the acquirer. 524 @type acquirer: str 525 """ 526 527 # Debugging. 528 if self._status.debug: 529 sys.stdout.write("debug> Lock '%s': Acquisition by '%s'.\n" % (self.name, acquirer)) 530 531 # Fake lock. 532 if self._fake_lock: 533 # Increment the lock level. 534 self._lock_level += 1 535 536 # Throw an error. 537 if self._lock_level > 1: 538 raise 539 540 # Return to prevent real locking. 541 return 542 543 # Acquire the real lock. 544 lock = self._lock.acquire() 545 546 # Return the real lock. 547 return lock
548 549
550 - def locked(self):
551 """Simulate the RLock.locked() mechanism.""" 552 553 # Call the real method. 554 return self._lock.locked()
555 556
557 - def release(self, acquirer='unknown'):
558 """Simulate the RLock.release() mechanism. 559 560 @keyword acquirer: The optional name of the acquirer. 561 @type acquirer: str 562 """ 563 564 # Debugging. 565 if self._status.debug: 566 sys.stdout.write("debug> Lock '%s': Release by '%s'.\n" % (self.name, acquirer)) 567 568 # Fake lock. 569 if self._fake_lock: 570 # Decrement the lock level. 571 self._lock_level -= 1 572 573 # Return to prevent real lock release. 574 return 575 576 # Release the real lock. 577 release = self._lock.release() 578 579 # Return the status. 580 return release
581 582 583
584 -class Observer_container:
585 """The container for holding all the observer objects.""" 586
587 - def info(self):
588 """Print out info about all the status objects.""" 589 590 # Blacklisted objects. 591 blacklist = list(self.__class__.__dict__.keys() + dict.__dict__.keys()) 592 593 # Loop over all objects in this container. 594 for name in dir(self): 595 # Skip blacklisted objects. 596 if name in blacklist: 597 continue 598 599 # Get the object. 600 obj = getattr(self, name) 601 602 # An observer object. 603 print("Observer '%s' keys: %s" % (obj._name, obj._keys))
604