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