Source code for openquake.hazardlib.speedups
# The Hazard Library
# Copyright (C) 2012-2014, GEM Foundation
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""
Module :mod:`openquake.hazardlib.speedups` contains internal utilities for
managing alternative implementation of the same functionality depending on
their availability.
"""
import inspect
[docs]class SpeedupsRegistry(object):
    """
    Speedups registry allows to manage alternative implementations
    of functions. Typical use case for it is something like this:
    .. code-block:: python
        # in the module namespace
        def do_foo(foo, bar):
            # code in pure python
            ...
        def do_bar(baz, quux):
            # code in pure python
            ...
        # in the end of the module
        try:
            import _foobar_speedups
        except ImportError:
            import warnings
            warnings.warn("foobar speedups are not available", RuntimeWarning)
        else:
            from openquake.hazardlib import speedups
            def _c_do_foo(foo, bar):
                return _foobar_speedups.do_foo(foo, bar)
            speedups.register(do_foo, _c_do_foo)
            del _c_do_foo
            def _c_do_bar(baz, quux):
                return _foobar_speedups.do_foo(baz, quux)
            speedups.register(do_bar, _c_do_bar)
            del _c_do_bar
    Global registry is being used here. All speedups are enabled by default.
    In order to disable them, use :meth:`disable`.
    """
    def __init__(self):
        self.enabled = True
        self.funcs = {}
[docs]    def register(self, func, altfunc):
        """
        Add a function and its alternative implementation to the registry.
        If registry is enabled, function code will be substituted
        by an alternative implementation immediately.
        :param func:
            A function object to patch.
        :param altfunc:
            An alternative implementation of the function. Must have
            the same signature and is supposed to behave exactly
            the same way as ``func``.
        """
        assert inspect.getargspec(func) == inspect.getargspec(altfunc), \
            
"functions signatures are different in %s and %s" % \
            
(func, altfunc)
        self.funcs[func] = (func.__code__, altfunc.__code__)
        if self.enabled:
            # here we substitute the "func_code" attribute of the function,
            # which allows us not to worry of when and how is this function
            # being imported by other modules
            func.__code__ = altfunc.__code__ 
[docs]    def enable(self):
        """
        Set implementation to "alternative" for all the registered functions.
        """
        for func in self.funcs:
            origcode, altcode = self.funcs[func]
            func.__code__ = altcode
        self.enabled = True 
[docs]    def disable(self):
        """
        Set implementation to "original" for all the registered functions.
        """
        for func in self.funcs:
            origcode, altcode = self.funcs[func]
            func.__code__ = origcode
        self.enabled = False  
global_registry = SpeedupsRegistry()
#: Global (default) registry :meth:`register`.
register = global_registry.register
#: Global (default) registry :meth:`enable`.
enable = global_registry.enable
#: Global (default) registry :meth:`disable`.
disable = global_registry.disable