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-2013 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 # Make the directory. 313 try: 314 makedirs(dir) 315 except OSError: 316 if verbosity: 317 print("Directory ." + sep + dir + " already exists.\n")
318 319
320 -def open_read_file(file_name=None, dir=None, verbosity=1):
321 """Open the file 'file' and return all the data. 322 323 @param file_name: The name of the file to extract the data from. 324 @type file_name: str 325 @param dir: The path where the file is located. If None, then the current directory is 326 assumed. 327 @type dir: str 328 @param verbosity: The verbosity level. 329 @type verbosity: int 330 @return: The open file object. 331 @rtype: file object 332 """ 333 334 # A file descriptor object. 335 if is_filetype(file_name): 336 # Nothing to do here! 337 return file_name 338 339 # Invalid file name. 340 if not file_name and not isinstance(file_name, str): 341 raise RelaxError("The file name " + repr(file_name) + " " + repr(type(file_name)) + " is invalid and cannot be opened.") 342 343 # File path. 344 file_path = get_file_path(file_name, dir) 345 346 # Test if the file exists and determine the compression type. 347 compress_type, file_path = determine_compression(file_path) 348 349 # Open the file for reading. 350 try: 351 # Print out. 352 if verbosity: 353 print("Opening the file " + repr(file_path) + " for reading.") 354 355 # Uncompressed text. 356 if compress_type == 0: 357 file_obj = open(file_path, 'r') 358 359 # Bzip2 compressed text. 360 elif compress_type == 1: 361 file_obj = bz2_open(file=file_path, mode='r') 362 363 # Gzipped compressed text. 364 elif compress_type == 2: 365 file_obj = gz_open(file=file_path, mode='r') 366 367 # Cannot open. 368 except IOError: 369 message = sys.exc_info()[1] 370 raise RelaxError("Cannot open the file " + repr(file_path) + ". " + message.args[1] + ".") 371 372 # Return the opened file. 373 return file_obj
374 375
376 -def open_write_file(file_name=None, dir=None, force=False, compress_type=0, verbosity=1, return_path=False):
377 """Function for opening a file for writing and creating directories if necessary. 378 379 @param file_name: The name of the file to extract the data from. 380 @type file_name: str 381 @param dir: The path where the file is located. If None, then the current directory 382 is assumed. 383 @type dir: str 384 @param force: Boolean argument which if True causes the file to be overwritten if it 385 already exists. 386 @type force: bool 387 @param compress_type: The compression type. The integer values correspond to the compression 388 type: 0, no compression; 1, Bzip2 compression; 2, Gzip compression. 389 @type compress_type: int 390 @param verbosity: The verbosity level. 391 @type verbosity: int 392 @param return_path: If True, the function will return a tuple of the file object and the 393 full file path. 394 @type return_path: bool 395 @return: The open, writable file object and, if the return_path is True, then the 396 full file path is returned as well. 397 @rtype: writable file object (if return_path, then a tuple of the writable file 398 and the full file path) 399 """ 400 401 # No file name? 402 if file_name == None: 403 raise RelaxError("The name of the file must be supplied.") 404 405 # A file descriptor object. 406 if is_filetype(file_name): 407 # Nothing to do here! 408 return file_name 409 410 # Something pretending to be a file object. 411 if hasattr(file_name, 'write'): 412 # Nothing to do here! 413 return file_name 414 415 # The null device. 416 if search('devnull', file_name): 417 # Print out. 418 if verbosity: 419 print("Opening the null device file for writing.") 420 421 # Open the null device. 422 file_obj = open(devnull, 'w') 423 424 # Return the file. 425 if return_path: 426 return file_obj, None 427 else: 428 return file_obj 429 430 # Create the directories. 431 mkdir_nofail(dir, verbosity=0) 432 433 # File path. 434 file_path = get_file_path(file_name, dir) 435 436 # Bzip2 compression. 437 if compress_type == 1 and not search('.bz2$', file_path): 438 # Bz2 module exists. 439 if dep_check.bz2_module: 440 file_path = file_path + '.bz2' 441 442 # Switch to gzip compression. 443 else: 444 warn(RelaxWarning("Cannot use Bzip2 compression, using gzip compression instead. " + dep_check.bz2_module_message + ".")) 445 compress_type = 2 446 447 # Gzip compression. 448 if compress_type == 2 and not search('.gz$', file_path): 449 file_path = file_path + '.gz' 450 451 # Fail if the file already exists and the force flag is set to 0. 452 if access(file_path, F_OK) and not force: 453 raise RelaxFileOverwriteError(file_path, 'force flag') 454 455 # Open the file for writing. 456 try: 457 # Print out. 458 if verbosity: 459 print("Opening the file " + repr(file_path) + " for writing.") 460 461 # Uncompressed text. 462 if compress_type == 0: 463 file_obj = open(file_path, 'w') 464 465 # Bzip2 compressed text. 466 elif compress_type == 1: 467 file_obj = bz2_open(file=file_path, mode='w') 468 469 # Gzipped compressed text. 470 elif compress_type == 2: 471 file_obj = gz_open(file=file_path, mode='w') 472 473 # Cannot open. 474 except IOError: 475 message = sys.exc_info()[1] 476 raise RelaxError("Cannot open the file " + repr(file_path) + ". " + message.args[1] + ".") 477 478 # Return the opened file. 479 if return_path: 480 return file_obj, file_path 481 else: 482 return file_obj
483 484
485 -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):
486 """Generator function for reading the spin specific data from file. 487 488 Description 489 =========== 490 491 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. 492 493 494 @keyword file: The name of the file to open. 495 @type file: str 496 @keyword dir: The directory containing the file (defaults to the current directory if None). 497 @type dir: str or None 498 @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. 499 @type file_data: list of lists 500 @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. 501 @type spin_id_col: int or None 502 @keyword mol_name_col: The column containing the molecule name information. If supplied, spin_id_col must be None. 503 @type mol_name_col: int or None 504 @keyword res_name_col: The column containing the residue name information. If supplied, spin_id_col must be None. 505 @type res_name_col: int or None 506 @keyword res_num_col: The column containing the residue number information. If supplied, spin_id_col must be None. 507 @type res_num_col: int or None 508 @keyword spin_name_col: The column containing the spin name information. If supplied, spin_id_col must be None. 509 @type spin_name_col: int or None 510 @keyword spin_num_col: The column containing the spin number information. If supplied, spin_id_col must be None. 511 @type spin_num_col: int or None 512 @keyword data_col: The column containing the data. 513 @type data_col: int or None 514 @keyword error_col: The column containing the errors. 515 @type error_col: int or None 516 @keyword sep: The column separator which, if None, defaults to whitespace. 517 @type sep: str or None 518 @keyword spin_id: The spin ID string used to restrict data loading to a subset of all spins. 519 @type spin_id: None or str 520 @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. 521 @rtype: str, list of [str, float], or list of [str, float, float] 522 """ 523 524 # Argument tests. 525 col_args = [spin_id_col, mol_name_col, res_name_col, res_num_col, spin_name_col, spin_num_col, data_col, error_col] 526 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'] 527 for i in range(len(col_args)): 528 if col_args[i] == 0: 529 raise RelaxError("The '%s' argument cannot be zero, column numbering starts at one." % col_arg_names[i]) 530 if spin_id_col and (mol_name_col or res_name_col or res_num_col or spin_name_col or spin_num_col): 531 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.") 532 533 # Minimum number of columns. 534 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])) 535 536 # Extract the data from the file. 537 if not file_data: 538 # Extract. 539 file_data = extract_data(file, dir) 540 541 # Strip the data of all comments and empty lines. 542 if spin_id_col != None: 543 file_data = strip(file_data, comments=False) 544 else: 545 file_data = strip(file_data) 546 547 # No data! 548 if not file_data: 549 warn(RelaxFileEmptyWarning(file)) 550 return 551 552 # Yield the data, spin by spin. 553 missing_data = True 554 for line in file_data: 555 # Convert the spin IDs. 556 if spin_id_col != None and line[spin_id_col-1][0] in ["\"", "\'"]: 557 line[spin_id_col-1] = eval(line[spin_id_col-1]) 558 559 # Convert. 560 # Validate the sequence. 561 try: 562 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) 563 except RelaxInvalidSeqError: 564 # Extract the message string, without the RelaxError bit. 565 msg = sys.exc_info()[1] 566 string = msg.__str__()[12:-1] 567 568 # Give a warning. 569 warn(RelaxWarning(string)) 570 571 # Skip the line. 572 continue 573 574 # Get the spin data from the ID. 575 if spin_id_col: 576 # Invalid spin ID. 577 if line[spin_id_col-1] == '#': 578 warn(RelaxWarning("Invalid spin ID, skipping the line %s" % line)) 579 continue 580 581 mol_name, res_num, res_name, spin_num, spin_name = spin_id_to_data_list(line[spin_id_col-1]) 582 583 # Convert the spin data. 584 else: 585 # The molecule. 586 mol_name = None 587 if mol_name_col != None and line[mol_name_col-1] != 'None': 588 mol_name = line[mol_name_col-1] 589 590 # The residue number, catching bad values. 591 res_num = None 592 if res_num_col != None: 593 try: 594 if line[res_num_col-1] == 'None': 595 res_num = None 596 else: 597 res_num = int(line[res_num_col-1]) 598 except ValueError: 599 warn(RelaxWarning("Invalid residue number, skipping the line %s" % line)) 600 continue 601 602 # The residue name. 603 res_name = None 604 if res_name_col != None and line[res_name_col-1] != 'None': 605 res_name = line[res_name_col-1] 606 607 # The spin number, catching bad values. 608 spin_num = None 609 if spin_num_col != None: 610 try: 611 if line[spin_num_col-1] == 'None': 612 spin_num = None 613 else: 614 spin_num = int(line[spin_num_col-1]) 615 except ValueError: 616 warn(RelaxWarning("Invalid spin number, skipping the line %s" % line)) 617 continue 618 619 # The spin name. 620 spin_name = None 621 if spin_name_col != None and line[spin_name_col-1] != 'None': 622 spin_name = line[spin_name_col-1] 623 624 # Convert the data. 625 value = None 626 if data_col != None: 627 try: 628 # None. 629 if line[data_col-1] == 'None': 630 value = None 631 632 # A float. 633 else: 634 value = float(line[data_col-1]) 635 636 # Bad data. 637 except ValueError: 638 warn(RelaxWarning("Invalid data, skipping the line %s" % line)) 639 continue 640 641 # Convert the errors. 642 error = None 643 if error_col != None: 644 try: 645 # None. 646 if line[error_col-1] == 'None': 647 error = None 648 649 # A float. 650 else: 651 error = float(line[error_col-1]) 652 653 # Bad data. 654 except ValueError: 655 warn(RelaxWarning("Invalid errors, skipping the line %s" % line)) 656 continue 657 658 # Right, data is OK and exists. 659 missing_data = False 660 661 # Yield the data. 662 if data_col and error_col: 663 yield mol_name, res_num, res_name, spin_num, spin_name, value, error 664 elif data_col: 665 yield mol_name, res_num, res_name, spin_num, spin_name, value 666 elif error_col: 667 yield mol_name, res_num, res_name, spin_num, spin_name, error 668 else: 669 yield mol_name, res_num, res_name, spin_num, spin_name 670 671 # Hmmm, no data! 672 if missing_data: 673 raise RelaxError("No corresponding data could be found within the file.")
674 675
676 -def strip(data, comments=True):
677 """Remove all comment and empty lines from the file data structure. 678 679 @param data: The file data to clean up. 680 @type data: list of lists of str 681 @keyword comments: A flag which if True will cause comments to be deleted. 682 @type comments: bool 683 @return: The input data with the empty and comment lines removed. 684 @rtype: list of lists of str 685 """ 686 687 # Initialise the new data array. 688 new = [] 689 690 # Loop over the data. 691 for i in range(len(data)): 692 # Empty lines. 693 if len(data[i]) == 0: 694 continue 695 696 # Comment lines. 697 if comments and search("^#", data[i][0]): 698 continue 699 700 # Data lines. 701 new.append(data[i]) 702 703 # Return the new data structure. 704 return new
705 706
707 -def test_binary(binary):
708 """Function for testing that the binary string corresponds to a valid executable file. 709 710 @param binary: The name or path of the binary executable file. 711 @type binary: str 712 """ 713 714 # Path separator RE string. 715 if altsep: 716 path_sep = '[' + sep + altsep + ']' 717 else: 718 path_sep = sep 719 720 # The full path of the program has been given (if a directory separatory has been supplied). 721 if isfile(binary): 722 # Test that the binary exists. 723 if not access(binary, F_OK): 724 raise RelaxMissingBinaryError(binary) 725 726 # Test that if the binary is executable. 727 if not access(binary, X_OK): 728 raise RelaxNonExecError(binary) 729 730 # The path to the binary has not been given. 731 else: 732 # Get the PATH environmental variable. 733 path = getenv('PATH') 734 735 # Split PATH by the path separator. 736 path_list = path.split(pathsep) 737 738 # Test that the binary exists within the system path (and exit this function instantly once it has been found). 739 for path in path_list: 740 if access(path + sep + binary, F_OK) or access(path + sep + binary +".exe", F_OK): 741 return 742 743 # The binary is not located in the system path! 744 raise RelaxNoInPathError(binary)
745 746
747 -def write_data(out=None, headings=None, data=None, sep=None):
748 """Write out a table of the data to the given file handle. 749 750 @keyword out: The file handle to write to. 751 @type out: file handle 752 @keyword headings: The optional headings to print out. 753 @type headings: list of str or None 754 @keyword data: The data to print out. 755 @type data: list of list of str 756 @keyword sep: The column separator which, if None, defaults to whitespace. 757 @type sep: str or None 758 """ 759 760 # No data to print out. 761 if data in [None, []]: 762 return 763 764 # The number of rows and columns. 765 num_rows = len(data) 766 num_cols = len(data[0]) 767 768 # Pretty whitespace formatting. 769 if sep == None: 770 # Determine the widths for the headings. 771 widths = [] 772 for j in range(num_cols): 773 if headings != None: 774 if j == 0: 775 widths.append(len(headings[j]) + 2) 776 else: 777 widths.append(len(headings[j])) 778 779 # No headings. 780 else: 781 widths.append(0) 782 783 # Determine the maximum column widths for nice whitespace formatting. 784 for i in range(num_rows): 785 for j in range(num_cols): 786 size = len(data[i][j]) 787 if size > widths[j]: 788 widths[j] = size 789 790 # Convert to format strings. 791 formats = [] 792 for j in range(num_cols): 793 formats.append("%%-%ss" % (widths[j] + 4)) 794 795 # The headings. 796 if headings != None: 797 out.write(formats[0] % ("# " + headings[0])) 798 for j in range(1, num_cols): 799 out.write(formats[j] % headings[j]) 800 out.write('\n') 801 802 # The data. 803 for i in range(num_rows): 804 # The row. 805 for j in range(num_cols): 806 out.write(formats[j] % data[i][j]) 807 out.write('\n') 808 809 # Non-whitespace formatting. 810 else: 811 # The headings. 812 if headings != None: 813 out.write('#') 814 for j in range(num_cols): 815 # The column separator. 816 if j > 0: 817 out.write(sep) 818 819 # The heading. 820 out.write(headings[j]) 821 out.write('\n') 822 823 # The data. 824 for i in range(num_rows): 825 # The row. 826 for j in range(num_cols): 827 # The column separator. 828 if j > 0: 829 out.write(sep) 830 831 # The heading. 832 out.write(data[i][j]) 833 out.write('\n')
834 835
836 -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"):
837 """Generator function for reading the spin specific data from file. 838 839 Description 840 =========== 841 842 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. 843 844 845 @param file: The name of the file to write the data to (or alternatively an already opened file object). 846 @type file: str or file object 847 @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). 848 @type dir: str or None 849 @keyword sep: The column separator which, if None, defaults to whitespace. 850 @type sep: str or None 851 @keyword spin_ids: The list of spin ID strings. 852 @type spin_ids: None or list of str 853 @keyword mol_names: The list of molecule names. 854 @type mol_names: None or list of str 855 @keyword res_nums: The list of residue numbers. 856 @type res_nums: None or list of int 857 @keyword res_names: The list of residue names. 858 @type res_names: None or list of str 859 @keyword spin_nums: The list of spin numbers. 860 @type spin_nums: None or list of int 861 @keyword spin_names: The list of spin names. 862 @type spin_names: None or list of str 863 @keyword force: A flag which if True will cause an existing file to be overwritten. 864 @type force: bool 865 @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. 866 @type data: list or list of lists 867 @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. 868 @type data_name: str or list of str 869 @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. 870 @type error: list or list of lists 871 @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. 872 @type error_name: str or list of str 873 @keyword float_format: A float formatting string to use for the data and error whenever a float is found. 874 @type float_format: str 875 """ 876 877 # Data argument tests. 878 if data: 879 # Data is a list of lists. 880 if isinstance(data[0], list): 881 # Data and data_name don't match. 882 if not isinstance(data_name, list): 883 raise RelaxError("The data_name arg '%s' must be a list as the data argument is a list of lists." % data_name) 884 885 # Error doesn't match. 886 if error and (len(data) != len(error) or len(data[0]) != len(error[0])): 887 raise RelaxError("The data arg:\n%s\n\ndoes not have the same dimensions as the error arg:\n%s." % (data, error)) 888 889 # Data is a simple list. 890 else: 891 # Data and data_name don't match. 892 if not isinstance(data_name, str): 893 raise RelaxError("The data_name arg '%s' must be a string as the data argument is a simple list." % data_name) 894 895 # Error doesn't match. 896 if error and len(data) != len(error): 897 raise RelaxError("The data arg:\n%s\n\ndoes not have the same dimensions as the error arg:\n%s." % (data, error)) 898 899 # Error argument tests. 900 if error: 901 # Error is a list of lists. 902 if isinstance(error[0], list): 903 # Error and error_name don't match. 904 if not isinstance(error_name, list): 905 raise RelaxError("The error_name arg '%s' must be a list as the error argument is a list of lists." % error_name) 906 907 # Error is a simple list. 908 else: 909 # Error and error_name don't match. 910 if not isinstance(error_name, str): 911 raise RelaxError("The error_name arg '%s' must be a string as the error argument is a simple list." % error_name) 912 913 # Number of spins check. 914 args = [spin_ids, mol_names, res_nums, res_names, spin_nums, spin_names] 915 arg_names = ['spin_ids', 'mol_names', 'res_nums', 'res_names', 'spin_nums', 'spin_names'] 916 N = None 917 first_arg = None 918 first_arg_name = None 919 for i in range(len(args)): 920 if isinstance(args[i], list): 921 # First list match. 922 if N == None: 923 N = len(args[i]) 924 first_arg = args[i] 925 first_arg_name = arg_names[i] 926 927 # Length check. 928 if len(args[i]) != N: 929 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]))) 930 931 # Nothing?!? 932 if N == None: 933 raise RelaxError("No spin ID data is present.") 934 935 # Data and error length check. 936 if data and len(data) != N: 937 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))) 938 if error and len(error) != N: 939 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))) 940 941 # The spin arguments. 942 args = [spin_ids, mol_names, res_nums, res_names, spin_nums, spin_names] 943 arg_names = ['spin_id', 'mol_name', 'res_num', 'res_name', 'spin_num', 'spin_name'] 944 945 946 # Init. 947 headings = [] 948 file_data = [] 949 950 # Headers - the spin ID info. 951 for i in range(len(args)): 952 if args[i]: 953 headings.append(arg_names[i]) 954 955 # Headers - the data. 956 if data: 957 # List of lists. 958 if isinstance(data[0], list): 959 # Loop over the list. 960 for i in range(len(data[0])): 961 # The data. 962 headings.append(data_name[i]) 963 964 # The error. 965 if error: 966 headings.append(error_name[i]) 967 968 # Simple list. 969 else: 970 # The data. 971 headings.append(data_name) 972 973 # The error. 974 if error: 975 headings.append(error_name) 976 977 # Headers - only errors. 978 elif error: 979 # List of lists. 980 if isinstance(error[0], list): 981 for i in range(len(error[0])): 982 headings.append(error_name[i]) 983 984 # Simple list. 985 else: 986 headings.append(error_name) 987 988 # No headings. 989 if headings == []: 990 headings = None 991 992 # Spin specific data. 993 for spin_index in range(N): 994 # Append a new data row. 995 file_data.append([]) 996 997 # The spin ID info. 998 for i in range(len(args)): 999 if args[i]: 1000 value = args[i][spin_index] 1001 if not isinstance(value, str): 1002 value = repr(value) 1003 file_data[-1].append(value) 1004 1005 # The data. 1006 if data: 1007 # List of lists. 1008 if isinstance(data[0], list): 1009 # Loop over the list. 1010 for i in range(len(data[0])): 1011 # The data. 1012 if is_float(data[spin_index][i]): 1013 file_data[-1].append(float_format % data[spin_index][i]) 1014 else: 1015 file_data[-1].append(repr(data[spin_index][i])) 1016 1017 # The error. 1018 if error: 1019 if is_float(error[spin_index][i]): 1020 file_data[-1].append(float_format % error[spin_index][i]) 1021 else: 1022 file_data[-1].append(repr(error[spin_index][i])) 1023 1024 # Simple list. 1025 else: 1026 # The data. 1027 if is_float(data[spin_index]): 1028 file_data[-1].append(float_format % data[spin_index]) 1029 else: 1030 file_data[-1].append(repr(data[spin_index])) 1031 1032 # The error. 1033 if error: 1034 if is_float(error[spin_index]): 1035 file_data[-1].append(float_format % error[spin_index]) 1036 else: 1037 file_data[-1].append(repr(error[spin_index])) 1038 1039 # Only errors. 1040 elif error: 1041 # List of lists. 1042 if isinstance(error[0], list): 1043 for i in range(len(error[0])): 1044 file_data[-1].append(repr(error[spin_index][i])) 1045 1046 # Simple list. 1047 else: 1048 file_data[-1].append(repr(error[spin_index])) 1049 1050 # No data to write, so do nothing! 1051 if file_data == [] or file_data == [[]]: 1052 return 1053 1054 # Open the file for writing. 1055 file = open_write_file(file_name=file, dir=dir, force=force) 1056 1057 # Write out the file data. 1058 write_data(out=file, headings=headings, data=file_data, sep=sep)
1059 1060 1061
1062 -class DummyFileObject:
1063 - def __init__(self):
1064 """Set up the dummy object to act as a file object.""" 1065 1066 # Initialise an object for adding the string from all write calls to. 1067 self.data = '' 1068 1069 # Set the closed flag. 1070 self.closed = False
1071 1072
1073 - def close(self):
1074 """A method for 'closing' this object.""" 1075 1076 # Set the closed flag. 1077 self.closed = True
1078 1079
1080 - def write(self, str):
1081 """Mimic the file object write() method so that this class can be used as a file object. 1082 1083 @param str: The string to be written. 1084 @type str: str 1085 """ 1086 1087 # Check if the file is closed. 1088 if self.closed: 1089 raise ValueError('I/O operation on closed file') 1090 1091 # Append the string to the data object. 1092 self.data = self.data + str
1093 1094
1095 - def readlines(self):
1096 """Mimic the file object readlines() method. 1097 1098 This method works even if this dummy file object is closed! 1099 1100 1101 @return: The contents of the file object separated by newline characters. 1102 @rtype: list of str 1103 """ 1104 1105 # Split up the string. 1106 lines = self.data.split('\n') 1107 1108 # Remove the last line if empty. 1109 if lines[-1] == '': 1110 lines.pop() 1111 1112 # Loop over the lines, re-adding the newline character to match the file object readlines() method. 1113 for i in range(len(lines)): 1114 lines[i] = lines[i] + '\n' 1115 1116 # Return the file lines. 1117 return lines
1118 1119 1120
1121 -class SplitIO:
1122 - def __init__(self):
1123 """Class for splitting an IO stream to two outputs."""
1124 1125
1126 - def flush(self):
1127 """Flush all streams.""" 1128 1129 # Call the streams' methods. 1130 self.stream1.flush() 1131 self.stream2.flush()
1132 1133
1134 - def isatty(self):
1135 """Check that both streams are TTYs. 1136 1137 @return: True, only if both streams are TTYs. 1138 @rtype: bool 1139 """ 1140 1141 # Check both streams. 1142 return self.stream1.isatty() & self.stream2.isatty()
1143 1144
1145 - def split(self, stream1, stream2):
1146 """Function for setting the streams.""" 1147 1148 # Arguments. 1149 self.stream1 = stream1 1150 self.stream2 = stream2
1151 1152
1153 - def write(self, text):
1154 """Replacement write function.""" 1155 1156 # Write to stream1. 1157 self.stream1.write(text) 1158 1159 # Write to stream2. 1160 self.stream2.write(text)
1161