"Fossies" - the Fresh Open Source Software Archive

Member "relax-5.0.0/status.py" (5 Dec 2019, 20240 Bytes) of package /linux/privat/relax-5.0.0.src.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "status.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 4.1.3_vs_5.0.0.

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