Exemplo n.º 1
0
def get_function_return_names(fct):
    """ Return variable name(s) or return statement of the given function """
    lines = inspect.getsourcelines(fct)
    outputs = None
    for line in lines[0][::-1]:
        stripped = line.strip()
        if 'def' in stripped:
            # NOTE: This work as def is a reserved keyword which will trigger
            # invalid syntax if misused
            msg = 'No return statement found in {}'
            raise PyungoError(msg.format(fct.__name__))
        ast_tree = ast.parse(stripped)
        for ast_node in ast.walk(ast_tree):
            if isinstance(ast_node, ast.Return):
                if isinstance(ast_node.value, ast.Name):
                    outputs = [ast_node.value.id]
                elif isinstance(ast_node.value, ast.Tuple):
                    outputs = [
                        elt.id for elt in ast_node.value.elts
                        if isinstance(elt, ast.Name)
                    ]
                else:
                    name = ast_node.value.__class__.__name__
                    msg = ('Variable name or Tuple of variable names are '
                           'expected, got {}'.format(name))
                    raise PyungoError(msg)
                break
        if outputs:
            break
    return outputs
Exemplo n.º 2
0
 def _process_inputs(self, inputs, is_arg=False, is_kwarg=False):
     """ converter data passed to Input objects and store them """
     # if inputs are None, we inspect the function signature
     if inputs is None:
         inputs = list(inspect.signature(self._fct).parameters.keys())
     for input_ in inputs:
         if isinstance(input_, Input):
             new_input = input_
         elif isinstance(input_, str):
             if is_arg:
                 new_input = Input.arg(input_)
             elif is_kwarg:
                 new_input = Input.kwarg(input_)
             else:
                 new_input = Input(input_)
         elif isinstance(input_, dict):
             if len(input_) != 1:
                 msg = ('dict inputs should have only one key '
                        'and cannot be empty')
                 raise PyungoError(msg)
             key = next(iter(input_))
             value = input_[key]
             new_input = Input.constant(key, value)
         else:
             msg = 'inputs need to be of type Input, str or dict'
             raise PyungoError(msg)
         self._inputs.append(new_input)
Exemplo n.º 3
0
 def _register(self, f, **kwargs):
     """ check if all needed inputs are there and create a new node """
     inputs = kwargs.get('inputs')
     if not inputs:
         raise PyungoError('Missing inputs parameter')
     outputs = kwargs.get('outputs')
     if not outputs:
         raise PyungoError('Missing outputs parameters')
     args_names = kwargs.get('args')
     kwargs_names = kwargs.get('kwargs')
     self._create_node(f, inputs, outputs, args_names, kwargs_names)
Exemplo n.º 4
0
 def check_inputs(self, sim_inputs, sim_outputs, sim_kwargs):
     """ make sure data inputs provided are good enough """
     data_inputs = set(self.inputs.keys())
     diff = data_inputs - (data_inputs - set(sim_outputs))
     if diff:
         msg = 'The following inputs are already used in the model: {}'
         raise PyungoError(msg.format(list(diff)))
     inputs_to_provide = set(sim_inputs) - set(sim_outputs)
     diff = inputs_to_provide - data_inputs
     if diff:
         msg = 'The following inputs are needed: {}'.format(list(diff))
         raise PyungoError(msg)
     diff = data_inputs - inputs_to_provide - set(sim_kwargs)
     if diff:
         msg = 'The following inputs are not used by the model: {}'
         raise PyungoError(msg.format(list(diff)))
Exemplo n.º 5
0
def topological_sort(data):
    """ Topological sort algorithm

    Args:
        data (dict): dictionnary representing dependencies
            Example: {'a': ['b', 'c']} node id 'a' depends on
            node id 'b' and 'c'

    Returns:
        ordered (list): list of list of node ids
            Example: [['a'], ['b', 'c'], ['d']]
            The sequence is representing the order to be run.
            The nested lists are node ids that can be run in parallel

    Raises:
        PyungoError: In case a cyclic dependency exists
    """
    for key in data:
        data[key] = set(data[key])
    for k, v in data.items():
        v.discard(k)  # ignore self dependencies
    extra_items_in_deps = reduce(set.union, data.values()) - set(data.keys())
    data.update({item: set() for item in extra_items_in_deps})
    while True:
        ordered = set(item for item, dep in data.items() if not dep)
        if not ordered:
            break
        yield sorted(ordered)
        data = {
            item: (dep - ordered)
            for item, dep in data.items() if item not in ordered
        }
    if data:
        raise PyungoError('A cyclic dependency exists amongst {}'.format(data))
Exemplo n.º 6
0
 def _create_node(self, fct, inputs, outputs, args_names, kwargs_names):
     """ create a save the node to the graph """
     inputs = get_if_exists(inputs, self._inputs)
     outputs = get_if_exists(outputs, self._outputs)
     node = Node(fct, inputs, outputs, args_names, kwargs_names)
     # assume that we cannot have two nodes with the same output names
     for n in self._nodes.values():
         for out_name in n.output_names:
             if out_name in node.output_names:
                 msg = '{} output already exist'.format(out_name)
                 raise PyungoError(msg)
     self._nodes[node.id] = node
Exemplo n.º 7
0
    def set_value_to_input(self, input_name, value):
        """ set a value to the targeted input name

        Args:
            input_name (str): Name of the input
            value: value to be assigned to the input

        Raises:
            PyungoError: In case the input name is unknown
        """
        for input_ in self._inputs:
            if input_.name == input_name:
                input_.value = value
                return
        msg = 'input "{}" does not exist in this node'.format(input_name)
        raise PyungoError(msg)