Merge lp:~chipaca/u1db/u1db-client_first_pass_of_get-from-index into lp:u1db

Proposed by John Lenton
Status: Merged
Approved by: John Lenton
Approved revision: 307
Merged at revision: 306
Proposed branch: lp:~chipaca/u1db/u1db-client_first_pass_of_get-from-index
Merge into: lp:u1db
Diff against target: 199 lines (+154/-1)
2 files modified
u1db/commandline/client.py (+63/-1)
u1db/tests/commandline/test_client.py (+91/-0)
To merge this branch: bzr merge lp:~chipaca/u1db/u1db-client_first_pass_of_get-from-index
Reviewer Review Type Date Requested Status
Eric Casteleijn (community) Approve
Review via email: mp+106960@code.launchpad.net

Commit message

get-from-index for u1db-client.

Description of the change

A get-from-index for u1db-client. Missing is splitting the backend's InvalidValueForIndex to differentiate the "bad glob" error and provide better help.

I was going to shelve this and do that, but then realized it wasn't really necessary.

To post a comment you must log in.
Revision history for this message
Eric Casteleijn (thisfred) wrote :

Looks great

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'u1db/commandline/client.py'
2--- u1db/commandline/client.py 2012-05-21 17:32:38 +0000
3+++ u1db/commandline/client.py 2012-05-23 08:32:18 +0000
4@@ -18,6 +18,7 @@
5
6 import argparse
7 import os
8+import simplejson
9 import sys
10
11 from u1db import (
12@@ -322,9 +323,70 @@
13 return
14 return 1
15
16-
17 client_commands.register(CmdGetIndexKeys)
18
19
20+class CmdGetFromIndex(OneDbCmd):
21+ """Find documents by searching an index"""
22+
23+ name = "get-from-index"
24+ argv = None
25+
26+ @classmethod
27+ def _populate_subparser(cls, parser):
28+ parser.add_argument('database', help='The local database to query',
29+ metavar='database-path')
30+ parser.add_argument('index', help='the name of the index')
31+ parser.add_argument('values', metavar="value",
32+ help='the value to look up (one per index column)',
33+ nargs="+")
34+
35+ def run(self, database, index, values):
36+ try:
37+ db = self._open(database, create=False)
38+ docs = db.get_from_index(index, [values])
39+ except errors.DatabaseDoesNotExist:
40+ self.stderr.write("Database does not exist.\n")
41+ except errors.IndexDoesNotExist:
42+ self.stderr.write("Index does not exist.\n")
43+ except errors.InvalidValueForIndex:
44+ index_def = db._get_index_definition(index)
45+ msg = ["Invalid query;"]
46+ len_diff = len(index_def) - len(values)
47+ if len_diff:
48+ msg.append("index %r requires %d query expression%s"
49+ % (index, len(index_def),
50+ "s" if len(index_def) > 1 else ""))
51+ if len(values):
52+ msg[-1] += ", not %d" % len(values)
53+ if len_diff > 0:
54+ msg[-1] += ".\nPerhaps you meant:"
55+ argv = self.argv if self.argv is not None else sys.argv
56+ msg.extend(argv[:2])
57+ msg.append(repr(database))
58+ msg.extend(map(repr, values))
59+ msg.extend(["'*'" for i in range(len_diff)])
60+ else:
61+ # can't happen (HAH)
62+ msg.append("not sure how to help you (read the docs?)")
63+ self.stderr.write(" ".join(msg))
64+ self.stderr.write(".\n")
65+ else:
66+ self.stdout.write("[")
67+ for i, doc in enumerate(docs):
68+ if i:
69+ self.stdout.write(",")
70+ self.stdout.write(simplejson.dumps(dict(
71+ id=doc.doc_id,
72+ rev=doc.rev,
73+ content=simplejson.loads(doc.content)),
74+ indent=4))
75+ self.stdout.write("]\n")
76+ return
77+ return 1
78+
79+client_commands.register(CmdGetFromIndex)
80+
81+
82 def main(args):
83 return client_commands.run_argv(args, sys.stdin, sys.stdout, sys.stderr)
84
85=== modified file 'u1db/tests/commandline/test_client.py'
86--- u1db/tests/commandline/test_client.py 2012-05-21 17:32:38 +0000
87+++ u1db/tests/commandline/test_client.py 2012-05-23 08:32:18 +0000
88@@ -17,6 +17,7 @@
89 import cStringIO
90 import os
91 import sys
92+import simplejson
93 import subprocess
94
95 from u1db import (
96@@ -145,6 +146,13 @@
97 self.assertEqual('db', args.database)
98 self.assertEqual('index', args.index)
99
100+ def test_get_from_index(self):
101+ args = self.parse_args(['get-from-index', 'db', 'index', 'foo'])
102+ self.assertEqual(client.CmdGetFromIndex, args.subcommand)
103+ self.assertEqual('db', args.database)
104+ self.assertEqual('index', args.index)
105+ self.assertEqual(['foo'], args.values)
106+
107
108 class TestCaseWithDB(tests.TestCase):
109 """These next tests are meant to have one class per Command.
110@@ -472,6 +480,89 @@
111 self.assertEqual(cmd.stderr.getvalue(), 'Index does not exist.\n')
112
113
114+class TestCmdGetFromIndex(TestCaseWithDB):
115+
116+ def test_get_from_index(self):
117+ self.db.create_index("index", ["key"])
118+ doc1 = self.db.create_doc(tests.simple_doc)
119+ doc2 = self.db.create_doc(tests.nested_doc)
120+ cmd = self.make_command(client.CmdGetFromIndex)
121+ retval = cmd.run(self.db_path, "index", ["value"])
122+ self.assertEqual(retval, None)
123+ self.assertEqual(sorted(simplejson.loads(cmd.stdout.getvalue())),
124+ sorted([dict(id=doc1.doc_id,
125+ rev=doc1.rev,
126+ content=simplejson.loads(doc1.content)),
127+ dict(id=doc2.doc_id,
128+ rev=doc2.rev,
129+ content=simplejson.loads(doc2.content)),
130+ ]))
131+ self.assertEqual(cmd.stderr.getvalue(), '')
132+
133+ def test_get_from_index_empty(self):
134+ self.db.create_index("index", ["key"])
135+ cmd = self.make_command(client.CmdGetFromIndex)
136+ retval = cmd.run(self.db_path, "index", ["value"])
137+ self.assertEqual(retval, None)
138+ self.assertEqual(cmd.stdout.getvalue(), '[]\n')
139+ self.assertEqual(cmd.stderr.getvalue(), '')
140+
141+ def test_get_from_index_no_db(self):
142+ cmd = self.make_command(client.CmdGetFromIndex)
143+ retval = cmd.run(self.db_path + "__DOES_NOT_EXIST", "foo", [])
144+ self.assertEqual(retval, 1)
145+ self.assertEqual(cmd.stdout.getvalue(), '')
146+ self.assertEqual(cmd.stderr.getvalue(), 'Database does not exist.\n')
147+
148+ def test_get_from_index_no_index(self):
149+ cmd = self.make_command(client.CmdGetFromIndex)
150+ retval = cmd.run(self.db_path, "foo", [])
151+ self.assertEqual(retval, 1)
152+ self.assertEqual(cmd.stdout.getvalue(), '')
153+ self.assertEqual(cmd.stderr.getvalue(), 'Index does not exist.\n')
154+
155+ def test_get_from_index_two_expr_instead_of_one(self):
156+ self.db.create_index("index", ["key1"])
157+ cmd = self.make_command(client.CmdGetFromIndex)
158+ retval = cmd.run(self.db_path, "index", ["value1", "value2"])
159+ self.assertEqual(retval, 1)
160+ self.assertEqual(cmd.stdout.getvalue(), '')
161+ self.assertEqual(cmd.stderr.getvalue(), "Invalid query; index"
162+ " 'index' requires 1 query expression, not 2.\n")
163+
164+ def test_get_from_index_three_expr_instead_of_two(self):
165+ self.db.create_index("index", ["key1", "key2"])
166+ cmd = self.make_command(client.CmdGetFromIndex)
167+ retval = cmd.run(self.db_path, "index", ["value1", "value2", "value3"])
168+ self.assertEqual(retval, 1)
169+ self.assertEqual(cmd.stdout.getvalue(), '')
170+ self.assertEqual(cmd.stderr.getvalue(), "Invalid query; index"
171+ " 'index' requires 2 query expressions, not 3.\n")
172+
173+ def test_get_from_index_one_expr_instead_of_two(self):
174+ self.db.create_index("index", ["key1", "key2"])
175+ cmd = self.make_command(client.CmdGetFromIndex)
176+ cmd.argv = ["XX", "YY"]
177+ retval = cmd.run(self.db_path, "index", ["value1"])
178+ self.assertEqual(retval, 1)
179+ self.assertEqual(cmd.stdout.getvalue(), '')
180+ self.assertEqual(cmd.stderr.getvalue(), "Invalid query; index"
181+ " 'index' requires 2 query expressions, not 1.\n"
182+ "Perhaps you meant: XX YY %r 'value1' '*'.\n"
183+ % self.db_path)
184+
185+ def test_get_from_index_cant_bad_glob(self):
186+ self.db.create_index("index", ["key1", "key2"])
187+ cmd = self.make_command(client.CmdGetFromIndex)
188+ retval = cmd.run(self.db_path, "index", ["*", "a"])
189+ self.assertEqual(retval, 1)
190+ self.assertEqual(cmd.stdout.getvalue(), '')
191+ # temp error until we split exceptions in the backend
192+ # (in a later commit)
193+ self.assertEqual(cmd.stderr.getvalue(), "Invalid query;"
194+ " not sure how to help you (read the docs?).\n")
195+
196+
197 class RunMainHelper(object):
198
199 def run_main(self, args, stdin=None):

Subscribers

People subscribed via source and target branches