예제 #1
0
    def __update_return_type(self):
        """
        Updates the return types within self.kwargs['compss_retvalue']
        """
        from pycompss.api.parameter import Parameter
        from pycompss.api.parameter import DIRECTION

        # This condition is interesting, because a user can write returns=list
        # However, lists have the attribute __len__ but raise an exception.
        # Since the user does not indicate the length, it will be managed
        # as a single return.
        # When the user specifies the length, it is possible to manage the
        # elements independently.
        if not hasattr(self.kwargs['returns'],
                       '__len__') or type(self.kwargs['returns']) is type:
            # Simple return
            retType = get_COMPSs_type(self.kwargs['returns'])
            self.kwargs['compss_retvalue'] = Parameter(
                p_type=retType, p_direction=DIRECTION.OUT)
        else:
            # Multi return
            self.has_multireturn = True
            returns = []
            for r in self.kwargs['returns']:
                retType = get_COMPSs_type(r)
                returns.append(
                    Parameter(p_type=retType, p_direction=DIRECTION.OUT))
            self.kwargs['compss_retvalue'] = tuple(returns)
예제 #2
0
def fromDictToParameter(d):
    '''
    Convert a Dict defined by a user for a parameter into a real Parameter
    object.
    :param d: Dictionary (mandatory to have 'Type' key).
    :return: Parameter object.
    '''
    from pycompss.api.parameter import Parameter
    from pycompss.api.parameter import Type
    from pycompss.api.parameter import Direction
    from pycompss.api.parameter import Stream
    from pycompss.api.parameter import Prefix
    if Type not in d:  # If no Type specified => IN
        d[Type] = Parameter()
    p = d[Type]
    if Direction in d:
        p.setDirection(d[Direction])
    if Stream in d:
        p.setStream(d[Stream])
    if Prefix in d:
        p.setPrefix(d[Prefix])
    return p
예제 #3
0
def _build_return_objects(f_returns):
    """
    Build the return object from the f_returns dictionary and include their filename in f_returns.

    WARNING: Updates f_returns dictionary

    :param f_returns: Dictionary which contains the return objects and Parameters.
    :return: future object/s
    """

    fo = None
    if len(f_returns) == 0:
        # No return
        return fo
    elif len(f_returns) == 1:
        # Simple return
        if __debug__:
            logger.debug("Simple object return found.")
        # Build the appropriate future object
        ret_value = f_returns['compss_retvalue'].object
        if ret_value in _python_to_compss:  # primitives, string, dic, list, tuple
            fo = Future()
        elif inspect.isclass(ret_value):
            # For objects:
            # type of future has to be specified to allow o = func; o.func
            try:
                fo = ret_value()
            except TypeError:
                if __debug__:
                    logger.warning(
                        "Type {0} does not have an empty constructor, building generic future object"
                        .format(ret_value))
                fo = Future()
        else:
            fo = Future()  # modules, functions, methods
        obj_id = get_object_id(fo, True)
        if __debug__:
            logger.debug("Setting object %s of %s as a future" %
                         (obj_id, type(fo)))
        ret_filename = temp_dir + _temp_obj_prefix + str(obj_id)
        objid_to_filename[obj_id] = ret_filename
        pending_to_synchronize[obj_id] = fo
        f_returns['compss_retvalue'] = Parameter(p_type=TYPE.FILE,
                                                 p_direction=DIRECTION.OUT,
                                                 p_prefix="#")
        f_returns['compss_retvalue'].file_name = ret_filename
    else:
        # Multireturn
        fo = []
        if __debug__:
            logger.debug("Multiple objects return found.")
        for k, v in f_returns.items():
            # Build the appropriate future object
            if v.object in _python_to_compss:  # primitives, string, dic, list, tuple
                foe = Future()
            elif inspect.isclass(v.object):
                # For objects:
                # type of future has to be specified to allow o = func; o.func
                try:
                    foe = v.object()
                except TypeError:
                    if __debug__:
                        logger.warning(
                            "Type {0} does not have an empty constructor, building generic future object"
                            .format(v['Value']))
                    foe = Future()
            else:
                foe = Future()  # modules, functions, methods
            fo.append(foe)
            obj_id = get_object_id(foe, True)
            if __debug__:
                logger.debug("Setting object %s of %s as a future" %
                             (obj_id, type(foe)))
            ret_filename = temp_dir + _temp_obj_prefix + str(obj_id)
            objid_to_filename[obj_id] = ret_filename
            pending_to_synchronize[obj_id] = foe
            # Once determined the filename where the returns are going to
            # be stored, create a new Parameter object for each return object
            f_returns[k] = Parameter(p_type=TYPE.FILE,
                                     p_direction=DIRECTION.OUT,
                                     p_prefix="#")
            f_returns[k].file_name = ret_filename
    return fo
예제 #4
0
def reveal_objects(values, spec_args, deco_kwargs, compss_types, process_name,
                   has_return, is_multi_return, num_return):
    """
    Function that goes through all parameters in order to
    find and open the files.
    :param values: <List> - The value of each parameter.
    :param spec_args: <List> - Specific arguments.
    :param deco_kwargs: <List> - The decoratos.
    :param compss_types: <List> - The types of the values.
    :param process_name: <String> - Process name (id).
    :param has_returns: <Boolean>  - If the function returns a value.
    :param is_multi_return: <Boolean> - If the return is a multireturn.
    :param num_return: <Int> - Number of returns
    :return: a list with the real values
    """
    from pycompss.api.parameter import Parameter
    from pycompss.api.parameter import TYPE
    from pycompss.api.parameter import DIRECTION

    num_pars = len(spec_args)
    real_values = []
    to_serialize = []

    if has_return:
        num_pars -= num_return  # return value must not be passed to the function call

    # print('Spec args: %s'%(str(spec_args)))
    # print('COMPSs Types: %s'%(str(compss_types)))
    # print('Values: %s'%(str(values)))

    for i in range(num_pars):
        spec_arg = spec_args[i]
        compss_type = compss_types[i]
        value = values[i]
        if i == 0:
            if spec_arg == 'self':  # callee object
                if deco_kwargs['isModifier']:
                    d = DIRECTION.INOUT
                else:
                    d = DIRECTION.IN
                deco_kwargs[spec_arg] = Parameter(p_type=TYPE.OBJECT,
                                                  p_direction=d)
            elif inspect.isclass(value):  # class (it's a class method)
                real_values.append(value)
                continue

        p = deco_kwargs.get(spec_arg)
        if p is None:  # decoration not present, using default
            p = deco_kwargs['varargsType'] if spec_arg.startswith(
                '*args') else Parameter()

        if compss_type == TYPE.FILE and p.type != TYPE.FILE:
            # Getting ids and file names from passed files and objects pattern
            # is: "originalDataID:destinationDataID;flagToPreserveOriginalData:flagToWrite:PathToFile"
            complete_fname = value.split(':')
            if len(complete_fname) > 1:
                # In NIO we get more information
                forig = complete_fname[0]
                fdest = complete_fname[1]
                preserve = complete_fname[2]
                write_final = complete_fname[3]
                fname = complete_fname[4]
                preserve, write_final = list(
                    map(lambda x: x == "true", [preserve, write_final]))
                suffix_name = forig
            else:
                # In GAT we only get the name
                fname = complete_fname[0]

            value = fname
            # For COMPSs it is a file, but it is actually a Python object
            logger.debug("Processing a hidden object in parameter %d", i)
            obj = deserialize_from_file(value)
            real_values.append(obj)
            if p.direction != DIRECTION.IN:
                to_serialize.append((obj, value))
        else:
            # print('compss_type' + str(compss_type) + ' type' + str(p.type))
            if compss_type == TYPE.FILE:
                complete_fname = value.split(':')
                if len(complete_fname) > 1:
                    # In NIO we get more information
                    forig = complete_fname[0]
                    fdest = complete_fname[1]
                    preserve = complete_fname[2]
                    write_final = complete_fname[3]
                    fname = complete_fname[4]
                else:
                    # In GAT we only get the name
                    fname = complete_fname[0]
                value = fname
            real_values.append(value)
    return real_values, to_serialize
예제 #5
0
    def __init__(self, *args, **kwargs):
        """
        If there are decorator arguments, the function to be decorated is
        not passed to the constructor!
        """
        logger.debug("Init @task decorator...")

        # Defaults
        self.args = args  # Not used
        self.kwargs = kwargs  # The only ones actually used: (decorators)
        self.is_instance = False

        # Pre-process decorator arguments
        from pycompss.api.parameter import Parameter
        from pycompss.api.parameter import IN
        from pycompss.api.parameter import TYPE
        from pycompss.api.parameter import DIRECTION

        # Reserved PyCOMPSs keywords and default values

        reserved_keywords = {
            'isModifier': True,
            'returns': False,
            'priority': False,
            'isReplicated': False,
            'isDistributed': False,
            'varargsType': IN
        }

        for (reserved_keyword, default_value) in reserved_keywords.items():
            if reserved_keyword not in self.kwargs:
                self.kwargs[reserved_keyword] = default_value

        # Remove old args
        for old_vararg in [
                x for x in self.kwargs.keys() if x.startswith('*args')
        ]:
            self.kwargs.pop(old_vararg)

        import copy

        if i_am_at_master():
            for arg_name in self.kwargs.keys():
                if arg_name not in [
                        'isModifier', 'returns', 'priority', 'isReplicated',
                        'isDistributed'
                ]:
                    # Prevent p.value from being overwritten later by ensuring
                    # each Parameter is a separate object
                    p = self.kwargs[arg_name]
                    pcopy = copy.copy(p)  # shallow copy
                    self.kwargs[arg_name] = pcopy

        if self.kwargs['isModifier']:
            direction = DIRECTION.INOUT
        else:
            direction = DIRECTION.IN

        # Add callee object parameter
        self.kwargs['self'] = Parameter(p_type=TYPE.OBJECT,
                                        p_direction=direction)

        # Check the return type:
        if self.kwargs['returns']:
            # This condition is interesting, because a user can write
            # returns=list
            # However, lists have the attribute __len__ but raise an exception.
            # Since the user does not indicate the length, it will be managed
            # as a single return.
            # When the user specifies the length, it is possible to manage the
            # elements independently.
            if not hasattr(self.kwargs['returns'],
                           '__len__') or type(self.kwargs['returns']) is type:
                # Simple return
                retType = getCOMPSsType(self.kwargs['returns'])
                self.kwargs['compss_retvalue'] = Parameter(
                    p_type=retType, p_direction=DIRECTION.OUT)
            else:
                # Multi return
                i = 0
                returns = []
                for r in self.kwargs['returns']:  # This adds only one? - yep
                    retType = getCOMPSsType(r)
                    returns.append(
                        Parameter(p_type=retType, p_direction=DIRECTION.OUT))
                self.kwargs['compss_retvalue'] = tuple(returns)

        logger.debug("Init @task decorator finished.")
예제 #6
0
def infer_types_and_serialize_objects(spec_args, first_par, num_pars,
                                      fileNames, self_kwargs, args):
    '''
    Infer COMPSs types for the task parameters and serialize them.
    :param spec_args: <List of strings> - Names of the task arguments
    :param first_par: <Integer> - First parameter
    :param num_pars: <Integer> - Number of parameters
    :param fileNames: <Dictionary> - Return objects filenames
    :param self_kwargs: <Dictionary> - Decorator arguments
    :param args: <List> - Unnamed arguments
    :return: Tuple of self_kwargs updated and a dictionary containing
             if the objects are future elements.
    '''
    is_future = {}
    max_obj_arg_size = 320000
    for i in range(first_par, num_pars):
        spec_arg = spec_args[i]
        p = self_kwargs.get(spec_arg)
        if p is None:
            if __debug__:
                logger.debug('Adding default decoration for param %s' % spec_arg)
            p = Parameter()
            self_kwargs[spec_arg] = p
        elif type(p) is dict:
            # The user has provided some information about a parameter within
            # the @task parameter
            if __debug__:
                logger.debug('Checking decoration for param %s' % spec_arg)
            p = fromDictToParameter(p)

        if spec_args[0] != 'self':
            # It is a function
            if i < len(args):
                p.value = args[i]
            else:
                p.value = fileNames[spec_arg]
        else:
            # It is a class function
            if spec_arg == 'self':
                p.value = args[0]
                # Check if self is a persistent object and set its type if it really is.
                if is_PSCO(p.value):
                    p.type = TYPE.EXTERNAL_PSCO
            elif spec_arg.startswith('compss_retvalue'):
                p.value = fileNames[spec_arg]
            else:
                p.value = args[i]

        # Update the parameter type if changed between task calls
        # Respect p.type if none to be inferred later.
        # Respect p.type == TYPE.FILE since may be updated wrongly to str.
        new_type = python_to_compss.get(type(p.value))
        if p.type is not None and p.type != TYPE.FILE and p.type != new_type:
            p.type = new_type

        val_type = type(p.value)
        is_future[i] = (val_type == Future)
        if __debug__:
            logger.debug('Parameter ' + spec_arg)
            logger.debug('\t- Value type: ' + str(val_type))
            logger.debug('\t- User-defined type: ' + str(p.type))

        # Infer type if necessary
        if p.type is None:
            if __debug__:
                logger.debug('Inferring type due to None pType.')
            p.type = python_to_compss.get(val_type)
            if p.type is None:
                if is_PSCO(p.value):
                    p.type = TYPE.EXTERNAL_PSCO
                else:
                    p.type = TYPE.OBJECT
            if __debug__:
                logger.debug('\t- Inferred type: %d' % p.type)

        # Convert small objects to string if object_conversion enabled
        # Check if the object is small in order not to serialize it.
        if object_conversion:
            p, bytes = convert_object_to_string(p, is_future.get(i), max_obj_arg_size, policy='objectSize')
            max_obj_arg_size -= bytes

        # Serialize objects into files
        p = serialize_object_into_file(p, is_future, i, val_type)

        if __debug__:
            logger.debug('Final type for parameter %s: %d' % (spec_arg, p.type))

    return self_kwargs, is_future
예제 #7
0
def build_return_objects(self_kwargs, spec_args):
    '''
    Build the return object and updates the self_kwargs and spec_args
    structures (as tuple in the multireturn case).
    :param self_kwargs:
    :param spec_args:
    :return:
    '''
    # Check if the returns statement contains an integer value.
    # In such case, build a list of objects of value length and set it in ret_type.
    if isinstance(self_kwargs['returns'], int):
        num_rets = self_kwargs['returns']
        # Assume all as objects (generic type).
        # It will not work properly when using user defined classes, since
        # the future object built will not be of the same type as expected
        # and may cause "AttributeError" since the 'object' does not have
        # the attributes of the class
        if num_rets > 1:
            ret_type = [object for _ in range(num_rets)]
        else:
            ret_type = object
    else:
        ret_type = self_kwargs['returns']
    fileNames = {}  # return files locations
    if ret_type:
        fu = []
        # Create future for return value
        if isinstance(ret_type, list) or isinstance(ret_type, tuple):  # MULTIRETURN
            if __debug__:
                logger.debug('Multiple objects return found.')
            # This condition fixes the multiple calls to a function with multireturn bug.
            if 'compss_retvalue' in spec_args:
                spec_args.remove('compss_retvalue')  # remove single return... it contains more than one
                if 'compss_retvalue' in self_kwargs:
                    self_kwargs.pop('compss_retvalue')
                else:
                    assert 'compss_retvalue0' in self_kwargs, 'Inconsistent state: multireturn detected, but there is no compss_retvalue0'
            pos = 0
            for i in ret_type:
                # Build the appropriate future object
                if i in python_to_compss:  # primitives, string, dic, list, tuple
                    fue = Future()
                elif inspect.isclass(i):
                    # For objects:
                    # type of future has to be specified to allow o = func; o.func
                    try:
                        fue = i()
                    except TypeError:
                        if __debug__:
                            logger.warning('Type {0} does not have an empty constructor, building generic future object'.format(ret_type))
                        fue = Future()
                else:
                    fue = Future()  # modules, functions, methods
                fu.append(fue)
                obj_id = get_object_id(fue, True)
                if __debug__:
                    logger.debug('Setting object %s of %s as a future' % (obj_id, type(fue)))
                ret_filename = temp_dir + temp_obj_prefix + str(obj_id)
                objid_to_filename[obj_id] = ret_filename
                pending_to_synchronize[obj_id] = fue
                fileNames['compss_retvalue' + str(pos)] = ret_filename
                # Once determined the filename where the returns are going to
                # be stored, create a new Parameter object for each return
                # object
                self_kwargs['compss_retvalue' + str(pos)] = Parameter(p_type=TYPE.FILE, p_direction=DIRECTION.OUT, p_prefix="#")
                spec_args.append('compss_retvalue' + str(pos))
                pos += 1
            self_kwargs['num_returns'] = pos  # Update the amount of objects to be returned
        else:  # SIMPLE RETURN
            # Build the appropriate future object
            if ret_type in python_to_compss:  # primitives, string, dic, list, tuple
                fu = Future()
            elif inspect.isclass(ret_type):
                # For objects:
                # type of future has to be specified to allow o = func; o.func
                try:
                    fu = ret_type()
                except TypeError:
                    if __debug__:
                        logger.warning('Type {0} does not have an empty constructor, building generic future object'.format(ret_type))
                    fu = Future()
            else:
                fu = Future()  # modules, functions, methods
            obj_id = get_object_id(fu, True)
            if __debug__:
                logger.debug('Setting object %s of %s as a future' % (obj_id, type(fu)))
            ret_filename = temp_dir + temp_obj_prefix + str(obj_id)
            objid_to_filename[obj_id] = ret_filename
            pending_to_synchronize[obj_id] = fu
            fileNames['compss_retvalue'] = ret_filename
            self_kwargs['compss_retvalue'] = Parameter(p_type=TYPE.FILE, p_direction=DIRECTION.OUT, p_prefix="#")
    else:
        fu = None

    return fu, fileNames, self_kwargs, spec_args
예제 #8
0
    def __update_return_if_no_returns(self, f):
        """
        Checks the code looking for return statements if no returns is specified in @task decorator.
        :param f: Function to check
        """
        from pycompss.api.parameter import Parameter
        from pycompss.api.parameter import DIRECTION
        from pycompss.api.parameter import TYPE

        source_code = get_wrapped_source(f).strip()
        if self.is_instance or source_code.startswith(
                '@classmethod'):  # TODO: WHAT IF IS CLASSMETHOD FROM BOOLEAN?
            # It is a task defined within a class (can not parse the code with ast since the class does not
            # exist yet. Alternatively, the only way I see is to parse it manually line by line.
            retMask = []
            code = source_code.split('\n')
            for line in code:
                if 'return ' in line:
                    retMask.append(True)
                else:
                    retMask.append(False)
        else:
            code = [node for node in ast.walk(ast.parse(source_code))]
            retMask = [isinstance(node, ast.Return) for node in code]
        if any(retMask):
            print("INFO! Return found in function " + f.__name__ +
                  " without 'returns' statement at task definition.")
            self.has_return = True
            lines = [i for i, li in enumerate(retMask) if li]
            max_num_returns = 0
            if self.is_instance or source_code.startswith('@classmethod'):
                # Parse code as string (it is a task defined within a class)
                def _has_multireturn(statement):
                    v = ast.parse(statement.strip())
                    try:
                        if len(v.body[0].value.elts) > 1:
                            return True
                        else:
                            return False
                    except:
                        # KeyError: 'elts' means that it is a multiple return.
                        # "Ask forgiveness not permission"
                        return False

                def _get_return_elements(statement):
                    v = ast.parse(statement.strip())
                    return len(v.body[0].value.elts)

                for i in lines:
                    if _has_multireturn(code[i]):
                        self.has_multireturn = True
                        num_returns = _get_return_elements(code[i])
                        if num_returns > max_num_returns:
                            max_num_returns = num_returns
            else:
                # Parse code AST (it is not a task defined within a class)
                for i in lines:
                    try:
                        if 'elts' in code[i].value.__dict__:
                            self.has_multireturn = True
                            num_returns = len(code[i].value.__dict__['elts'])
                            if num_returns > max_num_returns:
                                max_num_returns = num_returns
                    except:
                        # KeyError: 'elts' means that it is a multiple return.
                        # "Ask forgiveness not permission"
                        pass
            if self.has_multireturn:
                if __debug__:
                    logger.debug("Multireturn found: %s" %
                                 str(max_num_returns))
                self.kwargs['returns'] = []
                returns = []
                for _ in range(max_num_returns):
                    self.kwargs['returns'].append(object())
                    returns.append(
                        Parameter(p_type=TYPE.FILE, p_direction=DIRECTION.OUT))
                self.kwargs['compss_retvalue'] = tuple(returns)
            else:
                if __debug__:
                    logger.debug("Return found")
                self.kwargs['returns'] = object()
                self.kwargs['compss_retvalue'] = Parameter(
                    p_type=TYPE.FILE, p_direction=DIRECTION.OUT)
            self.f_argspec.args.append('compss_retvalue')
예제 #9
0
    def __call__(self, f):
        """
        If there are decorator arguments, __call__() is only called
        once, as part of the decoration process! You can only give
        it a single argument, which is the function object.
        """
        # Check if under the PyCOMPSs scope
        if not self.scope:
            return self.__not_under_pycompss_scope(f)

        # Imports
        from pycompss.api.parameter import Parameter
        from pycompss.api.parameter import TYPE
        from pycompss.api.parameter import DIRECTION
        from pycompss.util.interactive_helpers import updateTasksCodeFile
        from pycompss.util.location import i_am_at_master

        if __debug__:
            logger.debug("Call in @task decorator...")

        # Assume it is an instance method if the first parameter of the
        # function is called 'self'
        # "I would rely on the convention that functions that will become
        # methods have a first argument named self, and other functions don't.
        # Fragile, but then, there's no really solid way."
        self.f_argspec = inspect.getargspec(f)

        # Set default booleans
        self.is_instance = False
        self.is_classmethod = False
        self.has_varargs = False
        self.has_keywords = False
        self.has_defaults = False
        self.has_return = False
        self.has_multireturn = False

        # Step 1.- Check if it is an instance method.
        # Question: Will the first condition evaluate to false? spec_args will
        # always be a named tuple, so it will always return true if evaluated
        # as a bool
        # Answer: The first condition evaluates if args exists (a list) and is
        # not empty in the spec_args. The second checks if the first argument
        # in that list is 'self'. In case that the args list exists and its
        # first element is self, then the function is considered as an instance
        # function (task defined within a class).
        if self.f_argspec.args and self.f_argspec.args[0] == 'self':
            self.is_instance = True
            if self.kwargs['isModifier']:
                direction = DIRECTION.INOUT
            else:
                direction = DIRECTION.IN
            # Add callee object parameter
            self.kwargs['self'] = Parameter(p_type=TYPE.OBJECT,
                                            p_direction=direction)

        # Step 2.- Check if it is a class method.
        # The check of 'cls' may be weak but it is PEP8 style agreements.
        if self.f_argspec.args and self.f_argspec.args[0] == 'cls':
            self.is_classmethod = True
            # Add class object parameter
            self.kwargs['self'] = Parameter(p_type=TYPE.OBJECT,
                                            p_direction=DIRECTION.IN)

        # Step 3.- Check if it has varargs (contains *args?)
        # Check if contains *args
        if self.f_argspec.varargs is not None:
            self.has_varargs = True

        # Step 4.- Check if it has keyword arguments (contains **kwargs?)
        # Check if contains **kwargs
        if self.f_argspec.keywords is not None:
            self.has_keywords = True

        # Step 5.- Check if it has default values
        # Check if has default values
        if self.f_argspec.defaults is not None:
            self.has_defaults = True

        # Step 6.- Check if the keyword returns has been specified by the user.
        # Check if the keyword returns has been specified by the user.
        if self.kwargs['returns']:
            self.has_return = True
            self.f_argspec.args.append(
                'compss_retvalue'
            )  # TODO: WHY THIS VARIABLE? THE INFORMATION IS IN SELF.KWARGS['COMPSS_RETVALUE']
            self.__update_return_type()
        else:
            # If no returns statement found, double check to see if the user has specified a return statement.
            self.__update_return_if_no_returns(f)

        # Get module (for invocation purposes in the worker)
        mod = inspect.getmodule(f)
        self.module = mod.__name__

        if self.module == '__main__' or self.module == 'pycompss.runtime.launch':
            # the module where the function is defined was run as __main__,
            # we need to find out the real module name

            # Get the real module name from our launch.py app_path global variable
            try:
                path = getattr(mod, "app_path")
            except AttributeError:
                # This exception is raised when the runtime is not running and the @task decorator is used.
                # The runtime has not been started yet.
                return self.__not_under_pycompss_scope(f)

            # Get the file name
            file_name = os.path.splitext(os.path.basename(path))[0]

            # Do any necessary preprocessing action before executing any code
            if file_name.startswith('InteractiveMode'):
                # If the file_name starts with 'InteractiveMode' means that
                # the user is using PyCOMPSs from jupyter-notebook.
                # Convention between this file and interactive.py
                # In this case it is necessary to do a pre-processing step
                # that consists of putting all user code that may be executed
                # in the worker on a file.
                # This file has to be visible for all workers.
                updateTasksCodeFile(f, path)
            else:
                # work as always
                pass

            # Get the module
            self.module = get_module_name(path, file_name)

        # The registration needs to be done only in the master node
        if i_am_at_master():
            self.__register_task(f)

        # Modified variables until now that will be used later:
        #   - self.f_argspec        : Function argspect (Named tuple)
        #                             e.g. ArgSpec(args=['a', 'b', 'compss_retvalue'], varargs=None, keywords=None, defaults=None)
        #   - self.is_instance      : Boolean - if the function is an instance (contains self in the f_argspec)
        #   - self.is_classmethod   : Boolean - if the function is a classmethod (contains cls in the f_argspec)
        #   - self.has_varargs      : Boolean - if the function has *args
        #   - self.has_keywords     : Boolean - if the function has **kwargs
        #   - self.has_defaults     : Boolean - if the function has default values
        #   - self.has_return       : Boolean - if the function has return
        #   - self.has_multireturn  : Boolean - if the function has multireturn
        #   - self.module_name      : String  - Module name (e.g. test.kmeans)
        #   - is_replicated         : Boolean - if the task is replicated
        #   - is_distributed    : Boolean - if the task is distributed
        # Other variables that will be used:
        #   - f                 : Decorated function
        #   - self.args         : Decorator args tuple (usually empty)
        #   - self.kwargs       : Decorator keywords dictionary.
        #                         e.g. {'priority': True, 'isModifier': True, 'returns': <type 'dict'>,
        #                               'self': <pycompss.api.parameter.Parameter instance at 0xXXXXXXXXX>,
        #                               'compss_retvalue': <pycompss.api.parameter.Parameter instance at 0xXXXXXXXX>}

        if __debug__:
            logger.debug("Call in @task decorator finished.")

        @wraps(f)
        def wrapped_f(*args, **kwargs):
            # args   - <Tuple>      - Contains the objects that the function has been called with (positional).
            # kwargs - <Dictionary> - Contains the named objects that the function has been called with.

            is_replicated = self.kwargs['isReplicated']
            is_distributed = self.kwargs['isDistributed']
            # By default, each task is set to use one core.
            computingNodes = 1
            if 'computingNodes' in kwargs:
                # There is a @mpi decorator over task that overrides the
                # default value of computing nodes
                computingNodes = kwargs['computingNodes']
                del kwargs['computingNodes']

            # Check if this call is nested using the launch_pycompss_module
            # function from launch.py.
            is_nested = False
            istack = inspect.stack()
            for i_s in istack:
                if i_s[3] == 'launch_pycompss_module':
                    is_nested = True
                if i_s[3] == 'launch_pycompss_application':
                    is_nested = True

            if not i_am_at_master() and (not is_nested):
                # Task decorator worker body code.
                newTypes, newValues = self.worker_code(f, args, kwargs)
                return newTypes, newValues
            else:
                # Task decorator master body code.
                # Returns the future object that will be used instead of the
                # actual function return.
                fo = self.master_code(f, is_replicated, is_distributed,
                                      computingNodes, args, kwargs)
                return fo

        return wrapped_f