def sortListedObjects(objList, template): sortedObjList = [] for obj in objList: sortedObj = {key: obj[key] for key in template if key in obj} sortedObj.update(obj) sortedObjList.append(sortedObj) if len(objList) != len(sortedObjList): return objList for obj, sobj in zip(objList, sortedObjList): if obj != sobj: print_warning("Sorted list does not represent" " original list.") return objList return sortedObjList
def customSortInvocationByInput(invocation, descriptor): descriptor = loadJson(descriptor) # sort invoc according to input's order in decsriptor sortedInvoc = { key: invocation[key] for key in [ inp['id'] for inp in descriptor['inputs'] if descriptor['inputs'] is not None ] if key in invocation } if sortedInvoc != invocation: print_warning("Sorted invocation does not represent" " original invocation.") return invocation return sortedInvoc
def customSortDescriptorByKey(descriptor, template=os.path.join(os.path.dirname(bfile), "templates", "ordered_keys_desc.json")): def sortListedObjects(objList, template): sortedObjList = [] for obj in objList: sortedObj = {key: obj[key] for key in template if key in obj} sortedObj.update(obj) sortedObjList.append(sortedObj) if len(objList) != len(sortedObjList): return objList for obj, sobj in zip(objList, sortedObjList): if obj != sobj: print_warning("Sorted list does not represent" " original list.") return objList return sortedObjList template = loadJson(template) sortedDesc = {} # Add k:v to sortedDesc according to their order in template for key in [k for k in template if k in descriptor]: if type(descriptor[key]) is list: sortedDesc[key] =\ sortListedObjects(descriptor[key], template[key][0]) else: sortedDesc[key] = descriptor[key] # Add remaining k:v that are missing from template sortedDesc.update(descriptor) if sortedDesc != descriptor: print_warning("Sorted descriptor does not represent" " original descriptor.") return descriptor return sortedDesc
def deprecate(zenodo_id, by_zenodo_id=None, sandbox=False, verbose=False, zenodo_token=None, download_function=urlretrieve): # Get the descriptor and Zenodo id puller = Puller([zenodo_id], verbose=verbose, sandbox=sandbox) descriptor_fname = puller.pull()[0] descriptor_json = loadJson(descriptor_fname, sandbox=sandbox, verbose=verbose) # Return if tool is already deprecated deprecated = descriptor_json.get('deprecated-by-doi') if deprecated is not None: if isinstance(deprecated, str): print_info('Tool {0} is already deprecated by {1} '.format( zenodo_id, deprecated)) if by_zenodo_id is not None: prompt = ("Tool {0} will be deprecated by {1}, " "this cannot be undone. Are you sure? (Y/n) ")\ .format(zenodo_id, by_zenodo_id) ret = input(prompt) if ret.upper() != "Y": return else: print_warning('Tool {0} is already deprecated'.format(zenodo_id)) return # Set record id and Zenodo id zhelper = ZenodoHelper(sandbox=sandbox, no_int=True, verbose=verbose) zid = zhelper.get_zid_from_filename(descriptor_fname) record_id = zhelper.get_record_id_from_zid(zid) # Return if tool has a newer version record = zhelper.zenodo_get_record(record_id) if not record['metadata']['relations']['version'][0]['is_last']: new_version = (record['metadata']['relations']['version'][0] ['last_child']['pid_value']) raise_error( DeprecateError, 'Tool {0} has a newer version ' '(zenodo.{1}), it cannot be deprecated.'.format( zenodo_id, new_version)) return # Add deprecated property if by_zenodo_id is None: descriptor_json['deprecated-by-doi'] = True else: # Check that by_zenodo_id exists by_record_id = zhelper.get_record_id_from_zid(by_zenodo_id) if zhelper.record_exists(by_record_id) is False: raise_error(DeprecateError, "Tool does not exist: {0}".format(by_zenodo_id)) # Assign deprecated-by property by_doi_id = zhelper.get_doi_from_zid(by_zenodo_id) descriptor_json['deprecated-by-doi'] = by_doi_id # Add doi to descriptor (mandatory for update) if descriptor_json.get('doi') is None: descriptor_json['doi'] = zhelper.get_doi_from_zid(zid) # Save descriptor in temp file tmp = tempfile.NamedTemporaryFile(delete=False, mode='w', suffix=".json") content = json.dumps(descriptor_json, indent=4, sort_keys=True) tmp.write(content) tmp.close() # Publish updated descriptor publisher = Publisher(tmp.name, zenodo_token, replace=True, sandbox=sandbox, no_int=True, id="zenodo." + zid, verbose=verbose) return publisher.publish()
def parseAction(self, action, **kwargs): # Case 1: input is a help flag # Desired outcome: we skip it if type(action) is argparse._HelpAction: if kwargs.get("verbose"): print_info("_HelpAction: Skipping") # If this action belongs to a subparser, return a flag alongside # the empty object, indicating it is not required if kwargs.get("subaction"): return {}, False return {} # Case 2: input is a subparser # Desired outcome: we add the subparser and options, and an input for # each of the subparser options elif (type(action) is argparse._SubParsersAction and not kwargs.get("addParser")): if kwargs.get("verbose"): print_info("_SubParsersAction: Interpretting & Adding") # First, add the subparser itself as an input. subparser = self.parseAction(action, addParser=True) subparser["value-requires"] = {} inpts = {} # For every option specified by the subparser... for act in subparser["value-choices"]: inpts[act] = [] subparser["value-requires"][act] = [] # ... And for each choice specified by each subparser... for subact in action.choices[act]._actions: # Process the action, and record its "required" status tmpinput, reqd = self.parseAction(subact, subaction=True, **kwargs) # If it's not empty, add it to an inputs dictionaryi, and # add the input to the descriptor. if tmpinput != {}: inpts[act] += [tmpinput["id"]] # If the input was required by the subparser, record it if reqd: subparser["value-requires"][act] += [ tmpinput["id"] ] self.descriptor["inputs"] += [tmpinput] # Once all subparsers are processed, identify which inputs need to # be disabled by which subparsers. inpt_ids = set([inp for iact in inpts for inp in inpts[iact]]) subparser["value-disables"] = {} for act in subparser["value-choices"]: # Add all IDs created by the subparser that do not also belong # to the current selection to the disabled list. subparser["value-disables"][act] = [ ckey for ckey in inpt_ids if ckey not in inpts[act] ] return subparser # Case 3: input is a regular input # Desired outcome: we add it, unless it's already been added else: if kwargs.get("verbose"): actstring = str(type(action)) actstring = actstring.split("'")[1].split(".")[-1] print_info("{0}: Adding".format(actstring)) actdict = vars(action) if action.dest == "==SUPPRESS==": adest = "subparser_{0}".format(self.sp_count) if kwargs.get("verbose"): print_warning("Subparser has no destination set, " "invocation parsing may not work as " "expected. This can be fixed by adding " "\"dest='mysubparser'\" to subparser " "creation.") self.sp_count += 1 else: adest = action.dest # If an input already exists with this ID, don't re-add it if any(adest == it["id"] for it in self.descriptor["inputs"]): if kwargs.get("verbose"): print_info("Duplicate: Argument won't be added multiple " "times ({0})".format(adest)) # If this action belongs to a subparser return a flag alongside # the empty object, indicating it is not required if kwargs.get("subaction"): return {}, False return {} # If no argument exists yet by this name, process and add it. # First, by setting some reasonable defaults or obvious values, # and then by updating others. newinput = { "id": adest, "name": adest, "description": action.help, "optional": kwargs.get("subaction") or not action.required, "type": "String", "value-key": "[{0}]".format(adest.upper().strip("[]")) } if action.type is not None: if action.type in [int, float]: newinput["type"] = "Number" if action.type == list: newinput["list"] = True if action.nargs is not None: if ((isinstance(action.nargs, str) and action.nargs == "+") or (isinstance(action.nargs, int) and action.nargs > 1)): newinput["list"] = True if action.default: newinput["default-value"] = action.default if action.choices: try: # Subparsers have choices in the form of OrderedDicts... newinput["value-choices"] = list(action.choices.keys()) except AttributeError as e: # ... but "choice" inputs have them in the form a list. newinput["value-choices"] = action.choices if len(action.option_strings): newinput["command-line-flag"] = action.option_strings[0] if type(action) is argparse._StoreTrueAction: newinput["type"] = "Flag" self.descriptor["command-line"] += " [{0}]".format( adest.upper().strip("[]")) # If this action belongs to a subparser, return a flag along # with the object, indicating its required/not required status. if kwargs.get("subaction"): return newinput, action.required return newinput