1 from __future__ import absolute_import
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
31 import dep_check
32
33
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
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
65 file_path = get_file_path(file_name, dir)
66
67
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
80 remove(file_path)
81
82
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
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
103 elif access(file_path + '.bz2', F_OK):
104 file_path = file_path + '.bz2'
105 compress_type = 1
106
107
108 elif access(file_path + '.gz', F_OK):
109 file_path = file_path + '.gz'
110 compress_type = 2
111
112
113 else:
114 raise RelaxFileError(file_path)
115
116
117 return compress_type, file_path
118
119
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
140 if not file_data:
141
142 if isinstance(file, str):
143 file = open_read_file(file_name=file, dir=dir)
144
145
146 file_data = file.readlines()
147
148
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
158 if not file_data:
159 file.close()
160
161
162 return data
163
164
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
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
191 file_path = file_name
192
193
194 if dir:
195 file_path = dir + sep + file_path
196
197
198 if file_path:
199 file_path = expanduser(file_path)
200
201
202 return file_path
203
204
206 """Restore all IO streams to the Python defaults.
207
208 @param verbosity: The verbosity level.
209 @type verbosity: int
210 """
211
212
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
219 sys.stdin = sys.__stdin__
220 sys.stdout = sys.__stdout__
221 sys.stderr = sys.__stderr__
222
223
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
237 log_file, file_path = open_write_file(file_name=file_name, dir=dir, force=True, verbosity=verbosity, return_path=True)
238
239
240 log_stdin = stdin
241 log_stdout = None
242 log_stderr = SplitIO()
243
244
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
251 log_stdout = log_file
252 log_stderr.split(stderr, log_file)
253
254
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
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
279 tee_stdin = stdin
280 tee_stdout = SplitIO()
281 tee_stderr = SplitIO()
282
283
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
290 tee_stdout.split(stdout, tee_file)
291 tee_stderr.split(stderr, tee_file)
292
293
294 sys.stdin = tee_stdin
295 sys.stdout = tee_stdout
296 sys.stderr = tee_stderr
297
298
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
309 if dir == None:
310 return
311
312
313 try:
314 makedirs(dir)
315 except OSError:
316 if verbosity:
317 print("Directory ." + sep + dir + " already exists.\n")
318
319
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
335 if is_filetype(file_name):
336
337 return file_name
338
339
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
344 file_path = get_file_path(file_name, dir)
345
346
347 compress_type, file_path = determine_compression(file_path)
348
349
350 try:
351
352 if verbosity:
353 print("Opening the file " + repr(file_path) + " for reading.")
354
355
356 if compress_type == 0:
357 file_obj = open(file_path, 'r')
358
359
360 elif compress_type == 1:
361 file_obj = bz2_open(file=file_path, mode='r')
362
363
364 elif compress_type == 2:
365 file_obj = gz_open(file=file_path, mode='r')
366
367
368 except IOError:
369 message = sys.exc_info()[1]
370 raise RelaxError("Cannot open the file " + repr(file_path) + ". " + message.args[1] + ".")
371
372
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
402 if file_name == None:
403 raise RelaxError("The name of the file must be supplied.")
404
405
406 if is_filetype(file_name):
407
408 return file_name
409
410
411 if hasattr(file_name, 'write'):
412
413 return file_name
414
415
416 if search('devnull', file_name):
417
418 if verbosity:
419 print("Opening the null device file for writing.")
420
421
422 file_obj = open(devnull, 'w')
423
424
425 if return_path:
426 return file_obj, None
427 else:
428 return file_obj
429
430
431 mkdir_nofail(dir, verbosity=0)
432
433
434 file_path = get_file_path(file_name, dir)
435
436
437 if compress_type == 1 and not search('.bz2$', file_path):
438
439 if dep_check.bz2_module:
440 file_path = file_path + '.bz2'
441
442
443 else:
444 warn(RelaxWarning("Cannot use Bzip2 compression, using gzip compression instead. " + dep_check.bz2_module_message + "."))
445 compress_type = 2
446
447
448 if compress_type == 2 and not search('.gz$', file_path):
449 file_path = file_path + '.gz'
450
451
452 if access(file_path, F_OK) and not force:
453 raise RelaxFileOverwriteError(file_path, 'force flag')
454
455
456 try:
457
458 if verbosity:
459 print("Opening the file " + repr(file_path) + " for writing.")
460
461
462 if compress_type == 0:
463 file_obj = open(file_path, 'w')
464
465
466 elif compress_type == 1:
467 file_obj = bz2_open(file=file_path, mode='w')
468
469
470 elif compress_type == 2:
471 file_obj = gz_open(file=file_path, mode='w')
472
473
474 except IOError:
475 message = sys.exc_info()[1]
476 raise RelaxError("Cannot open the file " + repr(file_path) + ". " + message.args[1] + ".")
477
478
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
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
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
537 if not file_data:
538
539 file_data = extract_data(file, dir)
540
541
542 if spin_id_col != None:
543 file_data = strip(file_data, comments=False)
544 else:
545 file_data = strip(file_data)
546
547
548 if not file_data:
549 warn(RelaxFileEmptyWarning(file))
550 return
551
552
553 missing_data = True
554 for line in file_data:
555
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
560
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
565 msg = sys.exc_info()[1]
566 string = msg.__str__()[12:-1]
567
568
569 warn(RelaxWarning(string))
570
571
572 continue
573
574
575 if spin_id_col:
576
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
584 else:
585
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
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
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
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
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
625 value = None
626 if data_col != None:
627 try:
628
629 if line[data_col-1] == 'None':
630 value = None
631
632
633 else:
634 value = float(line[data_col-1])
635
636
637 except ValueError:
638 warn(RelaxWarning("Invalid data, skipping the line %s" % line))
639 continue
640
641
642 error = None
643 if error_col != None:
644 try:
645
646 if line[error_col-1] == 'None':
647 error = None
648
649
650 else:
651 error = float(line[error_col-1])
652
653
654 except ValueError:
655 warn(RelaxWarning("Invalid errors, skipping the line %s" % line))
656 continue
657
658
659 missing_data = False
660
661
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
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
688 new = []
689
690
691 for i in range(len(data)):
692
693 if len(data[i]) == 0:
694 continue
695
696
697 if comments and search("^#", data[i][0]):
698 continue
699
700
701 new.append(data[i])
702
703
704 return new
705
706
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
715 if altsep:
716 path_sep = '[' + sep + altsep + ']'
717 else:
718 path_sep = sep
719
720
721 if isfile(binary):
722
723 if not access(binary, F_OK):
724 raise RelaxMissingBinaryError(binary)
725
726
727 if not access(binary, X_OK):
728 raise RelaxNonExecError(binary)
729
730
731 else:
732
733 path = getenv('PATH')
734
735
736 path_list = path.split(pathsep)
737
738
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
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
761 if data in [None, []]:
762 return
763
764
765 num_rows = len(data)
766 num_cols = len(data[0])
767
768
769 if sep == None:
770
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
780 else:
781 widths.append(0)
782
783
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
791 formats = []
792 for j in range(num_cols):
793 formats.append("%%-%ss" % (widths[j] + 4))
794
795
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
803 for i in range(num_rows):
804
805 for j in range(num_cols):
806 out.write(formats[j] % data[i][j])
807 out.write('\n')
808
809
810 else:
811
812 if headings != None:
813 out.write('#')
814 for j in range(num_cols):
815
816 if j > 0:
817 out.write(sep)
818
819
820 out.write(headings[j])
821 out.write('\n')
822
823
824 for i in range(num_rows):
825
826 for j in range(num_cols):
827
828 if j > 0:
829 out.write(sep)
830
831
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
878 if data:
879
880 if isinstance(data[0], list):
881
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
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
890 else:
891
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
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
900 if error:
901
902 if isinstance(error[0], list):
903
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
908 else:
909
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
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
922 if N == None:
923 N = len(args[i])
924 first_arg = args[i]
925 first_arg_name = arg_names[i]
926
927
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
932 if N == None:
933 raise RelaxError("No spin ID data is present.")
934
935
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
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
947 headings = []
948 file_data = []
949
950
951 for i in range(len(args)):
952 if args[i]:
953 headings.append(arg_names[i])
954
955
956 if data:
957
958 if isinstance(data[0], list):
959
960 for i in range(len(data[0])):
961
962 headings.append(data_name[i])
963
964
965 if error:
966 headings.append(error_name[i])
967
968
969 else:
970
971 headings.append(data_name)
972
973
974 if error:
975 headings.append(error_name)
976
977
978 elif error:
979
980 if isinstance(error[0], list):
981 for i in range(len(error[0])):
982 headings.append(error_name[i])
983
984
985 else:
986 headings.append(error_name)
987
988
989 if headings == []:
990 headings = None
991
992
993 for spin_index in range(N):
994
995 file_data.append([])
996
997
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
1006 if data:
1007
1008 if isinstance(data[0], list):
1009
1010 for i in range(len(data[0])):
1011
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
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
1025 else:
1026
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
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
1040 elif error:
1041
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
1047 else:
1048 file_data[-1].append(repr(error[spin_index]))
1049
1050
1051 if file_data == [] or file_data == [[]]:
1052 return
1053
1054
1055 file = open_write_file(file_name=file, dir=dir, force=force)
1056
1057
1058 write_data(out=file, headings=headings, data=file_data, sep=sep)
1059
1060
1061
1064 """Set up the dummy object to act as a file object."""
1065
1066
1067 self.data = ''
1068
1069
1070 self.closed = False
1071
1072
1074 """A method for 'closing' this object."""
1075
1076
1077 self.closed = True
1078
1079
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
1088 if self.closed:
1089 raise ValueError('I/O operation on closed file')
1090
1091
1092 self.data = self.data + str
1093
1094
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
1106 lines = self.data.split('\n')
1107
1108
1109 if lines[-1] == '':
1110 lines.pop()
1111
1112
1113 for i in range(len(lines)):
1114 lines[i] = lines[i] + '\n'
1115
1116
1117 return lines
1118
1119
1120
1123 """Class for splitting an IO stream to two outputs."""
1124
1125
1127 """Flush all streams."""
1128
1129
1130 self.stream1.flush()
1131 self.stream2.flush()
1132
1133
1135 """Check that both streams are TTYs.
1136
1137 @return: True, only if both streams are TTYs.
1138 @rtype: bool
1139 """
1140
1141
1142 return self.stream1.isatty() & self.stream2.isatty()
1143
1144
1145 - def split(self, stream1, stream2):
1146 """Function for setting the streams."""
1147
1148
1149 self.stream1 = stream1
1150 self.stream2 = stream2
1151
1152
1154 """Replacement write function."""
1155
1156
1157 self.stream1.write(text)
1158
1159
1160 self.stream2.write(text)
1161