mdanse 13.7 KB
Newer Older
eric pellegrini's avatar
eric pellegrini committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
#!python

import cPickle
import glob
import optparse
import os
import subprocess               
import sys
import textwrap

from MDANSE import LOGGER
from MDANSE.Core.Error import Error
from MDANSE import ELEMENTS, PLATFORM, REGISTRY
from MDANSE.Framework.Jobs.JobStatus import JobState

class IndentedHelp(optparse.IndentedHelpFormatter):
    '''This class modify slightly the help formatter of the optparse.OptionParser class.
    
    This allows to take into account the line feed properly.
    
    @note: code taken as it is from an implementation made by Tim Chase
    (http://groups.google.com/group/comp.lang.python/browse_thread/thread/6df6e6b541a15bc2/09f28e26af0699b1)
    '''

    def format_description(self, description):
        if not description: 
            return ""
        desc_width = self.width - self.current_indent
        indent = " "*self.current_indent
        bits = description.split('\n')
        formatted_bits = [textwrap.fill(bit,desc_width,initial_indent=indent,subsequent_indent=indent) for bit in bits]
        result = "\n".join(formatted_bits) + "\n"
        
        return result

    def format_option(self, option):
        
        result = []
        opts = self.option_strings[option]
        opt_width = self.help_position - self.current_indent - 2
        if len(opts) > opt_width:
            opts = "%*s%s\n" % (self.current_indent, "", opts)
            indent_first = self.help_position
        else: # start help on same line as opts
            opts = "%*s%-*s  " % (self.current_indent, "", opt_width, opts)
            indent_first = 0
        result.append(opts)
        if option.help:
            help_text = self.expand_default(option)
            # Everything is the same up through here
            help_lines = []
            for para in help_text.split("\n"):
                help_lines.extend(textwrap.wrap(para, self.help_width))
            # Everything is the same after here
            result.append("%*s%s\n" % (indent_first, "", help_lines[0]))
            result.extend(["%*s%s\n" % (self.help_position, "", line) for line in help_lines[1:]])
        elif opts[-1] != "\n":
            result.append("\n")
        
        return "".join(result)

class CommandLineParserError(Error):
    pass

class CommandLineParser(optparse.OptionParser):
    '''A sublcass of OptionParser.
    
    Creates the MDANSE commad line parser.
    '''
    
    def __init__(self,*args,**kwargs):
        
        optparse.OptionParser.__init__(self,*args,**kwargs)
                
        LOGGER.add_handler("terminal", REGISTRY['handler']['terminal'](), level="info", start=True)
        LOGGER.start()
    
    def add_mmtk_definition(self, option, opt_str, value, parser):
        
        if len(parser.rargs) != 3:
            raise CommandLineParserError("Invalid number of arguments for %r option" % opt_str)
        
        from MDANSE.Framework.MMTKDefinitions import MMTK_DEFINITIONS
        
        MMTK_DEFINITIONS.add(*parser.rargs)
        
        MMTK_DEFINITIONS.save()

    def check_job(self, option, opt_str, value, parser):
        '''Display the jobs list
            
        @param option: the option that triggered the callback.
        @type option: optparse.Option instance
        
        @param opt_str: the option string seen on the command line.
        @type opt_str: str
    
        @param value: the argument for the option.
        @type value: str
    
        @param parser: the MDANSE option parser.
        @type parser: instance of MDANSEOptionParser
        '''

        if len(parser.rargs) != 1:
            raise CommandLineParserError("Invalid number of arguments for %r option" % opt_str)
        
        basename = parser.rargs[0]
        
        filename = os.path.join(PLATFORM.temporary_files_directory(),basename)
        
        if not os.path.exists(filename):
            raise CommandLineParserError("Invalid job name")
            
        # Open the job temporary file
        try:
            f = open(filename, 'rb')
            info = cPickle.load(f)
            f.close()
            
        # If the file could not be opened/unpickled for whatever reason, try at the next checkpoint
        except:
            raise CommandLineParserError("The job %r could not be opened properly." % basename)

        # The job file could be opened and unpickled properly
        else:
            # Check that the unpickled object is a JobStatus object
            if not isinstance(info,JobState):
                raise CommandLineParserError("Invalid contents for job %r." % basename)
            
            print "Information about %s job:" % basename
            for k,v in info.iteritems():            
                print "%-20s [%s]" % (k,v)

    def display_element_info(self, option, opt_str, value, parser):        
    
        if len(parser.rargs) != 1:
            raise CommandLineParserError("Invalid number of arguments for %r option" % opt_str)
        
        element = parser.rargs[0]
                
        try:
            print ELEMENTS.info(element)
        except ValueError:
            raise CommandLineParserError("The entry %r is not registered in the database" % element)
        
    def display_jobs_list(self, option, opt_str, value, parser):
        '''Display the jobs list
            
        @param option: the option that triggered the callback.
        @type option: optparse.Option instance
        
        @param opt_str: the option string seen on the command line.
        @type opt_str: str
    
        @param value: the argument for the option.
        @type value: str
    
        @param parser: the MDANSE option parser.
        @type parser: instance of MDANSEOptionParser
        '''

        if len(parser.rargs) != 0:
            raise CommandLineParserError("Invalid number of arguments for %r option" % opt_str)

        jobs = [f for f in glob.glob(os.path.join(PLATFORM.temporary_files_directory(),'*'))]
                                               
        for j in jobs:

            # Open the job temporary file
            try:
                f = open(j, 'rb')
                info = cPickle.load(f)
                f.close()
                
            # If the file could not be opened/unpickled for whatever reason, try at the next checkpoint
            except:
                continue

            # The job file could be opened and unpickled properly
            else:
                # Check that the unpickled object is a JobStatus object
                if not isinstance(info,JobState):
                    continue
                
                print "%-20s [%s]" % (os.path.basename(j),info["state"])

    def display_trajectory_contents(self, option, opt_str, value, parser):
        '''Displays trajectory contents
            
        @param option: the option that triggered the callback.
        @type option: optparse.Option instance
        
        @param opt_str: the option string seen on the command line.
        @type opt_str: str
    
        @param value: the argument for the option.
        @type value: str
    
        @param parser: the MDANSE option parser.
        @type parser: instance of MDANSEOptionParser
        '''
                
        trajName = parser.rargs[0]
        inputTraj = REGISTRY["input_data"]["mmtk_trajectory"](trajName)
        print inputTraj.info()
       
       
    def error(self, msg):
        '''Called when an error occured in the command line.
        
        @param msg: the error message.
        @type msg: str
        '''
        
        self.print_help(sys.stderr)
        print "\n"
        self.exit(2, "Error: %s\n" % msg)
    

    def query_classes_registry(self, option, opt_str, value, parser):
        '''
        Callback that displays the list of the jobs available in MDANSE
        
        @param option: the Option instance calling the callback.
        
        @param opt_str: the option string seen on the command-line triggering the callback
        
        @param value: the argument to this option seen on the command-line.
        
        @param parser: the MDANSEOptionParser instance.
        '''
            
        if len(parser.rargs) == 0:
            print "Registered interfaces:"
236
            for interfaceName in REGISTRY.interfaces:
eric pellegrini's avatar
eric pellegrini committed
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
                print "\t- %s" % interfaceName
        elif len(parser.rargs) == 1:
            val = parser.rargs[0]                    
            print REGISTRY.info(val.lower())
        else:
            raise CommandLineParserError("Invalid number of arguments for %r option" % opt_str)
            


    def run_job(self, option, opt_str, value, parser):
        '''Run job file(s).
            
        @param option: the option that triggered the callback.
        @type option: optparse.Option instance
        
        @param opt_str: the option string seen on the command line.
        @type opt_str: str
    
        @param value: the argument for the option.
        @type value: str
    
        @param parser: the MDANSE option parser.
        @type parser: instance of MDANSEOptionParser
        '''

        if len(parser.rargs) != 1:
            raise CommandLineParserError("Invalid number of arguments for %r option" % opt_str)

        filename = parser.rargs[0]
        
        if not os.path.exists(filename):
            raise CommandLineParserError("The job file %r could not be executed" % filename)
            
        subprocess.Popen([sys.executable, filename])


    def save_job(self, option, opt_str, value, parser):
        '''
        Save job templates.
            
        @param option: the option that triggered the callback.
        @type option: optparse.Option instance
        
        @param opt_str: the option string seen on the command line.
        @type opt_str: str
    
        @param value: the argument for the option.
        @type value: str
    
        @param parser: the MDANSE option parser.
        @type parser: instance of MDANSEOptionParser
        '''

        if len(parser.rargs) != 1:
            raise CommandLineParserError("Invalid number of arguments for %r option" % opt_str)
        
        jobs = REGISTRY["job"]
        
        name = parser.rargs[0]
                    
        # A name for the template is built.
eric pellegrini's avatar
eric pellegrini committed
298
        filename = os.path.abspath('template_%s.py' % name.lower())
eric pellegrini's avatar
eric pellegrini committed
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
        jobs[name].save(filename)    

        # Try to save the template for the job.  
        try:
            jobs[name].save(filename)    
        # Case where an error occured when writing the template.
        except IOError:
            raise CommandLineParserError("Could not write the job template as %r" % filename)
        # If the job class has no save method, thisis not a valid MDANSE job.
        except KeyError:
            raise CommandLineParserError("The job %r is not a valid MDANSE job" % name)
        # Otherwise, print some information about the saved template.
        else:
            print "Saved template for job %r as %r" % (name, filename)

    def save_job_template(self, option, opt_str, value, parser):
        '''
        Save job templates.
            
        @param option: the option that triggered the callback.
        @type option: optparse.Option instance
        
        @param opt_str: the option string seen on the command line.
        @type opt_str: str
    
        @param value: the argument for the option.
        @type value: str
    
        @param parser: the MDANSE option parser.
        @type parser: instance of MDANSEOptionParser
        '''

        nargs = len(parser.rargs)

        from MDANSE.Framework.Jobs.IJob import IJob        

        if nargs != 2:
336
337
            print "Two arguments required resp. the name and the shortname of the class to be templated"
            return
eric pellegrini's avatar
eric pellegrini committed
338

339
        classname,shortname = parser.rargs
eric pellegrini's avatar
eric pellegrini committed
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377

        try:
            IJob.save_template(shortname,classname)
        except (IOError,KeyError) as e:
            LOGGER(str(e),'error',['console'])
            return
                                    
if __name__ == "__main__":

    from MDANSE.__pkginfo__ import __version__, __date__

    # Creates the option parser.
    parser = CommandLineParser(formatter=IndentedHelp(), version = 'MDANSE %s (%s)' % (__version__, __date__))        

    # Creates a first the group of general options.
    group = optparse.OptionGroup(parser, "General options")
    group.add_option('--add-mmtk-def', action='callback', callback=parser.add_mmtk_definition, help='Add a definition to the MMTK database.')
    group.add_option('-d', '--database', action='callback', callback=parser.display_element_info, help='Display chemical informations about a given element.')
    group.add_option('-r', '--registry', action='callback', callback=parser.query_classes_registry, help='Display the contents of MDANSE classes registry.')
    group.add_option('-t', '--traj', action='callback', callback=parser.display_trajectory_contents, help='Display the chemical contents of a trajectory.')

    # Add the goup to the parser.
    parser.add_option_group(group)
    
    # Creates a second group of job-specific options.
    group = optparse.OptionGroup(parser, "Job managing options")

    # Add the goup to the parser.
    parser.add_option_group(group)

    group.add_option('--jc', action='callback', callback=parser.check_job, help='Check the status of a given job.')
    group.add_option('--jl', action='callback', callback=parser.display_jobs_list, help='Display the jobs list.')
    group.add_option('--jr', action='callback', callback=parser.run_job, help='Run MDANSE job(s).')
    group.add_option('--js', action='callback', callback=parser.save_job, help='Save a job script with default patameters.', metavar = "MDANSE_SCRIPT")
    group.add_option('--jt', action='callback', callback=parser.save_job_template, help='Save a job template.', metavar = "MDANSE_SCRIPT")
    
    # The command line is parsed.        
    options, _ = parser.parse_args()