766a5c81d034cbba46950813be8915a3e58b833d
[OpenColorIO-Configs.git] / aces_1.0.0 / python / aces_ocio / process.py
1 #!/usr/bin/env python\r
2 # -*- coding: utf-8 -*-\r
3 \r
4 '''A process wrapper class that maintains the text output and execution status of a process\r
5 or a list of other process wrappers which carry such data.'''\r
6 \r
7 import os\r
8 import sys\r
9 import traceback\r
10 \r
11 def readText(textFile):\r
12     if( textFile != "" ):\r
13         fp = open(textFile, 'rb')\r
14         # Create a text/plain message\r
15         text = (fp.read())\r
16         fp.close()\r
17     return text\r
18 # readText\r
19 \r
20 def writeText(text, textFile):\r
21     if( textFile != "" ):\r
22         fp = open(textFile, 'wb')\r
23         # Create a text/plain message\r
24         fp.write(text)\r
25         fp.close()\r
26     return text\r
27 # readText\r
28 \r
29 class Process:\r
30     "A process with logged output"\r
31 \r
32     def __init__(self, description=None, cmd=None, args=[], cwd=None, env=None, batchWrapper=False):\r
33         "Initialize the standard class variables"\r
34         self.cmd = cmd\r
35         if not description:\r
36             self.description = cmd\r
37         else:\r
38             self.description = description\r
39         self.status = None\r
40         self.args = args\r
41         self.start = None\r
42         self.end = None\r
43         self.log = []\r
44         self.echo = True\r
45         self.cwd = cwd\r
46         self.env = env\r
47         self.batchWrapper = batchWrapper\r
48         self.processKeys = []\r
49     # __init__\r
50 \r
51     def getElapsedSeconds(self):\r
52         import math\r
53         if self.end and self.start:\r
54             delta = (self.end - self.start)\r
55             formatted = "%s.%s" % (delta.days * 86400 + delta.seconds, int(math.floor(delta.microseconds/1e3)))\r
56         else:\r
57             formatted = None\r
58         return formatted\r
59     # getElapsedtime\r
60 \r
61     def writeKey(self, writeDict, key=None, value=None, startStop=None):\r
62         "Write a key, value pair in a supported format"\r
63         if key != None and (value != None or startStop != None):\r
64             indent = '\t'*writeDict['indentationLevel']\r
65             if writeDict['format'] == 'xml':\r
66                 if startStop == 'start':\r
67                     writeDict['logHandle'].write( "%s<%s>\n" % (indent, key) )\r
68                 elif startStop == 'stop':\r
69                     writeDict['logHandle'].write( "%s</%s>\n" % (indent, key) )\r
70                 else:\r
71                     writeDict['logHandle'].write( "%s<%s>%s</%s>\n" % (indent, key, value, key) )\r
72             else: # writeDict['format'] == 'txt':\r
73                 writeDict['logHandle'].write( "%s%40s : %s\n" % (indent, key, value) )\r
74 \r
75     def writeLogHeader(self, writeDict):\r
76         import platform\r
77 \r
78         # Retrieve operating environment information\r
79         user = None\r
80         try:\r
81             user = os.getlogin()\r
82         except:\r
83             try:\r
84                 user = os.getenv("USERNAME")\r
85                 if user == None:\r
86                     user = os.getenv("USER")\r
87             except:\r
88                 user = "unknown_user"\r
89         try:\r
90             (sysname, nodename, release, version, machine, processor) = platform.uname()\r
91         except:\r
92             (sysname, nodename, release, version, machine, processor) = ("unknown_sysname", "unknown_nodename", "unknown_release", "unknown_version", "unknown_machine", "unknown_processor")\r
93         try:\r
94             hostname = platform.node()\r
95         except:\r
96             hostname = "unknown_hostname"\r
97 \r
98         self.writeKey(writeDict, 'process', None, 'start' )\r
99         writeDict['indentationLevel'] += 1\r
100 \r
101         self.writeKey(writeDict, 'description', self.description )\r
102         self.writeKey(writeDict, 'cmd', self.cmd )\r
103         if self.args: self.writeKey(writeDict, 'args', ' '.join(self.args) )\r
104         self.writeKey(writeDict, 'start', self.start )\r
105         self.writeKey(writeDict, 'end', self.end )\r
106         self.writeKey(writeDict, 'elapsed', self.getElapsedSeconds() )\r
107 \r
108         self.writeKey(writeDict, 'user', user )\r
109         self.writeKey(writeDict, 'sysname', sysname )\r
110         self.writeKey(writeDict, 'nodename', nodename )\r
111         self.writeKey(writeDict, 'release', release )\r
112         self.writeKey(writeDict, 'version', version )\r
113         self.writeKey(writeDict, 'machine', machine )\r
114         self.writeKey(writeDict, 'processor', processor )\r
115 \r
116         if len(self.processKeys) > 0:\r
117             self.writeKey(writeDict, 'processKeys', None, 'start' )\r
118             for pair in self.processKeys:\r
119                 (key, value) = pair\r
120                 writeDict['indentationLevel'] += 1\r
121                 self.writeKey(writeDict, key, value )\r
122                 writeDict['indentationLevel'] -= 1\r
123             self.writeKey(writeDict, 'processKeys', None, 'stop' )\r
124 \r
125         self.writeKey(writeDict, 'status', self.status )\r
126     # writeLogHeader\r
127 \r
128     def writeLogFooter(self, writeDict):\r
129         writeDict['indentationLevel'] -= 1\r
130         self.writeKey(writeDict, 'process', None, 'stop' )\r
131     # writeLogFooter\r
132 \r
133     def writeLog(self, logHandle=sys.stdout, indentationLevel=0,format='xml'):\r
134         "Write logging information to the specified handle"\r
135         \r
136         writeDict = {}\r
137         writeDict['logHandle'] = logHandle\r
138         writeDict['indentationLevel'] = indentationLevel\r
139         writeDict['format'] = format\r
140         \r
141         if logHandle:\r
142             self.writeLogHeader(writeDict)\r
143             \r
144             if self.log:\r
145                 self.writeKey(writeDict, 'output', None, 'start' )\r
146                 if format == 'xml':\r
147                     logHandle.write( "<![CDATA[\n" )\r
148                 for line in self.log:\r
149                     logHandle.write( '%s%s\n' % ("", line) )\r
150                 if format == 'xml':\r
151                     logHandle.write( "]]>\n" )\r
152                 self.writeKey(writeDict, 'output', None, 'stop' )\r
153 \r
154             self.writeLogFooter(writeDict)\r
155     # writeLog\r
156 \r
157     def writeLogToDisk(self, logFilename=None, format='xml', header=None):\r
158         if logFilename: \r
159             try:\r
160                 # This also doesn't seem like the best structure...\r
161                 # 3.1\r
162                 try:\r
163                     logHandle = open( logFilename, mode='wt', encoding="utf-8")\r
164                 # 2.6\r
165                 except:\r
166                     logHandle = open( logFilename, mode='wt')\r
167             except:\r
168                 print( "Couldn't open log : %s" % logFilename )\r
169                 logHandle = None\r
170 \r
171         if logHandle:\r
172             if header:\r
173                 if format == 'xml':\r
174                     logHandle.write( "<![CDATA[\n" )\r
175                 logHandle.write( header )\r
176                 if format == 'xml':\r
177                     logHandle.write( "]]>\n" )\r
178             self.writeLog(logHandle)\r
179             logHandle.close()\r
180     # writeLogToDisk\r
181 \r
182     def logLine(self, line):\r
183         "Add a line of text to the log"\r
184         self.log.append( line.rstrip() )\r
185         if self.echo:\r
186             print( "%s" % line.rstrip() )\r
187     # logLine\r
188 \r
189     def execute(self):\r
190         "Execute this process"\r
191         import re\r
192         import datetime\r
193         import traceback\r
194         \r
195         try:\r
196             import subprocess as sp\r
197         except:\r
198             sp = None\r
199 \r
200         self.start = datetime.datetime.now()\r
201 \r
202         cmdargs = [self.cmd]\r
203         cmdargs.extend(self.args)\r
204         \r
205         if self.echo:\r
206             if sp:\r
207                 print( "\n%s : %s\n" % (self.__class__, sp.list2cmdline(cmdargs)) )\r
208             else:\r
209                 print( "\n%s : %s\n" % (self.__class__, " ".join(cmdargs)) )\r
210 \r
211         # intialize a few variables that may or may not be set later\r
212         process = None\r
213         tmpWrapper = None\r
214         stdout = None\r
215         stdin = None\r
216         parentenv = os.environ\r
217         parentcwd = os.getcwd()\r
218 \r
219         try:\r
220             # Using subprocess\r
221             if sp:\r
222                 if self.batchWrapper:\r
223                     cmd = " ".join(cmdargs)\r
224                     tmpWrapper = os.path.join(self.cwd, "process.bat")\r
225                     writeText(cmd, tmpWrapper)\r
226                     print( "%s : Running process through wrapper %s\n" % (self.__class__, tmpWrapper) )\r
227                     process = sp.Popen([tmpWrapper], stdout=sp.PIPE, stderr=sp.STDOUT, \r
228                         cwd=self.cwd, env=self.env)\r
229                 else:\r
230                     process = sp.Popen(cmdargs, stdout=sp.PIPE, stderr=sp.STDOUT, \r
231                         cwd=self.cwd, env=self.env)\r
232 \r
233             # using os.popen4\r
234             else:\r
235                 if self.env:\r
236                     os.environ = self.env\r
237                 if self.cwd:\r
238                     os.chdir( self.cwd )\r
239                 \r
240                 stdin, stdout = os.popen4( cmdargs, 'r')\r
241         except:\r
242             print( "Couldn't execute command : %s" % cmdargs[0] )\r
243             traceback.print_exc()\r
244 \r
245         # Using subprocess\r
246         if sp:\r
247             if process != None:\r
248                 #pid = process.pid\r
249                 #log.logLine( "process id %s\n" % pid )\r
250 \r
251                 try:\r
252                     # This is more proper python, and resolves some issues with a process ending before all\r
253                     #  of its output has been processed, but it also seems to stall when the read buffer\r
254                     #  is near or over it's limit. this happens relatively frequently with processes\r
255                     #  that generate lots of print statements.\r
256                     #\r
257                     for line in process.stdout:\r
258                         self.logLine(line)\r
259                     #\r
260                     # So we go with the, um, uglier  option below\r
261 \r
262                     # This is now used to ensure that the process has finished\r
263                     line = ""\r
264                     while line != None and process.poll() == None:\r
265                         try:\r
266                             line = process.stdout.readline()\r
267                         except:\r
268                             break\r
269                         # 3.1\r
270                         try:\r
271                             self.logLine( str(line, encoding="utf-8") )\r
272                         # 2.6\r
273                         except:\r
274                             self.logLine( line )\r
275                 except:\r
276                     self.logLine( "Logging error : %s" % sys.exc_info()[0] )\r
277 \r
278                 self.status = process.returncode\r
279                 \r
280                 if self.batchWrapper and tmpWrapper:\r
281                     try:\r
282                         os.remove(tmpWrapper)\r
283                     except:\r
284                         print( "Couldn't remove temp wrapper : %s" % tmpWrapper )\r
285                         traceback.print_exc()\r
286 \r
287         # Using os.popen4\r
288         else:\r
289             exitCode = -1\r
290             try:\r
291                 #print( "reading stdout lines" )\r
292                 stdoutLines = stdout.readlines()\r
293                 exitCode = stdout.close()\r
294 \r
295                 stdout.close()\r
296                 stdin.close()\r
297 \r
298                 if self.env:\r
299                     os.environ = parentenv\r
300                 if self.cwd:\r
301                     os.chdir( parentcwd )\r
302                 \r
303                 if len( stdoutLines ) > 0:\r
304                     for line in stdoutLines:\r
305                         self.logLine(line)\r
306 \r
307                 if not exitCode:\r
308                     exitCode = 0\r
309             except:\r
310                 self.logLine( "Logging error : %s" % sys.exc_info()[0] )\r
311 \r
312             self.status = exitCode\r
313             \r
314         self.end = datetime.datetime.now()\r
315     #execute\r
316 # Process\r
317 \r
318 class ProcessList(Process):\r
319     "A list of processes with logged output"\r
320 \r
321     def __init__(self, description, blocking=True, cwd=None, env=None):\r
322         Process.__init__(self, description, None, None, cwd, env)\r
323         "Initialize the standard class variables"\r
324         self.processes = []\r
325         self.blocking = blocking\r
326     # __init__\r
327 \r
328     def generateReport(self, writeDict):\r
329         "Generate a log based on the success of the child processes"\r
330         if self.processes:\r
331             _status = True\r
332             indent = '\t'*(writeDict['indentationLevel']+1)\r
333             \r
334             self.log = []\r
335             \r
336             for child in self.processes:\r
337                 if isinstance(child, ProcessList):\r
338                     child.generateReport(writeDict)\r
339                 \r
340                 childResult = ""\r
341                 key = child.description\r
342                 value = child.status\r
343                 if writeDict['format'] == 'xml':\r
344                     childResult = ( "%s<result description=\"%s\">%s</result>" % (indent, key, value) )\r
345                 else: # writeDict['format'] == 'txt':\r
346                     childResult = ( "%s%40s : %s" % (indent, key, value) )\r
347                 self.log.append( childResult )\r
348                 \r
349                 if child.status != 0:\r
350                     _status = False\r
351             if not _status:\r
352                 self.status = -1\r
353             else:\r
354                 self.status = 0\r
355         else:\r
356             self.log = ["No child processes available to generate a report"]\r
357             self.status = -1\r
358 \r
359     def writeLogHeader(self, writeDict):\r
360         self.writeKey(writeDict, 'processList', None, 'start' )\r
361         writeDict['indentationLevel'] += 1\r
362 \r
363         self.writeKey(writeDict, 'description', self.description )\r
364         self.writeKey(writeDict, 'start', self.start )\r
365         self.writeKey(writeDict, 'end', self.end )\r
366         self.writeKey(writeDict, 'elapsed', self.getElapsedSeconds() )\r
367 \r
368         self.generateReport(writeDict)\r
369 \r
370         self.writeKey(writeDict, 'status', self.status )\r
371     # writeLogHeader\r
372 \r
373     def writeLogFooter(self, writeDict):\r
374         writeDict['indentationLevel'] -= 1\r
375         self.writeKey(writeDict, 'processList', None, 'stop' )\r
376     # writeLogFooter\r
377 \r
378     def writeLog(self, logHandle=sys.stdout, indentationLevel=0,format='xml'):\r
379         "Write logging information to the specified handle"\r
380         \r
381         writeDict = {}\r
382         writeDict['logHandle'] = logHandle\r
383         writeDict['indentationLevel'] = indentationLevel\r
384         writeDict['format'] = format\r
385         \r
386         if logHandle:\r
387             self.writeLogHeader(writeDict)\r
388             \r
389             if self.log:\r
390                 self.writeKey(writeDict, 'output', None, 'start' )\r
391                 for line in self.log:\r
392                     logHandle.write( '%s%s\n' % ("", line) )\r
393                 self.writeKey(writeDict, 'output', None, 'stop' )\r
394 \r
395             if self.processes:\r
396                 self.writeKey(writeDict, 'processes', None, 'start' )\r
397                 for child in self.processes:\r
398                     child.writeLog( logHandle, indentationLevel + 1, format )\r
399                 self.writeKey(writeDict, 'processes', None, 'stop' )\r
400 \r
401             self.writeLogFooter(writeDict)\r
402     # writeLog\r
403 \r
404     def execute(self):\r
405         "Execute this list of processes"\r
406         import datetime\r
407 \r
408         self.start = datetime.datetime.now()\r
409 \r
410         self.status = 0\r
411         if self.processes:\r
412             for child in self.processes:\r
413                 if child:\r
414                     try:\r
415                         child.execute()\r
416                     except:\r
417                         print( "%s : caught exception in child class %s" % (self.__class__, child.__class__) )\r
418                         traceback.print_exc()\r
419                         child.status = -1\r
420 \r
421                     if self.blocking and child.status != 0:\r
422                         print( "%s : child class %s finished with an error" % (self.__class__, child.__class__) )\r
423                         self.status = -1\r
424                         break\r
425 \r
426         self.end = datetime.datetime.now()\r
427     # execute\r
428 # ProcessList\r
429 \r
430 def main():\r
431     import optparse\r
432 \r
433     p = optparse.OptionParser(description='A process logging script',\r
434                                 prog='process',\r
435                                 version='process 0.1',\r
436                                 usage='%prog [options] [options for the logged process]')\r
437     p.add_option('--cmd', '-c', default=None)\r
438     p.add_option('--log', '-l', default=None)\r
439 \r
440     options, arguments = p.parse_args()\r
441 \r
442     #\r
443     # Get options\r
444     # \r
445     cmd = options.cmd\r
446     logFilename = options.log\r
447 \r
448     try:\r
449         argsStart = sys.argv.index('--') + 1\r
450         args = sys.argv[argsStart:]\r
451     except:\r
452         argsStart = len(sys.argv)+1\r
453         args = []\r
454 \r
455     if cmd == None:\r
456         print( "process: No command specified" )\r
457  \r
458     #\r
459     # Test regular logging\r
460     #\r
461     process = Process(description="a process",cmd=cmd, args=args)\r
462 \r
463     #\r
464     # Test report generation and writing a log\r
465     #\r
466     processList = ProcessList("a process list")\r
467     processList.processes.append( process )\r
468     processList.echo = True\r
469     processList.execute()\r
470     \r
471     processList.writeLogToDisk(logFilename)\r
472 # main\r
473 \r
474 if __name__ == '__main__':\r
475     main()\r