Package omero :: Module cli
[hide private]
[frames] | no frames]

Source Code for Module omero.cli

   1  #!/usr/bin/env python 
   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 as pyshlex 
  27  from exceptions import Exception as Exc 
  28  from threading import Thread, Lock 
  29  from omero_version import omero_version 
  30  from path import path 
  31   
  32  # 
  33  # Static setup 
  34  # 
  35   
  36  VERSION=omero_version 
  37  DEBUG = False 
  38  if os.environ.has_key("DEBUG"): 
  39      print "Deprecated warning: use the 'bin/omero debug [args]' to debug" 
  40      print "Running omero with debugging on" 
  41      DEBUG = True 
  42  TEXT=""" 
  43    OMERO Python Shell. Version %s 
  44    Type "help" for more information, "quit" or Ctrl-D to exit 
  45  """ % str(VERSION) 
  46   
  47  OMEROCLI = path(__file__).expand().dirname() 
  48  OMERODIR = os.getenv('OMERODIR', None) 
  49  if OMERODIR is not None: 
  50      OMERODIR = path(OMERODIR) 
  51  else: 
  52      OMERODIR = OMEROCLI.dirname().dirname().dirname() 
  53   
  54  COMMENT = re.compile("^\s*#") 
  55  RELFILE = re.compile("^\w") 
  56  LINEWSP = re.compile("^\s*\w+\s+") 
  57   
  58  # 
  59  # Possibilities: 
  60  #  - Always return and print any output 
  61  #  - Have a callback on the fired event 
  62  #  - switch register() to take a class. 
  63  #  - how should state machine work? 
  64  #   -- is the last control stored somwhere? in a stack history[3] 
  65  #   -- or do they all share a central memory? self.ctx["MY_VARIABLE"] 
  66  #  - add an argument class which is always used at the top of a control 
  67  #    def somemethod(self, args): # args always assumed to be a shlex'd arg, but checked 
  68  #        arg = Argument(args) 
  69  #  - In almost all cases, mark a flag in the CLI "lastError" and continue, 
  70  #    allowing users to do something of the form: on_success or on_fail 
  71   
  72   
  73  ##################################################### 
  74  # 
  75  # Exceptions 
  76  # 
77 -class NonZeroReturnCode(Exc):
78 - def __init__(self, rv, *args):
79 self.rv = rv 80 Exc.__init__(self, *args)
81 82 83 ##################################################### 84 #
85 -class Arguments:
86 """ 87 Wrapper for arguments in all controls. All non-"_" control methods are 88 assumed to take some representation of the command line. This can either 89 be: 90 91 - the line as a string 92 - the shlex'd line as a string list 93 94 To simplify usage, this class can be used at the beginning of every 95 method so:: 96 97 def method(self, args): 98 args = Arguments(args) 99 100 and it will handle the above cases as well as wrapping other Argument 101 instances. If the method takes varargs and it is desired to test for 102 single argument of the above type, then use:: 103 104 args = Arguments(*args) 105 106 """ 107
108 - def __init__(self, args = []):
109 if args == None: 110 self.args = [] 111 self.argmap = {} 112 elif isinstance(args, Arguments): 113 self.args = args.args 114 self.argmap = args.argmap 115 elif isinstance(args, str): 116 self.args = self.shlex(args) 117 self.make_argmap() 118 elif isinstance(args, list): 119 for l in args: 120 assert (isinstance(l, str) or isinstance(l, unicode)) 121 self.args = args 122 self.make_argmap() 123 else: 124 raise exceptions.Exception("Unknown argument: %s" % args)
125
126 - def make_argmap(self):
127 self.argmap = {} 128 for arg in self.args: 129 parts = arg.split("=", 1) 130 if len(parts) == 1: 131 self.argmap[parts[0]] = True 132 else: 133 self.argmap[parts[0]] = parts[1]
134
135 - def firstOther(self):
136 if len(self.args) == 0: 137 return (None,[]) 138 elif len(self.args) == 1: 139 return (self.args[0], []) 140 else: 141 return (self.args[0], self.args[1:])
142
143 - def popFirst(self):
144 return self.args.pop(0)
145
146 - def shlex(self, input):
147 """ 148 Used to split a string argument via shlex.split(). If the 149 argument is not a string, then it is returned unchnaged. 150 This is useful since the arg argument to all plugins can 151 be either a list or a string. 152 """ 153 if None == input: 154 return [] 155 elif isinstance(input, str): 156 return pyshlex.split(input) 157 else: 158 return input
159 160 ####################################### 161 # 162 # Usability methods 163 #
164 - def __iter__(self):
165 return iter(self.args)
166 - def __len__(self):
167 return len(self.args)
168 - def __str__(self):
169 return ", ".join(self.args)
170 - def join(self, text):
171 return text.join(self.args)
172 - def __getitem__(self, idx):
173 """ 174 For every argument without an "=" we return True. Otherwise, 175 the value following the first "=" is returned. 176 """ 177 return self.argmap[idx]
178 179 ##################################################### 180 #
181 -class Context:
182 """Simple context used for default logic. The CLI registry which registers 183 the plugins installs itself as a fully functional Context. 184 185 The Context class is designed to increase pluggability. Rather than 186 making calls directly on other plugins directly, the pub() method 187 routes messages to other commands. Similarly, out() and err() should 188 be used for printing statements to the user, and die() should be 189 used for exiting fatally. 190 191 """ 192
193 - def __init__(self, controls = {}):
194 self.controls = controls 195 self.dir = OMERODIR 196 self.isdebug = DEBUG # This usage will go away and default will be False
197
198 - def setdebug(self):
199 self.isdebug = True
200
201 - def safePrint(self, text, stream, newline = True):
202 """ 203 Prints text to a given string, caputring any exceptions. 204 """ 205 try: 206 stream.write(text % {"program_name": sys.argv[0]}) 207 if newline: 208 stream.write("\n") 209 else: 210 stream.flush() 211 except: 212 print >>sys.stderr, "Error printing text" 213 print >>sys.stdout, text 214 if self.isdebug: 215 traceback.print_exc()
216
217 - def pythonpath(self):
218 """ 219 Converts the current sys.path to a PYTHONPATH string 220 to be used by plugins which must start a new process. 221 222 Note: this was initially created for running during 223 testing when PYTHONPATH is not properly set. 224 """ 225 path = list(sys.path) 226 for i in range(0,len(path)-1): 227 if path[i] == '': 228 path[i] = os.getcwd() 229 pythonpath = ":".join(path) 230 return pythonpath
231
232 - def userdir(self):
233 """ 234 Returns a user directory (as path.path) which can be used 235 for storing configuration. The directory is guaranteed to 236 exist and be private (700) after execution. 237 """ 238 dir = path(os.path.expanduser("~")) / "omero" / "cli" 239 if not dir.exists(): 240 dir.mkdir() 241 elif not dir.isdir(): 242 raise Exc("%s is not a directory"%dir) 243 dir.chmod(0700) 244 return dir
245
246 - def pub(self, args):
247 self.safePrint(str(args), sys.stdout)
248
249 - def input(self, prompt, hidden = False):
250 """ 251 Reads from standard in. If hidden == True, then 252 uses getpass 253 """ 254 if hidden: 255 import getpass 256 defuser = getpass.getuser() 257 return getpass.getpass(prompt) 258 else: 259 return raw_input(prompt)
260
261 - def out(self, text, newline = True):
262 """ 263 Expects as single string as argument" 264 """ 265 self.safePrint(text, sys.stdout, newline)
266
267 - def err(self, text, newline = True):
268 """ 269 Expects a single string as argument. 270 """ 271 self.safePrint(text, sys.stderr, newline)
272
273 - def dbg(self, text, newline = True):
274 """ 275 Passes text to err() if self.isdebug is set 276 """ 277 if self.isdebug: 278 self.err(text, newline)
279
280 - def die(self, rc, args):
281 raise exceptions.Exception((rc,args))
282
283 - def exit(self, args):
284 self.out(args) 285 self.interrupt_loop = True
286
287 - def call(self, args):
288 self.out(str(args))
289
290 - def popen(self, args):
291 self.out(str(args))
292
293 - def conn(self):
294 raise NotImplementedException()
295 296 297 298 ##################################################### 299 #
300 -class BaseControl:
301 """Controls get registered with a CLI instance on loadplugins(). 302 303 To create a new control, subclass BaseControl and end your module with:: 304 305 try: 306 registry("name", MyControl) 307 except: 308 MyControl()._main() 309 310 This module should be put in the omero.plugins package. 311 312 All methods which do NOT begin with "_" are assumed to be accessible 313 to CLI users. 314 """ 315 316 ############################################### 317 # 318 # Mostly reusable code 319 #
320 - def __init__(self, ctx = Context(), dir = OMERODIR):
321 self.dir = path(dir) # Guaranteed to be a path 322 self.ctx = ctx
323
324 - def _isWindows(self):
325 p_s = platform.system() 326 if p_s == 'Windows': 327 return True 328 else: 329 return False
330
331 - def _host(self):
332 """ 333 Return hostname of current machine. Termed to be the 334 value return from socket.gethostname() up to the first 335 decimal. 336 """ 337 if not hasattr(self, "hostname") or not self.hostname: 338 self.hostname = socket.gethostname() 339 if self.hostname.find(".") > 0: 340 self.hostname = self.hostname.split(".")[0] 341 return self.hostname
342
343 - def _node(self, omero_node = None):
344 """ 345 Return the name of this node, using either the environment 346 vairable OMERO_NODE or _host(). Some subclasses may 347 override this functionality, most notably "admin" commands 348 which assume a node name of "master". 349 350 If the optional argument is not None, then the OMERO_NODE 351 environment variable will be set. 352 """ 353 if omero_node != None: 354 os.environ["OMERO_NODE"] = omero_node 355 356 if os.environ.has_key("OMERO_NODE"): 357 return os.environ["OMERO_NODE"] 358 else: 359 return self._host()
360
361 - def _icedata(self, property):
362 """ 363 General data method for creating a path from an Ice property. 364 """ 365 try: 366 nodepath = self._properties()[property] 367 368 if RELFILE.match(nodepath): 369 nodedata = self.dir / path(nodepath) 370 else: 371 nodedata = path(nodepath) 372 373 created = False 374 if not nodedata.exists(): 375 self.ctx.out("Creating "+nodedata) 376 nodedata.makedirs() 377 created = True 378 return (nodedata, created) 379 380 except KeyError, ke: 381 self.ctx.err(property + " is not configured") 382 self.ctx.die(4, str(ke))
383
384 - def _initDir(self):
385 """ 386 Initialize the directory into which the current node will log. 387 """ 388 props = self._properties() 389 nodedata = self._nodedata() 390 logdata = self.dir / path(props["Ice.StdOut"]).dirname() 391 if not logdata.exists(): 392 self.ctx.out("Initializing %s" % logdata) 393 logdata.makedirs()
394 395
396 - def _nodedata(self):
397 """ 398 Returns the data directory path for this node. This is determined 399 from the "IceGrid.Node.Data" property in the _properties() 400 map. 401 402 The directory will be created if it does not exist. 403 """ 404 data, created = self._icedata("IceGrid.Node.Data") 405 return data
406
407 - def _regdata(self):
408 """ 409 Returns the data directory for the IceGrid registry. 410 This is determined from the "IceGrid.Registry.Data" property 411 in the _properties() map. 412 413 The directory will be created if it does not exist, and 414 a warning issued. 415 """ 416 data, created = self._icedata("IceGrid.Registry.Data")
417
418 - def _pid(self):
419 """ 420 Returns a path of the form "_nodedata() / _node() + ".pid", 421 i.e. a file named NODENAME.pid in the node's data directory. 422 """ 423 pidfile = self._nodedata() / (self._node() + ".pid") 424 return pidfile
425
426 - def _cfglist(self):
427 """ 428 Returns a list of configuration files for this node. This 429 defaults to the internal configuration for all nodes, 430 followed by a file named NODENAME.cfg under the etc/ 431 directory, following by PLATFORM.cfg if it exists. 432 """ 433 cfgs = self.dir / "etc" 434 internal = cfgs / "internal.cfg" 435 owncfg = cfgs / self._node() + ".cfg" 436 results = [internal,owncfg] 437 # Look for <platform>.cfg 438 p_s = platform.system() 439 p_c = cfgs / p_s + ".cfg" 440 if p_c.exists(): 441 results.append(p_c) 442 return results
443
444 - def _icecfg(self):
445 """ 446 Uses _cfglist() to return a string argument of the form 447 "--Ice.Config=..." suitable for passing to omero.client 448 as an argument. 449 """ 450 icecfg = "--Ice.Config=%s" % ",".join(self._cfglist()) 451 return str(icecfg)
452
453 - def _intcfg(self):
454 """ 455 Returns an Ice.Config string with only the internal configuration 456 file for connecting to the IceGrid Locator. 457 """ 458 intcfg = self.dir / "etc" / "internal.cfg" 459 intcfg.abspath() 460 return str("--Ice.Config=%s" % intcfg)
461
462 - def _properties(self, prefix=""):
463 """ 464 Loads all files returned by _cfglist() into a new 465 Ice.Properties instance and return the map from 466 getPropertiesForPrefix(prefix) where the default is 467 to return all properties. 468 """ 469 import Ice 470 if not hasattr(self, "_props") or self._props == None: 471 self._props = Ice.createProperties() 472 for cfg in self._cfglist(): 473 try: 474 self._props.load(str(cfg)) 475 except Exc, exc: 476 self.ctx.die(3, "Could not find file: "+cfg + "\nDid you specify the proper node?") 477 return self._props.getPropertiesForPrefix(prefix)
478
479 - def _ask_for_password(self, reason = "", root_pass = None):
480 while not root_pass or len(root_pass) < 1: 481 root_pass = self.ctx.input("Please enter password%s: "%reason, hidden = True) 482 if root_pass == None or root_pass == "": 483 self.ctx.err("Password cannot be empty") 484 continue 485 confirm = self.ctx.input("Please re-enter password%s: "%reason, hidden = True) 486 if root_pass != confirm: 487 root_pass = None 488 self.ctx.err("Passwords don't match") 489 continue 490 break 491 return root_pass
492 493 ############################################### 494 # 495 # Methods likely to be implemented by subclasses 496 #
497 - def help(self, args = []):
498 return """ Help not implemented """
499
500 - def _complete(self, text, line, begidx, endidx):
501 try: 502 import readline 503 # import rlcompleter 504 except ImportError, ie: 505 self.ctx.err("No readline") 506 return [] 507 508 # readline.parse_and_bind("tab: complete") 509 # readline.set_completer_delims(' \t\n`~!@#$%^&*()-=+[{]}\\|;:\'",<>;?') 510 511 completions = [method for method in dir(self) if callable(getattr(self, method)) ] 512 completions = [ str(method + " ") for method in completions if method.startswith(text) and not method.startswith("_") ] 513 return completions
514
515 - def _likes(self, args):
516 """ 517 Checks whether or not it is likely for the given args 518 to be run successfully by the given command. This is 519 useful for plugins which have significant start up 520 times. 521 522 Simply return True is a possible solution. The default 523 implementation checks that the subclass has a method 524 matching the first argument, such that the default 525 __call__() implementation could dispatch to it. Or if 526 no arguments are given, True is returned since self._noargs() 527 can be called. 528 """ 529 args = Arguments(args) 530 first, other = args.firstOther() 531 if first == None or hasattr(self, first): 532 return True 533 return False
534
535 - def __call__(self, *args):
536 """ 537 Main dispatch method for a control instance. The default 538 implementation assumes that the *args consists of either 539 no elements or exactly one list of strings ==> (["str"],) 540 541 If no args are present, _noargs is called. Subclasses may want 542 to read from stdin or drop into a shell from _noargs(). 543 544 Otherwise, the rest of the arguments are passed to the method 545 named by the first argument, if _likes() returns True. 546 """ 547 args = Arguments(*args) 548 first,other = args.firstOther() 549 if first == None: 550 self._noargs() 551 else: 552 if not self._likes(args): 553 if self.ctx.isdebug: 554 # Throwing an exception 555 # so we can see how we got here. 556 raise Exc("Bad arguments: " + str(args)) 557 self.ctx.err("Bad arguments: " + ",".join(args)) 558 self.help() 559 self.ctx.die(8, "Exiting.") 560 else: 561 m = getattr(self, first) 562 return m(other)
563
564 - def _noargs(self):
565 """ 566 Method called when __call__() is called without any arguments. Some implementations 567 may want to drop the user into a shell or read from standard in. By default, help() 568 is printed. 569 """ 570 self.help()
571
572 - def _main(self):
573 """ 574 Simple _main() logic which is reusable by subclasses to do something when the control 575 is executed directly. It is unlikely that such an excution will function properly, 576 but it may be useful for testing purposes. 577 """ 578 if __name__ == "__main__": 579 if not self._likes(sys.argv[1:]): 580 self.help() 581 else: 582 self.__call__(sys.argv[1:])
583
584 -class HelpControl(BaseControl):
585 """ 586 Defined here since the background loading might be too 587 slow to have all help available 588 """ 589
590 - def _complete(self, text, line, begidx, endidx):
591 """ 592 This is something of a hack. This should either be a part 593 of the context interface, or we should put it somewhere 594 in a utility. FIXME. 595 """ 596 return self.ctx.completenames(text, line, begidx, endidx)
597
598 - def help(self, args = None):
599 self.out("Print help")
600
601 - def __call__(self, *args):
602 603 args = Arguments(*args) 604 first, other = args.firstOther() 605 606 self.ctx.waitForPlugins() 607 controls = self.ctx.controls.keys() 608 controls.sort() 609 610 if not first: 611 print """OmeroCli client, version %(version)s 612 613 Usage: %(program_name)s <command> [options] args 614 See 'help <command>' for more information on syntax 615 Type 'quit' to exit 616 617 Available commands: 618 """ % {"program_name":sys.argv[0],"version":VERSION} 619 620 for name in controls: 621 print """ %s""" % name 622 print """ 623 For additional information, see http://trac.openmicroscopy.org.uk/omero/wiki/OmeroCli""" 624 625 else: 626 try: 627 self.ctx.controls[first].help_method() 628 ##event = [first, "help"] 629 ##event.extend(other) 630 ##self.ctx.pub(event) 631 except KeyError, ke: 632 self.ctx.unknown_command(first)
633
634 -class CLI(cmd.Cmd, Context):
635 """ 636 Command line interface class. Supports various styles of executing the 637 registered plugins. Each plugin is given the chance to update this class 638 by adding methods of the form "do_<plugin name>". 639 """ 640
641 - class PluginsLoaded(object):
642 """ 643 Thread-safe class for storing whether or not all the plugins 644 have been loaded 645 """
646 - def __init__(self):
647 self.lock = Lock() 648 self.done = False
649 - def get(self):
650 self.lock.acquire() 651 try: 652 return self.done 653 finally: 654 self.lock.release()
655 - def set(self):
656 self.lock.acquire() 657 try: 658 self.done = True 659 finally: 660 self.lock.release()
661
662 - def __init__(self):
663 """ 664 Also sets the "_client" field for this instance to None. Each cli 665 maintains a single active client. 666 """ 667 cmd.Cmd.__init__(self) 668 Context.__init__(self) 669 self.prompt = 'omero> ' 670 self.interrupt_loop = False 671 self._client = None 672 self._pluginsLoaded = CLI.PluginsLoaded() 673 self.rv = 0 # Return value to be returned
674
675 - def invoke(self, line):
676 """ 677 Copied from cmd.py 678 """ 679 line = self.precmd(line) 680 stop = self.onecmd(line) 681 stop = self.postcmd(stop, line)
682
683 - def invokeloop(self):
684 self.selfintro = TEXT 685 if not self.stdin.isatty(): 686 self.selfintro = "" 687 self.prompt = "" 688 while not self.interrupt_loop: 689 try: 690 self.cmdloop(self.selfintro) 691 except KeyboardInterrupt, ki: 692 # We've done the intro once now. Don't repeat yourself. 693 self.selfintro = "" 694 try: 695 import readline 696 if len(readline.get_line_buffer()) > 0: 697 self.out("") 698 else: 699 self.out("Use quit to exit") 700 except ImportError: 701 self.out("Use quit to exit")
702
703 - def precmd(self, input):
704 if isinstance(input,str): 705 if COMMENT.match(input): 706 return "" 707 return input
708
709 - def onecmd(self, line):
710 args = Arguments(line) 711 try: 712 # Starting a new command. Reset the return value to 0 713 # If err or die are called, set rv non-0 value 714 self.rv = 0 715 return cmd.Cmd.onecmd(self, args) 716 except AttributeError, ae: 717 self.err("Possible error in plugin:") 718 self.err(str(ae)) 719 if self.isdebug: 720 traceback.print_exc() 721 except NonZeroReturnCode, nzrc: 722 self.rv = nzrc.rv 723 return False # Continue
724
725 - def postcmd(self, stop, line):
726 return self.interrupt_loop
727
728 - def emptyline(self):
729 pass
730
731 - def parseline(self, line):
732 """ 733 Overrides the parseline functionality of cmd.py in order to 734 take command line parameters without shlex'ing and unshlex'ing 735 them. If "line" is an array, then the first element will be 736 returned as "cmd" and the rest as "args". 737 """ 738 if isinstance(line,list): 739 if not line: 740 return (None, None, None) 741 elif len(line) == 0: 742 return (None, None, "") 743 elif len(line) == 1: 744 return (line[0],None,line[0]) 745 else: 746 return (line[0],line[1:],Arguments(line)) 747 elif isinstance(line, Arguments): 748 first,other = line.firstOther() 749 return (first, other, line) 750 else: 751 return cmd.Cmd.parseline(self,line)
752
753 - def default(self,arg):
754 arg = Arguments(arg) 755 try: 756 arg["EOF"] 757 self.exit("") 758 except KeyError: 759 first, other = arg.firstOther() 760 file = OMEROCLI / "plugins" / (first + ".py") 761 loc = {"register": self.register} 762 try: 763 execfile( str(file), loc ) 764 except Exc, ex: 765 self.dbg("Could not load %s: %s" % (first, ex)) 766 self.waitForPlugins() 767 768 if self.controls.has_key(first): 769 return self.invoke(arg.args) 770 else: 771 self.unknown_command(first)
772
773 - def unknown_command(self, first):
774 self.err("""Unknown command: "%s" Try "help".""" % first)
775
776 - def completenames(self, text, line, begidx, endidx):
777 names = self.controls.keys() 778 return [ str(n + " ") for n in names if n.startswith(line) ]
779 780 # Delegation
781 - def do_start(self, args):
782 """ 783 Alias for "node start" 784 """ 785 args = pyshlex.split(args) 786 if not args: 787 args = ["node","start"] 788 else: 789 args = ["node","start"] + args 790 self.pub(args)
791 792 ########################################## 793 ## 794 ## Context interface 795 ##
796 - def exit(self, args):
797 self.out(args) 798 self.interrupt_loop = True
799
800 - def die(self, rc, text):
801 self.err(text) 802 self.rv = rc 803 self.interrupt_loop = True 804 raise NonZeroReturnCode(rc, "die called")
805
806 - def pub(self, args):
807 """ 808 Publishes the command, using the first argument as routing 809 information, i.e. the name of the plugin to be instantiated, 810 and the rest as the arguments to its __call__() method. 811 """ 812 try: 813 args = Arguments(args) 814 first, other = args.firstOther() 815 if first == None: 816 self.ctx.die(2, "No plugin given. Giving up") 817 else: 818 control = self.controls[first] 819 control(other) 820 except KeyError, ke: 821 self.die(11, "Missing required plugin: "+ str(ke))
822
823 - def _env(self):
824 """ 825 Configure environment with PYTHONPATH as 826 setup by bin/omero 827 """ 828 home = str(self.dir / "lib" / "python") 829 env = dict(os.environ) 830 pypath = env.get("PYTHONPATH", None) 831 if pypath is None: 832 pypath = home 833 else: 834 if pypath.endswith(os.path.pathsep): 835 pypath = "%s%s" % (pypath, home) 836 else: 837 pypath = "%s%s%s" % (pypath, os.path.pathsep, home) 838 env["PYTHONPATH"] = pypath 839 return env
840
841 - def _cwd(self, cwd):
842 if cwd is None: 843 cwd = str(OMERODIR) 844 else: 845 cwd = str(cwd) 846 return cwd
847
848 - def call(self, args, strict = True, cwd = None):
849 """ 850 Calls the string in a subprocess and dies if the return value is not 0 851 """ 852 self.dbg("Executing: %s" % args) 853 rv = subprocess.call(args, env = self._env(), cwd = self._cwd(cwd)) 854 if strict and not rv == 0: 855 raise NonZeroReturnCode(rv, "%s => %d" % (" ".join(args), rv)) 856 return rv
857
858 - def popen(self, args, cwd = None):
859 self.dbg("Returning popen: %s" % args) 860 return subprocess.Popen(args, env = self._env(), cwd = self._cwd(cwd), stdout = subprocess.PIPE, stderr = subprocess.PIPE)
861
862 - def readDefaults(self):
863 try: 864 f = path(OMERODIR) / "etc" / "omero.properties" 865 f = f.open() 866 output = "".join(f.readlines()) 867 f.close() 868 except: 869 if self.isdebug: 870 raise 871 print "No omero.properties found" 872 output = "" 873 return output
874
875 - def parsePropertyFile(self, data, output):
876 for line in output.splitlines(): 877 if line.startswith("Listening for transport dt_socket at address"): 878 self.dbg("Ignoring stdout 'Listening for transport' from DEBUG=1") 879 continue 880 parts = line.split("=",1) 881 if len(parts) == 2: 882 data.properties.setProperty(parts[0],parts[1]) 883 self.dbg("Set property: %s=%s" % (parts[0],parts[1]) ) 884 else: 885 self.dbg("Bad property:"+str(parts)) 886 return data
887
888 - def initData(self, properties={}):
889 """ 890 Uses "omero prefs" to create an Ice.InitializationData(). 891 """ 892 from omero.plugins.prefs import getprefs 893 output = getprefs(["get"], str(OMERODIR / "lib")) 894 895 import Ice 896 data = Ice.InitializationData() 897 data.properties = Ice.createProperties() 898 for k,v in properties.items(): 899 data.properties.setProperty(k,v) 900 self.parsePropertyFile(data, output) 901 return data
902 903
904 - def conn(self, properties={}, profile=None):
905 """ 906 Either creates or returns the exiting omero.client instance. 907 Uses the comm() method with the same signature. 908 """ 909 910 if self._client: 911 return self._client 912 913 import omero 914 try: 915 data = self.initData(properties) 916 self._client = omero.client(sys.argv, id = data) 917 self._client.createSession() 918 return self._client 919 except Exc, exc: 920 self._client = None 921 raise exc
922 923 ## 924 ## Plugin registry 925 ## 926
927 - def register(self, name, Control):
928 """ This method is added to the globals when execfile() is 929 called on each plugin. An instance of the control should be 930 passed to the register method which will be added to the CLI. 931 """ 932 933 class Wrapper: 934 def __init__(self, ctx, control): 935 self.ctx = ctx 936 self.Control = Control 937 self.control = None
938 def _setup(self): 939 if self.control == None: 940 self.control = self.Control(ctx = self.ctx)
941 def do_method(self, *args): 942 try: 943 self._setup() 944 return self.control.__call__(*args) 945 except NonZeroReturnCode, nzrc: 946 raise 947 except Exc, exc: 948 if self.ctx.isdebug: 949 traceback.print_exc() 950 ## Prevent duplication - self.ctx.err("Error:"+str(exc)) 951 self.ctx.die(10, str(exc)) 952 def complete_method(self, *args): 953 try: 954 self._setup() 955 return self.control._complete(*args) 956 except Exc, exc: 957 self.ctx.err("Completion error:"+str(exc)) 958 def help_method(self, *args): 959 try: 960 self._setup() 961 return self.control.help(*args) 962 except Exc, exc: 963 self.ctx.err("Help error:"+str(exc)) 964 def __call__(self, *args): 965 """ 966 If the wrapper gets treated like the control 967 instance, and __call__()'d, then pass the *args 968 to do_method() 969 """ 970 return self.do_method(*args) 971 972 wrapper = Wrapper(self, Control) 973 setattr(self, "do_" + name, wrapper.do_method) 974 setattr(self, "complete_" + name, wrapper.complete_method) 975 setattr(self, "help_" + name, wrapper.help_method) 976 self.controls[name] = wrapper 977
978 - def waitForPlugins(self):
979 self.dbg("Starting waitForPlugins") 980 while not self._pluginsLoaded.get(): 981 self.dbg("Waiting for plugins...") 982 time.sleep(0.1)
983
984 - def loadplugins(self):
985 """ Finds all plugins and gives them a chance to register 986 themselves with the CLI instance """ 987 988 loc = {"register": self.register} 989 990 plugins = OMEROCLI / "plugins" 991 for plugin in plugins.walkfiles("*.py"): 992 if self.isdebug: 993 print "Loading " + plugin 994 if -1 == plugin.find("#"): # Omit emacs files 995 try: 996 execfile( plugin, loc ) 997 except KeyboardInterrupt: 998 raise 999 except: 1000 self.err("Error loading:"+plugin) 1001 traceback.print_exc() 1002 self._pluginsLoaded.set()
1003 1004 ## End Cli 1005 ########################################################### 1006
1007 -def argv(args=sys.argv):
1008 """ 1009 Main entry point for the OMERO command-line interface. First 1010 loads all plugins by passing them the classes defined here 1011 so they can register their methods. 1012 1013 Then the case where arguments are passed on the command line are 1014 handled. 1015 1016 Finally, the cli enters a command loop reading from standard in. 1017 """ 1018 1019 # Modiying the run-time environment 1020 old_ice_config = os.getenv("ICE_CONFIG") 1021 os.unsetenv("ICE_CONFIG") 1022 try: 1023 1024 # Modifying the args list if the name of the file 1025 # has arguments encoded in it 1026 executable = path(args[0]) 1027 executable = str(executable.basename()) 1028 if executable.find("-") >= 0: 1029 parts = executable.split("-") 1030 for arg in args[1:]: 1031 parts.append(arg) 1032 args = parts 1033 1034 cli = CLI() 1035 cli.register("help", HelpControl) 1036 class PluginLoader(Thread): 1037 def run(self): 1038 cli.loadplugins()
1039 # Disabling background loading 1040 # until 2.4 hangs are fixed 1041 PluginLoader().run() # start() 1042 1043 if len(args) > 1: 1044 cli.invoke(args[1:]) 1045 return cli.rv 1046 else: 1047 cli.invokeloop() 1048 return cli.rv 1049 finally: 1050 if old_ice_config: 1051 os.putenv("ICE_CONFIG", old_ice_config) 1052