Check-in [1a510a86e6]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Pass database object to all the commands.
Timelines: family | ancestors | descendants | both | dev
Files: files | file ages | folders
SHA1: 1a510a86e620067e9be65e612d2ad7f6bc44918d
User & Date: mvnathan 2014-09-19 04:57:48.639
Context
2014-09-19
07:28
Generalized Morg so it's no longer task-specific. Rather, it aspires to be a general hierarchical list manager and, by default, its predefined properties set it up to manage shopping lists and TODO lists. check-in: d74ecb0949 user: mvnathan tags: dev
04:57
Pass database object to all the commands. check-in: 1a510a86e6 user: mvnathan tags: dev
04:20
Updated documentation about watermarking. check-in: ea1817df1f user: mvnathan tags: dev
Changes
Unified Diff Ignore Whitespace Patch
Changes to py/morg.py.
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
    try:
        args = morglib.args.parse(sys.argv[1:], str(version()))
        morglib.log.init(args.debug_level, args.log_file)
        logger.info('starting Morg')
        morglib.args.dump(args)

        logger.info('opening database {}'.format(args.tasks_file))
        db = morglib.database(args.tasks_file, morglib.properties.defaults)

        if (args.command):
            morglib.interpreter.execute(args.command[0], args.command)
        else:
            morglib.command.repl(morglib.interpreter)

    except morglib.args.args_error, e:
        sys.stderr.write('{}: {}\n'.
                         format(os.path.basename(sys.argv[0]), e))
        sys.exit(2)

    except morglib.error, e:







|


|

|







86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
    try:
        args = morglib.args.parse(sys.argv[1:], str(version()))
        morglib.log.init(args.debug_level, args.log_file)
        logger.info('starting Morg')
        morglib.args.dump(args)

        logger.info('opening database {}'.format(args.tasks_file))
        db = morglib.db(args.tasks_file, morglib.properties.defaults)

        if (args.command):
            morglib.interpreter.execute(args.command[0], args.command, db)
        else:
            morglib.command.repl(morglib.interpreter, db)

    except morglib.args.args_error, e:
        sys.stderr.write('{}: {}\n'.
                         format(os.path.basename(sys.argv[0]), e))
        sys.exit(2)

    except morglib.error, e:
Changes to py/morglib/__init__.py.
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
        importlib.import_module(full_name)

# Pull in exception base class and alias it so clients can use it simply
# as morglib.error instead of the fully qualified and hideously verbose
# form morglib.morg_error.error_base, which is quite a handful.
from morg_error import error_base as error

# Expose morglib.database.database simply as morglib.database
from database import database

#------------------------------- EXPORTS --------------------------------

__all__ = ['args',
           'command',
           'database',
           'log',







|
|







50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
        importlib.import_module(full_name)

# Pull in exception base class and alias it so clients can use it simply
# as morglib.error instead of the fully qualified and hideously verbose
# form morglib.morg_error.error_base, which is quite a handful.
from morg_error import error_base as error

# Expose morglib.database.database simply as morglib.db
from database import database as db

#------------------------------- EXPORTS --------------------------------

__all__ = ['args',
           'command',
           'database',
           'log',
Changes to py/morglib/command.py.
56
57
58
59
60
61
62




63
64
65
66
67
68
69
70

71
72
73
74
75
76
77

    All Morg commands should derive from this class and implement the
    <tt>__call__</tt> method, which will be passed a list of strings
    containing the command-line used to invoke that command. The first
    member of this list will be the command name itself; the remaining
    elements are the command-line arguments.





    To automate help processing, each command should be able to handle a
    <tt>--help</tt> option.

    '''
    def __call__(self, argv):
        '''Execute command.

        @param argv (string list) Arguments provided via command-line.


        The call method acts as the means to execute commands. In this
        base class, we simply raise an exception to indicate that a
        subclass has failed to implement the desired functionality.

        '''
        raise unimplemented_cmd(argv[0])







>
>
>
>




|



>







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

    All Morg commands should derive from this class and implement the
    <tt>__call__</tt> method, which will be passed a list of strings
    containing the command-line used to invoke that command. The first
    member of this list will be the command name itself; the remaining
    elements are the command-line arguments.

    In addition to the command-line arguments, each command's
    <tt>__call__</tt> method will receive a database object, which it can
    use to search and/or manipulate the tasks database as required.

    To automate help processing, each command should be able to handle a
    <tt>--help</tt> option.

    '''
    def __call__(self, argv, db):
        '''Execute command.

        @param argv (string list) Arguments provided via command-line.
        @param db (morglib.database) Interface object for tasks database.

        The call method acts as the means to execute commands. In this
        base class, we simply raise an exception to indicate that a
        subclass has failed to implement the desired functionality.

        '''
        raise unimplemented_cmd(argv[0])
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
        '''Return an iterator over the keys.'''
        return self._dispatch_table.iterkeys()

    def iteritems(self):
        '''Return an iterator over the key-value pairs.'''
        return self._dispatch_table.iteritems()

    def execute(self, key, args):
        '''Execute specified command.

        @param key (string) Command name.
        @param args (string list) Command-line arguments.


        '''
        try:
            logger.debug('executing command: {}'.format(args))
            f = self[key]
            f(args)
        except KeyError:
            logger.debug('unable to find command {}'.format(key))
            potential_completions = filter(lambda k: k.startswith(key),
                                           self._dispatch_table)
            n = len(potential_completions)
            logger.debug('found {} potential completions'.format(n))
            if (n <= 0):
                raise unknown_cmd(key)
            elif (n == 1):
                k = potential_completions[0]
                f = self[k]
                logger.debug('{} is a unique prefix for {}'.format(key, k))
                args = [k] + args[1:]
                f(args)
            else:
                raise ambiguous_prefix(key, potential_completions)

class unknown_cmd(morg_error.error_base):
    '''For reporting unknown commands.'''
    def __str__(self):
        return '{}: no such command'.format(self.message)







|




>





|













|







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
        '''Return an iterator over the keys.'''
        return self._dispatch_table.iterkeys()

    def iteritems(self):
        '''Return an iterator over the key-value pairs.'''
        return self._dispatch_table.iteritems()

    def execute(self, key, args, db):
        '''Execute specified command.

        @param key (string) Command name.
        @param args (string list) Command-line arguments.
        @param db (morglib.database) Tasks database interface object.

        '''
        try:
            logger.debug('executing command: {}'.format(args))
            f = self[key]
            f(args, db)
        except KeyError:
            logger.debug('unable to find command {}'.format(key))
            potential_completions = filter(lambda k: k.startswith(key),
                                           self._dispatch_table)
            n = len(potential_completions)
            logger.debug('found {} potential completions'.format(n))
            if (n <= 0):
                raise unknown_cmd(key)
            elif (n == 1):
                k = potential_completions[0]
                f = self[k]
                logger.debug('{} is a unique prefix for {}'.format(key, k))
                args = [k] + args[1:]
                f(args, db)
            else:
                raise ambiguous_prefix(key, potential_completions)

class unknown_cmd(morg_error.error_base):
    '''For reporting unknown commands.'''
    def __str__(self):
        return '{}: no such command'.format(self.message)
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
    '''Interactive mode's main loop.

    This class implements a read-eval-print loop (i.e., a REPL) so that
    users can interact with Morg beyond the "one-liner" permitted by the
    invocation via command-line arguments.

    '''
    def __init__(self, interpreter):
        '''Construct repl object.

        @param interpreter (dispatcher) For executing Morg commands.


        When instantiating the <tt>repl</tt> class, you should supply it
        with the <tt>morglib.interpreter</tt> so that this object knows
        how to interpret and execute the commands it receives
        interactively.






        '''
        cmd.Cmd.__init__(self)
        self.prompt = 'morg> '
        self._interpreter = interpreter


        # Helper function object to call the _dispatch_cmd() method,
        # passing it the command name plus the input line.
        class _dispatch:
            def __init__(self, cmd_name, repl_obj):
                self._cmd  = cmd_name
                self._repl = repl_obj







|



>






>
>
>
>
>




>







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
    '''Interactive mode's main loop.

    This class implements a read-eval-print loop (i.e., a REPL) so that
    users can interact with Morg beyond the "one-liner" permitted by the
    invocation via command-line arguments.

    '''
    def __init__(self, interpreter, db):
        '''Construct repl object.

        @param interpreter (dispatcher) For executing Morg commands.
        @param db (morglib.database) To pass to command objects.

        When instantiating the <tt>repl</tt> class, you should supply it
        with the <tt>morglib.interpreter</tt> so that this object knows
        how to interpret and execute the commands it receives
        interactively.

        Additionally, all Morg command objects have to be passed the
        tasks database so they can operate on it as required. Thus, you
        should also pass the database's interface object to this
        constructor.

        '''
        cmd.Cmd.__init__(self)
        self.prompt = 'morg> '
        self._interpreter = interpreter
        self._db = db

        # Helper function object to call the _dispatch_cmd() method,
        # passing it the command name plus the input line.
        class _dispatch:
            def __init__(self, cmd_name, repl_obj):
                self._cmd  = cmd_name
                self._repl = repl_obj
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
        simply splits the input line using shell-like syntax and then
        uses the morglib interpreter to execute the command.

        '''
        try:
            args = shlex.split(line)
            if (args):
                self._interpreter.execute(args[0], args)

        except morg_error.error_base, e:
            logger.error(e)
            sys.stderr.write('{}\n'.format(e))

    def default(self, line):
        '''How to deal with unrecognized input.







|







258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
        simply splits the input line using shell-like syntax and then
        uses the morglib interpreter to execute the command.

        '''
        try:
            args = shlex.split(line)
            if (args):
                self._interpreter.execute(args[0], args, self._db)

        except morg_error.error_base, e:
            logger.error(e)
            sys.stderr.write('{}\n'.format(e))

    def default(self, line):
        '''How to deal with unrecognized input.
Changes to py/morglib/exit.py.
36
37
38
39
40
41
42
43
44
45
46

47
48
49
50
51
52
53
# Standard library
import sys

#---------------------------- EXIT COMMANDS -----------------------------

class exit(command.base):
    '''Command to exit Morg.'''
    def __call__(self, argv):
        '''Execute exit command.

        @param argv (string list) Exit command-line.


        '''
        if (len(argv) > 1 and argv[1] in ['-h', '--help']):
            print(_help())
        else:
            sys.exit(0)








|



>







36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# Standard library
import sys

#---------------------------- EXIT COMMANDS -----------------------------

class exit(command.base):
    '''Command to exit Morg.'''
    def __call__(self, argv, db):
        '''Execute exit command.

        @param argv (string list) Exit command-line.
        @param db (morglib.database) Tasks database.

        '''
        if (len(argv) > 1 and argv[1] in ['-h', '--help']):
            print(_help())
        else:
            sys.exit(0)

Changes to py/morglib/help.py.
44
45
46
47
48
49
50
51
52
53
54

55
56
57
58
59
60
61
#---------------------------- HELP COMMAND ------------------------------

class help(command.base):
    '''Command for printing help about other commands.'''
    def __init__(self):
        self._help = None

    def __call__(self, argv):
        '''Execute help command.

        @param argv (string list) Help command-line.


        Without any parameters, prints the list of available commands and
        some general comments.

        With a parameter, executes the specified command, passing it the
        --help option. Thus, all commands should implement --help to be
        able to print something useful when the user asks for help.







|



>







44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#---------------------------- HELP COMMAND ------------------------------

class help(command.base):
    '''Command for printing help about other commands.'''
    def __init__(self):
        self._help = None

    def __call__(self, argv, db):
        '''Execute help command.

        @param argv (string list) Help command-line.
        @param db (morglib.database) Tasks database.

        Without any parameters, prints the list of available commands and
        some general comments.

        With a parameter, executes the specified command, passing it the
        --help option. Thus, all commands should implement --help to be
        able to print something useful when the user asks for help.
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
            if (kmd.startswith('-')): # ignore any command-line options
                kmd = 'help'
        if (kmd is 'help'):
            if (self._help is None):
                self._help = _cache_help_text()
            print(self._help)
        else:
            morglib.interpreter.execute(kmd, [kmd, '--help'])


def _cache_help_text():
    prolog = 'The following commands are available:'

    commands = []
    for k, c in morglib.interpreter.iteritems():







|







74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
            if (kmd.startswith('-')): # ignore any command-line options
                kmd = 'help'
        if (kmd is 'help'):
            if (self._help is None):
                self._help = _cache_help_text()
            print(self._help)
        else:
            morglib.interpreter.execute(kmd, [kmd, '--help'], db)


def _cache_help_text():
    prolog = 'The following commands are available:'

    commands = []
    for k, c in morglib.interpreter.iteritems():
Changes to wiki/todo.wiki.
19
20
21
22
23
24
25

26
27
28
29
30
  *  Create <tt>property_NNN</tt> tables on database init.
  *  Implement sanity check on database initialization.
  *  Update database constructor doc string.
  *  Use the BETWEEN operator in property range constraints.
  *  Implement a watermark class to ease verification and "Morgification."
  *  The watermark table should be created and populated in a transaction.
  *  Update doc strings to reflect recent changes about the watermark.


<h2>PENDING</h2>

  *  Pass database to all commands' <tt>__call__</tt> method.
  *  Implement <tt>new</tt> command.







>



<

19
20
21
22
23
24
25
26
27
28
29

30
  *  Create <tt>property_NNN</tt> tables on database init.
  *  Implement sanity check on database initialization.
  *  Update database constructor doc string.
  *  Use the BETWEEN operator in property range constraints.
  *  Implement a watermark class to ease verification and "Morgification."
  *  The watermark table should be created and populated in a transaction.
  *  Update doc strings to reflect recent changes about the watermark.
  *  Pass database to all commands' <tt>__call__</tt> method.

<h2>PENDING</h2>


  *  Implement <tt>new</tt> command.