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