~jaap.karssenberg/zim/pyzim-gtk3

« back to all changes in this revision

Viewing changes to zim/command.py

  • Committer: Jaap Karssenberg
  • Date: 2014-03-08 11:47:43 UTC
  • mfrom: (668.1.49 pyzim-refactor)
  • Revision ID: jaap.karssenberg@gmail.com-20140308114743-fero6uvy9zirbb4o
Merge branch with refactoring

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
 
 
3
# Copyright 2013 Jaap Karssenberg <jaap.karssenberg@gmail.com>
 
4
 
 
5
 
 
6
from getopt import gnu_getopt, GetoptError
 
7
 
 
8
import logging
 
9
 
 
10
from zim import __version__
 
11
from zim.errors import Error
 
12
 
 
13
 
 
14
class UsageError(Error):
 
15
        '''Error raised when commands do not have correct
 
16
        number or type of arguments
 
17
        '''
 
18
        pass
 
19
 
 
20
 
 
21
class Command(object):
 
22
        '''Base class for commandline commands, used by zim to abstract
 
23
        part of the C{main()} functionality and allow better testability
 
24
        of commandline arguments.
 
25
 
 
26
        Sub-classes can define the options and arguments that they require.
 
27
        Then only the C{run()} method needs to be defined to implement the
 
28
        actual command. In the C{run()} method C{self.opts} and C{self.args}
 
29
        can be accessed to get the commandline options (dict) and the
 
30
        commandline arguments (list) respectively.
 
31
        '''
 
32
 
 
33
        arguments = () #: Define arguments, e.g ('NOTEBOOK', '[PAGE]')
 
34
 
 
35
        options = () #: Define options by 3-tuple of long, short & description.
 
36
                #: E.g. ("foo=", "f", "set parameter for foo")
 
37
                #: For options that can appear multiple times,
 
38
                #: assign a list "[]" in "self.opts" before parse_options is called
 
39
 
 
40
        default_options  = (
 
41
                ('verbose', 'V', 'Verbose output'),
 
42
                ('debug', 'D', 'Debug output'),
 
43
        )
 
44
 
 
45
        use_gtk = False #: Flag whether this command uses a graphical interface
 
46
 
 
47
        def __init__(self, command, *args, **opts):
 
48
                '''Constructor
 
49
                @param command: the command switch (first commandline argument)
 
50
                @param args: positional commandline arguments
 
51
                @param opts: command options
 
52
                '''
 
53
                self.command = command
 
54
                self.args = list(args)
 
55
                self.opts = opts
 
56
 
 
57
        def parse_options(self, *args):
 
58
                '''Parse commandline options for this command
 
59
                Sets the attributes 'args' and 'opts' to a list of arguments
 
60
                and a dictionary of options respectively
 
61
                @param args: all remaining options to be parsed
 
62
                @raises GetOptError: when options are not correct
 
63
                '''
 
64
                options = ''
 
65
                long_options = []
 
66
                options_map = {}
 
67
                for l, s, desc in self.default_options + self.options:
 
68
                        long_options.append(l)
 
69
                        if s and l.endswith('='):
 
70
                                options += s + ':'
 
71
                                options_map[s] = l.strip('=')
 
72
                        elif s:
 
73
                                options += s
 
74
                                options_map[s] = l
 
75
 
 
76
                optlist, args = gnu_getopt(args, options, long_options)
 
77
                self.args += args
 
78
 
 
79
                for o, a in optlist:
 
80
                        key = o.strip('-')
 
81
                        key = options_map.get(key, key)
 
82
                        if a == '':
 
83
                                self.opts[key] = True
 
84
                        elif key in self.opts and isinstance(self.opts[key], list):
 
85
                                self.opts[key].append(a)
 
86
                        else:
 
87
                                self.opts[key] = a
 
88
 
 
89
        def get_options(self, *names):
 
90
                '''Retrieve a dict with a sub-set of the command options
 
91
                @param names: that options in the subset
 
92
                '''
 
93
                return dict((k, self.opts.get(k)) for k in names)
 
94
 
 
95
        def get_arguments(self):
 
96
                '''Get the arguments, to be used by the implementation of C{run()}
 
97
                @raises UsageError: when arguments are not correct
 
98
                @returns: tuple of arguments, padded with None to correct length
 
99
                '''
 
100
                minimum = len([a for a in self.arguments if not a.startswith('[')])
 
101
                if len(self.args) < minimum:
 
102
                        raise UsageError, 'Command %s takes %i arguments' % (self.command, minimum)
 
103
                elif len(self.args) > len(self.arguments):
 
104
                        raise UsageError, 'Command %s takes only %i arguments' % (self.command, len(self.args))
 
105
                else:
 
106
                        return tuple(self.args) \
 
107
                                + (None,) * (len(self.arguments) - len(self.args))
 
108
 
 
109
        def set_logging(self):
 
110
                '''Configure the logging module for output based on the
 
111
                default options -V and -D
 
112
                '''
 
113
                if self.opts.get('debug'):
 
114
                        level = logging.DEBUG
 
115
                elif self.opts.get('verbose'):
 
116
                        level = logging.INFO
 
117
                else:
 
118
                        level = logging.WARN
 
119
 
 
120
                root = logging.getLogger() # root
 
121
                root.setLevel(level)
 
122
 
 
123
                logger = logging.getLogger('zim')
 
124
                logger.info('This is zim %s', __version__)
 
125
                if level == logging.DEBUG:
 
126
                        import sys
 
127
                        import os
 
128
                        import zim.config
 
129
 
 
130
                        logger.debug('Python version is %s', str(sys.version_info))
 
131
                        logger.debug('Platform is %s', os.name)
 
132
                        logger.debug(zim.get_zim_revision())
 
133
                        zim.config.log_basedirs()
 
134
 
 
135
 
 
136
        def run(self):
 
137
                '''Run the command
 
138
                @raises UsageError: when arguments are not correct
 
139
                @implementation: must be implemented by subclasses
 
140
                '''
 
141
                raise NotImplementedError