Source code for schrodinger.infra.mminit
"""
Assist in initialization and termination of mmlibs.
"""
# Copyright Schrodinger, LLC. All rights reserved.
import contextlib
import functools
from schrodinger.infra import mm
[docs]class Initializer:
    """
    A class to handle initialization and termination of needed mmlibs.
    It is designed to be created on import and initialize needed mmlibs for
    a module. In general, automatic termination is not supported, but terminate
    method can be called manually.
    """
[docs]    def __init__(self, initializers, terminators):
        self.initializers = initializers
        self.terminators = terminators
        self.error_handler = None
        self.init_count = 0
        self.initialize() 
[docs]    def initialize(self, error_handler=None):
        """
        Initialize all needed mmlibs.
        """
        if error_handler is None:
            error_handler = mm.error_handler
        # Keep track of the error handler used to initialize.
        self.error_handler = error_handler
        for init in self.initializers:
            init(error_handler)
        self.init_count += 1 
[docs]    def terminate(self):
        """
        Terminate all previously initialized mmlibs.
        """
        for term in self.terminators:
            term()
        self.init_count -= 1 
    def __copy__(self):
        # This implementation just returns 'self', effectively preventing
        # copying. Each Initializer instance should be a singleton providing
        # for initialization and termination of its set of mmlibs.
        #
        # I can't think of a reason to need an actual copy, but if a need
        # arises, be sure that init_count in the new instance is reset to
        # zero.
        return self
    def __deepcopy__(self, memo=None):
        # Prevent copying. See discussion in __copy__.
        return self 
class _mmlib_new_delete:
    """
    Use new and delete as context manager pairs.
    Useful if new returns an `int`, which of course has no
    knowledge of the underlying object's lifetime.
    """
    def __init__(self, new, delete):
        self._new = new
        self._delete = delete
    def new(self, *args):
        v = self._new(*args)
        @contextlib.contextmanager
        def scope():
            try:
                yield v
            finally:
                self._delete(v)
        return scope()
[docs]@contextlib.contextmanager
def MMLib(init, term, new=None, delete=None):
    """
    Decorator and context manager to control initialization of a single
    library.
    """
    init(mm.error_handler)
    try:
        obj = None
        if new and delete:
            obj = _mmlib_new_delete(new, delete)
        elif new or delete:
            raise ValueError(
                'Please provide both `new` and `delete`, or neither.')
        yield obj
    finally:
        term() 
# Make life easier by canning definitions for specific mmlibs:
#
# Use like::
#
#    with mminit.mmfrag() as mmfrag:
#       with mmfrag.new() as h:
#           # do stuff with `h`
#
mmct = functools.partial(MMLib, mm.mmct_initialize, mm.mmct_terminate)
mmfrag = functools.partial(MMLib, mm.mmfrag_initialize, mm.mmfrag_terminate,
                           mm.mmfrag_new, mm.mmfrag_delete)