Check-in [acba4497fc]
Not logged in

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

Overview
Comment:Since completions logic was being applied in several different places, refactored it into a separate function, which makes the call sites much more compact and readable.
Timelines: family | ancestors | descendants | both | dev
Files: files | file ages | folders
SHA1:acba4497fc1e85745dd14b5fb2590ddda97a9e40
User & Date: mvnathan 2014-09-20 21:48:28
Context
2014-09-20
23:26
Added a couple of FAQ's about using Morg. check-in: 0dc70df2e7 user: mvnathan tags: dev
21:48
Since completions logic was being applied in several different places, refactored it into a separate function, which makes the call sites much more compact and readable. check-in: acba4497fc user: mvnathan tags: dev
09:42
Implemented the new command. check-in: 1bafafa24d user: mvnathan tags: dev
Changes

Changes to py/morglib/command.py.

27
28
29
30
31
32
33

34
35
36
37
38
39
40
...
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
# You should have received a copy of the GNU General Public License
# along with Morg. If not, see <http://www.gnu.org/licenses/>.
#

#------------------------------- IMPORTS --------------------------------

# Morg

import morg_error

# Standard library
import cmd
import readline
import shlex
import logging
................................................................................

        '''
        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)


class ambiguous_prefix(morg_error.error_base):
    '''For reporting ambiguous command prefixes.'''
    def __init__(self, prefix, completions):
        msg = ('ambiguous prefix: {}; can complete: {}'.
               format(prefix, ', '.join(completions)))
        morg_error.error_base.__init__(self, msg)

#-------------------------------- REPL ----------------------------------

class repl(cmd.Cmd):
    '''Interactive mode's main loop.








>







 







|
|
|
|
|
|
|
|
|
|
|
<
<
<
<



|
|
<
>
|
<
<
<
<







27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
...
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
# You should have received a copy of the GNU General Public License
# along with Morg. If not, see <http://www.gnu.org/licenses/>.
#

#------------------------------- IMPORTS --------------------------------

# Morg
import morg_util
import morg_error

# Standard library
import cmd
import readline
import shlex
import logging
................................................................................

        '''
        try:
            logger.debug('executing command: {}'.format(args))
            f = self[key]
            f(args, db)
        except KeyError:
            try:
                logger.debug('unable to find command {}; trying completions'.
                             format(key))
                k = morg_util.complete(key, self._dispatch_table)
                logger.debug('{} expands to {}'.format(key, k))
                f = self[k]
                args =  [k] + args[1:]
                f(args, db)
            except morg_util. completions_error, e:
                logger.debug('completions failed: {}'.format(e))
                raise unknown_cmd(key, e)





class unknown_cmd(morg_error.error_base):
    '''For reporting unknown commands.'''
    def __init__(self, kmd, reason = None):
        msg = '{}: no such command'.format(kmd)

        if (reason):
            msg = '{} ({})'.format(msg, reason)




        morg_error.error_base.__init__(self, msg)

#-------------------------------- REPL ----------------------------------

class repl(cmd.Cmd):
    '''Interactive mode's main loop.

Changes to py/morglib/database.py.

27
28
29
30
31
32
33

34
35
36
37
38
39
40
...
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
# You should have received a copy of the GNU General Public License
# along with Morg. If not, see <http://www.gnu.org/licenses/>.
#

#------------------------------- IMPORTS --------------------------------

# morglib

import morg_error

# SQLite wrapper
import apsw

# Standard library
import socket
................................................................................
        except Exception, e:
            msg = ('unable to convert property value {} to float '.format(v) +
                   '(exception: {})'.format(e))
            raise property_error(n, t, r, msg)

    # Property type must be text
    if (r is not None):
        logger.debug('looking for value matching "{}"'.format(v))
        completions = filter(lambda s: s.startswith(v), r.split(','))
        n = len(completions)
        if (n < 1):
            raise property_error(n, t, r,
                                 'no property value matching "{}"'.format(v))
        if (n > 1):
            msg = ('ambiguous property value prefix "{}"; can complete {}'.
                   format(v, ', '.join(completions)))
            raise property_error(n, t, r, msg)
        logger.debug('found property value "{}" matching "{}"'.
                     format(completions[0], v))
        return completions[0]
    return v

#------------------------------------------------------------------------

##############################################
# Editor config:                             #
##############################################







>







 







|
|
|
|
|
|
|
|
<
<
<
<
<







27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
...
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896





897
898
899
900
901
902
903
# You should have received a copy of the GNU General Public License
# along with Morg. If not, see <http://www.gnu.org/licenses/>.
#

#------------------------------- IMPORTS --------------------------------

# morglib
import morg_util
import morg_error

# SQLite wrapper
import apsw

# Standard library
import socket
................................................................................
        except Exception, e:
            msg = ('unable to convert property value {} to float '.format(v) +
                   '(exception: {})'.format(e))
            raise property_error(n, t, r, msg)

    # Property type must be text
    if (r is not None):
        try:
            logger.debug('looking for value matching "{}"'.format(v))
            val = morg_util.complete(v, r.split(','))
            logger.debug('found property value "{}" matching "{}"'.
                         format(val, v))
            return val
        except morg_util.completions_error, e:
            raise property_error(n, t, r, e)





    return v

#------------------------------------------------------------------------

##############################################
# Editor config:                             #
##############################################

Added py/morglib/morg_util.py.





































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
#
# @file  morg_util.py
# @brief Various helper classes and functions.
#

#
# Copyright (C) 2014 The Morg Project Developers
#
# See wiki/copyright.wiki in the top-level directory of the Morg source
# distribution for the full list of authors who have contributed to this
# project.
#

#
# This file is part of Morg.
#
# Morg is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# Morg is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with Morg. If not, see <http://www.gnu.org/licenses/>.
#

#------------------------------- IMPORTS --------------------------------

# Morg
import morg_error

#------------------------- PREFIX COMPLETIONS ---------------------------

class completions_error(morg_error.error_base):
    '''For reporting ambiguous prefixes or those that don't match.'''
    pass

class unmatched_prefix(completions_error):
    '''When a prefix doesn't match any of the candidates.'''
    def __init__(self, prefix, candidates):
        msg = ('prefix "{}" does not match any of ({})'.
               format(prefix, ', '.join(candidates)))
        completions_error.__init__(self, msg)

class ambiguous_prefix(completions_error):
    '''When a prefix matches too many candidates.'''
    def __init__(self, prefix, candidates):
        msg = ('ambiguous prefix "{}"; can complete ({})'.
               format(prefix, ', '.join(candidates)))
        completions_error.__init__(self, msg)

def complete(abbrev, candidates):
    '''Expand abbreviation given list of candidates.

    @param  abbrev (str) Prefix to be expanded.
    @param  candidates (str iterable) Sequence of possible prefix completions.
    @return Abbreviation expanded to one of the candidates or exception.

    This function searches the list of candidate strings to see if any of
    them are a unique match for the supplied abbreviation. If so, it will
    return the matching candidate string. If there are no matches, it
    will raise an <tt>unmatched_prefix</tt> exception. If there are too
    many possible completions, it will raise an <tt>ambiguous_prefix</tt>
    exception.

    Here are some examples of how this function works:

    @verbatim
        complete('f', ('foo', 'bar', 'baz')) ==> 'foo'
        complete('x', ('foo', 'bar', 'baz')) ==> unmatched_prefix
        complete('b', ('foo', 'bar', 'baz')) ==> ambiguous_prefix
    @endverbatim

    '''
    completions = filter(lambda s: s.startswith(abbrev), candidates)
    if (len(completions) < 1):
        raise unmatched_prefix(abbrev, candidates)
    if (len(completions) > 1):
        raise ambiguous_prefix(abbrev, completions)
    return completions[0]

#------------------------------------------------------------------------

##############################################
# Editor config:                             #
##############################################
# Local Variables:                           #
# indent-tabs-mode: nil                      #
# py-indent-offset: 4                        #
# python-indent: 4                           #
# End:                                       #
##############################################
# vim: set expandtab shiftwidth=4 tabstop=4: #
##############################################

Changes to py/morglib/new.py.

29
30
31
32
33
34
35

36
37
38
39
40
41
42
...
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
#

#------------------------------- IMPORTS --------------------------------

# Morg
import command
import args


# Standard library
import logging
import argparse

#---------------------------- MODULE LOGGER -----------------------------

................................................................................
Any other syntax for property ranges will result in an error.
'''
    return help_text

# Allow property types to be abbreviated and ensure they're restricted to
# the ones that Morg supports.
def _validate_property_type(optval):
    completions = filter(lambda s: s.startswith(optval),
                         ('integer', 'real', 'text'))
    n = len(completions)
    if (n < 1):
        raise args.args_error('{}: unknown property type'.format(optval))
    if (n > 1):
        msg = ('{}: ambiguous property type; can be: {}'.
               format(optval, ', '.join(completions)))
        raise args.args_error(msg)
    return completions[0]

#------------------------------------------------------------------------

##############################################
# Editor config:                             #
##############################################
# Local Variables:                           #







>







 







|
|
|
<
|
<
<
|
<
<







29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
...
229
230
231
232
233
234
235
236
237
238

239


240


241
242
243
244
245
246
247
#

#------------------------------- IMPORTS --------------------------------

# Morg
import command
import args
import morg_util

# Standard library
import logging
import argparse

#---------------------------- MODULE LOGGER -----------------------------

................................................................................
Any other syntax for property ranges will result in an error.
'''
    return help_text

# Allow property types to be abbreviated and ensure they're restricted to
# the ones that Morg supports.
def _validate_property_type(optval):
    try:
        return morg_util.complete(optval, ('integer', 'real', 'text'))
    except morg_util.completions_error, e:

        raise args.args_error('unknown property type {} ({})'.


                              format(optval, e))



#------------------------------------------------------------------------

##############################################
# Editor config:                             #
##############################################
# Local Variables:                           #

Changes to wiki/todo.wiki.

25
26
27
28
29
30
31

32
33
34
35
36
  *  Update doc strings to reflect recent changes about the watermark.
  *  Pass database to all commands' <tt>__call__</tt> method.
  *  Generalize Morg so it isn't necessarily task-centric.
  *  Implement <tt>morglib.database.add_item()</tt>.
  *  Implement <tt>new</tt> command-line processing.
  *  Hook up <tt>new</tt> command to <tt>add_item()</tt>.
  *  Hook up <tt>new</tt> command to <tt>add_property()</tt>.


<h2>PENDING</h2>

  *  Refactor completions code and exception into utils module.
  *  Add some user documentation.







>



<

25
26
27
28
29
30
31
32
33
34
35

36
  *  Update doc strings to reflect recent changes about the watermark.
  *  Pass database to all commands' <tt>__call__</tt> method.
  *  Generalize Morg so it isn't necessarily task-centric.
  *  Implement <tt>morglib.database.add_item()</tt>.
  *  Implement <tt>new</tt> command-line processing.
  *  Hook up <tt>new</tt> command to <tt>add_item()</tt>.
  *  Hook up <tt>new</tt> command to <tt>add_property()</tt>.
  *  Refactor completions code and exception into utils module.

<h2>PENDING</h2>


  *  Add some user documentation.