1
2
3 """
4 Python driver for OMERO
5
6 Provides access to various OMERO.blitz server- and client-side
7 utilities, including starting and stopping servers, running
8 analyses, configuration, and more.
9
10 Usable via the ./omero script provided with the distribution
11 as well as from python via "import omero.cli; omero.cli.argv()"
12
13 Arguments are taken from (in order of priority): the run method
14 arguments, sys.argv, and finally from standard-in using the
15 cmd.Cmd.cmdloop method.
16
17 Josh Moore, josh at glencoesoftware.com
18 Copyright (c) 2007, Glencoe Software, Inc.
19 See LICENSE for details.
20
21 """
22
23 sys = __import__("sys")
24
25 import cmd, string, re, os, subprocess, socket, exceptions, traceback, glob, platform, time
26 import shlex
27 from exceptions import Exception as Exc
28 from threading import Thread, Lock
29 from path import path
30
31 from omero_ext.argparse import ArgumentError
32 from omero_ext.argparse import ArgumentParser
33 from omero_ext.argparse import FileType
34 from omero_ext.argparse import Namespace
35
36
37 from omero_ext.argparse import ArgumentDefaultsHelpFormatter
38 from omero_ext.argparse import RawDescriptionHelpFormatter
39 from omero_ext.argparse import RawTextHelpFormatter
40 from omero_ext.argparse import SUPPRESS
41
42 from omero.util.concurrency import get_event
43 from omero.util.sessions import SessionsStore
44
45 import omero
46
47
48
49
50
51 try:
52 from omero_version import omero_version
53 VERSION=omero_version
54 except ImportError:
55 VERSION="Unknown"
56
57 DEBUG = 0
58 if os.environ.has_key("DEBUG"):
59 try:
60 DEBUG = int(os.environ["DEBUG"])
61 except ValueError:
62 DEBUG = 1
63 print "Deprecated warning: use the 'bin/omero --debug=x [args]' to debug"
64 print "Running omero with debugging == 1"
65
66 OMERODOC = """
67 Command-line tool for local and remote interactions with OMERO.
68 """
69 OMEROSHELL = """OMERO Python Shell. Version %s""" % str(VERSION)
70 OMEROHELP = """Type "help" for more information, "quit" or Ctrl-D to exit"""
71 OMEROSUBS = """Use %(prog)s <subcommand> -h for more information."""
72 OMEROSUBM = """<subcommand>"""
73 OMEROCLI = path(__file__).expand().dirname()
74 OMERODIR = os.getenv('OMERODIR', None)
75 if OMERODIR is not None:
76 OMERODIR = path(OMERODIR)
77 else:
78 OMERODIR = OMEROCLI.dirname().dirname().dirname()
79
80 COMMENT = re.compile("^\s*#")
81 RELFILE = re.compile("^\w")
82 LINEWSP = re.compile("^\s*\w+\s+")
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
101 self.rv = rv
102 Exc.__init__(self, *args)
103
104
105
106
107
127
128
130 """
131 Namespace subclass which prevents overwriting any values by accident.
132 """
134 if hasattr(self, name):
135 raise exceptions.Exception("%s already has field %s" % (self.__class__.__name__, name))
136 else:
137 return Namespace.__setattr__(self, name, value)
138
139
141 """
142 Extension of ArgumentParser for simplifying the
143 _configure() code in most Controls
144 """
145
147 kwargs["formatter_class"] = HelpFormatter
148 ArgumentParser.__init__(self, *args, **kwargs)
149 self._positionals.title = "Positional Arguments"
150 self._optionals.title = "Optional Arguments"
151 self._optionals.description = "In addition to any higher level options"
152
154 return self.add_subparsers(title = "Subcommands", description = OMEROSUBS, metavar = OMEROSUBM)
155
156 - def add(self, sub, func, help, **kwargs):
160
162
163 if action.choices is not None and value not in action.choices:
164 msg = 'invalid choice: %r\n\nchoose from:\n' % value
165 choices = sorted(action.choices)
166 msg += self._format_list(choices)
167 raise ArgumentError(action, msg)
168
178
180 """
181 Extension of the argparse.FileType to prevent
182 overwrite existing files.
183 """
185 if os.path.exists(string):
186 raise ValueError("File exists: %s" % string)
187 return FileType.__call__(self, string)
188
189
191 """
192 Extension of the argparse.FileType to only allow
193 existing directories.
194 """
196 p = path(string)
197 if not p.exists():
198 raise ValueError("Directory does not exist: %s" % string)
199 elif not p.isdir():
200 raise ValueError("Path is not a directory: %s" % string)
201 return str(p.abspath())
202
203
205 """Simple context used for default logic. The CLI registry which registers
206 the plugins installs itself as a fully functional Context.
207
208 The Context class is designed to increase pluggability. Rather than
209 making calls directly on other plugins directly, the pub() method
210 routes messages to other commands. Similarly, out() and err() should
211 be used for printing statements to the user, and die() should be
212 used for exiting fatally.
213
214 """
215
216 - def __init__(self, controls = {}, params = {}):
217 self.event = get_event()
218 self.params = {}
219 self.controls = controls
220 self.dir = OMERODIR
221 self.isdebug = DEBUG
222 self.topics = {"debug":"""
223
224 debug options for developers:
225
226 The value to the debug argument is a comma-separated list of commands:
227
228 * 'debug' prints at the "debug" level. Similar to setting DEBUG=1 in the environment.
229 * 'trace' runs the command with tracing enabled.
230 * 'profile' runs the command with profiling enabled.
231
232 Only one of "trace" and "profile" can be chosen.
233
234 Example:
235
236 bin/omero --debug=debug,trace admin start # Debugs at level 1 and prints tracing
237 bin/omero -d1 admin start # Debugs at level 1
238 bin/omero -dp admin start # Prints profiling
239 bin/omero -dt,p admin start # Fails!; can't print tracing and profiling together
240 bin/omero -d0 admin start # Disables debugging
241 """}
242 self.parser = Parser(prog = sys.argv[0],
243 description = OMERODOC)
244 self.subparsers = self.parser_init(self.parser)
245
246 - def post_process(self):
247 """
248 Runs further processing once all the controls have been added.
249 """
250 sessions = self.controls["sessions"]
251
252 login = self.subparsers.add_parser("login", help="Shortcut for 'sessions login'")
253 login.set_defaults(func=lambda args:sessions.login(args))
254 self.add_login(login)
255 sessions._configure_login(login)
256
257 logout = self.subparsers.add_parser("logout", help="Shortcut for 'sessions logout'")
258 logout.set_defaults(func=lambda args:self.controls["sessions"].logout(args))
259
260 - def add_login(self, parser):
261 parser.add_argument("-C", "--create", action="store_true", help="Create a new session regardless of existing ones")
262 parser.add_argument("-s", "--server")
263 parser.add_argument("-p", "--port")
264 parser.add_argument("-g", "--group")
265 parser.add_argument("-u", "--user")
266 parser.add_argument("-w", "--password")
267 parser.add_argument("-k", "--key", help="UUID of an active session")
268
269 - def parser_init(self, parser):
270 parser.add_argument("-v", "--version", action="version", version="%%(prog)s %s" % VERSION)
271 parser.add_argument("-d", "--debug", help="Use 'help debug' for more information", default = SUPPRESS)
272 parser.add_argument("--path", help="Add file or directory to plugin list. Supports globs.", action = "append")
273 self.add_login(parser)
274 subparsers = parser.add_subparsers(title="Subcommands", description=OMEROSUBS, metavar=OMEROSUBM)
275 return subparsers
276
277 - def get(self, key, defvalue = None):
278 return self.params.get(key, defvalue)
279
280 - def set(self, key, value = True):
281 self.params[key] = value
282
283 - def safePrint(self, text, stream, newline = True):
284 """
285 Prints text to a given string, caputring any exceptions.
286 """
287 try:
288 stream.write(text % {"program_name": sys.argv[0]})
289 if newline:
290 stream.write("\n")
291 else:
292 stream.flush()
293 except:
294 print >>sys.stderr, "Error printing text"
295 print >>sys.stdout, text
296 if self.isdebug:
297 traceback.print_exc()
298
299 - def pythonpath(self):
300 """
301 Converts the current sys.path to a PYTHONPATH string
302 to be used by plugins which must start a new process.
303
304 Note: this was initially created for running during
305 testing when PYTHONPATH is not properly set.
306 """
307 path = list(sys.path)
308 for i in range(0,len(path)-1):
309 if path[i] == '':
310 path[i] = os.getcwd()
311 pythonpath = ":".join(path)
312 return pythonpath
313
315 """
316 Returns a user directory (as path.path) which can be used
317 for storing configuration. The directory is guaranteed to
318 exist and be private (700) after execution.
319 """
320 dir = path(os.path.expanduser("~")) / "omero" / "cli"
321 if not dir.exists():
322 dir.mkdir()
323 elif not dir.isdir():
324 raise Exc("%s is not a directory"%dir)
325 dir.chmod(0700)
326 return dir
327
328 - def pub(self, args, strict = False):
329 self.safePrint(str(args), sys.stdout)
330
331 - def input(self, prompt, hidden = False, required = False):
332 """
333 Reads from standard in. If hidden == True, then
334 uses getpass
335 """
336 try:
337 while True:
338 if hidden:
339 import getpass
340 defuser = getpass.getuser()
341 rv = getpass.getpass(prompt)
342 else:
343 rv = raw_input(prompt)
344 if required and not rv:
345 self.out("Input required")
346 continue
347 return rv
348 except KeyboardInterrupt:
349 self.die(1, "Cancelled")
350
351 - def out(self, text, newline = True):
352 """
353 Expects as single string as argument"
354 """
355 self.safePrint(text, sys.stdout, newline)
356
357 - def err(self, text, newline = True):
358 """
359 Expects a single string as argument.
360 """
361 self.safePrint(text, sys.stderr, newline)
362
363 - def dbg(self, text, newline = True, level = 1):
364 """
365 Passes text to err() if self.isdebug is set
366 """
367 if self.isdebug >= level:
368 self.err(text, newline)
369
370 - def die(self, rc, args):
371 raise exceptions.Exception((rc,args))
372
373 - def exit(self, args):
374 self.out(args)
375 self.interrupt_loop = True
376
377 - def call(self, args):
379
380 - def popen(self, args):
382
383 - def sleep(self, time):
384 self.event.wait(time)
385
386
387
389 """Controls get registered with a CLI instance on loadplugins().
390
391 To create a new control, subclass BaseControl, implement _configure,
392 and end your module with::
393
394 try:
395 register("name", MyControl, HELP)
396 except:
397 if __name__ == "__main__":
398 cli = CLI()
399 cli.register("name", MyControl, HELP)
400 cli.invoke(sys.argv[1:])
401
402 This module should be put in the omero.plugins package.
403
404 All methods which do NOT begin with "_" are assumed to be accessible
405 to CLI users.
406 """
407
408
409
410
411
415
417 p_s = platform.system()
418 if p_s == 'Windows':
419 return True
420 else:
421 return False
422
424 """
425 Return hostname of current machine. Termed to be the
426 value return from socket.gethostname() up to the first
427 decimal.
428 """
429 if not hasattr(self, "hostname") or not self.hostname:
430 self.hostname = socket.gethostname()
431 if self.hostname.find(".") > 0:
432 self.hostname = self.hostname.split(".")[0]
433 return self.hostname
434
435 - def _node(self, omero_node = None):
436 """
437 Return the name of this node, using either the environment
438 vairable OMERO_NODE or _host(). Some subclasses may
439 override this functionality, most notably "admin" commands
440 which assume a node name of "master".
441
442 If the optional argument is not None, then the OMERO_NODE
443 environment variable will be set.
444 """
445 if omero_node != None:
446 os.environ["OMERO_NODE"] = omero_node
447
448 if os.environ.has_key("OMERO_NODE"):
449 return os.environ["OMERO_NODE"]
450 else:
451 return self._host()
452
454 """
455 General data method for creating a path from an Ice property.
456 """
457 try:
458 nodepath = self._properties()[property]
459
460 if RELFILE.match(nodepath):
461 nodedata = self.dir / path(nodepath)
462 else:
463 nodedata = path(nodepath)
464
465 created = False
466 if not nodedata.exists():
467 self.ctx.out("Creating "+nodedata)
468 nodedata.makedirs()
469 created = True
470 return (nodedata, created)
471
472 except KeyError, ke:
473 self.ctx.err(property + " is not configured")
474 self.ctx.die(4, str(ke))
475
477 """
478 Initialize the directory into which the current node will log.
479 """
480 props = self._properties()
481 nodedata = self._nodedata()
482 logdata = self.dir / path(props["Ice.StdOut"]).dirname()
483 if not logdata.exists():
484 self.ctx.out("Initializing %s" % logdata)
485 logdata.makedirs()
486
487
489 """
490 Returns the data directory path for this node. This is determined
491 from the "IceGrid.Node.Data" property in the _properties()
492 map.
493
494 The directory will be created if it does not exist.
495 """
496 data, created = self._icedata("IceGrid.Node.Data")
497 return data
498
500 """
501 Returns the data directory for the IceGrid registry.
502 This is determined from the "IceGrid.Registry.Data" property
503 in the _properties() map.
504
505 The directory will be created if it does not exist, and
506 a warning issued.
507 """
508 data, created = self._icedata("IceGrid.Registry.Data")
509
511 """
512 Returns a path of the form "_nodedata() / _node() + ".pid",
513 i.e. a file named NODENAME.pid in the node's data directory.
514 """
515 pidfile = self._nodedata() / (self._node() + ".pid")
516 return pidfile
517
519 """
520 Returns a list of configuration files for this node. This
521 defaults to the internal configuration for all nodes,
522 followed by a file named NODENAME.cfg under the etc/
523 directory, following by PLATFORM.cfg if it exists.
524 """
525 cfgs = self.dir / "etc"
526 internal = cfgs / "internal.cfg"
527 owncfg = cfgs / self._node() + ".cfg"
528 results = [internal,owncfg]
529
530 p_s = platform.system()
531 p_c = cfgs / p_s + ".cfg"
532 if p_c.exists():
533 results.append(p_c)
534 return results
535
537 """
538 Uses _cfglist() to return a string argument of the form
539 "--Ice.Config=..." suitable for passing to omero.client
540 as an argument.
541 """
542 icecfg = "--Ice.Config=%s" % ",".join(self._cfglist())
543 return str(icecfg)
544
546 """
547 Returns an Ice.Config string with only the internal configuration
548 file for connecting to the IceGrid Locator.
549 """
550 intcfg = self.dir / "etc" / "internal.cfg"
551 intcfg.abspath()
552 return str("--Ice.Config=%s" % intcfg)
553
555 """
556 Loads all files returned by _cfglist() into a new
557 Ice.Properties instance and return the map from
558 getPropertiesForPrefix(prefix) where the default is
559 to return all properties.
560 """
561 import Ice
562 if not hasattr(self, "_props") or self._props == None:
563 self._props = Ice.createProperties()
564 for cfg in self._cfglist():
565 try:
566 self._props.load(str(cfg))
567 except Exc, exc:
568 self.ctx.die(3, "Could not find file: "+cfg + "\nDid you specify the proper node?")
569 return self._props.getPropertiesForPrefix(prefix)
570
572 while not root_pass or len(root_pass) < 1:
573 root_pass = self.ctx.input("Please enter password%s: "%reason, hidden = True)
574 if root_pass == None or root_pass == "":
575 self.ctx.err("Password cannot be empty")
576 continue
577 confirm = self.ctx.input("Please re-enter password%s: "%reason, hidden = True)
578 if root_pass != confirm:
579 root_pass = None
580 self.ctx.err("Passwords don't match")
581 continue
582 break
583 return root_pass
584
585
586
587
588
589
591 """
592 f: path part
593 """
594 if dir is None:
595 dir = self.dir
596 else:
597 dir = path(dir)
598 p = path(f)
599 if p.exists() and p.isdir():
600 if not f.endswith(os.sep):
601 return [p.basename()+os.sep]
602 return [ str(x)[len(f):] for x in p.listdir() ]
603 else:
604 results = [ str(x.basename()) for x in dir.glob(f+"*") ]
605 if len(results) == 1:
606
607 maybe_dir = path(results[0])
608 if maybe_dir.exists() and maybe_dir.isdir():
609 return [ results[0] + os.sep ]
610 return results
611
612 - def _complete(self, text, line, begidx, endidx):
613 try:
614 return self._complete2(text, line, begidx, endidx)
615 except:
616 self.ctx.dbg("Complete error: %s" % traceback.format_exc())
617
618 - def _complete2(self, text, line, begidx, endidx):
619 items = shlex.split(line)
620 parser = getattr(self, "parser", None)
621 if parser:
622 result = []
623 actions = getattr(parser, "_actions")
624 if actions:
625 if len(items) > 1:
626 subparsers = [x for x in actions if x.__class__.__name__ == "_SubParsersAction"]
627 if subparsers:
628 subparsers = subparsers[0]
629 choice = subparsers.choices.get(items[-1])
630 if choice and choice._actions:
631 actions = choice._actions
632 if len(items) > 2:
633 actions = []
634
635 for action in actions:
636 if action.__class__.__name__ == "_HelpAction":
637 result.append("-h")
638 elif action.__class__.__name__ == "_SubParsersAction":
639 result.extend(action.choices)
640
641 return ["%s " % x for x in result if (not text or x.startswith(text)) and line.find(" %s " % x) < 0]
642
643
644 completions = [method for method in dir(self) if callable(getattr(self, method)) ]
645 return [ str(method + " ") for method in completions if method.startswith(text) and not method.startswith("_") ]
646
647
648 -class CLI(cmd.Cmd, Context):
649 """
650 Command line interface class. Supports various styles of executing the
651 registered plugins. Each plugin is given the chance to update this class
652 by adding methods of the form "do_<plugin name>".
653 """
654
656 """
657 Thread-safe class for storing whether or not all the plugins
658 have been loaded
659 """
661 self.lock = Lock()
662 self.done = False
664 self.lock.acquire()
665 try:
666 return self.done
667 finally:
668 self.lock.release()
670 self.lock.acquire()
671 try:
672 self.done = True
673 finally:
674 self.lock.release()
675
677 """
678 Also sets the "_client" field for this instance to None. Each cli
679 maintains a single active client. The "session" plugin is responsible
680 for the loading of the client object.
681 """
682 cmd.Cmd.__init__(self)
683 Context.__init__(self)
684 self.prompt = 'omero> '
685 self.interrupt_loop = False
686 self.rv = 0
687 self._stack = []
688 self._client = None
689 self._plugin_paths = [OMEROCLI / "plugins"]
690 self._pluginsLoaded = CLI.PluginsLoaded()
691
695
696 - def invoke(self, line, strict = False, previous_args = None):
697 """
698 Copied from cmd.py
699 """
700 try:
701 line = self.precmd(line)
702 stop = self.onecmd(line, previous_args)
703 stop = self.postcmd(stop, line)
704 if strict:
705 self.assertRC()
706 finally:
707 if len(self._stack) == 0:
708 self.close()
709 else:
710 self.dbg("Delaying close for stack: %s" % len(self._stack), level = 2)
711
713
714 class PWD(BaseControl):
715 def __call__(self, args):
716 self.ctx.out(os.getcwd())
717 class LS(BaseControl):
718 def __call__(self, args):
719 for p in sorted(path(os.getcwd()).listdir()):
720 self.ctx.out(str(p.basename()))
721 class CD(BaseControl):
722 def _complete(self, text, line, begidx, endidx):
723 RE = re.compile("\s*cd\s*")
724 m = RE.match(line)
725 if m:
726 replaced = RE.sub('', line)
727 return self._complete_file(replaced, path(os.getcwd()))
728 return []
729 def _configure(self, parser):
730 parser.set_defaults(func=self.__call__)
731 parser.add_argument("dir", help = "Target directory")
732 def __call__(self, args):
733 os.chdir(args.dir)
734 self.register("pwd", PWD, "Print the current directory")
735 self.register("ls", LS, "Print files in the current directory")
736 self.register("dir", LS, "Alias for 'ls'")
737 self.register("cd", CD, "Change the current directory")
738
739 try:
740 self.selfintro = "\n".join([OMEROSHELL, OMEROHELP])
741 if not self.stdin.isatty():
742 self.selfintro = ""
743 self.prompt = ""
744 while not self.interrupt_loop:
745 try:
746
747 self.cmdloop(self.selfintro)
748 except KeyboardInterrupt, ki:
749 self.selfintro = ""
750 self.out("Use quit to exit")
751 finally:
752 self.close()
753
754 - def postloop(self):
755
756 self.selfintro = ""
757
758 - def onecmd(self, line, previous_args = None):
759 """
760 Single command logic. Overrides the cmd.Cmd logic
761 by calling execute. Also handles various exception
762 conditions.
763 """
764 try:
765
766
767 self.rv = 0
768 try:
769 self._stack.insert(0, line)
770 self.dbg("Stack+: %s" % len(self._stack), level=2)
771 self.execute(line, previous_args)
772 return True
773 finally:
774 self._stack.pop(0)
775 self.dbg("Stack-: %s" % len(self._stack), level=2)
776 except SystemExit, exc:
777 self.dbg("SystemExit raised\n%s" % traceback.format_exc())
778 self.rv = exc.code
779 return False
780
781
782
783
784
785
786
787
788
789
790 except NonZeroReturnCode, nzrc:
791 self.dbg(traceback.format_exc())
792 self.rv = nzrc.rv
793 return False
794
795 - def postcmd(self, stop, line):
796 """
797 Checks interrupt_loop for True and return as much
798 which will end the call to cmdloop. Otherwise use
799 the default postcmd logic (which simply returns stop)
800 """
801 if self.interrupt_loop:
802 return True
803 return cmd.Cmd.postcmd(self, stop, line)
804
805 - def execute(self, line, previous_args):
806 """
807 String/list handling as well as EOF and comment handling.
808 Otherwise, parses the arguments as shlexed and runs the
809 function returned by argparse.
810 """
811
812 if isinstance(line, (str, unicode)):
813 if COMMENT.match(line):
814 return
815 args = shlex.split(line)
816 elif isinstance(line, (tuple, list)):
817 args = list(line)
818 else:
819 self.die(1, "Bad argument type: %s ('%s')" % (type(line), line))
820
821 if not args:
822 return
823 elif args == ["EOF"]:
824 self.exit("")
825 return
826
827 args = self.parser.parse_args(args, previous_args)
828 args.prog = self.parser.prog
829 self.waitForPlugins()
830
831 debug_str = getattr(args, "debug", "")
832 debug_opts = set([x.lower() for x in debug_str.split(",")])
833 if "" in debug_opts:
834 debug_opts.remove("")
835
836 old_debug = self.isdebug
837 if "debug" in debug_opts:
838 self.isdebug = 1
839 debug_opts.remove("debug")
840 elif "0" in debug_opts:
841 self.isdebug = 0
842 debug_opts.remove("0")
843
844 for x in range(1, 9):
845 if str(x) in debug_opts:
846 self.isdebug = x
847 debug_opts.remove(str(x))
848
849 try:
850 if len(debug_opts) == 0:
851 args.func(args)
852 elif len(debug_opts) > 1:
853 self.die(9, "Conflicting debug options: %s" % ", ".join(debug_opts))
854 elif "t" in debug_opts or "trace" in debug_opts:
855 import trace
856 tracer = trace.Trace()
857 tracer.runfunc(args.func, args)
858 elif "p" in debug_opts or "profile" in debug_opts:
859 import hotshot
860 from hotshot import stats
861 prof = hotshot.Profile("hotshot_edi_stats")
862 rv = prof.runcall( lambda: args.func(args) )
863 prof.close()
864 s = stats.load("hotshot_edi_stats")
865 s.sort_stats("time").print_stats()
866 else:
867 self.die(10, "Unknown debug action: %s" % debug_opts)
868 finally:
869 self.isdebug = old_debug
870
873
877
878
879
880
881
882 - def exit(self, args, newline=True):
883 self.out(args, newline)
884 self.interrupt_loop = True
885
886 - def die(self, rc, text, newline=True):
891
893 """
894 Configure environment with PYTHONPATH as
895 setup by bin/omero
896
897 This list needs to be kept in line with OmeroPy/bin/omero
898
899 """
900 lpy = str(self.dir / "lib" / "python")
901 ipy = str(self.dir / "lib" / "fallback")
902 vlb = str(self.dir / "var" / "lib")
903 paths = os.path.pathsep.join([lpy, vlb, ipy])
904
905 env = dict(os.environ)
906 pypath = env.get("PYTHONPATH", None)
907 if pypath is None:
908 pypath = paths
909 else:
910 if pypath.endswith(os.path.pathsep):
911 pypath = "%s%s" % (pypath, paths)
912 else:
913 pypath = "%s%s%s" % (pypath, os.path.pathsep, paths)
914 env["PYTHONPATH"] = pypath
915 return env
916
917 - def _cwd(self, cwd):
918 if cwd is None:
919 cwd = str(OMERODIR)
920 else:
921 cwd = str(cwd)
922 return cwd
923
924 - def call(self, args, strict = True, cwd = None):
925 """
926 Calls the string in a subprocess and dies if the return value is not 0
927 """
928 self.dbg("Executing: %s" % args)
929 rv = subprocess.call(args, env = self._env(), cwd = self._cwd(cwd))
930 if strict and not rv == 0:
931 raise NonZeroReturnCode(rv, "%s => %d" % (" ".join(args), rv))
932 return rv
933
934 - def popen(self, args, cwd = None, stdout = subprocess.PIPE, stderr = subprocess.PIPE, **kwargs):
935 self.dbg("Returning popen: %s" % args)
936 env = self._env()
937 env.update(kwargs)
938 return subprocess.Popen(args, env = env, cwd = self._cwd(cwd), stdout = stdout, stderr = stderr)
939
941 try:
942 f = path(OMERODIR) / "etc" / "omero.properties"
943 f = f.open()
944 output = "".join(f.readlines())
945 f.close()
946 except:
947 if self.isdebug:
948 raise
949 print "No omero.properties found"
950 output = ""
951 return output
952
954 for line in output.splitlines():
955 if line.startswith("Listening for transport dt_socket at address"):
956 self.dbg("Ignoring stdout 'Listening for transport' from DEBUG=1")
957 continue
958 parts = line.split("=",1)
959 if len(parts) == 2:
960 data.properties.setProperty(parts[0],parts[1])
961 self.dbg("Set property: %s=%s" % (parts[0],parts[1]) )
962 else:
963 self.dbg("Bad property:"+str(parts))
964 return data
965
967 """
968 Uses "omero prefs" to create an Ice.InitializationData().
969 """
970 from omero.plugins.prefs import getprefs
971 try:
972 output = getprefs(["get"], str(OMERODIR / "lib"))
973 except OSError, err:
974 self.err("Error getting preferences")
975 self.dbg(err)
976 output = ""
977
978 import Ice
979 data = Ice.InitializationData()
980 data.properties = Ice.createProperties()
981 for k,v in properties.items():
982 data.properties.setProperty(k,v)
983 self.parsePropertyFile(data, output)
984 return data
985
986 - def conn(self, args = None):
987 """
988 Returns any active _client object. If one is present but
989 not alive, it will be removed.
990
991 If no client is found and arguments are available,
992 will use the current settings to connect.
993
994 If required attributes are missing, will delegate to the login command.
995
996 FIXME: Currently differing setting sessions on the same CLI instance
997 will misuse a client.
998 """
999 if self._client:
1000 self.dbg("Found client")
1001 try:
1002 self._client.getSession().keepAlive(None)
1003 self.dbg("Using client")
1004 return self._client
1005 except KeyboardInterrupt:
1006 raise
1007 except exceptions.Exception, e:
1008 self.dbg("Removing client: %s" % e)
1009 self._client.closeSession()
1010 self._client = None
1011
1012 if args is not None:
1013 self.controls["sessions"].login(args)
1014
1015 return self._client
1016
1022
1023
1024
1025
1026
1027 - def register(self, name, Control, help):
1030
1032 """ This method is added to the globals when execfile() is
1033 called on each plugin. A Control class should be
1034 passed to the register method which will be added to the CLI.
1035 """
1036 self.controls[name] = (Control, help)
1037
1058
1060 if True:
1061 return
1062 self.dbg("Starting waitForPlugins")
1063 while not self._pluginsLoaded.get():
1064 self.dbg("Waiting for plugins...")
1065 time.sleep(0.1)
1066
1068 """
1069 Finds all plugins and gives them a chance to register
1070 themselves with the CLI instance. Here register_only()
1071 is used to guarantee the orderedness of the plugins
1072 in the parser
1073 """
1074
1075 for plugin_path in self._plugin_paths:
1076 self.loadpath(path(plugin_path))
1077
1078 self.configure_plugins()
1079 self._pluginsLoaded.set()
1080 self.post_process()
1081
1083 if pathobj.isdir():
1084 for plugin in pathobj.walkfiles("*.py"):
1085 if -1 == plugin.find("#"):
1086 self.loadpath(path(plugin))
1087 else:
1088 if self.isdebug:
1089 print "Loading %s" % pathobj
1090 try:
1091 loc = {"register": self.register_only}
1092 execfile( str(pathobj), loc )
1093 except KeyboardInterrupt:
1094 raise
1095 except:
1096 self.err("Error loading: %s" % pathobj)
1097 traceback.print_exc()
1098
1099
1100
1101
1103 """
1104 Main entry point for the OMERO command-line interface. First
1105 loads all plugins by passing them the classes defined here
1106 so they can register their methods.
1107
1108 Then the case where arguments are passed on the command line are
1109 handled.
1110
1111 Finally, the cli enters a command loop reading from standard in.
1112 """
1113
1114
1115 old_ice_config = os.getenv("ICE_CONFIG")
1116 os.unsetenv("ICE_CONFIG")
1117 try:
1118
1119
1120
1121 executable = path(args[0])
1122 executable = str(executable.basename())
1123 if executable.find("-") >= 0:
1124 parts = executable.split("-")
1125 for arg in args[1:]:
1126 parts.append(arg)
1127 args = parts
1128
1129
1130 cli = CLI()
1131
1132 parser = Parser(add_help = False)
1133
1134 parser.add_argument("--path", help="Add file or directory to plugin list. Supports globs.", action = "append")
1135 ns, args = parser.parse_known_args(args)
1136 if getattr(ns, "path"):
1137 for p in ns.path:
1138 for g in glob.glob(p):
1139 cli._plugin_paths.append(g)
1140
1141 class PluginLoader(Thread):
1142 def run(self):
1143 cli.loadplugins()
1144
1145
1146 PluginLoader().run()
1147
1148 if len(args) > 1:
1149 cli.invoke(args[1:])
1150 return cli.rv
1151 else:
1152 cli.invokeloop()
1153 return cli.rv
1154 finally:
1155 if old_ice_config:
1156 os.putenv("ICE_CONFIG", old_ice_config)
1157