1
2 """
3 Scripting types
4 - Classes:
5 - Type -- Top of parameter type hierarchy
6 - Long --
7 - String --
8 - Bool --
9 - List
10 - Map
11 - Set
12 - Functions:
13 - client -- Produces an omero.client object with given input/output constraints.
14
15 Copyright 2008 Glencoe Software, Inc. All rights reserved.
16 Use is subject to license terms supplied in LICENSE.txt
17
18 """
19
20 import os
21 import Ice
22 import uuid
23 import logging
24 import exceptions
25
26 import omero
27 import omero_Scripts_ice
28 import omero.callbacks
29 import omero.util.concurrency
30 import omero.util.temp_files
31
32 from omero.rtypes import *
33
34
35 TYPE_LOG = logging.getLogger("omero.scripts.Type")
36 PROC_LOG = logging.getLogger("omero.scripts.ProcessCallback")
37
38
39 -class Type(omero.grid.Param):
40 """
41 omero.grid.Param subclass which provides convenience methods for input/output specification.
42 Further subclasses are responsible for creating proper prototypes.
43
44 kwargs
45 """
46 PROTOTYPE_FUNCTION = None
47 PROTOTYPE_DEFAULT = None
48 PROTOTYPE_MIN = None
49 PROTOTYPE_MAX = None
50 PROTOTYPE_VALUES = None
51
52 - def __init__(self, name, optional = True, out = False, description = None, default = None, **kwargs):
53
54 omero.grid.Param.__init__(self)
55
56
57 self._name = name
58 self._in = True
59 self._out = out
60
61
62
63 self.description = description
64 self.optional = optional
65
66
67 for k, v in kwargs.items():
68 if not hasattr(self, k):
69 TYPE_LOG.warn("Unknown property: %s", k)
70 setattr(self, k, v)
71
72 _DEF = self.__get(self.PROTOTYPE_DEFAULT, False)
73 _FUN = self.__get(self.PROTOTYPE_FUNCTION)
74 _MAX = self.__get(self.PROTOTYPE_MAX)
75 _MIN = self.__get(self.PROTOTYPE_MIN)
76 _VAL = self.__get(self.PROTOTYPE_VALUES)
77
78
79
80 if default is not None:
81 newfunc = _FUN
82 newdefault = default
83 if isinstance(self, List):
84 if isinstance(default, (list, tuple)):
85 newdefault = wrap(default).val
86 elif isinstance(default, omero.RCollection):
87 newfunc = lambda x: x
88 elif isinstance(default, omero.RType):
89 default = [default]
90 else:
91 newfunc = lambda x: x
92 newdefault = rlist([rtype(default)])
93 self.useDefault = True
94 self.prototype = newfunc(newdefault)
95 else:
96 if not callable(_FUN):
97 raise ValueError("Bad prototype function: %s" % _FUN)
98
99
100
101
102 try:
103 _def = _DEF()
104 except TypeError:
105 _def = _DEF
106 self.prototype = _FUN(_def)
107
108
109 if self.min is not None:
110 if _MIN is None:
111 self.min = _FUN(self.min)
112 else:
113 self.min = _MIN(self.min)
114
115 if self.max is not None:
116 if _MAX is None:
117 self.max = _FUN(self.max)
118 else:
119 self.max = MAX(self.max)
120
121 if self.values is not None:
122 if _VAL is None:
123 self.values = wrap(self.values)
124 else:
125 self.values = _VAL(self.values)
126
127
128
129
130 if self.values is not None and self.values and self.useDefault:
131 if isinstance(self.prototype, omero.RCollection):
132 test = unwrap(self.prototype.val[0])
133 else:
134 test = unwrap(self.prototype)
135 values = unwrap(self.values)
136 if test not in values:
137 raise ValueError("%s is not in %s" % (test, values))
138
139
141 self._in = False
142 self._out = True
143 return self
144
146 self._in = True
147 self._out = True
148 return self
149
150 - def type(self, *arg):
151 self.prototype = wrap(arg)
152 return self
153
154 - def __get(self, val, func = True):
155 if val is not None:
156 if func:
157 return val.im_func
158 else:
159 return val
160
161
168
169
176
177
184
185
192
193
200
201
208
209
216
217
224
225
232
233
235 """
236 Base type providing the append and extend functionality.
237 Not for user use.
238 """
239 PROTOTYPE_DEFAULT = list
240
242 self.prototype.val.append(*arg)
243 return self
244
246 self.prototype.val.extend(*arg)
247 return self
248
250 if callable(obj):
251 obj = obj()
252
253
254
255 if self.useDefault and self.prototype.val:
256 if not isinstance(obj, self.prototype.val[0].__class__):
257 raise ValueError("ofType values doesn't match default value: %s <> %s" % (unwrap(obj), unwrap(self.prototype.val[0])))
258 else:
259 self.prototype.val.append(wrap(obj))
260
261 return self
262
264 """
265 Wraps an rset. To add values to the contents of the set,
266 use "append" or "extend" since set.val is of type list.
267 """
268 PROTOTYPE_FUNCTION = rset
269
270
272 """
273 Wraps an rlist. To add values to the contents of the list,
274 use "append" or "extend" since set.val is of type list.
275 """
276 PROTOTYPE_FUNCTION = rlist
277
278
280 """
281 Wraps an rmap. To add values to the contents of the map,
282 use "update" since map.val is of type dict.
283 """
284 PROTOTYPE_FUNCTION = rmap
285 PROTOTYPE_DEFAULT = dict
286
287 - def update(self, *args, **kwargs):
288 self.prototype.val.update(*args, **kwargs)
289 return self
290
291
293 """
294 Raised when this script should just parse parameters and return.
295 """
296
300
301
303 """
304 Entry point for all script engine scripts.
305
306 Typical usage consists of::
307
308 client = omero.scripts.client("name","description", \
309 omero.scripts.Long("name"),...)
310
311 where the returned client is created via the empty constructor to omero.client
312 using only --Ice.Config or ICE_CONFIG, and the function arguments are taken
313 as metdata about the current script. With this information, all script
314 consumers should be able to determine the required types for execution.
315
316 Possible types are all subclasses of omero.scripts.Type
317
318 To change the omero.model.Format of the stdout and stderr produced by
319 this script, use the constructor arguments::
320
321 client = omero.scripts.client(..., \
322 stdoutFormat = "text/plain",
323 stderrFormat = "text/plain")
324
325 If you would like to prevent stdout and/or stderr from being
326 uploaded, set the corresponding value to None. If you would like
327 to overwrite the value with another file, use
328 client.setOutput(). Though it is possible to attach any RType to
329 "stdout" or "stderr", there is an assumption that the value will
330 be an robject(OriginalFileI())
331
332 Providing your own client is possible via the kwarg "client = ...",
333 but be careful since this may break usage with the rest of the
334 scripting framework. The client should not have a session, and
335 must be configured for the argumentless version of createSession()
336 """
337
338 args = list(args)
339 if len(args) >= 1:
340 if isinstance(args[0], str):
341 kwargs["name"] = args.pop(0)
342 if len(args) >= 1:
343 if isinstance(args[0], str):
344 kwargs["description"] = args.pop(0)
345
346 if not kwargs.has_key("client"):
347 kwargs["client"] = omero.client()
348 c = kwargs["client"]
349 c.setAgent("OMERO.scripts")
350
351 if args and isinstance(args[0], omero.grid.JobParams):
352 c.params = args.pop(0)
353 else:
354 c.params = omero.grid.JobParams()
355 c.params.inputs = {}
356 c.params.outputs = {}
357
358 for k, v in kwargs.items():
359 if hasattr(c.params, k):
360 setattr(c.params, k, v)
361
362 if not c.params.stdoutFormat:
363 c.params.stdoutFormat = "text/plain"
364
365 if not c.params.stderrFormat:
366 c.params.stderrFormat = "text/plain"
367
368 for p in args:
369 if isinstance(p, Type):
370 if p._in:
371 c.params.inputs[p._name] = p
372 if p._out:
373 c.params.outputs[p._name] = p
374 else:
375 raise ValueError("Not Type: %s" % type(p))
376
377 handleParse(c)
378
379 c.createSession().detachOnDestroy()
380 return c
381
383 """
384 Raises ParseExit if the client has the configuration property
385 "omero.scripts.parse". If the value is anything other than "only",
386 then the parameters will also be sent to the server.
387 """
388 parse = c.getProperty("omero.scripts.parse")
389 if len(parse) > 0:
390 if parse != "only":
391 c.createSession().detachOnDestroy()
392 c.setOutput("omero.scripts.parse", rinternal(c.params))
393 raise ParseExit(c.params)
394
395 -def parse_text(scriptText):
396 """
397 Parses the given script text with "omero.scripts.parse" set
398 and catches the exception. The parameters are returned.
399
400 WARNING: This method calls "exec" on the given text.
401 Do NOT use this on data you don't trust.
402 """
403 try:
404 cfg = omero.util.temp_files.create_path()
405 cfg.write_lines(["omero.scripts.parse=only", "omero.host=localhost"])
406 old = os.environ.get("ICE_CONFIG")
407 try:
408 os.environ["ICE_CONFIG"] = cfg.abspath()
409 exec(scriptText, {"__name__":"__main__"})
410 raise exceptions.Exception("Did not throw ParseExit: %s" % scriptText)
411 finally:
412 if old:
413 os.environ["ICE_CONFIG"] = old
414 except ParseExit, exit:
415 return exit.params
416
418 """
419 Parses the given script file with "omero.scripts.parse" set
420 and catches the exception. The parameters are returned.
421
422 WARNING: This method calls "exec" on the given file's contents.
423 Do NOT use this on data you don't trust.
424 """
425 from path import path
426 scriptText = path(filename).text()
427 return parse_text(scriptText)
428
429
434
435
463
464
495
496
498 """
499 Walks through the inputs of the given JobParams
500 and returns a map-of-maps with Param names as
501 the leaf nodes.
502
503 For example, for the following:
504
505 Params("1", grouping = "A") # "A." is equivalent
506 Params("2", grouping = "A.B")
507 Params("3", grouping = "A.C")
508
509 this function returns:
510
511 {"A" {"": "1" : "B" : "2", "C" : "3"} }
512
513 while:
514
515 Params("1", grouping = "A")
516
517 returns:
518
519 {"A" : "1"}
520
521 """
522 groupings = dict()
523 for k, v in params.inputs.items():
524
525 val = v.grouping
526 if not val.endswith("."):
527 val = val + "."
528
529 parts = val.split(".")
530
531 g = groupings
532 while parts:
533 p = parts.pop(0)
534 try:
535 g = g[p]
536 except KeyError:
537 if parts:
538 g[p] = dict()
539 g = g[p]
540 else:
541 g[p] = k
542
543
544
545 tuples = [(groupings, k, v) for k, v in groupings.items()]
546 while tuples:
547 new_tuples = []
548 for g, k, v in tuples:
549 if isinstance(v, dict):
550 if len(v) == 1 and "" in v:
551 g[k] = v[""]
552 else:
553 new_tuples.extend([(v, k2, v2) for k2, v2 in v.items()])
554 tuples = new_tuples
555
556 return groupings
557
558 -def error_msg(category, key, format_string, *args):
559 c = "%s" % (category.upper())
560 s = """%s for "%s": %s\n""" % (c, key, format_string)
561 return s % args
562
564
565 if cache is None:
566 cache = {}
567
568 if id(proto) in cache and id(input) in cache:
569 return ""
570 else:
571 cache[id(proto)] = True
572 cache[id(input)] = True
573
574 itype = input is None and None or input.__class__
575 ptype = proto is None and None or proto.__class__
576
577 if not isinstance(input, ptype):
578 return error_msg("Wrong type", key, "%s != %s", itype, ptype)
579
580
581 errors = ""
582 if isinstance(proto, omero.RMap) and len(proto.val) > 0:
583 for x in input.val.values():
584 errors += compare_proto(key, proto.val.values()[0], x, cache)
585 elif isinstance(proto, omero.RCollection) and len(proto.val) > 0:
586 for x in input.val:
587 errors += compare_proto(key, proto.val[0], x, cache)
588 return errors
589
591 if input is None:
592 items = []
593 elif isinstance(input, (list, tuple)):
594 items = list(input)
595 elif isinstance(input, dict):
596 items = input.values()
597 else:
598 items = [input]
599 return items
600
617
619 errors = ""
620
621
622 values = unwrap(values)
623 input = unwrap(input)
624 items = expand(input)
625 values = expand(values)
626
627 if not values:
628 return errors
629
630 for x in items:
631 if x not in values:
632 errors += error_msg("Value list", key, "%s not in %s", x, values)
633
634 return errors
635
664
671
672
673
674
675 ProcessCallbackI = omero.callbacks.ProcessCallbackI
676
677 -def wait(client, process, ms = 500):
684