Exemplo n.º 1
0
    def __init__(self,
                 executable=None,
                 input_name_prefix=None,
                 input_spec=None):
        self.executable = executable
        self._desc = {} if self.executable is None else executable.describe()
        self.input_spec = collections.OrderedDict(
        ) if 'inputSpec' in self._desc or input_spec else None
        self.required_inputs, self.optional_inputs, self.array_inputs = [], [], set(
        )
        self.input_name_prefix = input_name_prefix

        if input_spec is None:
            input_spec = self._desc.get('inputSpec', [])

        for spec_atom in input_spec:
            if spec_atom['class'].startswith('array:'):
                self.array_inputs.add(spec_atom['name'])
            self.input_spec[spec_atom['name']] = spec_atom
            if "default" in spec_atom or spec_atom.get("optional") == True:
                self.optional_inputs.append(spec_atom['name'])
            else:
                self.required_inputs.append(spec_atom['name'])

        self.inputs = OrderedDefaultdict(list)
Exemplo n.º 2
0
    def __init__(self, executable=None, input_name_prefix=None):
        self.executable = executable
        self._desc = {} if self.executable is None else executable.describe()
        self.input_spec = collections.OrderedDict() if 'inputSpec' in self._desc else None
        self.required_inputs, self.optional_inputs, self.array_inputs = [], [], set()
        self.input_name_prefix = input_name_prefix

        for spec_atom in self._desc.get('inputSpec', []):
            if spec_atom['class'].startswith('array:'):
                self.array_inputs.add(spec_atom['name'])
            self.input_spec[spec_atom['name']] = spec_atom
            if "default" in spec_atom or spec_atom.get("optional") == True:
                self.optional_inputs.append(spec_atom['name'])
            else:
                self.required_inputs.append(spec_atom['name'])

        self.inputs = OrderedDefaultdict(list)
Exemplo n.º 3
0
class ExecutableInputs(object):
    def __init__(self, executable=None, input_name_prefix=None):
        self.executable = executable
        self._desc = {} if self.executable is None else executable.describe()
        self.input_spec = collections.OrderedDict() if 'inputSpec' in self._desc else None
        self.required_inputs, self.optional_inputs, self.array_inputs = [], [], set()
        self.input_name_prefix = input_name_prefix

        for spec_atom in self._desc.get('inputSpec', []):
            if spec_atom['class'].startswith('array:'):
                self.array_inputs.add(spec_atom['name'])
            self.input_spec[spec_atom['name']] = spec_atom
            if "default" in spec_atom or spec_atom.get("optional") == True:
                self.optional_inputs.append(spec_atom['name'])
            else:
                self.required_inputs.append(spec_atom['name'])

        self.inputs = OrderedDefaultdict(list)

    def update(self, new_inputs, strip_prefix=True):
        if strip_prefix and self.input_name_prefix is not None:
            for i in new_inputs:
                if i.startswith(self.input_name_prefix):
                    self.inputs[i[len(self.input_name_prefix):]] = new_inputs[i]
        else:
            self.inputs.update(new_inputs)

    def add(self, input_name, input_value):
        if self.input_name_prefix is not None:
            if input_name.startswith(self.input_name_prefix):
                input_name = input_name[len(self.input_name_prefix):]
            else: # Skip inputs that don't start with prefix
                return

        if ':' in input_name:
            input_class = input_name[input_name.find(':') + 1:]
            input_name = input_name[:input_name.find(':')]
        else:
            input_class = None

        if self.input_spec is not None:
            if input_name not in self.input_spec:
                raise Exception('Input field called ' + input_name + ' was not found in the input spec')

            input_class = self.input_spec[input_name]['class']

        if input_class is None:
            done = False
            try:
                # Resolve "job-xxxx:output-name" syntax into a canonical job ref
                job_id, field = split_unescaped(':', input_value)
                if is_job_id(job_id):
                    input_value = {"job": job_id, "field": field}
                    done = True
            except:
                pass
            if not done:
                try:
                    parsed_input_value = json.loads(input_value, object_pairs_hook=collections.OrderedDict)
                    if type(parsed_input_value) in (collections.OrderedDict, list, int, long, float):
                        input_value = parsed_input_value
                    else:
                        raise Exception()
                except:
                    # Not recognized JSON (list or dict), so resolve it as a name
                    try:
                        project, folderpath, entity_result = resolve_existing_path(input_value,
                                                                                   expected='entity')
                    except:
                        # If not possible, then leave it as a string
                        project, folderpath, entity_result = None, None, None
                    if entity_result is not None:
                        if is_hashid(input_value):
                            input_value = {'$dnanexus_link': entity_result['id']}
                        else:
                            input_value = {"$dnanexus_link": {"project": entity_result['describe']['project'],
                                                              "id": entity_result['id']}}
            self.inputs[input_name].append(input_value)
        else:
            # Input class is known.  Respect the "array" class.

            input_value = parse_input_or_jbor(input_class, input_value)

            if input_class.startswith('array:'):
                self.inputs[input_name].append(input_value)
            else:
                self.inputs[input_name] = input_value

    def init_completer(self):
        try:
            import readline
            import rlcompleter
            readline.parse_and_bind("tab: complete")

            readline.set_completer_delims("")

            readline.write_history_file(os.path.expanduser('~/.dnanexus_config/.dx_history'))
            readline.clear_history()
            readline.set_completer()
        except:
            pass

    def uninit_completer(self):
        try:
            readline.set_completer()
            readline.clear_history()
        except:
            pass

    def prompt_for_missing(self):
        # No-op if there is no input spec
        if self.input_spec is None:
            return

        # If running from the command-line (not in the shell), bring up the tab-completer
        self.init_completer()

        # Select input interactively
        no_prior_inputs = True if len(self.inputs) == 0 else False
        for i in self.required_inputs:
            if i not in self.inputs:
                if len(self.inputs) == 0:
                    print 'Entering interactive mode for input selection.'
                self.inputs[i] = self.prompt_for_input(i)
        if no_prior_inputs and len(self.optional_inputs) > 0:
            self.prompt_for_optional_inputs()

        self.uninit_completer()

    def prompt_for_input(self, input_name):
        if input_name in self.array_inputs:
            return get_input_array(self.input_spec[input_name])
        else:
            return get_input_single(self.input_spec[input_name])

    def prompt_for_optional_inputs(self):
        while True:
            print '\n' + fill('Select an optional parameter to set by its # (^D or <ENTER> to finish):') + '\n'
            for i in range(len(self.optional_inputs)):
                opt_str = ' [' + str(i) + '] ' + \
                    get_optional_input_str(self.input_spec[self.optional_inputs[i]])
                if self.optional_inputs[i] in self.inputs:
                    opt_str += ' [=' + GREEN()
                    opt_str += json.dumps(self.inputs[self.optional_inputs[i]])
                    opt_str += ENDC() + ']'
                elif 'default' in self.input_spec[self.optional_inputs[i]]:
                    opt_str += ' [default=' + json.dumps(self.input_spec[self.optional_inputs[i]]['default']) + ']'
                print opt_str
            print ""
            try:
                while True:
                    selected = raw_input('Optional param #: ')
                    if selected == '':
                        return
                    try:
                        opt_num = int(selected)
                        if opt_num < 0 or opt_num >= len(self.optional_inputs):
                            raise ValueError('Error: Selection is out of range')
                        break
                    except ValueError as details:
                        print unicode(details)
                        continue
            except EOFError:
                return
            try:
                self.inputs[self.optional_inputs[opt_num]] = self.prompt_for_input(self.optional_inputs[opt_num])
            except:
                pass

    def update_from_args(self, args):
        if args.filename is not None:
            try:
                if args.filename == "-":
                    data = sys.stdin.read()
                else:
                    with open(args.filename, 'r') as fd:
                        data = fd.read()
                self.update(json.loads(data, object_pairs_hook=collections.OrderedDict))
            except Exception as e:
                raise Exception('Error while parsing input JSON file: %s' % unicode(e))

        if args.input_json is not None:
            try:
                self.update(json.loads(args.input_json, object_pairs_hook=collections.OrderedDict))
            except Exception as e:
                raise Exception('Error while parsing input JSON: %s' % unicode(e))

        if args.input is not None:
            for keyeqval in args.input:
                try:
                    first_eq_pos = get_first_pos_of_char('=', keyeqval)
                    if first_eq_pos == -1:
                        raise
                    name = split_unescaped('=', keyeqval)[0]
                    value = keyeqval[first_eq_pos + 1:]
                except:
                    raise Exception('An input was found that did not conform to the syntax: -i<input name>=<input value>')
                self.add(name, value)

        if self.input_spec is None:
            for i in self.inputs:
                if type(self.inputs[i]) == list and len(self.inputs[i]) == 1:
                    self.inputs[i] = self.inputs[i][0]
        
        if sys.stdout.isatty():
            self.prompt_for_missing()
        elif not all(i in self.inputs for i in self.required_inputs):
            raise Exception('Some inputs are missing, and interactive mode is not available')
Exemplo n.º 4
0
class ExecutableInputs(object):
    def __init__(self,
                 executable=None,
                 input_name_prefix=None,
                 input_spec=None):
        self.executable = executable
        self._desc = {} if self.executable is None else executable.describe()
        self.input_spec = collections.OrderedDict(
        ) if 'inputSpec' in self._desc or input_spec else None
        self.required_inputs, self.optional_inputs, self.array_inputs = [], [], set(
        )
        self.input_name_prefix = input_name_prefix

        if input_spec is None:
            input_spec = self._desc.get('inputSpec', [])

        for spec_atom in input_spec:
            if spec_atom['class'].startswith('array:'):
                self.array_inputs.add(spec_atom['name'])
            self.input_spec[spec_atom['name']] = spec_atom
            if "default" in spec_atom or spec_atom.get("optional") == True:
                self.optional_inputs.append(spec_atom['name'])
            else:
                self.required_inputs.append(spec_atom['name'])

        self.inputs = OrderedDefaultdict(list)

    def update(self, new_inputs, strip_prefix=True):
        if strip_prefix and self.input_name_prefix is not None:
            for i in new_inputs:
                if i.startswith(self.input_name_prefix):
                    self.inputs[
                        i[len(self.input_name_prefix):]] = new_inputs[i]
        else:
            self.inputs.update(new_inputs)

    def add(self, input_name, input_value):
        if self.input_name_prefix is not None:
            if input_name.startswith(self.input_name_prefix):
                input_name = input_name[len(self.input_name_prefix):]
            else:  # Skip inputs that don't start with prefix
                return

        if ':' in input_name:
            input_class = input_name[input_name.find(':') + 1:]
            input_name = input_name[:input_name.find(':')]
        else:
            input_class = None

        if self.input_spec is not None:
            if input_name not in self.input_spec and self._desc.get(
                    'class') != 'workflow':
                raise Exception('Input field called ' + input_name +
                                ' was not found in the input spec')
            elif input_name in self.input_spec:
                input_class = self.input_spec[input_name]['class']

        if input_class is None:
            done = False
            try:
                # Resolve "job-xxxx:output-name" syntax into a canonical job ref
                job_id, field = split_unescaped(':', input_value)
                if is_job_id(job_id) or is_localjob_id(job_id):
                    input_value = {"job": job_id, "field": field}
                    done = True
            except:
                pass
            if not done:
                try:
                    parsed_input_value = json.loads(
                        input_value, object_pairs_hook=collections.OrderedDict)
                    if type(parsed_input_value) in (collections.OrderedDict,
                                                    list, int, long, float):
                        input_value = parsed_input_value
                    else:
                        raise Exception()
                except:
                    # Not recognized JSON (list or dict), so resolve it as a name
                    try:
                        project, folderpath, entity_result = resolve_existing_path(
                            input_value, expected='entity')
                    except:
                        # If not possible, then leave it as a string
                        project, folderpath, entity_result = None, None, None
                    if entity_result is not None:
                        if is_hashid(input_value):
                            input_value = {
                                '$dnanexus_link': entity_result['id']
                            }
                        else:
                            input_value = {
                                "$dnanexus_link": {
                                    "project":
                                    entity_result['describe']['project'],
                                    "id": entity_result['id']
                                }
                            }
            if isinstance(self.inputs[input_name], list) and \
               not isinstance(self.inputs[input_name], basestring):
                self.inputs[input_name].append(input_value)
            else:
                self.inputs[input_name] = input_value
        else:
            # Input class is known.  Respect the "array" class.

            input_value = parse_input_or_jbor(input_class, input_value)

            if input_class.startswith('array:'):
                self.inputs[input_name].append(input_value)
            else:
                self.inputs[input_name] = input_value

    def init_completer(self):
        try:
            import readline
            import rlcompleter
            readline.parse_and_bind("tab: complete")

            readline.set_completer_delims("")

            readline.write_history_file(
                os.path.expanduser('~/.dnanexus_config/.dx_history'))
            readline.clear_history()
            readline.set_completer()
        except:
            pass

    def uninit_completer(self):
        try:
            readline.set_completer()
            readline.clear_history()
        except:
            pass

    def prompt_for_missing(self):
        # No-op if there is no input spec
        if self.input_spec is None:
            return

        # If running from the command-line (not in the shell), bring up the tab-completer
        self.init_completer()

        # Select input interactively
        no_prior_inputs = True if len(self.inputs) == 0 else False
        for i in self.required_inputs:
            if i not in self.inputs:
                if len(self.inputs) == 0:
                    print 'Entering interactive mode for input selection.'
                self.inputs[i] = self.prompt_for_input(i)
        if no_prior_inputs and len(self.optional_inputs) > 0:
            self.prompt_for_optional_inputs()

        self.uninit_completer()

    def prompt_for_input(self, input_name):
        if input_name in self.array_inputs:
            return get_input_array(self.input_spec[input_name])
        else:
            return get_input_single(self.input_spec[input_name])

    def prompt_for_optional_inputs(self):
        while True:
            print '\n' + fill(
                'Select an optional parameter to set by its # (^D or <ENTER> to finish):'
            ) + '\n'
            for i in range(len(self.optional_inputs)):
                opt_str = ' [' + str(i) + '] ' + \
                    get_optional_input_str(self.input_spec[self.optional_inputs[i]])
                if self.optional_inputs[i] in self.inputs:
                    opt_str += ' [=' + GREEN()
                    opt_str += json.dumps(self.inputs[self.optional_inputs[i]])
                    opt_str += ENDC() + ']'
                elif 'default' in self.input_spec[self.optional_inputs[i]]:
                    opt_str += ' [default=' + json.dumps(self.input_spec[
                        self.optional_inputs[i]]['default']) + ']'
                print opt_str
            print ""
            try:
                while True:
                    selected = raw_input('Optional param #: ')
                    if selected == '':
                        return
                    try:
                        opt_num = int(selected)
                        if opt_num < 0 or opt_num >= len(self.optional_inputs):
                            raise ValueError(
                                'Error: Selection is out of range')
                        break
                    except ValueError as details:
                        print unicode(details)
                        continue
            except EOFError:
                return
            try:
                self.inputs[
                    self.optional_inputs[opt_num]] = self.prompt_for_input(
                        self.optional_inputs[opt_num])
            except:
                pass

    def update_from_args(self, args):
        if args.filename is not None:
            try:
                if args.filename == "-":
                    data = sys.stdin.read()
                else:
                    with open(args.filename, 'r') as fd:
                        data = fd.read()
                self.update(
                    json.loads(data,
                               object_pairs_hook=collections.OrderedDict))
            except Exception as e:
                raise Exception('Error while parsing input JSON file: %s' %
                                unicode(e))

        if args.input_json is not None:
            try:
                self.update(
                    json.loads(args.input_json,
                               object_pairs_hook=collections.OrderedDict))
            except Exception as e:
                raise Exception('Error while parsing input JSON: %s' %
                                unicode(e))

        if args.input is not None:
            for keyeqval in args.input:
                try:
                    first_eq_pos = get_first_pos_of_char('=', keyeqval)
                    if first_eq_pos == -1:
                        raise
                    name = split_unescaped('=', keyeqval)[0]
                    value = keyeqval[first_eq_pos + 1:]
                except:
                    raise Exception(
                        'An input was found that did not conform to the syntax: -i<input name>=<input value>'
                    )
                self.add(self.executable._get_input_name(name) if \
                         self._desc.get('class') == 'workflow' else name, value)

        if self.input_spec is None:
            for i in self.inputs:
                if type(self.inputs[i]) == list and len(self.inputs[i]) == 1:
                    self.inputs[i] = self.inputs[i][0]

        # For now, we do not handle prompting for workflow inputs nor
        # recognizing when not all inputs haven't been bound
        if sys.stdout.isatty() and self._desc.get('class') != 'workflow':
            self.prompt_for_missing()
        elif self._desc.get('class') != 'workflow':
            missing_required_inputs = set(self.required_inputs) - set(
                self.inputs.keys())
            if missing_required_inputs:
                raise Exception(
                    'Some inputs (%s) are missing, and interactive mode is not available'
                    % (', '.join(missing_required_inputs)))