# @GPL_HEADER_START
# Use of the RAVE and related source code is subject to the terms
# of the following licenses:
# 
# GNU Public License (GPL) Rights pursuant to Version 2, June 1991
# Government Purpose License Rights (GPLR) pursuant to DFARS 252.225-7013
# 
# NO WARRANTY
# 
# ANY INFORMATION, MATERIALS, SERVICES, INTELLECTUAL PROPERTY OR OTHER
# PROPERTY OR RIGHTS GRANTED OR PROVIDED BY CARNEGIE MELLON UNIVERSITY
# PURSUANT TO THIS LICENSE (HEREINAFTER THE "DELIVERABLES") ARE ON AN
# "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY
# KIND, EITHER EXPRESS OR IMPLIED AS TO ANY MATTER INCLUDING, BUT NOT
# LIMITED TO, WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE,
# MERCHANTABILITY, INFORMATIONAL CONTENT, NONINFRINGEMENT, OR ERROR-FREE
# OPERATION. CARNEGIE MELLON UNIVERSITY SHALL NOT BE LIABLE FOR INDIRECT,
# SPECIAL OR CONSEQUENTIAL DAMAGES, SUCH AS LOSS OF PROFITS OR INABILITY
# TO USE SAID INTELLECTUAL PROPERTY, UNDER THIS LICENSE, REGARDLESS OF
# WHETHER SUCH PARTY WAS AWARE OF THE POSSIBILITY OF SUCH DAMAGES.
# LICENSEE AGREES THAT IT WILL NOT MAKE ANY WARRANTY ON BEHALF OF
# CARNEGIE MELLON UNIVERSITY, EXPRESS OR IMPLIED, TO ANY PERSON
# CONCERNING THE APPLICATION OF OR THE RESULTS TO BE OBTAINED WITH THE
# DELIVERABLES UNDER THIS LICENSE.
# 
# Licensee hereby agrees to defend, indemnify, and hold harmless Carnegie
# Mellon University, its trustees, officers, employees, and agents from
# all claims or demands made against them (and any related losses,
# expenses, or attorney's fees) arising out of, or relating to Licensee's
# and/or its sub licensees' negligent use or willful misuse of or
# negligent conduct or willful misconduct regarding the Software,
# facilities, or other rights or assistance granted by Carnegie Mellon
# University under this License, including, but not limited to, any
# claims of product liability, personal injury, death, damage to
# property, or violation of any laws or regulations.
# 
# Carnegie Mellon University Software Engineering Institute authored
# documents are sponsored by the U.S. Department of Defense under
# Contract F19628-00-C-0003. Carnegie Mellon University retains
# copyrights in all material produced under this contract. The U.S.
# Government retains a non-exclusive, royalty-free license to publish or
# reproduce these documents, or allow others to do so, for U.S.
# Government purposes only pursuant to the copyright license under the
# contract clause at 252.227.7013.
# 
# @GPL_HEADER_END
"""
The RAVE Request object, and functions used to obtain and release it.
"""
import threading

# Thread-local, module-scope storage
class ThreadContext(threading.local):
    def __init__(self):
        self.requests = []
    def export_state(self):
        return (self.requests[0], self.requests[-1])
    def import_state(self, state):
        self.requests = list(state)
    def current_request(self):
        return self.requests[-1]
    def allocate(self, *args, **kwargs):
        if len(self.requests) == 0:
            kwargs['global_req'] = None
        else:
            kwargs['global_req'] = self.requests[0]
        req = Request(*args, **kwargs)
        self.requests.append(req)
        return req
    def free(self):
        self.requests.pop()
            
storage = ThreadContext()

class Request(object):
    """
    A state container for invocations of RAVE operations.
    """
    def __init__(self, op, options, args, kwargs, cache_key, global_req=None):
        """Called internally by RAVE to create Request objects.
        Users should not have to create Requests themselves."""
        self._op = op
        self._options = options
        self._args = args
        self._kwargs = kwargs
        self._cache_key = cache_key
        self._global_req = global_req
        self._job_scheduled = False
        self._shared_state = {}
    def args(self):
        "Positional arguments used to invoke this function."
        return self._args
    def kwargs(self):
        "Positional arguments used to invoke this function."
        return self._kwargs
    def job_scheduled(self, scheduled=None):
        if scheduled is not None:
            self._job_scheduled = scheduled
        return self._job_scheduled
    def cache_key(self):
        return self._cache_key
    def operation(self):
        "The Operation currently being run."
        return self._op
    def cache(self):
        """The cache into which data from the current operation will be 
        written.""" 
        return self.operation().cache()
    def normalize(self, *args, **kwargs):
        """
        Normalize arguments into the form the cache expects. Operations that 
        must manipulate the cache directly must do this so the cache can 
        compare entries effectively. This is an advanced technique; most 
        operations should never use this method.
        """
        return self.operation().normalize(*args, **kwargs)
    def option(self, optname, check_global=False):
        """
        Get meta-information passed to the operation as special
        arguments.  This facility is used internally by RAVE to
        direct how and whether to cache the results of operations;
        certain options are exposed and documented in the _RAVE
        Administrator's Guide_. They are read-only, and presented
        only for informational purposes.  Options have a local scope
        (the current operation being run) and a global scope (the
        topmost parent operation of the operation being run).

        Options are read-only. If you need to share state with other
        functions (like your expiration strategy) use the get() and
        set() methods.

        Parameters:
        optname: str
            The name of the option to look for
        check_global: boolean
            Whether to look for the option in global scope 
            By default, look only in the local object.
        """
        try:
            return self._options[optname]
        except KeyError:
            if check_global and self._global_req:
                return self._global_req.option(optname)
            else:
                return None
    def get(self, name):
        """
        Get an arbitrary property associated with this request. Use
        these properties to share information between the operation
        functions like the expiration strategy which also have
        access to the Request object.

        Parameters:
        name: <any>
            The name of the shared value
        Returns: rc
            rc: <any> | None
                The stored value, or None if there is no value by
                that name.
        """
        return self._shared_state[name]
    def set(self, name, value):
        """
        Set a property associated with this request. See get() for
        a slightly fuller discussion of this facility.

        Parameters:
        name: <any>
            The name of the shared value
        value: <any>
            The value of the shared value
        """
        self._shared_state[name] = value
        
        

__all__ = ['Request']
