Package lib :: Module io
[hide private]
[frames] | no frames]

Source Code for Module lib.io

   1  from __future__ import absolute_import 
   2  ############################################################################### 
   3  #                                                                             # 
   4  # Copyright (C) 2003-2014 Edward d'Auvergne                                   # 
   5  #                                                                             # 
   6  # This file is part of the program relax (http://www.nmr-relax.com).          # 
   7  #                                                                             # 
   8  # This program is free software: you can redistribute it and/or modify        # 
   9  # it under the terms of the GNU General Public License as published by        # 
  10  # the Free Software Foundation, either version 3 of the License, or           # 
  11  # (at your option) any later version.                                         # 
  12  #                                                                             # 
  13  # This program is distributed in the hope that it will be useful,             # 
  14  # but WITHOUT ANY WARRANTY; without even the implied warranty of              # 
  15  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               # 
  16  # GNU General Public License for more details.                                # 
  17  #                                                                             # 
  18  # You should have received a copy of the GNU General Public License           # 
  19  # along with this program.  If not, see <http://www.gnu.org/licenses/>.       # 
  20  #                                                                             # 
  21  ############################################################################### 
  22   
  23  # Module docstring. 
  24  """Module containing advanced IO functions for relax. 
  25   
  26  This includes IO redirection, automatic loading and writing of compressed files (both Gzip and BZ2 
  27  compression), reading and writing of files, processing of the contents of files, etc. 
  28  """ 
  29   
  30  # Dependency check module. 
  31  import dep_check 
  32   
  33  # Python module imports. 
  34  from os import devnull 
  35  from os import F_OK, X_OK, access, altsep, getenv, makedirs, pathsep, remove, sep 
  36  from os.path import expanduser, basename, splitext, isfile 
  37  from re import search 
  38  import sys 
  39  from sys import stdin, stdout, stderr 
  40  from warnings import warn 
  41   
  42  # relax module imports. 
  43  from compat import bz2_open, gz_open 
  44  import pipe_control 
  45  from lib.check_types import is_filetype, is_float 
  46  from lib.errors import RelaxError, RelaxFileError, RelaxFileOverwriteError, RelaxInvalidSeqError, RelaxMissingBinaryError, RelaxNoInPathError, RelaxNonExecError 
  47  from lib.selection import spin_id_to_data_list 
  48  from lib.warnings import RelaxWarning, RelaxFileEmptyWarning 
  49   
  50   
  51   
52 -def delete(file_name, dir=None, fail=True):
53 """Deleting the given file, taking into account missing compression extensions. 54 55 @param file_name: The name of the file to delete. 56 @type file_name: str 57 @keyword dir: The directory containing the file. 58 @type dir: None or str 59 @keyword fail: A flag which if True will cause RelaxFileError to be raised. 60 @type fail: bool 61 @raises RelaxFileError: If the file does not exist, and fail is set to true. 62 """ 63 64 # File path. 65 file_path = get_file_path(file_name, dir) 66 67 # Test if the file exists and determine the compression type. 68 if access(file_path, F_OK): 69 pass 70 elif access(file_path + '.bz2', F_OK): 71 file_path = file_path + '.bz2' 72 elif access(file_path + '.gz', F_OK): 73 file_path = file_path + '.gz' 74 elif fail: 75 raise RelaxFileError(file_path) 76 else: 77 return 78 79 # Remove the file. 80 remove(file_path)
81 82
83 -def determine_compression(file_path):
84 """Function for determining the compression type, and for also testing if the file exists. 85 86 @param file_path: The full file path of the file. 87 @type file_path: str 88 @return: A tuple of the compression type and full path of the file (including its 89 extension). A value of 0 corresponds to no compression. Bzip2 compression 90 corresponds to a value of 1. Gzip compression corresponds to a value of 2. 91 @rtype: (int, str) 92 """ 93 94 # The file has been supplied without its compression extension. 95 if access(file_path, F_OK): 96 compress_type = 0 97 if search('.bz2$', file_path): 98 compress_type = 1 99 elif search('.gz$', file_path): 100 compress_type = 2 101 102 # The file has been supplied with the '.bz2' extension. 103 elif access(file_path + '.bz2', F_OK): 104 file_path = file_path + '.bz2' 105 compress_type = 1 106 107 # The file has been supplied with the '.gz' extension. 108 elif access(file_path + '.gz', F_OK): 109 file_path = file_path + '.gz' 110 compress_type = 2 111 112 # The file doesn't exist. 113 else: 114 raise RelaxFileError(file_path) 115 116 # Return the compression type. 117 return compress_type, file_path
118 119
120 -def extract_data(file=None, dir=None, file_data=None, sep=None):
121 """Return all data in the file as a list of lines where each line is a list of line elements. 122 123 @param file: The file to extract the data from. 124 @type file: str or file object 125 @param dir: The path where the file is located. If None and the file argument is a 126 string, then the current directory is assumed. 127 @type dir: str or None 128 @param file_data: If the file data has already been extracted from the file, it can be 129 passed into this function using this argument. If data is supplied 130 here, then the file_name and dir args are ignored. 131 @type file_data: list of str 132 @param sep: The character separating the columns in the file data. If None, then 133 whitespace is assumed. 134 @type sep: str 135 @return: The file data. 136 @rtype: list of lists of str 137 """ 138 139 # Data not already extracted from the file. 140 if not file_data: 141 # Open the file. 142 if isinstance(file, str): 143 file = open_read_file(file_name=file, dir=dir) 144 145 # Read lines. 146 file_data = file.readlines() 147 148 # Create a data structure from the contents of the file split by either whitespace or the separator, sep. 149 data = [] 150 for i in range(len(file_data)): 151 if sep: 152 row = file_data[i].split(sep) 153 else: 154 row = file_data[i].split() 155 data.append(row) 156 157 # Close the file. 158 if not file_data: 159 file.close() 160 161 # Return the data. 162 return data
163 164
165 -def file_root(file_path):
166 """Return the root file name, striped of path and extension details. 167 168 @param file_path: The full path to the file. 169 @type file_path: str 170 @return: The file root (with all directories and the extension stripped away). 171 @rtype: str 172 """ 173 174 root, ext = splitext(file_path) 175 return basename(root)
176 177
178 -def get_file_path(file_name=None, dir=None):
179 """Generate and expand the full file path. 180 181 @param file_name: The name of the file to extract the data from. 182 @type file_name: str 183 @param dir: The path where the file is located. If None, then the current directory is 184 assumed. 185 @type dir: str 186 @return: The full file path. 187 @rtype: str 188 """ 189 190 # File name. 191 file_path = file_name 192 193 # Add the directory. 194 if dir: 195 file_path = dir + sep + file_path 196 197 # Expand any ~ characters. 198 if file_path: # Catch a file path of None, as expanduser can't handle this. 199 file_path = expanduser(file_path) 200 201 # Return the file path. 202 return file_path
203 204
205 -def io_streams_restore(verbosity=1):
206 """Restore all IO streams to the Python defaults. 207 208 @param verbosity: The verbosity level. 209 @type verbosity: int 210 """ 211 212 # Print out. 213 if verbosity: 214 print("Restoring the sys.stdin IO stream to the Python STDIN IO stream.") 215 print("Restoring the sys.stdout IO stream to the Python STDOUT IO stream.") 216 print("Restoring the sys.stderr IO stream to the Python STDERR IO stream.") 217 218 # Restore streams. 219 sys.stdin = sys.__stdin__ 220 sys.stdout = sys.__stdout__ 221 sys.stderr = sys.__stderr__
222 223
224 -def io_streams_log(file_name=None, dir=None, verbosity=1):
225 """Turn on logging, sending both STDOUT and STDERR streams to a file. 226 227 @param file_name: The name of the file. 228 @type file_name: str 229 @param dir: The path where the file is located. If None, then the current directory is 230 assumed. 231 @type dir: str 232 @param verbosity: The verbosity level. 233 @type verbosity: int 234 """ 235 236 # Log file. 237 log_file, file_path = open_write_file(file_name=file_name, dir=dir, force=True, verbosity=verbosity, return_path=True) 238 239 # Logging IO streams. 240 log_stdin = stdin 241 log_stdout = None 242 log_stderr = SplitIO() 243 244 # Print out. 245 if verbosity: 246 print("Redirecting the sys.stdin IO stream to the Python stdin IO stream.") 247 print("Redirecting the sys.stdout IO stream to the log file '%s'." % file_path) 248 print("Redirecting the sys.stderr IO stream to both the Python stderr IO stream and the log file '%s'." % file_path) 249 250 # Set the logging IO streams. 251 log_stdout = log_file 252 log_stderr.split(stderr, log_file) 253 254 # IO stream redirection. 255 sys.stdin = log_stdin 256 sys.stdout = log_stdout 257 sys.stderr = log_stderr
258 259
260 -def io_streams_tee(file_name=None, dir=None, compress_type=0, verbosity=1):
261 """Turn on teeing to split both STDOUT and STDERR streams and sending second part to a file. 262 263 @param file_name: The name of the file. 264 @type file_name: str 265 @param dir: The path where the file is located. If None, then the current directory 266 is assumed. 267 @type dir: str 268 @param compress_type: The compression type. The integer values correspond to the compression 269 type: 0, no compression; 1, Bzip2 compression; 2, Gzip compression. 270 @type compress_type: int 271 @param verbosity: The verbosity level. 272 @type verbosity: int 273 """ 274 275 # Tee file. 276 tee_file, file_path = open_write_file(file_name=file_name, dir=dir, force=True, compress_type=compress_type, verbosity=verbosity, return_path=1) 277 278 # Tee IO streams. 279 tee_stdin = stdin 280 tee_stdout = SplitIO() 281 tee_stderr = SplitIO() 282 283 # Print out. 284 if verbosity: 285 print("Redirecting the sys.stdin IO stream to the Python stdin IO stream.") 286 print("Redirecting the sys.stdout IO stream to both the Python stdout IO stream and the log file '%s'." % file_path) 287 print("Redirecting the sys.stderr IO stream to both the Python stderr IO stream and the log file '%s'." % file_path) 288 289 # Set the tee IO streams. 290 tee_stdout.split(stdout, tee_file) 291 tee_stderr.split(stderr, tee_file) 292 293 # IO stream redirection. 294 sys.stdin = tee_stdin 295 sys.stdout = tee_stdout 296 sys.stderr = tee_stderr
297 298
299 -def mkdir_nofail(dir=None, verbosity=1):
300 """Create the given directory, or exit without raising an error if the directory exists. 301 302 @param dir: The directory to create. 303 @type dir: str 304 @param verbosity: The verbosity level. 305 @type verbosity: int 306 """ 307 308 # No directory given. 309 if dir == None: 310 return 311 312 # Expand any ~ characters. 313 dir = expanduser(dir) 314 315 # Make the directory. 316 try: 317 makedirs(dir) 318 except OSError: 319 if verbosity: 320 print("Directory ." + sep + dir + " already exists.\n")
321 322
323 -def open_read_file(file_name=None, dir=None, verbosity=1):
324 """Open the file 'file' and return all the data. 325 326 @param file_name: The name of the file to extract the data from. 327 @type file_name: str 328 @param dir: The path where the file is located. If None, then the current directory is 329 assumed. 330 @type dir: str 331 @param verbosity: The verbosity level. 332 @type verbosity: int 333 @return: The open file object. 334 @rtype: file object 335 """ 336 337 # A file descriptor object. 338 if is_filetype(file_name): 339 # Nothing to do here! 340 return file_name 341 342 # Invalid file name. 343 if not file_name and not isinstance(file_name, str): 344 raise RelaxError("The file name " + repr(file_name) + " " + repr(type(file_name)) + " is invalid and cannot be opened.") 345 346 # File path. 347 file_path = get_file_path(file_name, dir) 348 349 # Test if the file exists and determine the compression type. 350 compress_type, file_path = determine_compression(file_path) 351 352 # Open the file for reading. 353 try: 354 # Print out. 355 if verbosity: 356 print("Opening the file " + repr(file_path) + " for reading.") 357 358 # Uncompressed text. 359 if compress_type == 0: 360 file_obj = open(file_path, 'r') 361 362 # Bzip2 compressed text. 363 elif compress_type == 1: 364 file_obj = bz2_open(file=file_path, mode='r') 365 366 # Gzipped compressed text. 367 elif compress_type == 2: 368 file_obj = gz_open(file=file_path, mode='r') 369 370 # Cannot open. 371 except IOError: 372 message = sys.exc_info()[1] 373 raise RelaxError("Cannot open the file " + repr(file_path) + ". " + message.args[1] + ".") 374 375 # Return the opened file. 376 return file_obj
377 378
379 -def open_write_file(file_name=None, dir=None, force=False, compress_type=0, verbosity=1, return_path=False):
380 """Function for opening a file for writing and creating directories if necessary. 381 382 @param file_name: The name of the file to extract the data from. 383 @type file_name: str 384 @param dir: The path where the file is located. If None, then the current directory 385 is assumed. 386 @type dir: str 387 @param force: Boolean argument which if True causes the file to be overwritten if it 388 already exists. 389 @type force: bool 390 @param compress_type: The compression type. The integer values correspond to the compression 391 type: 0, no compression; 1, Bzip2 compression; 2, Gzip compression. 392 @type compress_type: int 393 @param verbosity: The verbosity level. 394 @type verbosity: int 395 @param return_path: If True, the function will return a tuple of the file object and the 396 full file path. 397 @type return_path: bool 398 @return: The open, writable file object and, if the return_path is True, then the 399 full file path is returned as well. 400 @rtype: writable file object (if return_path, then a tuple of the writable file 401 and the full file path) 402 """ 403 404 # No file name? 405 if file_name == None: 406 raise RelaxError("The name of the file must be supplied.") 407 408 # A file descriptor object. 409 if is_filetype(file_name): 410 # Nothing to do here! 411 return file_name 412 413 # Something pretending to be a file object. 414 if hasattr(file_name, 'write'): 415 # Nothing to do here! 416 return file_name 417 418 # The null device. 419 if search('devnull', file_name): 420 # Print out. 421 if verbosity: 422 print("Opening the null device file for writing.") 423 424 # Open the null device. 425 file_obj = open(devnull, 'w') 426 427 # Return the file. 428 if return_path: 429 return file_obj, None 430 else: 431 return file_obj 432 433 # Create the directories. 434 mkdir_nofail(dir, verbosity=0) 435 436 # File path. 437 file_path = get_file_path(file_name, dir) 438 439 # Bzip2 compression. 440 if compress_type == 1 and not search('.bz2$', file_path): 441 # Bz2 module exists. 442 if dep_check.bz2_module: 443 file_path = file_path + '.bz2' 444 445 # Switch to gzip compression. 446 else: 447 warn(RelaxWarning("Cannot use Bzip2 compression, using gzip compression instead. " + dep_check.bz2_module_message + ".")) 448 compress_type = 2 449 450 # Gzip compression. 451 if compress_type == 2 and not search('.gz$', file_path): 452 file_path = file_path + '.gz' 453 454 # Fail if the file already exists and the force flag is set to 0. 455 if access(file_path, F_OK) and not force: 456 raise RelaxFileOverwriteError(file_path, 'force flag') 457 458 # Open the file for writing. 459 try: 460 # Print out. 461 if verbosity: 462 print("Opening the file " + repr(file_path) + " for writing.") 463 464 # Uncompressed text. 465 if compress_type == 0: 466 file_obj = open(file_path, 'w') 467 468 # Bzip2 compressed text. 469 elif compress_type == 1: 470 file_obj = bz2_open(file=file_path, mode='w') 471 472 # Gzipped compressed text. 473 elif compress_type == 2: 474 file_obj = gz_open(file=file_path, mode='w') 475 476 # Cannot open. 477 except IOError: 478 message = sys.exc_info()[1] 479 raise RelaxError("Cannot open the file " + repr(file_path) + ". " + message.args[1] + ".") 480 481 # Return the opened file. 482 if return_path: 483 return file_obj, file_path 484 else: 485 return file_obj
486 487
488 -def read_spin_data(file=None, dir=None, file_data=None, spin_id_col=None, mol_name_col=None, res_num_col=None, res_name_col=None, spin_num_col=None, spin_name_col=None, data_col=None, error_col=None, sep=None, spin_id=None):
489 """Generator function for reading the spin specific data from file. 490 491 Description 492 =========== 493 494 This function reads a columnar formatted file where each line corresponds to a spin system. Spin identification is either through a spin ID string or through columns containing the molecule name, residue name and number, and/or spin name and number. 495 496 497 @keyword file: The name of the file to open. 498 @type file: str 499 @keyword dir: The directory containing the file (defaults to the current directory if None). 500 @type dir: str or None 501 @keyword file_data: An alternative to opening a file, if the data already exists in the correct format. The format is a list of lists where the first index corresponds to the row and the second the column. 502 @type file_data: list of lists 503 @keyword spin_id_col: The column containing the spin ID strings. If supplied, the mol_name_col, res_name_col, res_num_col, spin_name_col, and spin_num_col arguments must be none. 504 @type spin_id_col: int or None 505 @keyword mol_name_col: The column containing the molecule name information. If supplied, spin_id_col must be None. 506 @type mol_name_col: int or None 507 @keyword res_name_col: The column containing the residue name information. If supplied, spin_id_col must be None. 508 @type res_name_col: int or None 509 @keyword res_num_col: The column containing the residue number information. If supplied, spin_id_col must be None. 510 @type res_num_col: int or None 511 @keyword spin_name_col: The column containing the spin name information. If supplied, spin_id_col must be None. 512 @type spin_name_col: int or None 513 @keyword spin_num_col: The column containing the spin number information. If supplied, spin_id_col must be None. 514 @type spin_num_col: int or None 515 @keyword data_col: The column containing the data. 516 @type data_col: int or None 517 @keyword error_col: The column containing the errors. 518 @type error_col: int or None 519 @keyword sep: The column separator which, if None, defaults to whitespace. 520 @type sep: str or None 521 @keyword spin_id: The spin ID string used to restrict data loading to a subset of all spins. 522 @type spin_id: None or str 523 @return: A list of the spin specific data is yielded. The format is a list consisting of the spin ID string, the data value (if data_col is give), and the error value (if error_col is given). If both data_col and error_col are None, then the spin ID string is simply yielded. 524 @rtype: str, list of [str, float], or list of [str, float, float] 525 """ 526 527 # Argument tests. 528 col_args = [spin_id_col, mol_name_col, res_name_col, res_num_col, spin_name_col, spin_num_col, data_col, error_col] 529 col_arg_names = ['spin_id_col', 'mol_name_col', 'res_name_col', 'res_num_col', 'spin_name_col', 'spin_num_col', 'data_col', 'error_col'] 530 for i in range(len(col_args)): 531 if col_args[i] == 0: 532 raise RelaxError("The '%s' argument cannot be zero, column numbering starts at one." % col_arg_names[i]) 533 if spin_id_col and (mol_name_col or res_name_col or res_num_col or spin_name_col or spin_num_col): 534 raise RelaxError("If the 'spin_id_col' argument has been supplied, then the mol_name_col, res_name_col, res_num_col, spin_name_col, and spin_num_col must all be set to None.") 535 536 # Minimum number of columns. 537 min_col_num = max(filter(None, [spin_id_col, mol_name_col, res_num_col, res_name_col, spin_num_col, spin_name_col, data_col, error_col])) 538 539 # Extract the data from the file. 540 if not file_data: 541 # Extract. 542 file_data = extract_data(file, dir) 543 544 # Strip the data of all comments and empty lines. 545 if spin_id_col != None: 546 file_data = strip(file_data, comments=False) 547 else: 548 file_data = strip(file_data) 549 550 # No data! 551 if not file_data: 552 warn(RelaxFileEmptyWarning(file)) 553 return 554 555 # Yield the data, spin by spin. 556 missing_data = True 557 for line in file_data: 558 # Convert the spin IDs. 559 if spin_id_col != None and line[spin_id_col-1][0] in ["\"", "\'"]: 560 line[spin_id_col-1] = eval(line[spin_id_col-1]) 561 562 # Convert. 563 # Validate the sequence. 564 try: 565 pipe_control.sequence.validate_sequence(line, spin_id_col=spin_id_col, mol_name_col=mol_name_col, res_num_col=res_num_col, res_name_col=res_name_col, spin_num_col=spin_num_col, spin_name_col=spin_name_col, data_col=data_col, error_col=error_col) 566 except RelaxInvalidSeqError: 567 # Extract the message string, without the RelaxError bit. 568 msg = sys.exc_info()[1] 569 string = msg.__str__()[12:-1] 570 571 # Give a warning. 572 warn(RelaxWarning(string)) 573 574 # Skip the line. 575 continue 576 577 # Get the spin data from the ID. 578 if spin_id_col: 579 # Invalid spin ID. 580 if line[spin_id_col-1] == '#': 581 warn(RelaxWarning("Invalid spin ID, skipping the line %s" % line)) 582 continue 583 584 mol_name, res_num, res_name, spin_num, spin_name = spin_id_to_data_list(line[spin_id_col-1]) 585 586 # Convert the spin data. 587 else: 588 # The molecule. 589 mol_name = None 590 if mol_name_col != None and line[mol_name_col-1] != 'None': 591 mol_name = line[mol_name_col-1] 592 593 # The residue number, catching bad values. 594 res_num = None 595 if res_num_col != None: 596 try: 597 if line[res_num_col-1] == 'None': 598 res_num = None 599 else: 600 res_num = int(line[res_num_col-1]) 601 except ValueError: 602 warn(RelaxWarning("Invalid residue number, skipping the line %s" % line)) 603 continue 604 605 # The residue name. 606 res_name = None 607 if res_name_col != None and line[res_name_col-1] != 'None': 608 res_name = line[res_name_col-1] 609 610 # The spin number, catching bad values. 611 spin_num = None 612 if spin_num_col != None: 613 try: 614 if line[spin_num_col-1] == 'None': 615 spin_num = None 616 else: 617 spin_num = int(line[spin_num_col-1]) 618 except ValueError: 619 warn(RelaxWarning("Invalid spin number, skipping the line %s" % line)) 620 continue 621 622 # The spin name. 623 spin_name = None 624 if spin_name_col != None and line[spin_name_col-1] != 'None': 625 spin_name = line[spin_name_col-1] 626 627 # Convert the data. 628 value = None 629 if data_col != None: 630 try: 631 # None. 632 if line[data_col-1] == 'None': 633 value = None 634 635 # A float. 636 else: 637 value = float(line[data_col-1]) 638 639 # Bad data. 640 except ValueError: 641 warn(RelaxWarning("Invalid data, skipping the line %s" % line)) 642 continue 643 644 # Convert the errors. 645 error = None 646 if error_col != None: 647 try: 648 # None. 649 if line[error_col-1] == 'None': 650 error = None 651 652 # A float. 653 else: 654 error = float(line[error_col-1]) 655 656 # Bad data. 657 except ValueError: 658 warn(RelaxWarning("Invalid errors, skipping the line %s" % line)) 659 continue 660 661 # Right, data is OK and exists. 662 missing_data = False 663 664 # Yield the data. 665 if data_col and error_col: 666 yield mol_name, res_num, res_name, spin_num, spin_name, value, error 667 elif data_col: 668 yield mol_name, res_num, res_name, spin_num, spin_name, value 669 elif error_col: 670 yield mol_name, res_num, res_name, spin_num, spin_name, error 671 else: 672 yield mol_name, res_num, res_name, spin_num, spin_name 673 674 # Hmmm, no data! 675 if missing_data: 676 raise RelaxError("No corresponding data could be found within the file.")
677 678
679 -def strip(data, comments=True):
680 """Remove all comment and empty lines from the file data structure. 681 682 @param data: The file data to clean up. 683 @type data: list of lists of str 684 @keyword comments: A flag which if True will cause comments to be deleted. 685 @type comments: bool 686 @return: The input data with the empty and comment lines removed. 687 @rtype: list of lists of str 688 """ 689 690 # Initialise the new data array. 691 new = [] 692 693 # Loop over the data. 694 for i in range(len(data)): 695 # Empty lines. 696 if len(data[i]) == 0: 697 continue 698 699 # Comment lines. 700 if comments and search("^#", data[i][0]): 701 continue 702 703 # Data lines. 704 new.append(data[i]) 705 706 # Return the new data structure. 707 return new
708 709
710 -def test_binary(binary):
711 """Function for testing that the binary string corresponds to a valid executable file. 712 713 @param binary: The name or path of the binary executable file. 714 @type binary: str 715 """ 716 717 # Path separator RE string. 718 if altsep: 719 path_sep = '[' + sep + altsep + ']' 720 else: 721 path_sep = sep 722 723 # The full path of the program has been given (if a directory separatory has been supplied). 724 if isfile(binary): 725 # Test that the binary exists. 726 if not access(binary, F_OK): 727 raise RelaxMissingBinaryError(binary) 728 729 # Test that if the binary is executable. 730 if not access(binary, X_OK): 731 raise RelaxNonExecError(binary) 732 733 # The path to the binary has not been given. 734 else: 735 # Get the PATH environmental variable. 736 path = getenv('PATH') 737 738 # Split PATH by the path separator. 739 path_list = path.split(pathsep) 740 741 # Test that the binary exists within the system path (and exit this function instantly once it has been found). 742 for path in path_list: 743 if access(path + sep + binary, F_OK) or access(path + sep + binary +".exe", F_OK): 744 return 745 746 # The binary is not located in the system path! 747 raise RelaxNoInPathError(binary)
748 749
750 -def write_data(out=None, headings=None, data=None, sep=None):
751 """Write out a table of the data to the given file handle. 752 753 @keyword out: The file handle to write to. 754 @type out: file handle 755 @keyword headings: The optional headings to print out. 756 @type headings: list of str or None 757 @keyword data: The data to print out. 758 @type data: list of list of str 759 @keyword sep: The column separator which, if None, defaults to whitespace. 760 @type sep: str or None 761 """ 762 763 # No data to print out. 764 if data in [None, []]: 765 return 766 767 # The number of rows and columns. 768 num_rows = len(data) 769 num_cols = len(data[0]) 770 771 # Pretty whitespace formatting. 772 if sep == None: 773 # Determine the widths for the headings. 774 widths = [] 775 for j in range(num_cols): 776 if headings != None: 777 if j == 0: 778 widths.append(len(headings[j]) + 2) 779 else: 780 widths.append(len(headings[j])) 781 782 # No headings. 783 else: 784 widths.append(0) 785 786 # Determine the maximum column widths for nice whitespace formatting. 787 for i in range(num_rows): 788 for j in range(num_cols): 789 size = len(data[i][j]) 790 if size > widths[j]: 791 widths[j] = size 792 793 # Convert to format strings. 794 formats = [] 795 for j in range(num_cols): 796 formats.append("%%-%ss" % (widths[j] + 4)) 797 798 # The headings. 799 if headings != None: 800 out.write(formats[0] % ("# " + headings[0])) 801 for j in range(1, num_cols): 802 out.write(formats[j] % headings[j]) 803 out.write('\n') 804 805 # The data. 806 for i in range(num_rows): 807 # The row. 808 for j in range(num_cols): 809 out.write(formats[j] % data[i][j]) 810 out.write('\n') 811 812 # Non-whitespace formatting. 813 else: 814 # The headings. 815 if headings != None: 816 out.write('#') 817 for j in range(num_cols): 818 # The column separator. 819 if j > 0: 820 out.write(sep) 821 822 # The heading. 823 out.write(headings[j]) 824 out.write('\n') 825 826 # The data. 827 for i in range(num_rows): 828 # The row. 829 for j in range(num_cols): 830 # The column separator. 831 if j > 0: 832 out.write(sep) 833 834 # The heading. 835 out.write(data[i][j]) 836 out.write('\n')
837 838
839 -def write_spin_data(file, dir=None, sep=None, spin_ids=None, mol_names=None, res_nums=None, res_names=None, spin_nums=None, spin_names=None, force=False, data=None, data_name=None, error=None, error_name=None, float_format="%20.15g"):
840 """Generator function for reading the spin specific data from file. 841 842 Description 843 =========== 844 845 This function writes a columnar formatted file where each line corresponds to a spin system. Spin identification is either through a spin ID string or through columns containing the molecule name, residue name and number, and/or spin name and number. 846 847 848 @param file: The name of the file to write the data to (or alternatively an already opened file object). 849 @type file: str or file object 850 @keyword dir: The directory to place the file into (defaults to the current directory if None and the file argument is not a file object). 851 @type dir: str or None 852 @keyword sep: The column separator which, if None, defaults to whitespace. 853 @type sep: str or None 854 @keyword spin_ids: The list of spin ID strings. 855 @type spin_ids: None or list of str 856 @keyword mol_names: The list of molecule names. 857 @type mol_names: None or list of str 858 @keyword res_nums: The list of residue numbers. 859 @type res_nums: None or list of int 860 @keyword res_names: The list of residue names. 861 @type res_names: None or list of str 862 @keyword spin_nums: The list of spin numbers. 863 @type spin_nums: None or list of int 864 @keyword spin_names: The list of spin names. 865 @type spin_names: None or list of str 866 @keyword force: A flag which if True will cause an existing file to be overwritten. 867 @type force: bool 868 @keyword data: A list of the data to write out. The first dimension corresponds to the spins. A second dimension can also be given if multiple data sets across multiple columns are desired. 869 @type data: list or list of lists 870 @keyword data_name: A name corresponding to the data argument. If the data argument is a list of lists, then this must also be a list with the same length as the second dimension of the data arg. 871 @type data_name: str or list of str 872 @keyword error: A list of the errors to write out. The first dimension corresponds to the spins. A second dimension can also be given if multiple data sets across multiple columns are desired. These will be inter-dispersed between the data columns, if the data is given. If the data arg is not None, then this must have the same dimensions as that object. 873 @type error: list or list of lists 874 @keyword error_name: A name corresponding to the error argument. If the error argument is a list of lists, then this must also be a list with the same length at the second dimension of the error arg. 875 @type error_name: str or list of str 876 @keyword float_format: A float formatting string to use for the data and error whenever a float is found. 877 @type float_format: str 878 """ 879 880 # Data argument tests. 881 if data: 882 # Data is a list of lists. 883 if isinstance(data[0], list): 884 # Data and data_name don't match. 885 if not isinstance(data_name, list): 886 raise RelaxError("The data_name arg '%s' must be a list as the data argument is a list of lists." % data_name) 887 888 # Error doesn't match. 889 if error and (len(data) != len(error) or len(data[0]) != len(error[0])): 890 raise RelaxError("The data arg:\n%s\n\ndoes not have the same dimensions as the error arg:\n%s." % (data, error)) 891 892 # Data is a simple list. 893 else: 894 # Data and data_name don't match. 895 if not isinstance(data_name, str): 896 raise RelaxError("The data_name arg '%s' must be a string as the data argument is a simple list." % data_name) 897 898 # Error doesn't match. 899 if error and len(data) != len(error): 900 raise RelaxError("The data arg:\n%s\n\ndoes not have the same dimensions as the error arg:\n%s." % (data, error)) 901 902 # Error argument tests. 903 if error: 904 # Error is a list of lists. 905 if isinstance(error[0], list): 906 # Error and error_name don't match. 907 if not isinstance(error_name, list): 908 raise RelaxError("The error_name arg '%s' must be a list as the error argument is a list of lists." % error_name) 909 910 # Error is a simple list. 911 else: 912 # Error and error_name don't match. 913 if not isinstance(error_name, str): 914 raise RelaxError("The error_name arg '%s' must be a string as the error argument is a simple list." % error_name) 915 916 # Number of spins check. 917 args = [spin_ids, mol_names, res_nums, res_names, spin_nums, spin_names] 918 arg_names = ['spin_ids', 'mol_names', 'res_nums', 'res_names', 'spin_nums', 'spin_names'] 919 N = None 920 first_arg = None 921 first_arg_name = None 922 for i in range(len(args)): 923 if isinstance(args[i], list): 924 # First list match. 925 if N == None: 926 N = len(args[i]) 927 first_arg = args[i] 928 first_arg_name = arg_names[i] 929 930 # Length check. 931 if len(args[i]) != N: 932 raise RelaxError("The %s and %s arguments do not have the same number of spins ('%s' vs. '%s' respectively)." % (first_arg_name, arg_names[i], len(first_arg), len(args[i]))) 933 934 # Nothing?!? 935 if N == None: 936 raise RelaxError("No spin ID data is present.") 937 938 # Data and error length check. 939 if data and len(data) != N: 940 raise RelaxError("The %s and data arguments do not have the same number of spins ('%s' vs. '%s' respectively)." % (first_arg_name, len(first_arg), len(data))) 941 if error and len(error) != N: 942 raise RelaxError("The %s and error arguments do not have the same number of spins ('%s' vs. '%s' respectively)." % (first_arg_name, len(first_arg), len(error))) 943 944 # The spin arguments. 945 args = [spin_ids, mol_names, res_nums, res_names, spin_nums, spin_names] 946 arg_names = ['spin_id', 'mol_name', 'res_num', 'res_name', 'spin_num', 'spin_name'] 947 948 949 # Init. 950 headings = [] 951 file_data = [] 952 953 # Headers - the spin ID info. 954 for i in range(len(args)): 955 if args[i]: 956 headings.append(arg_names[i]) 957 958 # Headers - the data. 959 if data: 960 # List of lists. 961 if isinstance(data[0], list): 962 # Loop over the list. 963 for i in range(len(data[0])): 964 # The data. 965 headings.append(data_name[i]) 966 967 # The error. 968 if error: 969 headings.append(error_name[i]) 970 971 # Simple list. 972 else: 973 # The data. 974 headings.append(data_name) 975 976 # The error. 977 if error: 978 headings.append(error_name) 979 980 # Headers - only errors. 981 elif error: 982 # List of lists. 983 if isinstance(error[0], list): 984 for i in range(len(error[0])): 985 headings.append(error_name[i]) 986 987 # Simple list. 988 else: 989 headings.append(error_name) 990 991 # No headings. 992 if headings == []: 993 headings = None 994 995 # Spin specific data. 996 for spin_index in range(N): 997 # Append a new data row. 998 file_data.append([]) 999 1000 # The spin ID info. 1001 for i in range(len(args)): 1002 if args[i]: 1003 value = args[i][spin_index] 1004 if not isinstance(value, str): 1005 value = repr(value) 1006 file_data[-1].append(value) 1007 1008 # The data. 1009 if data: 1010 # List of lists. 1011 if isinstance(data[0], list): 1012 # Loop over the list. 1013 for i in range(len(data[0])): 1014 # The data. 1015 if is_float(data[spin_index][i]): 1016 file_data[-1].append(float_format % data[spin_index][i]) 1017 else: 1018 file_data[-1].append(repr(data[spin_index][i])) 1019 1020 # The error. 1021 if error: 1022 if is_float(error[spin_index][i]): 1023 file_data[-1].append(float_format % error[spin_index][i]) 1024 else: 1025 file_data[-1].append(repr(error[spin_index][i])) 1026 1027 # Simple list. 1028 else: 1029 # The data. 1030 if is_float(data[spin_index]): 1031 file_data[-1].append(float_format % data[spin_index]) 1032 else: 1033 file_data[-1].append(repr(data[spin_index])) 1034 1035 # The error. 1036 if error: 1037 if is_float(error[spin_index]): 1038 file_data[-1].append(float_format % error[spin_index]) 1039 else: 1040 file_data[-1].append(repr(error[spin_index])) 1041 1042 # Only errors. 1043 elif error: 1044 # List of lists. 1045 if isinstance(error[0], list): 1046 for i in range(len(error[0])): 1047 file_data[-1].append(repr(error[spin_index][i])) 1048 1049 # Simple list. 1050 else: 1051 file_data[-1].append(repr(error[spin_index])) 1052 1053 # No data to write, so do nothing! 1054 if file_data == [] or file_data == [[]]: 1055 return 1056 1057 # Open the file for writing. 1058 file = open_write_file(file_name=file, dir=dir, force=force) 1059 1060 # Write out the file data. 1061 write_data(out=file, headings=headings, data=file_data, sep=sep)
1062 1063 1064
1065 -class DummyFileObject:
1066 - def __init__(self):
1067 """Set up the dummy object to act as a file object.""" 1068 1069 # Initialise an object for adding the string from all write calls to. 1070 self.data = '' 1071 1072 # Set the closed flag. 1073 self.closed = False
1074 1075
1076 - def close(self):
1077 """A method for 'closing' this object.""" 1078 1079 # Set the closed flag. 1080 self.closed = True
1081 1082
1083 - def write(self, str):
1084 """Mimic the file object write() method so that this class can be used as a file object. 1085 1086 @param str: The string to be written. 1087 @type str: str 1088 """ 1089 1090 # Check if the file is closed. 1091 if self.closed: 1092 raise ValueError('I/O operation on closed file') 1093 1094 # Append the string to the data object. 1095 self.data = self.data + str
1096 1097
1098 - def readlines(self):
1099 """Mimic the file object readlines() method. 1100 1101 This method works even if this dummy file object is closed! 1102 1103 1104 @return: The contents of the file object separated by newline characters. 1105 @rtype: list of str 1106 """ 1107 1108 # Split up the string. 1109 lines = self.data.split('\n') 1110 1111 # Remove the last line if empty. 1112 if lines[-1] == '': 1113 lines.pop() 1114 1115 # Loop over the lines, re-adding the newline character to match the file object readlines() method. 1116 for i in range(len(lines)): 1117 lines[i] = lines[i] + '\n' 1118 1119 # Return the file lines. 1120 return lines
1121 1122 1123
1124 -class SplitIO:
1125 - def __init__(self):
1126 """Class for splitting an IO stream to two outputs."""
1127 1128
1129 - def flush(self):
1130 """Flush all streams.""" 1131 1132 # Call the streams' methods. 1133 self.stream1.flush() 1134 self.stream2.flush()
1135 1136
1137 - def isatty(self):
1138 """Check that both streams are TTYs. 1139 1140 @return: True, only if both streams are TTYs. 1141 @rtype: bool 1142 """ 1143 1144 # Check both streams. 1145 return self.stream1.isatty() & self.stream2.isatty()
1146 1147
1148 - def split(self, stream1, stream2):
1149 """Function for setting the streams.""" 1150 1151 # Arguments. 1152 self.stream1 = stream1 1153 self.stream2 = stream2
1154 1155
1156 - def write(self, text):
1157 """Replacement write function.""" 1158 1159 # Write to stream1. 1160 self.stream1.write(text) 1161 1162 # Write to stream2. 1163 self.stream2.write(text)
1164