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
179
181 """
182 Extension of the argparse.FileType to prevent
183 overwrite existing files.
184 """
186 if os.path.exists(string):
187 raise ValueError("File exists: %s" % string)
188 return FileType.__call__(self, string)
189
190
192 """
193 Extension of the argparse.FileType to only allow
194 existing directories.
195 """
197 p = path(string)
198 if not p.exists():
199 raise ValueError("Directory does not exist: %s" % string)
200 elif not p.isdir():
201 raise ValueError("Path is not a directory: %s" % string)
202 return str(p.abspath())
203
204
206 """Simple context used for default logic. The CLI registry which registers
207 the plugins installs itself as a fully functional Context.
208
209 The Context class is designed to increase pluggability. Rather than
210 making calls directly on other plugins directly, the pub() method
211 routes messages to other commands. Similarly, out() and err() should
212 be used for printing statements to the user, and die() should be
213 used for exiting fatally.
214
215 """
216
217 - def __init__(self, controls = {}, params = {}, prog = sys.argv[0]):
218 self.event = get_event(name="CLI")
219 self.params = {}
220 self.controls = controls
221 self.dir = OMERODIR
222 self.isdebug = DEBUG
223 self.topics = {"debug":"""
224
225 debug options for developers:
226
227 The value to the debug argument is a comma-separated list of commands:
228
229 * 'debug' prints at the "debug" level. Similar to setting DEBUG=1 in the environment.
230 * 'trace' runs the command with tracing enabled.
231 * 'profile' runs the command with profiling enabled.
232
233 Only one of "trace" and "profile" can be chosen.
234
235 Example:
236
237 bin/omero --debug=debug,trace admin start # Debugs at level 1 and prints tracing
238 bin/omero -d1 admin start # Debugs at level 1
239 bin/omero -dp admin start # Prints profiling
240 bin/omero -dt,p admin start # Fails!; can't print tracing and profiling together
241 bin/omero -d0 admin start # Disables debugging
242 """}
243 self.parser = Parser(prog = prog, 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(str(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
413 self.dir = path(dir)
414 self.ctx = ctx
415 if self.ctx is None:
416 self.ctx = Context()
417
419 p_s = platform.system()
420 if p_s == 'Windows':
421 return True
422 else:
423 return False
424
426 """
427 Return hostname of current machine. Termed to be the
428 value return from socket.gethostname() up to the first
429 decimal.
430 """
431 if not hasattr(self, "hostname") or not self.hostname:
432 self.hostname = socket.gethostname()
433 if self.hostname.find(".") > 0:
434 self.hostname = self.hostname.split(".")[0]
435 return self.hostname
436
437 - def _node(self, omero_node = None):
438 """
439 Return the name of this node, using either the environment
440 vairable OMERO_NODE or _host(). Some subclasses may
441 override this functionality, most notably "admin" commands
442 which assume a node name of "master".
443
444 If the optional argument is not None, then the OMERO_NODE
445 environment variable will be set.
446 """
447 if omero_node != None:
448 os.environ["OMERO_NODE"] = omero_node
449
450 if os.environ.has_key("OMERO_NODE"):
451 return os.environ["OMERO_NODE"]
452 else:
453 return self._host()
454
456 """
457 General data method for creating a path from an Ice property.
458 """
459 try:
460 nodepath = self._properties()[property]
461
462 if RELFILE.match(nodepath):
463 nodedata = self.dir / path(nodepath)
464 else:
465 nodedata = path(nodepath)
466
467 created = False
468 if not nodedata.exists():
469 self.ctx.out("Creating "+nodedata)
470 nodedata.makedirs()
471 created = True
472 return (nodedata, created)
473
474 except KeyError, ke:
475 self.ctx.err(property + " is not configured")
476 self.ctx.die(4, str(ke))
477
479 """
480 Initialize the directory into which the current node will log.
481 """
482 props = self._properties()
483 nodedata = self._nodedata()
484 logdata = self.dir / path(props["Ice.StdOut"]).dirname()
485 if not logdata.exists():
486 self.ctx.out("Initializing %s" % logdata)
487 logdata.makedirs()
488
489
491 """
492 Returns the data directory path for this node. This is determined
493 from the "IceGrid.Node.Data" property in the _properties()
494 map.
495
496 The directory will be created if it does not exist.
497 """
498 data, created = self._icedata("IceGrid.Node.Data")
499 return data
500
502 """
503 Returns the data directory for the IceGrid registry.
504 This is determined from the "IceGrid.Registry.Data" property
505 in the _properties() map.
506
507 The directory will be created if it does not exist, and
508 a warning issued.
509 """
510 data, created = self._icedata("IceGrid.Registry.Data")
511
513 """
514 Returns a path of the form "_nodedata() / _node() + ".pid",
515 i.e. a file named NODENAME.pid in the node's data directory.
516 """
517 pidfile = self._nodedata() / (self._node() + ".pid")
518 return pidfile
519
521 """
522 Returns a list of configuration files for this node. This
523 defaults to the internal configuration for all nodes,
524 followed by a file named NODENAME.cfg under the etc/
525 directory, following by PLATFORM.cfg if it exists.
526 """
527 cfgs = self.dir / "etc"
528 internal = cfgs / "internal.cfg"
529 owncfg = cfgs / self._node() + ".cfg"
530 results = [internal,owncfg]
531
532 p_s = platform.system()
533 p_c = cfgs / p_s + ".cfg"
534 if p_c.exists():
535 results.append(p_c)
536 return results
537
539 """
540 Uses _cfglist() to return a string argument of the form
541 "--Ice.Config=..." suitable for passing to omero.client
542 as an argument.
543 """
544 icecfg = "--Ice.Config=%s" % ",".join(self._cfglist())
545 return str(icecfg)
546
548 """
549 Returns an Ice.Config string with only the internal configuration
550 file for connecting to the IceGrid Locator.
551 """
552 intcfg = self.dir / "etc" / "internal.cfg"
553 intcfg.abspath()
554 return str("--Ice.Config=%s" % intcfg)
555
557 """
558 Loads all files returned by _cfglist() into a new
559 Ice.Properties instance and return the map from
560 getPropertiesForPrefix(prefix) where the default is
561 to return all properties.
562 """
563 import Ice
564 if not hasattr(self, "_props") or self._props == None:
565 self._props = Ice.createProperties()
566 for cfg in self._cfglist():
567 try:
568 self._props.load(str(cfg))
569 except Exc, exc:
570 self.ctx.die(3, "Could not find file: "+cfg + "\nDid you specify the proper node?")
571 return self._props.getPropertiesForPrefix(prefix)
572
574 while not root_pass or len(root_pass) < 1:
575 root_pass = self.ctx.input("Please enter password%s: "%reason, hidden = True)
576 if root_pass == None or root_pass == "":
577 self.ctx.err("Password cannot be empty")
578 continue
579 confirm = self.ctx.input("Please re-enter password%s: "%reason, hidden = True)
580 if root_pass != confirm:
581 root_pass = None
582 self.ctx.err("Passwords don't match")
583 continue
584 break
585 return root_pass
586
587
588
589
590
591
593 """
594 f: path part
595 """
596 if dir is None:
597 dir = self.dir
598 else:
599 dir = path(dir)
600 p = path(f)
601 if p.exists() and p.isdir():
602 if not f.endswith(os.sep):
603 return [p.basename()+os.sep]
604 return [ str(x)[len(f):] for x in p.listdir() ]
605 else:
606 results = [ str(x.basename()) for x in dir.glob(f+"*") ]
607 if len(results) == 1:
608
609 maybe_dir = path(results[0])
610 if maybe_dir.exists() and maybe_dir.isdir():
611 return [ results[0] + os.sep ]
612 return results
613
614 - def _complete(self, text, line, begidx, endidx):
615 try:
616 return self._complete2(text, line, begidx, endidx)
617 except:
618 self.ctx.dbg("Complete error: %s" % traceback.format_exc())
619
620 - def _complete2(self, text, line, begidx, endidx):
621 items = shlex.split(line)
622 parser = getattr(self, "parser", None)
623 if parser:
624 result = []
625 actions = getattr(parser, "_actions")
626 if actions:
627 if len(items) > 1:
628 subparsers = [x for x in actions if x.__class__.__name__ == "_SubParsersAction"]
629 if subparsers:
630 subparsers = subparsers[0]
631 choice = subparsers.choices.get(items[-1])
632 if choice and choice._actions:
633 actions = choice._actions
634 if len(items) > 2:
635 actions = []
636
637 for action in actions:
638 if action.__class__.__name__ == "_HelpAction":
639 result.append("-h")
640 elif action.__class__.__name__ == "_SubParsersAction":
641 result.extend(action.choices)
642
643 return ["%s " % x for x in result if (not text or x.startswith(text)) and line.find(" %s " % x) < 0]
644
645
646 completions = [method for method in dir(self) if callable(getattr(self, method)) ]
647 return [ str(method + " ") for method in completions if method.startswith(text) and not method.startswith("_") ]
648
649
650 -class CLI(cmd.Cmd, Context):
651 """
652 Command line interface class. Supports various styles of executing the
653 registered plugins. Each plugin is given the chance to update this class
654 by adding methods of the form "do_<plugin name>".
655 """
656
658 """
659 Thread-safe class for storing whether or not all the plugins
660 have been loaded
661 """
663 self.lock = Lock()
664 self.done = False
666 self.lock.acquire()
667 try:
668 return self.done
669 finally:
670 self.lock.release()
672 self.lock.acquire()
673 try:
674 self.done = True
675 finally:
676 self.lock.release()
677
679 """
680 Also sets the "_client" field for this instance to None. Each cli
681 maintains a single active client. The "session" plugin is responsible
682 for the loading of the client object.
683 """
684 cmd.Cmd.__init__(self)
685 Context.__init__(self, prog = prog)
686 self.prompt = 'omero> '
687 self.interrupt_loop = False
688 self.rv = 0
689 self._stack = []
690 self._client = None
691 self._plugin_paths = [OMEROCLI / "plugins"]
692 self._pluginsLoaded = CLI.PluginsLoaded()
693
697
698 - def invoke(self, line, strict = False, previous_args = None):
699 """
700 Copied from cmd.py
701 """
702 try:
703 line = self.precmd(line)
704 stop = self.onecmd(line, previous_args)
705 stop = self.postcmd(stop, line)
706 if strict:
707 self.assertRC()
708 finally:
709 if len(self._stack) == 0:
710 self.close()
711 else:
712 self.dbg("Delaying close for stack: %s" % len(self._stack), level = 2)
713
715
716 class PWD(BaseControl):
717 def __call__(self, args):
718 self.ctx.out(os.getcwd())
719 class LS(BaseControl):
720 def __call__(self, args):
721 for p in sorted(path(os.getcwd()).listdir()):
722 self.ctx.out(str(p.basename()))
723 class CD(BaseControl):
724 def _complete(self, text, line, begidx, endidx):
725 RE = re.compile("\s*cd\s*")
726 m = RE.match(line)
727 if m:
728 replaced = RE.sub('', line)
729 return self._complete_file(replaced, path(os.getcwd()))
730 return []
731 def _configure(self, parser):
732 parser.set_defaults(func=self.__call__)
733 parser.add_argument("dir", help = "Target directory")
734 def __call__(self, args):
735 os.chdir(args.dir)
736 self.register("pwd", PWD, "Print the current directory")
737 self.register("ls", LS, "Print files in the current directory")
738 self.register("dir", LS, "Alias for 'ls'")
739 self.register("cd", CD, "Change the current directory")
740
741 try:
742 self.selfintro = "\n".join([OMEROSHELL, OMEROHELP])
743 if not self.stdin.isatty():
744 self.selfintro = ""
745 self.prompt = ""
746 while not self.interrupt_loop:
747 try:
748
749 self.cmdloop(self.selfintro)
750 except KeyboardInterrupt, ki:
751 self.selfintro = ""
752 self.out("Use quit to exit")
753 finally:
754 self.close()
755
756 - def postloop(self):
757
758 self.selfintro = ""
759
760 - def onecmd(self, line, previous_args = None):
761 """
762 Single command logic. Overrides the cmd.Cmd logic
763 by calling execute. Also handles various exception
764 conditions.
765 """
766 try:
767
768
769 self.rv = 0
770 try:
771 self._stack.insert(0, line)
772 self.dbg("Stack+: %s" % len(self._stack), level=2)
773 self.execute(line, previous_args)
774 return True
775 finally:
776 self._stack.pop(0)
777 self.dbg("Stack-: %s" % len(self._stack), level=2)
778 except SystemExit, exc:
779 self.dbg("SystemExit raised\n%s" % traceback.format_exc())
780 self.rv = exc.code
781 return False
782
783
784
785
786
787
788
789
790
791
792 except NonZeroReturnCode, nzrc:
793 self.dbg(traceback.format_exc())
794 self.rv = nzrc.rv
795 return False
796
797 - def postcmd(self, stop, line):
798 """
799 Checks interrupt_loop for True and return as much
800 which will end the call to cmdloop. Otherwise use
801 the default postcmd logic (which simply returns stop)
802 """
803 if self.interrupt_loop:
804 return True
805 return cmd.Cmd.postcmd(self, stop, line)
806
807 - def execute(self, line, previous_args):
808 """
809 String/list handling as well as EOF and comment handling.
810 Otherwise, parses the arguments as shlexed and runs the
811 function returned by argparse.
812 """
813
814 if isinstance(line, (str, unicode)):
815 if COMMENT.match(line):
816 return
817 args = shlex.split(line)
818 elif isinstance(line, (tuple, list)):
819 args = list(line)
820 else:
821 self.die(1, "Bad argument type: %s ('%s')" % (type(line), line))
822
823 if not args:
824 return
825 elif args == ["EOF"]:
826 self.exit("")
827 return
828
829 args = self.parser.parse_args(args, previous_args)
830 args.prog = self.parser.prog
831 self.waitForPlugins()
832
833 debug_str = getattr(args, "debug", "")
834 debug_opts = set([x.lower() for x in debug_str.split(",")])
835 if "" in debug_opts:
836 debug_opts.remove("")
837
838 old_debug = self.isdebug
839 if "debug" in debug_opts:
840 self.isdebug = 1
841 debug_opts.remove("debug")
842 elif "0" in debug_opts:
843 self.isdebug = 0
844 debug_opts.remove("0")
845
846 for x in range(1, 9):
847 if str(x) in debug_opts:
848 self.isdebug = x
849 debug_opts.remove(str(x))
850
851 try:
852 if len(debug_opts) == 0:
853 args.func(args)
854 elif len(debug_opts) > 1:
855 self.die(9, "Conflicting debug options: %s" % ", ".join(debug_opts))
856 elif "t" in debug_opts or "trace" in debug_opts:
857 import trace
858 tracer = trace.Trace()
859 tracer.runfunc(args.func, args)
860 elif "p" in debug_opts or "profile" in debug_opts:
861 import hotshot
862 from hotshot import stats
863 prof = hotshot.Profile("hotshot_edi_stats")
864 rv = prof.runcall( lambda: args.func(args) )
865 prof.close()
866 s = stats.load("hotshot_edi_stats")
867 s.sort_stats("time").print_stats()
868 else:
869 self.die(10, "Unknown debug action: %s" % debug_opts)
870 finally:
871 self.isdebug = old_debug
872
875
879
880
881
882
883
884 - def exit(self, args, newline=True):
885 self.out(args, newline)
886 self.interrupt_loop = True
887
888 - def die(self, rc, text, newline=True):
893
895 """
896 Configure environment with PYTHONPATH as
897 setup by bin/omero
898
899 This list needs to be kept in line with OmeroPy/bin/omero
900
901 """
902 lpy = str(self.dir / "lib" / "python")
903 ipy = str(self.dir / "lib" / "fallback")
904 vlb = str(self.dir / "var" / "lib")
905 paths = os.path.pathsep.join([lpy, vlb, ipy])
906
907 env = dict(os.environ)
908 pypath = env.get("PYTHONPATH", None)
909 if pypath is None:
910 pypath = paths
911 else:
912 if pypath.endswith(os.path.pathsep):
913 pypath = "%s%s" % (pypath, paths)
914 else:
915 pypath = "%s%s%s" % (pypath, os.path.pathsep, paths)
916 env["PYTHONPATH"] = pypath
917 return env
918
919 - def _cwd(self, cwd):
920 if cwd is None:
921 cwd = str(OMERODIR)
922 else:
923 cwd = str(cwd)
924 return cwd
925
926 - def call(self, args, strict = True, cwd = None):
927 """
928 Calls the string in a subprocess and dies if the return value is not 0
929 """
930 self.dbg("Executing: %s" % args)
931 rv = subprocess.call(args, env = self._env(), cwd = self._cwd(cwd))
932 if strict and not rv == 0:
933 raise NonZeroReturnCode(rv, "%s => %d" % (" ".join(args), rv))
934 return rv
935
936 - def popen(self, args, cwd = None, stdout = subprocess.PIPE, stderr = subprocess.PIPE, **kwargs):
937 self.dbg("Returning popen: %s" % args)
938 env = self._env()
939 env.update(kwargs)
940 return subprocess.Popen(args, env = env, cwd = self._cwd(cwd), stdout = stdout, stderr = stderr)
941
943 try:
944 f = path(OMERODIR) / "etc" / "omero.properties"
945 f = f.open()
946 output = "".join(f.readlines())
947 f.close()
948 except:
949 if self.isdebug:
950 raise
951 print "No omero.properties found"
952 output = ""
953 return output
954
956 for line in output.splitlines():
957 if line.startswith("Listening for transport dt_socket at address"):
958 self.dbg("Ignoring stdout 'Listening for transport' from DEBUG=1")
959 continue
960 parts = line.split("=",1)
961 if len(parts) == 2:
962 data.properties.setProperty(parts[0],parts[1])
963 self.dbg("Set property: %s=%s" % (parts[0],parts[1]) )
964 else:
965 self.dbg("Bad property:"+str(parts))
966 return data
967
969 """
970 Uses "omero prefs" to create an Ice.InitializationData().
971 """
972 from omero.plugins.prefs import getprefs
973 try:
974 output = getprefs(["get"], str(OMERODIR / "lib"))
975 except OSError, err:
976 self.err("Error getting preferences")
977 self.dbg(err)
978 output = ""
979
980 import Ice
981 data = Ice.InitializationData()
982 data.properties = Ice.createProperties()
983 for k,v in properties.items():
984 data.properties.setProperty(k,v)
985 self.parsePropertyFile(data, output)
986 return data
987
988 - def conn(self, args = None):
989 """
990 Returns any active _client object. If one is present but
991 not alive, it will be removed.
992
993 If no client is found and arguments are available,
994 will use the current settings to connect.
995
996 If required attributes are missing, will delegate to the login command.
997
998 FIXME: Currently differing setting sessions on the same CLI instance
999 will misuse a client.
1000 """
1001 if self._client:
1002 self.dbg("Found client")
1003 try:
1004 self._client.getSession().keepAlive(None)
1005 self.dbg("Using client")
1006 return self._client
1007 except KeyboardInterrupt:
1008 raise
1009 except exceptions.Exception, e:
1010 self.dbg("Removing client: %s" % e)
1011 self._client.closeSession()
1012 self._client = None
1013
1014 if args is not None:
1015 self.controls["sessions"].login(args)
1016
1017 return self._client
1018
1024
1025
1026
1027
1028
1029 - def register(self, name, Control, help):
1032
1034 """ This method is added to the globals when execfile() is
1035 called on each plugin. A Control class should be
1036 passed to the register method which will be added to the CLI.
1037 """
1038 self.controls[name] = (Control, help)
1039
1060
1062 if True:
1063 return
1064 self.dbg("Starting waitForPlugins")
1065 while not self._pluginsLoaded.get():
1066 self.dbg("Waiting for plugins...")
1067 time.sleep(0.1)
1068
1070 """
1071 Finds all plugins and gives them a chance to register
1072 themselves with the CLI instance. Here register_only()
1073 is used to guarantee the orderedness of the plugins
1074 in the parser
1075 """
1076
1077 for plugin_path in self._plugin_paths:
1078 self.loadpath(path(plugin_path))
1079
1080 self.configure_plugins()
1081 self._pluginsLoaded.set()
1082 self.post_process()
1083
1085 if pathobj.isdir():
1086 for plugin in pathobj.walkfiles("*.py"):
1087 if -1 == plugin.find("#"):
1088 self.loadpath(path(plugin))
1089 else:
1090 if self.isdebug:
1091 print "Loading %s" % pathobj
1092 try:
1093 loc = {"register": self.register_only}
1094 execfile( str(pathobj), loc )
1095 except KeyboardInterrupt:
1096 raise
1097 except:
1098 self.err("Error loading: %s" % pathobj)
1099 traceback.print_exc()
1100
1101
1102
1103
1105 """
1106 Main entry point for the OMERO command-line interface. First
1107 loads all plugins by passing them the classes defined here
1108 so they can register their methods.
1109
1110 Then the case where arguments are passed on the command line are
1111 handled.
1112
1113 Finally, the cli enters a command loop reading from standard in.
1114 """
1115
1116
1117 old_ice_config = os.getenv("ICE_CONFIG")
1118 os.unsetenv("ICE_CONFIG")
1119 try:
1120
1121
1122
1123 original_executable = path(args[0])
1124 base_executable = str(original_executable.basename())
1125 if base_executable.find("-") >= 0:
1126 parts = base_executable.split("-")
1127 for arg in args[1:]:
1128 parts.append(arg)
1129 args = parts
1130
1131
1132 cli = CLI(prog = original_executable.split("-")[0])
1133
1134 parser = Parser(add_help = False)
1135
1136 parser.add_argument("--path", help="Add file or directory to plugin list. Supports globs.", action = "append")
1137 ns, args = parser.parse_known_args(args)
1138 if getattr(ns, "path"):
1139 for p in ns.path:
1140 for g in glob.glob(p):
1141 cli._plugin_paths.append(g)
1142
1143 class PluginLoader(Thread):
1144 def run(self):
1145 cli.loadplugins()
1146
1147
1148 PluginLoader().run()
1149
1150 if len(args) > 1:
1151 cli.invoke(args[1:])
1152 return cli.rv
1153 else:
1154 cli.invokeloop()
1155 return cli.rv
1156 finally:
1157 if old_ice_config:
1158 os.putenv("ICE_CONFIG", old_ice_config)
1159