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