Mercurial > repos > oinizan > demultiplex
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) |