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 (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 relax_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 = '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 = 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 = None 250 self._mode = None 251 252 # Script nesting level. 253 self._script_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 # Do not acquire if lunching a script from a script. 277 if mode == 'script' and self._mode == 'script' and self.locked(): 278 # Increment the nesting counter. 279 self._script_nest += 1 280 281 # Debugging. 282 if self._fake_lock: 283 self.log.write("Nested by %s (to level %s)\n" % (name, self._script_nest)) 284 self.log.flush() 285 286 # Return without doing anything. 287 return 288 289 # Skip locking if an auto-analysis is called from a script. 290 if self.locked() and self._mode == 'script' and mode == 'auto-analysis': 291 # Debugging. 292 if self._fake_lock: 293 self.log.write("Skipped unlocking of '%s' lock by '%s'\n" % (self._name, name)) 294 self.log.flush() 295 296 # Switch the flag. 297 self._auto_from_script = True 298 299 # Return without doing anything. 300 return 301 302 # Store the new name and mode. 303 self._name = name 304 self._mode = mode 305 306 # Debugging. 307 if self._fake_lock: 308 self.log.write("Acquired by %s\n" % self._name) 309 self.log.flush() 310 return 311 312 # Acquire the real lock. 313 lock = self._lock.acquire() 314 315 # Notify observers. 316 status = Status() 317 status.observers.exec_lock.notify() 318 319 # Return the real lock. 320 return lock
321 322
323 - def locked(self):
324 """Simulate the Lock.locked() mechanism.""" 325 326 # Debugging (pseudo-locking based on _name). 327 if self._fake_lock: 328 if self._name: 329 return True 330 else: 331 return False 332 333 # Call the real method. 334 return self._lock.locked()
335 336
337 - def release(self):
338 """Simulate the Lock.release() mechanism.""" 339 340 # Debugging. 341 if self._status.debug: 342 sys.stdout.write("debug> Execution lock: Release by '%s' ('%s' mode).\n" % (self._name, self._mode)) 343 344 # Nested scripting. 345 if self._script_nest: 346 # Debugging. 347 if self._fake_lock: 348 self.log.write("Script termination, nest decrement (%s -> %s)\n" % (self._script_nest, self._script_nest-1)) 349 self.log.flush() 350 351 # Decrement. 352 self._script_nest -= 1 353 354 # Return without releasing the lock. 355 return 356 357 # Auto-analysis launched from script. 358 if self._auto_from_script: 359 # Debugging. 360 if self._fake_lock: 361 self.log.write("Auto-analysis launched from script, skipping release.\n") 362 self.log.flush() 363 364 # Unset the flag. 365 self._auto_from_script = False 366 367 # Return without releasing the lock. 368 return 369 370 # Reset the name and mode. 371 self._name = None 372 self._mode = None 373 374 # Debugging. 375 if self._fake_lock: 376 # Main text. 377 text = 'Release' 378 379 # Test suite info. 380 if hasattr(self, 'test_name'): 381 text = text + 'd by %s' % self.test_name 382 383 # Write out, flush, and exit the method. 384 self.log.write("%s\n\n" % text) 385 self.log.flush() 386 return 387 388 # Release the real lock. 389 release = self._lock.release() 390 391 # Notify observers. 392 status = Status() 393 status.observers.exec_lock.notify() 394 395 # Return the status. 396 return release
397 398 399
400 -class Observer(object):
401 """The observer design pattern base class.""" 402
403 - def __init__(self, name='unknown'):
404 """Set up the object. 405 406 @keyword name: The special name for the observer object, used in debugging. 407 @type name: str 408 """ 409 410 # Store the args. 411 self._name = name 412 413 # The dictionary of callback methods (and their names). 414 self._callback = {} 415 self._method_names = {} 416 417 # The list of keys, for ordered execution. 418 self._keys = [] 419 420 # The status container. 421 self._status = Status()
422 423
424 - def notify(self):
425 """Notify all observers of the state change.""" 426 427 # Loop over the callback methods and execute them. 428 for key in self._keys: 429 # Debugging. 430 if self._status.debug: 431 if self._method_names[key]: 432 sys.stdout.write("debug> Observer: '%s' notifying the '%s' method %s().\n" % (self._name, key, self._method_names[key])) 433 else: 434 sys.stdout.write("debug> Observer: '%s' notifying '%s'.\n" % (self._name, key)) 435 436 # Call the method. 437 self._callback[key]()
438 439
440 - def register(self, key, method, method_name=None):
441 """Register a method to be called when the state changes. 442 443 @param key: The key to identify the observer's method. 444 @type key: str 445 @param method: The observer's method to be called after a state change. 446 @type method: method 447 @keyword method_name: The optional method name used in debugging printouts. 448 @type method_name: str or None 449 """ 450 451 # Already exists. 452 if key in self._keys: 453 raise RelaxError("The observer '%s' already exists." % key) 454 455 # Blank key. 456 if key == None: 457 raise RelaxError("The observer key must be supplied.") 458 459 # Debugging. 460 if self._status.debug: 461 if method_name: 462 sys.stdout.write("debug> Observer: '%s' registering the '%s' method %s().\n" % (self._name, key, method_name)) 463 else: 464 sys.stdout.write("debug> Observer: '%s' registering '%s'.\n" % (self._name, key)) 465 466 # Add the method to the dictionary of callbacks. 467 self._callback[key] = method 468 469 # Add the method name. 470 self._method_names[key] = method_name 471 472 # Add the key to the ordered list. 473 self._keys.append(key)
474 475
476 - def reset(self):
477 """Reset the object.""" 478 479 # Debugging. 480 if self._status.debug: 481 sys.stdout.write("debug> Resetting observer '%s'.\n" % self._name) 482 483 # Reinitialise the dictionary of callback methods. 484 self._callback = {} 485 486 # Reinitialise the key list. 487 self._keys = []
488 489
490 - def unregister(self, key):
491 """Unregister the method corresponding to the key. 492 493 @param key: The key to identify the observer's method. 494 @type key: str 495 """ 496 497 # Debugging. 498 if self._status.debug: 499 sys.stdout.write("debug> Observer: '%s' unregistering '%s'.\n" % (self._name, key)) 500 501 # Does not exist, so return (allow multiple code paths to unregister methods). 502 if key not in self._keys: 503 if self._status.debug: 504 sys.stdout.write("debug> The key '%s' does not exist.\n" % key) 505 return 506 507 # Remove the method from the dictionary of callbacks. 508 self._callback.pop(key) 509 510 # Remove the name. 511 self._method_names.pop(key) 512 513 # Remove the key for the ordered key list. 514 self._keys.remove(key)
515 516 517
518 -class Relax_lock:
519 """A type of locking object for relax.""" 520
521 - def __init__(self, name='unknown', fake_lock=False):
522 """Set up the lock-like object. 523 524 @keyword name: The special name for the lock, used in debugging. 525 @type name: str 526 @keyword fake_lock: A flag which is True will allow this object to be debugged as the locking mechanism is turned off. 527 @type fake_lock: bool 528 """ 529 530 # Store the args. 531 self.name = name 532 self._fake_lock = fake_lock 533 534 # Init a reentrant lock object. 535 self._lock = RLock() 536 537 # The status container. 538 self._status = Status() 539 540 # Fake lock. 541 if self._fake_lock: 542 # Track the number of acquires. 543 self._lock_level = 0
544 545
546 - def acquire(self, acquirer='unknown'):
547 """Simulate the RLock.acquire() mechanism. 548 549 @keyword acquirer: The optional name of the acquirer. 550 @type acquirer: str 551 """ 552 553 # Debugging. 554 if self._status.debug: 555 sys.stdout.write("debug> Lock '%s': Acquisition by '%s'.\n" % (self.name, acquirer)) 556 557 # Fake lock. 558 if self._fake_lock: 559 # Increment the lock level. 560 self._lock_level += 1 561 562 # Throw an error. 563 if self._lock_level > 1: 564 raise 565 566 # Return to prevent real locking. 567 return 568 569 # Acquire the real lock. 570 lock = self._lock.acquire() 571 572 # Return the real lock. 573 return lock
574 575
576 - def locked(self):
577 """Simulate the RLock.locked() mechanism.""" 578 579 # Call the real method. 580 return self._lock.locked()
581 582
583 - def release(self, acquirer='unknown'):
584 """Simulate the RLock.release() mechanism. 585 586 @keyword acquirer: The optional name of the acquirer. 587 @type acquirer: str 588 """ 589 590 # Debugging. 591 if self._status.debug: 592 sys.stdout.write("debug> Lock '%s': Release by '%s'.\n" % (self.name, acquirer)) 593 594 # Fake lock. 595 if self._fake_lock: 596 # Decrement the lock level. 597 self._lock_level -= 1 598 599 # Return to prevent real lock release. 600 return 601 602 # Release the real lock. 603 release = self._lock.release() 604 605 # Return the status. 606 return release
607 608 609
610 -class Observer_container:
611 """The container for holding all the observer objects.""" 612
613 - def info(self):
614 """Print out info about all the status objects.""" 615 616 # Blacklisted objects. 617 blacklist = list(self.__class__.__dict__.keys() + dict.__dict__.keys()) 618 619 # Loop over all objects in this container. 620 for name in dir(self): 621 # Skip blacklisted objects. 622 if name in blacklist: 623 continue 624 625 # Get the object. 626 obj = getattr(self, name) 627 628 # An observer object. 629 print("Observer '%s' keys: %s" % (obj._name, obj._keys))
630