mdanse 13.9 KB
Newer Older
1
#!python
2

3
4
import cPickle
import glob
5
import optparse
6
7
8
9
import os
import subprocess               
import sys
import textwrap
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
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.
    '''
    
eric pellegrini's avatar
eric pellegrini committed
70
71
72
73
74
75
76
77
78
    def __init__(self,*args,**kwargs):
        
        optparse.OptionParser.__init__(self,*args,**kwargs)
        
        from MDANSE import LOGGER
        
        LOGGER.add_handler("terminal", REGISTRY['handler']['terminal'](), level="info", start=True)
        LOGGER.start()
    
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
236
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
    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:"
            for interfaceName in REGISTRY.get_interfaces():
                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])


274
    def save_job(self, option, opt_str, value, parser):
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
        '''
        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.
299
        filename = os.path.abspath('default_%s.py' % name.lower())
300
301
302
303
304
305
306
307
308
309
310
311
312
313
        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)
314

315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
    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        

336
        if nargs != 2:
337
            raise CommandLineParserError("Invalid number of arguments for %r option" % opt_str)
338
339

        shortname,classname = parser.rargs
340
                
341
        IJob.save_template(shortname,classname)
342
                    
343
344
345
346
347
348
349
350
351
352
353
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.')
354
    group.add_option('-r', '--registry', action='callback', callback=parser.query_classes_registry, help='Display the contents of MDANSE classes registry.')
355
356
357
358
359
360
361
362
363
364
365
    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)

366
367
368
369
370
    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")
371
372
373
    
    # The command line is parsed.        
    options, _ = parser.parse_args()