Package Bio :: Package Application
[hide private]
[frames] | no frames]

Source Code for Package Bio.Application

  1  # Copyright 2001-2004 Brad Chapman. 
  2  # Revisions copyright 2009-2013 by Peter Cock. 
  3  # All rights reserved. 
  4  # This code is part of the Biopython distribution and governed by its 
  5  # license.  Please see the LICENSE file that should have been included 
  6  # as part of this package. 
  7  """General mechanisms to access applications in Biopython. 
  8   
  9  This module is not intended for direct use. It provides the basic objects which 
 10  are subclassed by our command line wrappers, such as: 
 11   
 12   - Bio.Align.Applications 
 13   - Bio.Blast.Applications 
 14   - Bio.Emboss.Applications 
 15   - Bio.Sequencing.Applications 
 16   
 17  These modules provide wrapper classes for command line tools to help you 
 18  construct command line strings by setting the values of each parameter. 
 19  The finished command line strings are then normally invoked via the built-in 
 20  Python module subprocess. 
 21  """ 
 22  from __future__ import print_function 
 23  from Bio._py3k import basestring 
 24   
 25  import os 
 26  import platform 
 27  import sys 
 28  import subprocess 
 29  import re 
 30   
 31  from subprocess import CalledProcessError as _ProcessCalledError 
 32   
 33  from Bio import File 
 34   
 35   
 36  # Use this regular expression to test the property names are going to 
 37  # be valid as Python properties or arguments 
 38  _re_prop_name = re.compile(r"^[a-zA-Z][a-zA-Z0-9_]*$") 
 39  assert _re_prop_name.match("t") 
 40  assert _re_prop_name.match("test") 
 41  assert _re_prop_name.match("_test") is None  # we don't want private names 
 42  assert _re_prop_name.match("-test") is None 
 43  assert _re_prop_name.match("any-hyphen") is None 
 44  assert _re_prop_name.match("underscore_ok") 
 45  assert _re_prop_name.match("test_name") 
 46  assert _re_prop_name.match("test2") 
 47  # These are reserved names in Python itself, 
 48  _reserved_names = ["and", "del", "from", "not", "while", "as", "elif", 
 49                     "global", "or", "with", "assert", "else", "if", "pass", 
 50                     "yield", "break", "except", "import", "print", "class", 
 51                     "exec", "in", "raise", "continue", "finally", "is", 
 52                     "return", "def", "for", "lambda", "try"] 
 53  # These are reserved names due to the way the wrappers work 
 54  _local_reserved_names = ["set_parameter"] 
 55   
 56   
57 -class ApplicationError(_ProcessCalledError):
58 """Raised when an application returns a non-zero exit status. 59 60 The exit status will be stored in the returncode attribute, similarly 61 the command line string used in the cmd attribute, and (if captured) 62 stdout and stderr as strings. 63 64 This exception is a subclass of subprocess.CalledProcessError. 65 66 >>> err = ApplicationError(-11, "helloworld", "", "Some error text") 67 >>> err.returncode, err.cmd, err.stdout, err.stderr 68 (-11, 'helloworld', '', 'Some error text') 69 >>> print(err) 70 Non-zero return code -11 from 'helloworld', message 'Some error text' 71 72 """
73 - def __init__(self, returncode, cmd, stdout="", stderr=""):
74 self.returncode = returncode 75 self.cmd = cmd 76 self.stdout = stdout 77 self.stderr = stderr
78
79 - def __str__(self):
80 # get first line of any stderr message 81 try: 82 msg = self.stderr.lstrip().split("\n", 1)[0].rstrip() 83 except Exception: # TODO, ValueError? AttributeError? 84 msg = "" 85 if msg: 86 return "Non-zero return code %d from %r, message %r" \ 87 % (self.returncode, self.cmd, msg) 88 else: 89 return "Non-zero return code %d from %r" \ 90 % (self.returncode, self.cmd)
91
92 - def __repr__(self):
93 return "ApplicationError(%i, %s, %s, %s)" \ 94 % (self.returncode, self.cmd, self.stdout, self.stderr)
95 96
97 -class AbstractCommandline(object):
98 """Generic interface for constructing command line strings. 99 100 This class shouldn't be called directly; it should be subclassed to 101 provide an implementation for a specific application. 102 103 For a usage example we'll show one of the EMBOSS wrappers. You can set 104 options when creating the wrapper object using keyword arguments - or 105 later using their corresponding properties: 106 107 >>> from Bio.Emboss.Applications import WaterCommandline 108 >>> cline = WaterCommandline(gapopen=10, gapextend=0.5) 109 >>> cline 110 WaterCommandline(cmd='water', gapopen=10, gapextend=0.5) 111 112 You can instead manipulate the parameters via their properties, e.g. 113 114 >>> cline.gapopen 115 10 116 >>> cline.gapopen = 20 117 >>> cline 118 WaterCommandline(cmd='water', gapopen=20, gapextend=0.5) 119 120 You can clear a parameter you have already added by 'deleting' the 121 corresponding property: 122 123 >>> del cline.gapopen 124 >>> cline.gapopen 125 >>> cline 126 WaterCommandline(cmd='water', gapextend=0.5) 127 128 Once you have set the parameters you need, you can turn the object into 129 a string (e.g. to log the command): 130 131 >>> str(cline) 132 Traceback (most recent call last): 133 ... 134 ValueError: You must either set outfile (output filename), or enable filter or stdout (output to stdout). 135 136 In this case the wrapper knows certain arguments are required to construct 137 a valid command line for the tool. For a complete example, 138 139 >>> from Bio.Emboss.Applications import WaterCommandline 140 >>> water_cmd = WaterCommandline(gapopen=10, gapextend=0.5) 141 >>> water_cmd.asequence = "asis:ACCCGGGCGCGGT" 142 >>> water_cmd.bsequence = "asis:ACCCGAGCGCGGT" 143 >>> water_cmd.outfile = "temp_water.txt" 144 >>> print(water_cmd) 145 water -outfile=temp_water.txt -asequence=asis:ACCCGGGCGCGGT -bsequence=asis:ACCCGAGCGCGGT -gapopen=10 -gapextend=0.5 146 >>> water_cmd 147 WaterCommandline(cmd='water', outfile='temp_water.txt', asequence='asis:ACCCGGGCGCGGT', bsequence='asis:ACCCGAGCGCGGT', gapopen=10, gapextend=0.5) 148 149 You would typically run the command line via a standard Python operating 150 system call using the subprocess module for full control. For the simple 151 case where you just want to run the command and get the output: 152 153 stdout, stderr = water_cmd() 154 155 Note that by default we assume the underlying tool is installed on the 156 system $PATH environment variable. This is normal under Linux/Unix, but 157 may need to be done manually under Windows. Alternatively, you can specify 158 the full path to the binary as the first argument (cmd): 159 160 >>> from Bio.Emboss.Applications import WaterCommandline 161 >>> water_cmd = WaterCommandline("C:\Program Files\EMBOSS\water.exe", 162 ... gapopen=10, gapextend=0.5, 163 ... asequence="asis:ACCCGGGCGCGGT", 164 ... bsequence="asis:ACCCGAGCGCGGT", 165 ... outfile="temp_water.txt") 166 >>> print(water_cmd) 167 "C:\Program Files\EMBOSS\water.exe" -outfile=temp_water.txt -asequence=asis:ACCCGGGCGCGGT -bsequence=asis:ACCCGAGCGCGGT -gapopen=10 -gapextend=0.5 168 169 Notice that since the path name includes a space it has automatically 170 been quoted. 171 172 """ 173 # TODO - Replace the above example since EMBOSS doesn't work properly 174 # if installed into a folder with a space like "C:\Program Files\EMBOSS" 175 # 176 # Note the call example above is not a doctest as we can't handle EMBOSS 177 # (or any other tool) being missing in the unit tests. 178 179 parameters = None # will be a list defined in subclasses 180
181 - def __init__(self, cmd, **kwargs):
182 """Create a new instance of a command line wrapper object.""" 183 # Init method - should be subclassed! 184 # 185 # The subclass methods should look like this: 186 # 187 # def __init__(self, cmd="muscle", **kwargs): 188 # self.parameters = [...] 189 # AbstractCommandline.__init__(self, cmd, **kwargs) 190 # 191 # i.e. There should have an optional argument "cmd" to set the location 192 # of the executable (with a sensible default which should work if the 193 # command is on the path on Unix), and keyword arguments. It should 194 # then define a list of parameters, all objects derived from the base 195 # class _AbstractParameter. 196 # 197 # The keyword arguments should be any valid parameter name, and will 198 # be used to set the associated parameter. 199 self.program_name = cmd 200 try: 201 parameters = self.parameters 202 except AttributeError: 203 raise AttributeError("Subclass should have defined self.parameters") 204 # Create properties for each parameter at run time 205 aliases = set() 206 for p in parameters: 207 if not p.names: 208 assert isinstance(p, _StaticArgument), p 209 continue 210 for name in p.names: 211 if name in aliases: 212 raise ValueError("Parameter alias %s multiply defined" 213 % name) 214 aliases.add(name) 215 name = p.names[-1] 216 if _re_prop_name.match(name) is None: 217 raise ValueError("Final parameter name %s cannot be used as " 218 "an argument or property name in python" 219 % repr(name)) 220 if name in _reserved_names: 221 raise ValueError("Final parameter name %s cannot be used as " 222 "an argument or property name because it is " 223 "a reserved word in python" % repr(name)) 224 if name in _local_reserved_names: 225 raise ValueError("Final parameter name %s cannot be used as " 226 "an argument or property name due to the " 227 "way the AbstractCommandline class works" 228 % repr(name)) 229 230 # Beware of binding-versus-assignment confusion issues 231 def getter(name): 232 return lambda x: x._get_parameter(name)
233 234 def setter(name): 235 return lambda x, value: x.set_parameter(name, value)
236 237 def deleter(name): 238 return lambda x: x._clear_parameter(name) 239 240 doc = p.description 241 if isinstance(p, _Switch): 242 doc += "\n\nThis property controls the addition of the %s " \ 243 "switch, treat this property as a boolean." % p.names[0] 244 else: 245 doc += "\n\nThis controls the addition of the %s parameter " \ 246 "and its associated value. Set this property to the " \ 247 "argument value required." % p.names[0] 248 prop = property(getter(name), setter(name), deleter(name), doc) 249 setattr(self.__class__, name, prop) # magic! 250 for key, value in kwargs.items(): 251 self.set_parameter(key, value) 252
253 - def _validate(self):
254 """Make sure the required parameters have been set (PRIVATE). 255 256 No return value - it either works or raises a ValueError. 257 258 This is a separate method (called from __str__) so that subclasses may 259 override it. 260 """ 261 for p in self.parameters: 262 # Check for missing required parameters: 263 if p.is_required and not(p.is_set): 264 raise ValueError("Parameter %s is not set." 265 % p.names[-1])
266 # Also repeat the parameter validation here, just in case? 267
268 - def __str__(self):
269 """Make the commandline string with the currently set options. 270 271 e.g. 272 >>> from Bio.Emboss.Applications import WaterCommandline 273 >>> cline = WaterCommandline(gapopen=10, gapextend=0.5) 274 >>> cline.asequence = "asis:ACCCGGGCGCGGT" 275 >>> cline.bsequence = "asis:ACCCGAGCGCGGT" 276 >>> cline.outfile = "temp_water.txt" 277 >>> print(cline) 278 water -outfile=temp_water.txt -asequence=asis:ACCCGGGCGCGGT -bsequence=asis:ACCCGAGCGCGGT -gapopen=10 -gapextend=0.5 279 >>> str(cline) 280 'water -outfile=temp_water.txt -asequence=asis:ACCCGGGCGCGGT -bsequence=asis:ACCCGAGCGCGGT -gapopen=10 -gapextend=0.5' 281 """ 282 self._validate() 283 commandline = "%s " % _escape_filename(self.program_name) 284 for parameter in self.parameters: 285 if parameter.is_set: 286 # This will include a trailing space: 287 commandline += str(parameter) 288 return commandline.strip() # remove trailing space
289
290 - def __repr__(self):
291 """Return a representation of the command line object for debugging. 292 293 e.g. 294 >>> from Bio.Emboss.Applications import WaterCommandline 295 >>> cline = WaterCommandline(gapopen=10, gapextend=0.5) 296 >>> cline.asequence = "asis:ACCCGGGCGCGGT" 297 >>> cline.bsequence = "asis:ACCCGAGCGCGGT" 298 >>> cline.outfile = "temp_water.txt" 299 >>> print(cline) 300 water -outfile=temp_water.txt -asequence=asis:ACCCGGGCGCGGT -bsequence=asis:ACCCGAGCGCGGT -gapopen=10 -gapextend=0.5 301 >>> cline 302 WaterCommandline(cmd='water', outfile='temp_water.txt', asequence='asis:ACCCGGGCGCGGT', bsequence='asis:ACCCGAGCGCGGT', gapopen=10, gapextend=0.5) 303 """ 304 answer = "%s(cmd=%s" % (self.__class__.__name__, repr(self.program_name)) 305 for parameter in self.parameters: 306 if parameter.is_set: 307 if isinstance(parameter, _Switch): 308 answer += ", %s=True" % parameter.names[-1] 309 else: 310 answer += ", %s=%s" \ 311 % (parameter.names[-1], repr(parameter.value)) 312 answer += ")" 313 return answer
314
315 - def _get_parameter(self, name):
316 """Get a commandline option value.""" 317 for parameter in self.parameters: 318 if name in parameter.names: 319 if isinstance(parameter, _Switch): 320 return parameter.is_set 321 else: 322 return parameter.value 323 raise ValueError("Option name %s was not found." % name)
324
325 - def _clear_parameter(self, name):
326 """Reset or clear a commandline option value.""" 327 cleared_option = False 328 for parameter in self.parameters: 329 if name in parameter.names: 330 parameter.value = None 331 parameter.is_set = False 332 cleared_option = True 333 if not cleared_option: 334 raise ValueError("Option name %s was not found." % name)
335
336 - def set_parameter(self, name, value=None):
337 """Set a commandline option for a program (OBSOLETE). 338 339 Every parameter is available via a property and as a named 340 keyword when creating the instance. Using either of these is 341 preferred to this legacy set_parameter method which is now 342 OBSOLETE, and likely to be DEPRECATED and later REMOVED in 343 future releases. 344 """ 345 set_option = False 346 for parameter in self.parameters: 347 if name in parameter.names: 348 if isinstance(parameter, _Switch): 349 if value is None: 350 import warnings 351 warnings.warn("For a switch type argument like %s, " 352 "we expect a boolean. None is treated " 353 "as FALSE!" % parameter.names[-1]) 354 parameter.is_set = bool(value) 355 set_option = True 356 else: 357 if value is not None: 358 self._check_value(value, name, parameter.checker_function) 359 parameter.value = value 360 parameter.is_set = True 361 set_option = True 362 if not set_option: 363 raise ValueError("Option name %s was not found." % name)
364
365 - def _check_value(self, value, name, check_function):
366 """Check whether the given value is valid. 367 368 No return value - it either works or raises a ValueError. 369 370 This uses the passed function 'check_function', which can either 371 return a [0, 1] (bad, good) value or raise an error. Either way 372 this function will raise an error if the value is not valid, or 373 finish silently otherwise. 374 """ 375 if check_function is not None: 376 is_good = check_function(value) # May raise an exception 377 assert is_good in [0, 1, True, False] 378 if not is_good: 379 raise ValueError("Invalid parameter value %r for parameter %s" 380 % (value, name))
381
382 - def __setattr__(self, name, value):
383 """Set attribute name to value (PRIVATE). 384 385 This code implements a workaround for a user interface issue. 386 Without this __setattr__ attribute-based assignment of parameters 387 will silently accept invalid parameters, leading to known instances 388 of the user assuming that parameters for the application are set, 389 when they are not. 390 391 >>> from Bio.Emboss.Applications import WaterCommandline 392 >>> cline = WaterCommandline(gapopen=10, gapextend=0.5, stdout=True) 393 >>> cline.asequence = "a.fasta" 394 >>> cline.bsequence = "b.fasta" 395 >>> cline.csequence = "c.fasta" 396 Traceback (most recent call last): 397 ... 398 ValueError: Option name csequence was not found. 399 >>> print(cline) 400 water -stdout -asequence=a.fasta -bsequence=b.fasta -gapopen=10 -gapextend=0.5 401 402 This workaround uses a whitelist of object attributes, and sets the 403 object attribute list as normal, for these. Other attributes are 404 assumed to be parameters, and passed to the self.set_parameter method 405 for validation and assignment. 406 """ 407 if name in ['parameters', 'program_name']: # Allowed attributes 408 self.__dict__[name] = value 409 else: 410 self.set_parameter(name, value) # treat as a parameter
411
412 - def __call__(self, stdin=None, stdout=True, stderr=True, 413 cwd=None, env=None):
414 """Executes the command, waits for it to finish, and returns output. 415 416 Runs the command line tool and waits for it to finish. If it returns 417 a non-zero error level, an exception is raised. Otherwise two strings 418 are returned containing stdout and stderr. 419 420 The optional stdin argument should be a string of data which will be 421 passed to the tool as standard input. 422 423 The optional stdout and stderr argument may be filenames (string), 424 but otherwise are treated as a booleans, and control if the output 425 should be captured as strings (True, default), or ignored by sending 426 it to /dev/null to avoid wasting memory (False). If sent to a file 427 or ignored, then empty string(s) are returned. 428 429 The optional cwd argument is a string giving the working directory 430 to run the command from. See Python's subprocess module documentation 431 for more details. 432 433 The optional env argument is a dictionary setting the environment 434 variables to be used in the new process. By default the current 435 process' environment variables are used. See Python's subprocess 436 module documentation for more details. 437 438 Default example usage:: 439 440 from Bio.Emboss.Applications import WaterCommandline 441 water_cmd = WaterCommandline(gapopen=10, gapextend=0.5, 442 stdout=True, auto=True, 443 asequence="a.fasta", bsequence="b.fasta") 444 print("About to run: %s" % water_cmd) 445 std_output, err_output = water_cmd() 446 447 This functionality is similar to subprocess.check_output() added in 448 Python 2.7. In general if you require more control over running the 449 command, use subprocess directly. 450 451 As of Biopython 1.56, when the program called returns a non-zero error 452 level, a custom ApplicationError exception is raised. This includes 453 any stdout and stderr strings captured as attributes of the exception 454 object, since they may be useful for diagnosing what went wrong. 455 """ 456 if not stdout: 457 stdout_arg = open(os.devnull, "w") 458 elif isinstance(stdout, basestring): 459 stdout_arg = open(stdout, "w") 460 else: 461 stdout_arg = subprocess.PIPE 462 463 if not stderr: 464 stderr_arg = open(os.devnull, "w") 465 elif isinstance(stderr, basestring): 466 if stdout == stderr: 467 stderr_arg = stdout_arg # Write both to the same file 468 else: 469 stderr_arg = open(stderr, "w") 470 else: 471 stderr_arg = subprocess.PIPE 472 473 # We may not need to supply any piped input, but we setup the 474 # standard input pipe anyway as a work around for a python 475 # bug if this is called from a Windows GUI program. For 476 # details, see http://bugs.python.org/issue1124861 477 # 478 # Using universal newlines is important on Python 3, this 479 # gives unicode handles rather than bytes handles. 480 481 # Windows 7, 8 and 8.1 want shell = True 482 # TODO: Test under Windows 10 and revisit platform detection. 483 if sys.platform != "win32": 484 use_shell = True 485 else: 486 win_ver = platform.win32_ver()[0] 487 if win_ver in ["7", "8", "post2012Server"]: 488 use_shell = True 489 else: 490 use_shell = False 491 child_process = subprocess.Popen(str(self), stdin=subprocess.PIPE, 492 stdout=stdout_arg, stderr=stderr_arg, 493 universal_newlines=True, 494 cwd=cwd, env=env, 495 shell=use_shell) 496 # Use .communicate as can get deadlocks with .wait(), see Bug 2804 497 stdout_str, stderr_str = child_process.communicate(stdin) 498 if not stdout: 499 assert not stdout_str, stdout_str 500 if not stderr: 501 assert not stderr_str, stderr_str 502 return_code = child_process.returncode 503 504 # Particularly important to close handles on Jython and PyPy 505 # (where garbage collection is less predictable) and on Windows 506 # (where cannot delete files with an open handle): 507 if not stdout or isinstance(stdout, basestring): 508 # We opened /dev/null or a file 509 stdout_arg.close() 510 if not stderr or (isinstance(stderr, basestring) and stdout != stderr): 511 # We opened /dev/null or a file 512 stderr_arg.close() 513 514 if return_code: 515 raise ApplicationError(return_code, str(self), 516 stdout_str, stderr_str) 517 return stdout_str, stderr_str
518 519
520 -class _AbstractParameter(object):
521 """A class to hold information about a parameter for a commandline. 522 523 Do not use this directly, instead use one of the subclasses. 524 """
525 - def __init__(self):
526 raise NotImplementedError
527
528 - def __str__(self):
529 raise NotImplementedError
530 531
532 -class _Option(_AbstractParameter):
533 """Represent an option that can be set for a program. 534 535 This holds UNIXish options like --append=yes and -a yes, 536 where a value (here "yes") is generally expected. 537 538 For UNIXish options like -kimura in clustalw which don't 539 take a value, use the _Switch object instead. 540 541 Attributes: 542 543 o names -- a list of string names (typically two entries) by which 544 the parameter can be set via the legacy set_parameter method 545 (eg ["-a", "--append", "append"]). The first name in list is used 546 when building the command line. The last name in the list is a 547 "human readable" name describing the option in one word. This 548 must be a valid Python identifier as it is used as the property 549 name and as a keyword argument, and should therefore follow PEP8 550 naming. 551 552 o description -- a description of the option. This is used as 553 the property docstring. 554 555 o filename -- True if this argument is a filename and should be 556 automatically quoted if it contains spaces. 557 558 o checker_function -- a reference to a function that will determine 559 if a given value is valid for this parameter. This function can either 560 raise an error when given a bad value, or return a [0, 1] decision on 561 whether the value is correct. 562 563 o equate -- should an equals sign be inserted if a value is used? 564 565 o is_required -- a flag to indicate if the parameter must be set for 566 the program to be run. 567 568 o is_set -- if the parameter has been set 569 570 o value -- the value of a parameter 571 """
572 - def __init__(self, names, description, filename=False, checker_function=None, 573 is_required=False, equate=True):
574 self.names = names 575 assert isinstance(description, basestring), \ 576 "%r for %s" % (description, names[-1]) 577 self.is_filename = filename 578 self.checker_function = checker_function 579 self.description = description 580 self.equate = equate 581 self.is_required = is_required 582 583 self.is_set = False 584 self.value = None
585
586 - def __str__(self):
587 """Return the value of this option for the commandline. 588 589 Includes a trailing space. 590 """ 591 # Note: Before equate was handled explicitly, the old 592 # code would do either "--name " or "--name=value ", 593 # or " -name " or " -name value ". This choice is now 594 # now made explicitly when setting up the option. 595 if self.value is None: 596 return "%s " % self.names[0] 597 if self.is_filename: 598 v = _escape_filename(self.value) 599 else: 600 v = str(self.value) 601 if self.equate: 602 return "%s=%s " % (self.names[0], v) 603 else: 604 return "%s %s " % (self.names[0], v)
605 606
607 -class _Switch(_AbstractParameter):
608 """Represent an optional argument switch for a program. 609 610 This holds UNIXish options like -kimura in clustalw which don't 611 take a value, they are either included in the command string 612 or omitted. 613 614 o names -- a list of string names (typically two entries) by which 615 the parameter can be set via the legacy set_parameter method 616 (eg ["-a", "--append", "append"]). The first name in list is used 617 when building the command line. The last name in the list is a 618 "human readable" name describing the option in one word. This 619 must be a valid Python identifer as it is used as the property 620 name and as a keyword argument, and should therefore follow PEP8 621 naming. 622 623 o description -- a description of the option. This is used as 624 the property docstring. 625 626 o is_set -- if the parameter has been set 627 628 NOTE - There is no value attribute, see is_set instead, 629 """
630 - def __init__(self, names, description):
631 self.names = names 632 self.description = description 633 self.is_set = False 634 self.is_required = False
635
636 - def __str__(self):
637 """Return the value of this option for the commandline. 638 639 Includes a trailing space. 640 """ 641 assert not hasattr(self, "value") 642 if self.is_set: 643 return "%s " % self.names[0] 644 else: 645 return ""
646 647
648 -class _Argument(_AbstractParameter):
649 """Represent an argument on a commandline. 650 651 The names argument should be a list containing one string. 652 This must be a valid Python identifer as it is used as the 653 property name and as a keyword argument, and should therefore 654 follow PEP8 naming. 655 """
656 - def __init__(self, names, description, filename=False, 657 checker_function=None, is_required=False):
658 # if len(names) != 1: 659 # raise ValueError("The names argument to _Argument should be a " 660 # "single entry list with a PEP8 property name.") 661 self.names = names 662 assert isinstance(description, basestring), \ 663 "%r for %s" % (description, names[-1]) 664 self.is_filename = filename 665 self.checker_function = checker_function 666 self.description = description 667 self.is_required = is_required 668 self.is_set = False 669 self.value = None
670
671 - def __str__(self):
672 if self.value is None: 673 return " " 674 elif self.is_filename: 675 return "%s " % _escape_filename(self.value) 676 else: 677 return "%s " % self.value
678 679
680 -class _ArgumentList(_Argument):
681 """Represent a variable list of arguments on a command line, e.g. multiple filenames.""" 682 # TODO - Option to require at least one value? e.g. min/max count? 683
684 - def __str__(self):
685 assert isinstance(self.value, list), \ 686 "Arguments should be a list" 687 assert self.value, "Requires at least one filename" 688 # A trailing space is required so that parameters following the last filename 689 # do not appear merged. 690 # e.g.: samtools cat in1.bam in2.bam-o out.sam [without trailing space][Incorrect] 691 # samtools cat in1.bam in2.bam -o out.sam [with trailing space][Correct] 692 if self.is_filename: 693 return " ".join(_escape_filename(v) for v in self.value) + " " 694 else: 695 return " ".join(self.value) + " "
696 697
698 -class _StaticArgument(_AbstractParameter):
699 """Represent a static (read only) argument on a commandline. 700 701 This is not intended to be exposed as a named argument or 702 property of a command line wrapper object. 703 """
704 - def __init__(self, value):
705 self.names = [] 706 self.is_required = False 707 self.is_set = True 708 self.value = value
709
710 - def __str__(self):
711 return "%s " % self.value
712 713
714 -def _escape_filename(filename):
715 """Escape filenames with spaces by adding quotes (PRIVATE). 716 717 Note this will not add quotes if they are already included: 718 719 >>> print((_escape_filename('example with spaces'))) 720 "example with spaces" 721 >>> print((_escape_filename('"example with spaces"'))) 722 "example with spaces" 723 """ 724 # Is adding the following helpful 725 # if os.path.isfile(filename): 726 # # On Windows, if the file exists, we can ask for 727 # # its alternative short name (DOS style 8.3 format) 728 # # which has no spaces in it. Note that this name 729 # # is not portable between machines, or even folder! 730 # try: 731 # import win32api 732 # short = win32api.GetShortPathName(filename) 733 # assert os.path.isfile(short) 734 # return short 735 # except ImportError: 736 # pass 737 if " " not in filename: 738 return filename 739 # We'll just quote it - works on Windows, Mac OS X etc 740 if filename.startswith('"') and filename.endswith('"'): 741 # Its already quoted 742 return filename 743 else: 744 return '"%s"' % filename
745 746
747 -def _test():
748 """Run the Bio.Application module's doctests.""" 749 import doctest 750 doctest.testmod(verbose=1)
751 752 if __name__ == "__main__": 753 # Run the doctests 754 _test() 755