ยปCore Development>Code coverage>Tools/pybench/CommandLine.py

Python code coverage for Tools/pybench/CommandLine.py

#countcontent
1n/a""" CommandLine - Get and parse command line options
2n/a
3n/a NOTE: This still is very much work in progress !!!
4n/a
5n/a Different version are likely to be incompatible.
6n/a
7n/a TODO:
8n/a
9n/a * Incorporate the changes made by (see Inbox)
10n/a * Add number range option using srange()
11n/a
12n/a"""
13n/a
14n/afrom __future__ import print_function
15n/a
16n/a__copyright__ = """\
17n/aCopyright (c), 1997-2006, Marc-Andre Lemburg (mal@lemburg.com)
18n/aCopyright (c), 2000-2006, eGenix.com Software GmbH (info@egenix.com)
19n/aSee the documentation for further information on copyrights,
20n/aor contact the author. All Rights Reserved.
21n/a"""
22n/a
23n/a__version__ = '1.2'
24n/a
25n/aimport sys, getopt, glob, os, re, traceback
26n/a
27n/a### Helpers
28n/a
29n/adef _getopt_flags(options):
30n/a
31n/a """ Convert the option list to a getopt flag string and long opt
32n/a list
33n/a
34n/a """
35n/a s = []
36n/a l = []
37n/a for o in options:
38n/a if o.prefix == '-':
39n/a # short option
40n/a s.append(o.name)
41n/a if o.takes_argument:
42n/a s.append(':')
43n/a else:
44n/a # long option
45n/a if o.takes_argument:
46n/a l.append(o.name+'=')
47n/a else:
48n/a l.append(o.name)
49n/a return ''.join(s), l
50n/a
51n/adef invisible_input(prompt='>>> '):
52n/a
53n/a """ Get raw input from a terminal without echoing the characters to
54n/a the terminal, e.g. for password queries.
55n/a
56n/a """
57n/a import getpass
58n/a entry = getpass.getpass(prompt)
59n/a if entry is None:
60n/a raise KeyboardInterrupt
61n/a return entry
62n/a
63n/adef fileopen(name, mode='wb', encoding=None):
64n/a
65n/a """ Open a file using mode.
66n/a
67n/a Default mode is 'wb' meaning to open the file for writing in
68n/a binary mode. If encoding is given, I/O to and from the file is
69n/a transparently encoded using the given encoding.
70n/a
71n/a Files opened for writing are chmod()ed to 0600.
72n/a
73n/a """
74n/a if name == 'stdout':
75n/a return sys.stdout
76n/a elif name == 'stderr':
77n/a return sys.stderr
78n/a elif name == 'stdin':
79n/a return sys.stdin
80n/a else:
81n/a if encoding is not None:
82n/a import codecs
83n/a f = codecs.open(name, mode, encoding)
84n/a else:
85n/a f = open(name, mode)
86n/a if 'w' in mode:
87n/a os.chmod(name, 0o600)
88n/a return f
89n/a
90n/adef option_dict(options):
91n/a
92n/a """ Return a dictionary mapping option names to Option instances.
93n/a """
94n/a d = {}
95n/a for option in options:
96n/a d[option.name] = option
97n/a return d
98n/a
99n/a# Alias
100n/agetpasswd = invisible_input
101n/a
102n/a_integerRE = re.compile(r'\s*(-?\d+)\s*$')
103n/a_integerRangeRE = re.compile(r'\s*(-?\d+)\s*-\s*(-?\d+)\s*$')
104n/a
105n/adef srange(s,
106n/a
107n/a integer=_integerRE,
108n/a integerRange=_integerRangeRE):
109n/a
110n/a """ Converts a textual representation of integer numbers and ranges
111n/a to a Python list.
112n/a
113n/a Supported formats: 2,3,4,2-10,-1 - -3, 5 - -2
114n/a
115n/a Values are appended to the created list in the order specified
116n/a in the string.
117n/a
118n/a """
119n/a l = []
120n/a append = l.append
121n/a for entry in s.split(','):
122n/a m = integer.match(entry)
123n/a if m:
124n/a append(int(m.groups()[0]))
125n/a continue
126n/a m = integerRange.match(entry)
127n/a if m:
128n/a start,end = map(int,m.groups())
129n/a l[len(l):] = range(start,end+1)
130n/a return l
131n/a
132n/adef abspath(path,
133n/a
134n/a expandvars=os.path.expandvars,expanduser=os.path.expanduser,
135n/a join=os.path.join,getcwd=os.getcwd):
136n/a
137n/a """ Return the corresponding absolute path for path.
138n/a
139n/a path is expanded in the usual shell ways before
140n/a joining it with the current working directory.
141n/a
142n/a """
143n/a try:
144n/a path = expandvars(path)
145n/a except AttributeError:
146n/a pass
147n/a try:
148n/a path = expanduser(path)
149n/a except AttributeError:
150n/a pass
151n/a return join(getcwd(), path)
152n/a
153n/a### Option classes
154n/a
155n/aclass Option:
156n/a
157n/a """ Option base class. Takes no argument.
158n/a
159n/a """
160n/a default = None
161n/a helptext = ''
162n/a prefix = '-'
163n/a takes_argument = 0
164n/a has_default = 0
165n/a tab = 15
166n/a
167n/a def __init__(self,name,help=None):
168n/a
169n/a if not name[:1] == '-':
170n/a raise TypeError('option names must start with "-"')
171n/a if name[1:2] == '-':
172n/a self.prefix = '--'
173n/a self.name = name[2:]
174n/a else:
175n/a self.name = name[1:]
176n/a if help:
177n/a self.help = help
178n/a
179n/a def __str__(self):
180n/a
181n/a o = self
182n/a name = o.prefix + o.name
183n/a if o.takes_argument:
184n/a name = name + ' arg'
185n/a if len(name) > self.tab:
186n/a name = name + '\n' + ' ' * (self.tab + 1 + len(o.prefix))
187n/a else:
188n/a name = '%-*s ' % (self.tab, name)
189n/a description = o.help
190n/a if o.has_default:
191n/a description = description + ' (%s)' % o.default
192n/a return '%s %s' % (name, description)
193n/a
194n/aclass ArgumentOption(Option):
195n/a
196n/a """ Option that takes an argument.
197n/a
198n/a An optional default argument can be given.
199n/a
200n/a """
201n/a def __init__(self,name,help=None,default=None):
202n/a
203n/a # Basemethod
204n/a Option.__init__(self,name,help)
205n/a
206n/a if default is not None:
207n/a self.default = default
208n/a self.has_default = 1
209n/a self.takes_argument = 1
210n/a
211n/aclass SwitchOption(Option):
212n/a
213n/a """ Options that can be on or off. Has an optional default value.
214n/a
215n/a """
216n/a def __init__(self,name,help=None,default=None):
217n/a
218n/a # Basemethod
219n/a Option.__init__(self,name,help)
220n/a
221n/a if default is not None:
222n/a self.default = default
223n/a self.has_default = 1
224n/a
225n/a### Application baseclass
226n/a
227n/aclass Application:
228n/a
229n/a """ Command line application interface with builtin argument
230n/a parsing.
231n/a
232n/a """
233n/a # Options the program accepts (Option instances)
234n/a options = []
235n/a
236n/a # Standard settings; these are appended to options in __init__
237n/a preset_options = [SwitchOption('-v',
238n/a 'generate verbose output'),
239n/a SwitchOption('-h',
240n/a 'show this help text'),
241n/a SwitchOption('--help',
242n/a 'show this help text'),
243n/a SwitchOption('--debug',
244n/a 'enable debugging'),
245n/a SwitchOption('--copyright',
246n/a 'show copyright'),
247n/a SwitchOption('--examples',
248n/a 'show examples of usage')]
249n/a
250n/a # The help layout looks like this:
251n/a # [header] - defaults to ''
252n/a #
253n/a # [synopsis] - formatted as '<self.name> %s' % self.synopsis
254n/a #
255n/a # options:
256n/a # [options] - formatted from self.options
257n/a #
258n/a # [version] - formatted as 'Version:\n %s' % self.version, if given
259n/a #
260n/a # [about] - defaults to ''
261n/a #
262n/a # Note: all fields that do not behave as template are formatted
263n/a # using the instances dictionary as substitution namespace,
264n/a # e.g. %(name)s will be replaced by the applications name.
265n/a #
266n/a
267n/a # Header (default to program name)
268n/a header = ''
269n/a
270n/a # Name (defaults to program name)
271n/a name = ''
272n/a
273n/a # Synopsis (%(name)s is replaced by the program name)
274n/a synopsis = '%(name)s [option] files...'
275n/a
276n/a # Version (optional)
277n/a version = ''
278n/a
279n/a # General information printed after the possible options (optional)
280n/a about = ''
281n/a
282n/a # Examples of usage to show when the --examples option is given (optional)
283n/a examples = ''
284n/a
285n/a # Copyright to show
286n/a copyright = __copyright__
287n/a
288n/a # Apply file globbing ?
289n/a globbing = 1
290n/a
291n/a # Generate debug output ?
292n/a debug = 0
293n/a
294n/a # Generate verbose output ?
295n/a verbose = 0
296n/a
297n/a # Internal errors to catch
298n/a InternalError = BaseException
299n/a
300n/a # Instance variables:
301n/a values = None # Dictionary of passed options (or default values)
302n/a # indexed by the options name, e.g. '-h'
303n/a files = None # List of passed filenames
304n/a optionlist = None # List of passed options
305n/a
306n/a def __init__(self,argv=None):
307n/a
308n/a # Setup application specs
309n/a if argv is None:
310n/a argv = sys.argv
311n/a self.filename = os.path.split(argv[0])[1]
312n/a if not self.name:
313n/a self.name = os.path.split(self.filename)[1]
314n/a else:
315n/a self.name = self.name
316n/a if not self.header:
317n/a self.header = self.name
318n/a else:
319n/a self.header = self.header
320n/a
321n/a # Init .arguments list
322n/a self.arguments = argv[1:]
323n/a
324n/a # Setup Option mapping
325n/a self.option_map = option_dict(self.options)
326n/a
327n/a # Append preset options
328n/a for option in self.preset_options:
329n/a if not option.name in self.option_map:
330n/a self.add_option(option)
331n/a
332n/a # Init .files list
333n/a self.files = []
334n/a
335n/a # Start Application
336n/a rc = 0
337n/a try:
338n/a # Process startup
339n/a rc = self.startup()
340n/a if rc is not None:
341n/a raise SystemExit(rc)
342n/a
343n/a # Parse command line
344n/a rc = self.parse()
345n/a if rc is not None:
346n/a raise SystemExit(rc)
347n/a
348n/a # Start application
349n/a rc = self.main()
350n/a if rc is None:
351n/a rc = 0
352n/a
353n/a except SystemExit as rcException:
354n/a rc = rcException
355n/a pass
356n/a
357n/a except KeyboardInterrupt:
358n/a print()
359n/a print('* User Break')
360n/a print()
361n/a rc = 1
362n/a
363n/a except self.InternalError:
364n/a print()
365n/a print('* Internal Error (use --debug to display the traceback)')
366n/a if self.debug:
367n/a print()
368n/a traceback.print_exc(20, sys.stdout)
369n/a elif self.verbose:
370n/a print(' %s: %s' % sys.exc_info()[:2])
371n/a print()
372n/a rc = 1
373n/a
374n/a raise SystemExit(rc)
375n/a
376n/a def add_option(self, option):
377n/a
378n/a """ Add a new Option instance to the Application dynamically.
379n/a
380n/a Note that this has to be done *before* .parse() is being
381n/a executed.
382n/a
383n/a """
384n/a self.options.append(option)
385n/a self.option_map[option.name] = option
386n/a
387n/a def startup(self):
388n/a
389n/a """ Set user defined instance variables.
390n/a
391n/a If this method returns anything other than None, the
392n/a process is terminated with the return value as exit code.
393n/a
394n/a """
395n/a return None
396n/a
397n/a def exit(self, rc=0):
398n/a
399n/a """ Exit the program.
400n/a
401n/a rc is used as exit code and passed back to the calling
402n/a program. It defaults to 0 which usually means: OK.
403n/a
404n/a """
405n/a raise SystemExit(rc)
406n/a
407n/a def parse(self):
408n/a
409n/a """ Parse the command line and fill in self.values and self.files.
410n/a
411n/a After having parsed the options, the remaining command line
412n/a arguments are interpreted as files and passed to .handle_files()
413n/a for processing.
414n/a
415n/a As final step the option handlers are called in the order
416n/a of the options given on the command line.
417n/a
418n/a """
419n/a # Parse arguments
420n/a self.values = values = {}
421n/a for o in self.options:
422n/a if o.has_default:
423n/a values[o.prefix+o.name] = o.default
424n/a else:
425n/a values[o.prefix+o.name] = 0
426n/a flags,lflags = _getopt_flags(self.options)
427n/a try:
428n/a optlist,files = getopt.getopt(self.arguments,flags,lflags)
429n/a if self.globbing:
430n/a l = []
431n/a for f in files:
432n/a gf = glob.glob(f)
433n/a if not gf:
434n/a l.append(f)
435n/a else:
436n/a l[len(l):] = gf
437n/a files = l
438n/a self.optionlist = optlist
439n/a self.files = files + self.files
440n/a except getopt.error as why:
441n/a self.help(why)
442n/a sys.exit(1)
443n/a
444n/a # Call file handler
445n/a rc = self.handle_files(self.files)
446n/a if rc is not None:
447n/a sys.exit(rc)
448n/a
449n/a # Call option handlers
450n/a for optionname, value in optlist:
451n/a
452n/a # Try to convert value to integer
453n/a try:
454n/a value = int(value)
455n/a except ValueError:
456n/a pass
457n/a
458n/a # Find handler and call it (or count the number of option
459n/a # instances on the command line)
460n/a handlername = 'handle' + optionname.replace('-', '_')
461n/a try:
462n/a handler = getattr(self, handlername)
463n/a except AttributeError:
464n/a if value == '':
465n/a # count the number of occurrences
466n/a if optionname in values:
467n/a values[optionname] = values[optionname] + 1
468n/a else:
469n/a values[optionname] = 1
470n/a else:
471n/a values[optionname] = value
472n/a else:
473n/a rc = handler(value)
474n/a if rc is not None:
475n/a raise SystemExit(rc)
476n/a
477n/a # Apply final file check (for backward compatibility)
478n/a rc = self.check_files(self.files)
479n/a if rc is not None:
480n/a sys.exit(rc)
481n/a
482n/a def check_files(self,filelist):
483n/a
484n/a """ Apply some user defined checks on the files given in filelist.
485n/a
486n/a This may modify filelist in place. A typical application
487n/a is checking that at least n files are given.
488n/a
489n/a If this method returns anything other than None, the
490n/a process is terminated with the return value as exit code.
491n/a
492n/a """
493n/a return None
494n/a
495n/a def help(self,note=''):
496n/a
497n/a self.print_header()
498n/a if self.synopsis:
499n/a print('Synopsis:')
500n/a # To remain backward compatible:
501n/a try:
502n/a synopsis = self.synopsis % self.name
503n/a except (NameError, KeyError, TypeError):
504n/a synopsis = self.synopsis % self.__dict__
505n/a print(' ' + synopsis)
506n/a print()
507n/a self.print_options()
508n/a if self.version:
509n/a print('Version:')
510n/a print(' %s' % self.version)
511n/a print()
512n/a if self.about:
513n/a about = self.about % self.__dict__
514n/a print(about.strip())
515n/a print()
516n/a if note:
517n/a print('-'*72)
518n/a print('Note:',note)
519n/a print()
520n/a
521n/a def notice(self,note):
522n/a
523n/a print('-'*72)
524n/a print('Note:',note)
525n/a print('-'*72)
526n/a print()
527n/a
528n/a def print_header(self):
529n/a
530n/a print('-'*72)
531n/a print(self.header % self.__dict__)
532n/a print('-'*72)
533n/a print()
534n/a
535n/a def print_options(self):
536n/a
537n/a options = self.options
538n/a print('Options and default settings:')
539n/a if not options:
540n/a print(' None')
541n/a return
542n/a int = [x for x in options if x.prefix == '--']
543n/a short = [x for x in options if x.prefix == '-']
544n/a items = short + int
545n/a for o in options:
546n/a print(' ',o)
547n/a print()
548n/a
549n/a #
550n/a # Example handlers:
551n/a #
552n/a # If a handler returns anything other than None, processing stops
553n/a # and the return value is passed to sys.exit() as argument.
554n/a #
555n/a
556n/a # File handler
557n/a def handle_files(self,files):
558n/a
559n/a """ This may process the files list in place.
560n/a """
561n/a return None
562n/a
563n/a # Short option handler
564n/a def handle_h(self,arg):
565n/a
566n/a self.help()
567n/a return 0
568n/a
569n/a def handle_v(self, value):
570n/a
571n/a """ Turn on verbose output.
572n/a """
573n/a self.verbose = 1
574n/a
575n/a # Handlers for long options have two underscores in their name
576n/a def handle__help(self,arg):
577n/a
578n/a self.help()
579n/a return 0
580n/a
581n/a def handle__debug(self,arg):
582n/a
583n/a self.debug = 1
584n/a # We don't want to catch internal errors:
585n/a class NoErrorToCatch(Exception): pass
586n/a self.InternalError = NoErrorToCatch
587n/a
588n/a def handle__copyright(self,arg):
589n/a
590n/a self.print_header()
591n/a copyright = self.copyright % self.__dict__
592n/a print(copyright.strip())
593n/a print()
594n/a return 0
595n/a
596n/a def handle__examples(self,arg):
597n/a
598n/a self.print_header()
599n/a if self.examples:
600n/a print('Examples:')
601n/a print()
602n/a examples = self.examples % self.__dict__
603n/a print(examples.strip())
604n/a print()
605n/a else:
606n/a print('No examples available.')
607n/a print()
608n/a return 0
609n/a
610n/a def main(self):
611n/a
612n/a """ Override this method as program entry point.
613n/a
614n/a The return value is passed to sys.exit() as argument. If
615n/a it is None, 0 is assumed (meaning OK). Unhandled
616n/a exceptions are reported with exit status code 1 (see
617n/a __init__ for further details).
618n/a
619n/a """
620n/a return None
621n/a
622n/a# Alias
623n/aCommandLine = Application
624n/a
625n/adef _test():
626n/a
627n/a class MyApplication(Application):
628n/a header = 'Test Application'
629n/a version = __version__
630n/a options = [Option('-v','verbose')]
631n/a
632n/a def handle_v(self,arg):
633n/a print('VERBOSE, Yeah !')
634n/a
635n/a cmd = MyApplication()
636n/a if not cmd.values['-h']:
637n/a cmd.help()
638n/a print('files:',cmd.files)
639n/a print('Bye...')
640n/a
641n/aif __name__ == '__main__':
642n/a _test()