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 __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)
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')
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)))