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