comparison frogsUtils.py @ 0:da4101033e10 draft default tip

planemo upload
author oinizan
date Wed, 18 Oct 2017 05:30:40 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:da4101033e10
1 #
2 # Copyright (C) 2014 INRA
3 #
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17
18 __author__ = 'Frederic Escudie - Plateforme bioinformatique Toulouse'
19 __copyright__ = 'Copyright (C) 2015 INRA'
20 __license__ = 'GNU General Public License'
21 __version__ = '0.2.0'
22 __email__ = 'frogs@inra.fr'
23 __status__ = 'prod'
24
25 import os
26 import sys
27 import time
28 import subprocess
29 from subprocess import Popen, PIPE
30
31
32 def which(exec_name):
33 """
34 @summary: Returns the software absolute path.
35 @param exec_name: [str] The software file (example : blastn or classifier.jar).
36 @return: [str] The software absolute path.
37 """
38 path_locations = os.getenv("PATH").split(os.pathsep) + sys.path
39 exec_path = None
40 for current_location in path_locations:
41 if exec_path is None and os.path.isfile(os.path.join(current_location, exec_name)):
42 exec_path = os.path.abspath( os.path.join(current_location, exec_name) )
43 if exec_path is None:
44 raise Exception( "The software '" + exec_name + "' cannot be retrieved in path." )
45 return exec_path
46
47
48 def prevent_shell_injections(argparse_namespace, excluded_args=None):
49 """
50 @summary: Raises an exception if one parameter contains a backquote or a semi-colon.
51 @param argparse_namespace: [Namespase] The result of parser.parse_args().
52 @param excluded_args: [list] List of unchecked parameters.
53 """
54 exceptions = list() if excluded_args is None else excluded_args
55 for param_name in argparse_namespace.__dict__.keys():
56 if not param_name in exceptions:
57 param_val = getattr(argparse_namespace, param_name)
58 if issubclass(param_val.__class__, list):
59 new_param_val = list()
60 for val in param_val:
61 if ';' in val.encode('utf8') or '`' in val.encode('utf8') or '|' in val.encode('utf8'):
62 raise Exception( "';' and '`' are unauthorized characters." )
63 elif param_val is not None and issubclass(param_val.__class__, str):
64 if ';' in param_val.encode('utf8') or '`' in param_val.encode('utf8') or '|' in param_val.encode('utf8'):
65 raise Exception( "';' and '`' are unauthorized characters." )
66
67
68 class Cmd:
69 """
70 @summary : Command wrapper.
71 """
72 def __init__(self, program, description, exec_parameters, version_parameters=None):
73 """
74 @param exec_parameters: [str] The parameters to execute the program. Two possibles syntaxes.
75 If the parameter contains the string '##PROGRAM##', this tag will be replaced by the program parameter before submit.
76 Otherwise the parameters will be added after the program in command line.
77 @param version_parameters: [str] The parameters to get the program version. Two possibles syntaxes.
78 If the parameter contains the string '##PROGRAM##', this tag will be replaced by the program parameter before submit.
79 Otherwise the parameters will be added after the program in command line.
80 """
81 self.program = program
82 self.description = description
83 self.exec_parameters = exec_parameters
84 self.version_parameters = version_parameters
85
86 def get_cmd(self):
87 """
88 @summary : Returns the command line.
89 @return : [str] The command line.
90 """
91 cmd = None
92 if '##PROGRAM##' in self.exec_parameters:
93 cmd = self.exec_parameters.replace('##PROGRAM##', self.program)
94 else:
95 cmd = self.program + ' ' + self.exec_parameters
96 return cmd
97
98 def get_version(self, location='stderr'):
99 """
100 @summary : Returns the program version number.
101 @param location : [str] If the version command returns the version number on 'stdout' or on 'stderr'.
102 @return : [str] version number if this is possible, otherwise this method return 'unknown'.
103 """
104 if self.version_parameters is None:
105 return "unknown"
106 else:
107 try:
108 cmd = self.program + ' ' + self.version_parameters
109 if '##PROGRAM##' in self.exec_parameters:
110 cmd = self.version_parameters.replace('##PROGRAM##', self.program)
111 p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE)
112 stdout, stderr = p.communicate()
113 if location == 'stderr':
114 return stderr.strip()
115 else:
116 return stdout.strip()
117 except:
118 raise Exception( "Version cannot be retrieve for the software '" + self.program + "'." )
119
120 def parser(self, log_file):
121 """
122 @summary : Parse the command results to add information in log_file.
123 @log_file : [str] Path to the sample process log file.
124 """
125 pass
126
127 def submit(self, log_file=None):
128 """
129 @summary : Launch command, trace this action in log and parse results.
130 @log_file : [str] Path to the sample process log file.
131 """
132 # Log
133 if log_file is not None:
134 FH_log = Logger( log_file )
135 FH_log.write( '# ' + self.description + ' (' + os.path.basename(self.program) + ' version : ' + self.get_version() + ')\n' )
136 FH_log.write( 'Command:\n\t' + self.get_cmd() + '\n\n' )
137 FH_log.write( 'Execution:\n\tstart: ' + time.strftime("%d %b %Y %H:%M:%S", time.localtime()) + '\n' )
138 FH_log.close()
139 # Process
140 subprocess.check_output( self.get_cmd(), shell=True )
141 # Log
142 if log_file is not None:
143 FH_log = Logger( log_file )
144 FH_log.write( '\tend: ' + time.strftime("%d %b %Y %H:%M:%S", time.localtime()) + '\n\n' )
145 FH_log.close()
146 # Post-process results
147 self.parser(log_file)
148
149
150 class Logger:
151 """
152 @summary: Log file handler.
153 """
154
155 def __init__(self, filepath=None):
156 """
157 @param filepath: [str] The log filepath. [default : STDOUT]
158 """
159 self.filepath = filepath
160 if self.filepath is not None and self.filepath is not sys.stdout:
161 self.file_handle = open( self.filepath, "a" )
162 else:
163 self.file_handle = sys.stdout
164
165 def __del__(self):
166 """
167 @summary: Closed file handler when the logger is detroyed.
168 """
169 self.close()
170
171 def close(self):
172 """
173 @summary: Closed file handler.
174 """
175 if self.filepath is not None and self.filepath is not sys.stdout:
176 if self.file_handle is not None:
177 self.file_handle.close()
178 self.file_handle = None
179
180 def write(self, msg):
181 """
182 @summary: Writes msg on file.
183 @param msg: [str] The message to write.
184 """
185 self.file_handle.write( msg )
186
187 @staticmethod
188 def static_write(filepath, msg):
189 """
190 @summary: Writes msg on file.
191 @param filepath: [str] The log filepath. [default : STDOUT]
192 @param msg: [str] The message to write.
193 """
194 if filepath is not None and filepath is not sys.stdout:
195 FH_log = open( filepath, "a" )
196 FH_log.write( msg )
197 FH_log.close()
198 else:
199 sys.stdout.write( msg )
200
201
202 class TmpFiles:
203 """
204 @summary: Manager for temporary files.
205 @note:
206 tmpFiles = TmpFiles(out_dir)
207 try:
208 ...
209 tmp_seq = tmpFiles.add( "toto.fasta" )
210 ...
211 tmp_log = tmpFiles.add( "log.txt" )
212 ...
213 finaly:
214 tmpFiles.deleteAll()
215 """
216 def __init__(self, tmp_dir, prefix=None):
217 """
218 @param tmp_dir: [str] The temporary directory path.
219 @param prefix: [str] The prefix added to each temporary file [default: <TIMESTAMP>_<PID>].
220 """
221 if prefix is None:
222 prefix = str(time.time()) + "_" + str(os.getpid())
223 self.files = list()
224 self.dirs = list()
225 self.tmp_dir = tmp_dir
226 self.prefix = prefix
227
228 def add(self, filename, prefix=None, dir=None):
229 """
230 @summary: Add a temporary file.
231 @param filename: The filename without prefix.
232 @param prefix: The prefix added [default: TmpFiles.prefix].
233 @param dir: The directory path [default: TmpFiles.tmp_dir].
234 @return: [str] The filepath.
235 """
236 # Default
237 if prefix is None:
238 prefix = self.prefix
239 if dir is None:
240 dir = self.tmp_dir
241 # Process
242 filepath = os.path.join(dir, prefix + "_" + filename)
243 self.files.append(filepath)
244 return filepath
245
246 def add_dir(self, dirname, prefix=None, dir=None):
247 """
248 @summary: Add a temporary dir.
249 @param filename: The dirname without prefix.
250 @param prefix: The prefix added [default: TmpFiles.prefix].
251 @param dir: The directory path [default: TmpFiles.tmp_dir].
252 @return: [str] The filepath.
253 """
254 # Default
255 if prefix is None:
256 prefix = self.prefix
257 if dir is None:
258 dir = self.tmp_dir
259 # Process
260 dirpath = os.path.join(dir, prefix + "_" + dirname)
261 self.dirs.append(dirpath)
262 return dirpath
263
264 def delete(self, filepath):
265 """
266 @summary: Deletes the specified temporary file.
267 @param filepath: [str] The file path to delete.
268 """
269 self.files.remove(filepath)
270 if os.path.exists(filepath): os.remove(filepath)
271
272 def delete_dir(self, dirpath):
273 """
274 @summary: Deletes the specified temporary dir.
275 @param filepath: [str] The file path to delete.
276 """
277 if dirpath in self.dirs: self.dirs.remove(dirpath)
278
279 if os.path.exists(dirpath):
280 for root, dirnames,filenames in os.walk(dirpath):
281 for f in filenames:
282 if f in self.files: self.files.remove(os.path.join(dirpath,f))
283 if os.path.exists(os.path.join(dirpath,f)): os.remove(os.path.join(dirpath,f))
284 for d in dirnames:
285 if d in self.dirs: self.dirs.remove(os.path.join(dirpath,d))
286 if os.path.exists(os.path.join(dirpath,d)): self.delete_dir(os.path.join(dirpath,d))
287 os.rmdir(dirpath)
288
289 def deleteAll(self):
290 """
291 @summary: Deletes all temporary files.
292 """
293 all_tmp_files = [tmp_file for tmp_file in self.files]
294 for tmp_file in all_tmp_files:
295 self.delete(tmp_file)
296
297 all_tmp_dirs=[tmp_dir for tmp_dir in self.dirs]
298 for tmp_dir in all_tmp_dirs:
299 self.delete_dir(tmp_dir)