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