''' Create namespace convenience methods for all discovered ops. Assumes we're running in an environment with scyjava configured to have a classpath with some SciJava Ops implementations (i.e. org.scijava:scijava-ops-engine plus implementations) Classes: OpNamespace OpGateway Variables: env - OpEnvironment used to populate the OpGateway and OpNamespaces ops - OpGateway entry point ''' from scyjava import jimport from types import MethodType OpEnvironment = jimport('org.scijava.ops.api.OpEnvironment') env = OpEnvironment.build() op_names={str(name) for info in env.infos() for name in info.names()} class OpNamespace: ''' Represents intermediate ops categories. For example, "math.add" and "math.sub" are in both in the "math" namespace. ''' def __init__(self, env, ns): self.env = env self.ns = ns def help(self, op_name=None): ''' Convenience wrapper for OpEnvironment.help(), for information about available ops. Prints all returned information, line-by-line. ''' print(*self.env.help(op_name), sep = "\n") class OpGateway(OpNamespace): ''' Global base specialization of OpNamespace. Contains all other namespaces, in addition to all ops in the "global" namespace. ''' def __init__(self, env): super().__init__(env, 'global') def nary(env, fqop, arity): ''' Helper method to convert a numerical arity to the corresponding OpEnvironment method Parameters: env (OpEnvironment): the environment being used fqop (str): the fully-qualified op name arity (int): the desired arity of the op Returns: The appropriate OpBuilder.Arity instance for invoking the given op with the indicated number of parameters ''' arities=[env.op, env.op, env.op, env.op, env.op, env.op, env.op, env.op, env.op, env.op, env.op, env.arity11, env.arity12, env.arity13, env.arity14, env.arity15, env.arity16] return arities[arity](fqop) def add_op(c, op_name): ''' Helper method patch in a given op name as its corresponding function call within the given class Parameters: c (class): the OpNamespace/OpGateway to add a new function with the given op_name that calls the op op_name (str): the actual name of the op we're adding ''' if hasattr(c, op_name): return def f(self, *args, **kwargs): ''' Instance method to attach to our OpNamespace/OpGateway that does the actual op call ''' fqop = op_name if self.ns == 'global' else self.ns + "." + op_name run = kwargs.get('run', True) b = nary(self.env, fqop, len(args)).input(*args) # inplace # ops.filter.gauss(image, 5, inplace=0) if (inplace:=kwargs.get('inplace', None)) is not None: return b.mutate(inplace) if run else b.inplace(inplace) # computer # ops.filter.gauss(image, 5, out=result) if (out:=kwargs.get('out', None)) is not None: b=b.output(out) return b.compute() if run else b.computer() # function # gauss_op = ops.filter.gauss(image, 5) # result = ops.filter.gauss(image, 5) return b.apply() if run else b.function() if c == OpGateway: # op_name is a global. setattr(c, op_name, f) else: m=MethodType(f, c) setattr(c, op_name, m) def add_namespace(c, ns, op_name): ''' Helper method to add an op call with nested OpNamespace instances if needed Parameters: c (class): the OpNamespace/OpGateway to add the given op and namespace ns(str): the namespace to nest the given op within op_name (str): the actual name of the op we're adding ''' if not hasattr(c, ns): setattr(c, ns, OpNamespace(env, ns)) add_op(getattr(c, ns), op_name) # Entry point to do the namespace population. # This modifies the OpGateway class to add a call for each op, with intermediate accessors by namespace for op in op_names: dot = op.find('.') if dot >= 0: ns=op[:dot] op_name=op[dot+1:] add_namespace(OpGateway, ns, op_name) else: add_op(OpGateway, op) # Make an instance of our modified OpGateway class as a global for external use ops = OpGateway(env)