def _callable_executor_(self, callable, view_args, output_types, provenance): output_views = callable(**view_args) output_views = tuplize(output_views) # TODO this won't work if the user has annotated their "view API" to # return a `typing.Tuple` with some number of components. Python will # return a tuple when there are multiple return values, and this length # check will fail because the tuple as a whole should be matched up to # a single output type instead of its components. This is an edgecase # due to how Python handles multiple returns, and can be worked around # by using something like `typing.List` instead. if len(output_views) != len(output_types): raise TypeError( "Number of output views must match number of output " "semantic types: %d != %d" % (len(output_views), len(output_types))) output_artifacts = [] for output_view, (semantic_type, view_type) in \ zip(output_views, output_types.values()): if type(output_view) is not view_type: raise TypeError( "Expected output view type %r, received %r" % (view_type.__name__, type(output_view).__name__)) artifact = qiime.sdk.Artifact._from_view( semantic_type, output_view, view_type, provenance) output_artifacts.append(artifact) if len(output_artifacts) == 1: return output_artifacts[0] else: return tuple(output_artifacts)
def _callable_executor_(self, callable, view_args, output_types, provenance): output_views = callable(**view_args) output_views = tuplize(output_views) # TODO this won't work if the user has annotated their "view API" to # return a `typing.Tuple` with some number of components. Python will # return a tuple when there are multiple return values, and this length # check will fail because the tuple as a whole should be matched up to # a single output type instead of its components. This is an edgecase # due to how Python handles multiple returns, and can be worked around # by using something like `typing.List` instead. if len(output_views) != len(output_types): raise TypeError( "Number of output views must match number of output " "semantic types: %d != %d" % (len(output_views), len(output_types))) output_artifacts = [] for output_view, (semantic_type, view_type) in \ zip(output_views, output_types.values()): if type(output_view) is not view_type: raise TypeError( "Expected output view type %r, received %r" % (view_type.__name__, type(output_view).__name__)) artifact = qiime.sdk.Artifact._from_view( semantic_type, output_view, view_type, provenance.fork()) output_artifacts.append(artifact) if len(output_artifacts) == 1: return output_artifacts[0] else: return tuple(output_artifacts)
def __getitem__(self, fields): fields = tuplize(fields) if len(fields) != len(self.field_names): raise TypeError("%r takes %d field(s), %d provided." % (self, len(self.field_names), len(fields))) for args in zip(self.field_names, fields): self._validate_field_(*args) return self._apply_fields_(fields=fields)
def callable_wrapper(*args, **kwargs): provenance = archive.ActionProvenanceCapture( self.action_type, self.package, self.id) # This function's signature is rewritten below using # `decorator.decorator`. When the signature is rewritten, args[0] # is the function whose signature was used to rewrite this # function's signature. args = args[1:] # TODO this may be able to be simplified once Signature respects # order. wrapper_sig = self._callable_sig_converter_(self._callable) wrapper_sig = inspect.Signature.from_callable(wrapper_sig) wrapper_params = wrapper_sig.parameters user_input = { name: value for value, name in zip(args, wrapper_params) } user_input.update(kwargs) self.signature.check_types(**user_input) output_types = self.signature.solve_output(**user_input) artifacts = {} for name in self.signature.inputs: artifact = artifacts[name] = user_input[name] provenance.add_input(name, artifact) parameters = {} for name, (primitive_type, _) in self.signature.parameters.items(): parameter = parameters[name] = user_input[name] provenance.add_parameter(name, primitive_type, parameter) view_args = parameters.copy() for name, (_, view_type) in self.signature.inputs.items(): recorder = provenance.transformation_recorder(name) view_args[name] = artifacts[name]._view(view_type, recorder) outputs = self._callable_executor_(self._callable, view_args, output_types, provenance) # `outputs` matches a Python function's return: either a single # value is returned, or it is a tuple of return values. Treat both # cases uniformly. outputs_tuple = tuplize(outputs) for output in outputs_tuple: output._orphan(self._pid) if len(outputs_tuple) != len(self.signature.outputs): raise ValueError( "Number of callable outputs must match number of outputs " "defined in signature: %d != %d" % (len(outputs_tuple), len(self.signature.outputs))) # Wrap in a Results object mapping output name to value so users # have access to outputs by name or position. return Results(self.signature.outputs.keys(), outputs_tuple)
def callable_wrapper(*args, **kwargs): provenance = archive.ActionProvenanceCapture( self.action_type, self.package, self.id) # This function's signature is rewritten below using # `decorator.decorator`. When the signature is rewritten, args[0] # is the function whose signature was used to rewrite this # function's signature. args = args[1:] # TODO this may be able to be simplified once Signature respects # order. wrapper_sig = self._callable_sig_converter_(self._callable) wrapper_sig = inspect.Signature.from_callable(wrapper_sig) wrapper_params = wrapper_sig.parameters user_input = {name: value for value, name in zip(args, wrapper_params)} user_input.update(kwargs) self.signature.check_types(**user_input) output_types = self.signature.solve_output(**user_input) artifacts = {} for name in self.signature.inputs: artifact = artifacts[name] = user_input[name] provenance.add_input(name, artifact) parameters = {} for name, (primitive_type, _) in self.signature.parameters.items(): parameter = parameters[name] = user_input[name] provenance.add_parameter(name, primitive_type, parameter) view_args = parameters.copy() for name, (_, view_type) in self.signature.inputs.items(): recorder = provenance.transformation_recorder(name) view_args[name] = artifacts[name]._view(view_type, recorder) outputs = self._callable_executor_(self._callable, view_args, output_types, provenance) # `outputs` matches a Python function's return: either a single # value is returned, or it is a tuple of return values. Treat both # cases uniformly. outputs_tuple = tuplize(outputs) for output in outputs_tuple: output._orphan(self._pid) if len(outputs_tuple) != len(self.signature.outputs): raise ValueError( "Number of callable outputs must match number of outputs " "defined in signature: %d != %d" % (len(outputs_tuple), len(self.signature.outputs))) # Wrap in a Results object mapping output name to value so users # have access to outputs by name or position. return Results(self.signature.outputs.keys(), outputs_tuple)
def callable_wrapper(*args, **kwargs): # This function's signature is rewritten below using # `decorator.decorator`. When the signature is rewritten, args[0] # is the function whose signature was used to rewrite this # function's signature. args = args[1:] # TODO this may be able to be simplified once Signature respects # order. wrapper_sig = self._callable_sig_converter_(self._callable) wrapper_sig = inspect.Signature.from_callable(wrapper_sig) wrapper_params = wrapper_sig.parameters user_input = {name: value for value, name in zip(args, wrapper_params)} user_input.update(kwargs) self.signature.check_types(**user_input) output_types = self.signature.solve_output(**user_input) artifacts = {} for name in self.signature.inputs: artifacts[name] = user_input[name] parameters = {} for name in self.signature.parameters: parameters[name] = user_input[name] view_args = parameters.copy() for name, (_, view_type) in self.signature.inputs.items(): view_args[name] = artifacts[name].view(view_type) execution_uuid = uuid.uuid4() action_reference = ( "%s. Details on plugin, version, website, etc. will also be " "included, see https://github.com/qiime2/qiime2/issues/26" % self.id) artifact_uuids = {name: artifact.uuid for name, artifact in artifacts.items()} parameter_references = {name: str(param) for name, param in parameters.items()} provenance = qiime.sdk.Provenance( execution_uuid, action_reference, artifact_uuids, parameter_references) outputs = self._callable_executor_(self._callable, view_args, output_types, provenance) # `outputs` matches a Python function's return: either a single # value is returned, or it is a tuple of return values. Treat both # cases uniformly. outputs_tuple = tuplize(outputs) for output in outputs_tuple: output._orphan(self._pid) if len(outputs_tuple) != len(self.signature.outputs): raise ValueError( "Number of callable outputs must match number of outputs " "defined in signature: %d != %d" % (len(outputs_tuple), len(self.signature.outputs))) # If there is a single output, don't wrap in a Results object to # match how Python handles single return values. Otherwise, wrap in # a Results object mapping output name to value so users have # access to outputs by name or position. if len(outputs_tuple) == 1: return outputs_tuple[0] else: return Results(self.signature.outputs.keys(), outputs_tuple)