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