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