|
| 1 | +""" |
| 2 | +TODO: Module description. |
| 3 | +""" |
| 4 | + |
| 5 | +from types import MethodType |
| 6 | +from typing import List, Sequence |
| 7 | +import scyjava as sj |
| 8 | + |
| 9 | +endpoints = [ |
| 10 | + "net.imglib2:imglib2:6.4.0", |
| 11 | + "net.imglib2:imglib2-imglyb", |
| 12 | + "org.scijava:scijava-ops-engine", |
| 13 | + "org.scijava:scijava-ops-flim", |
| 14 | + "org.scijava:scijava-ops-image" |
| 15 | + ] |
| 16 | + |
| 17 | +class OpNamespace: |
| 18 | + """Op namespace class. |
| 19 | +
|
| 20 | + Represents intermediate Ops categories and Ops. For example, |
| 21 | + "math.add" and "features.haralick.asm". |
| 22 | + """ |
| 23 | + |
| 24 | + def __init__(self, env: "scijava.OpEnvironment", ns: str): |
| 25 | + self.op = env.op |
| 26 | + self._env = env |
| 27 | + self._ns = ns |
| 28 | + |
| 29 | + |
| 30 | +class OpsGateway(OpNamespace): |
| 31 | + """SciJava Ops Gateway class. |
| 32 | +
|
| 33 | + Contains all other namespaces, in addition to all Ops in |
| 34 | + the "global" namespace. |
| 35 | + """ |
| 36 | + |
| 37 | + def __init__(self, env): |
| 38 | + super().__init__(env, "global") |
| 39 | + |
| 40 | + def help(self, op_name: str = None): |
| 41 | + """SciJava Ops help. |
| 42 | +
|
| 43 | + :param op_name: |
| 44 | +
|
| 45 | + Namespace and Op name (e.g. "filter.gauss") |
| 46 | + """ |
| 47 | + if op_name: |
| 48 | + print(self._env.help(op_name), sep="\n") |
| 49 | + else: |
| 50 | + print(self._env.help(), sep="\n") |
| 51 | + |
| 52 | + def helpVerbose(self, op_name: str = None): |
| 53 | + """SciJava Ops verbose help. |
| 54 | +
|
| 55 | + :param op_name: |
| 56 | +
|
| 57 | + Namespace and Op name (e.g. "filter.gauss") |
| 58 | + """ |
| 59 | + if op_name: |
| 60 | + print(self._env.helpVerbose(op_name), sep="\n") |
| 61 | + else: |
| 62 | + print(self._env_helpVerbose(), sep="\n") |
| 63 | + |
| 64 | + |
| 65 | +def init(endpoints: List[str]) -> OpsGateway: |
| 66 | + """Get the SciJava Ops Gateway. |
| 67 | +
|
| 68 | + Initialize the JVM and return an instance of the |
| 69 | + SciJava Ops Gateway class. |
| 70 | +
|
| 71 | + :return: |
| 72 | +
|
| 73 | + The SciJava Ops Gateway. |
| 74 | + """ |
| 75 | + # configure and start the jvm |
| 76 | + if not sj.jvm_started(): |
| 77 | + sj.config.endpoints = endpoints |
| 78 | + sj.start_jvm() |
| 79 | + |
| 80 | + # build Ops environment |
| 81 | + env = sj.jimport("org.scijava.ops.api.OpEnvironment").build() |
| 82 | + |
| 83 | + # find op names, base namespaces and intermediate namespaces |
| 84 | + op_names = _find_op_names(env) |
| 85 | + op_base_ns = [] |
| 86 | + for op in op_names: |
| 87 | + op_sig = op.split(".") |
| 88 | + # skip "base" Ops |
| 89 | + if len(op_sig) == 1: |
| 90 | + continue |
| 91 | + else: |
| 92 | + op_base_ns.append(op_sig[0]) |
| 93 | + op_base_ns = set(op_base_ns) |
| 94 | + |
| 95 | + # populate base namespaces |
| 96 | + for ns in op_base_ns: |
| 97 | + _add_namespace(OpsGateway, env, ns) |
| 98 | + |
| 99 | + # populate nested namespaces and ops |
| 100 | + for op in op_names: |
| 101 | + op_sig = op.split(".") |
| 102 | + sig_size = len(op_sig) |
| 103 | + if sig_size > 1: |
| 104 | + # find/add nested namespaces |
| 105 | + gateway_ref = OpsGateway # used to reference nested namespaces |
| 106 | + for s in op_sig[:-1]: |
| 107 | + if hasattr(gateway_ref, s): |
| 108 | + gateway_ref = getattr(gateway_ref, s) |
| 109 | + else: |
| 110 | + _add_namespace(gateway_ref, env, s) |
| 111 | + gateway_ref = getattr(gateway_ref, s) |
| 112 | + # add the Op to the nested namespace |
| 113 | + _add_op(gateway_ref, env, op_sig[-1]) |
| 114 | + else: |
| 115 | + _add_op(OpsGateway, env, op_sig[0]) |
| 116 | + |
| 117 | + return OpsGateway(env) |
| 118 | + |
| 119 | +def _add_namespace(gc: OpsGateway, env: "scijava.OpEnvironment", ns: str): |
| 120 | + """Add an Op and it's namespace to the OpsGateway. |
| 121 | +
|
| 122 | + Helper method to add an Op call with the appropriate nested |
| 123 | + OpNamespace instances if needed. |
| 124 | +
|
| 125 | + :param gc: |
| 126 | +
|
| 127 | + OpsGateway class |
| 128 | + |
| 129 | + :param env: |
| 130 | +
|
| 131 | + SciJava Ops environment instance |
| 132 | + |
| 133 | + :param ns: |
| 134 | +
|
| 135 | + Namespace |
| 136 | + |
| 137 | + :param on: |
| 138 | +
|
| 139 | + Op name |
| 140 | + """ |
| 141 | + if not hasattr(gc, ns): |
| 142 | + setattr(gc, ns, OpNamespace(env, ns)) |
| 143 | + |
| 144 | + |
| 145 | +def _add_op(gc: OpsGateway, env: "scijava.OpEnvironment", on: str): |
| 146 | + """Add an Op to the OpsGateway. |
| 147 | +
|
| 148 | + Helper method to add an Op with its corresponding function call |
| 149 | + to the given class. |
| 150 | +
|
| 151 | + :param gc: |
| 152 | +
|
| 153 | + OpsGateway class |
| 154 | + |
| 155 | + :param env: |
| 156 | +
|
| 157 | + SciJava Ops environment instance |
| 158 | + |
| 159 | + :param on: |
| 160 | +
|
| 161 | + Op name |
| 162 | + """ |
| 163 | + if hasattr(gc, on): |
| 164 | + return |
| 165 | + |
| 166 | + def f(self, *args, **kwargs): |
| 167 | + """Op call instance methods. |
| 168 | +
|
| 169 | + Instance method to attach to the OpNamespace/OpsGateway that does |
| 170 | + the actual Op call. |
| 171 | + """ |
| 172 | + fqop = on if self._ns == "global" else self._ns + "." + on |
| 173 | + run = kwargs.get("run", True) |
| 174 | + req = env.op(fqop).input(*args) |
| 175 | + |
| 176 | + # inplace Op requests |
| 177 | + if (inplace := kwargs.get("inplace", None)) is not None: |
| 178 | + return req.mutate(inplace) if run else req.inplace(inplace) |
| 179 | + |
| 180 | + # computer Op requests |
| 181 | + if (out := kwargs.get("out", None)) is not None: |
| 182 | + req = req.output(out) |
| 183 | + return req.compute() if run else req.computer() |
| 184 | + |
| 185 | + # function Op requests |
| 186 | + return req.apply() if run else req.function() |
| 187 | + |
| 188 | + if gc == OpsGateway: |
| 189 | + # Op name is a global |
| 190 | + setattr(gc, on, f) |
| 191 | + else: |
| 192 | + m = MethodType(f, gc) |
| 193 | + setattr(gc, on, m) |
| 194 | + |
| 195 | + |
| 196 | +def _find_op_names(env: "scijava.OpEnvironment") -> set: |
| 197 | + """Find all Op names in a SciJava Ops environment. |
| 198 | +
|
| 199 | + :return: |
| 200 | + |
| 201 | + Set of all Op names/signatures |
| 202 | + """ |
| 203 | + return {str(name) for info in env.infos() for name in info.names()} |
| 204 | + |
| 205 | +ops = init(endpoints) |
0 commit comments