Skip to content

Commit 279fbda

Browse files
committed
improved cli helper
1 parent 439d836 commit 279fbda

5 files changed

Lines changed: 21 additions & 108 deletions

File tree

-236 Bytes
Binary file not shown.
-12.6 KB
Binary file not shown.
-11.6 KB
Binary file not shown.

src/jsondocstore/cli.py

Lines changed: 13 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def _configure_readline() -> None:
3232

3333

3434
class JsonDocStoreShell(cmd.Cmd):
35-
intro = "JsonDocStore interactive shell. Type 'help' for commands."
35+
intro = "JsonDocStore shell. Type 'help' or 'help COMMAND'."
3636
prompt = "jsondocstore> "
3737

3838
def __init__(self, store: JsonDocStore):
@@ -48,68 +48,19 @@ def emptyline(self):
4848
def _print_json(self, value):
4949
print(_json_dump(value))
5050

51-
def _complete_from_options(self, text, options):
52-
return sorted(option for option in options if option.startswith(text))
53-
54-
def _document_keys(self):
55-
root = getattr(self.store, "root", None)
56-
if root is None:
57-
return []
58-
return sorted(
59-
path.stem
60-
for path in Path(root).glob("*.json")
61-
if path.name != "index.json"
62-
)
63-
64-
def _document_fields(self):
65-
fields = set()
66-
root = getattr(self.store, "root", None)
67-
if root is None:
68-
return []
69-
for path in Path(root).glob("*.json"):
70-
if path.name == "index.json":
71-
continue
72-
try:
73-
doc = json.loads(path.read_text(encoding="utf-8"))
74-
fields.update(doc.keys())
75-
except Exception:
76-
continue
77-
return sorted(field for field in fields if isinstance(field, str))
78-
79-
def _query_fields(self):
80-
try:
81-
indexes = self.store.list_indexes()
82-
except Exception:
83-
return []
84-
return indexes or self._document_fields()
85-
86-
def complete_queryby(self, text, line, begidx, endidx):
87-
args = shlex.split(line[:begidx])
88-
if len(args) <= 1:
89-
return self._complete_from_options(text, self._query_fields())
51+
def completedefault(self, text, line, begidx, endidx):
9052
return []
9153

92-
def complete_createindex(self, text, line, begidx, endidx):
93-
indexed = set(self.store.list_indexes())
94-
fields = [field for field in self._document_fields() if field not in indexed]
95-
return self._complete_from_options(text, fields)
96-
97-
def complete_deleteindex(self, text, line, begidx, endidx):
98-
return self._complete_from_options(text, self.store.list_indexes())
99-
100-
def complete_delete(self, text, line, begidx, endidx):
101-
return self._complete_from_options(text, self._document_keys())
102-
10354
def do_list(self, arg):
104-
"""List all documents."""
55+
"""list: print document filenames"""
10556
self._print_json(self.store.list_all())
10657

10758
def do_listindexes(self, arg):
108-
"""List indexed fields."""
59+
"""listindexes: print indexed fields"""
10960
self._print_json(self.store.list_indexes())
11061

11162
def do_queryby(self, arg):
112-
"""queryby FIELD VALUE"""
63+
"""queryby FIELD VALUE: exact-match query on an indexed field"""
11364
try:
11465
field, value = shlex.split(arg)
11566
except ValueError:
@@ -122,7 +73,7 @@ def do_queryby(self, arg):
12273
print(f"Error: {e}")
12374

12475
def do_createindex(self, arg):
125-
"""createindex FIELD"""
76+
"""createindex FIELD: create an index on a top-level field"""
12677
field = arg.strip()
12778
if not field:
12879
print("Usage: createindex FIELD")
@@ -135,7 +86,7 @@ def do_createindex(self, arg):
13586
print(f"Error: {e}")
13687

13788
def do_deleteindex(self, arg):
138-
"""deleteindex FIELD"""
89+
"""deleteindex FIELD: delete an index"""
13990
field = arg.strip()
14091
if not field:
14192
print("Usage: deleteindex FIELD")
@@ -151,7 +102,7 @@ def do_deleteindex(self, arg):
151102
print(f"Error: {e}")
152103

153104
def do_insert(self, arg):
154-
"""insert KEY JSON_DOCUMENT"""
105+
"""insert KEY JSON_DOCUMENT: insert a new document"""
155106
if not arg.strip():
156107
print("Usage: insert KEY JSON_DOCUMENT")
157108
return
@@ -172,7 +123,7 @@ def do_insert(self, arg):
172123
print(f"Error: {e}")
173124

174125
def do_update(self, arg):
175-
"""update KEY JSON_DOCUMENT"""
126+
"""update KEY JSON_DOCUMENT: replace an existing document"""
176127
if not arg.strip():
177128
print("Usage: update KEY JSON_DOCUMENT")
178129
return
@@ -193,10 +144,10 @@ def do_update(self, arg):
193144
print(f"Error: {e}")
194145

195146
def do_delete(self, arg):
196-
"""delete PK"""
147+
"""delete KEY: delete a document by key"""
197148
pk = arg.strip()
198149
if not pk:
199-
print("Usage: delete PK")
150+
print("Usage: delete KEY")
200151
return
201152

202153
try:
@@ -206,11 +157,11 @@ def do_delete(self, arg):
206157
print(f"Error: {e}")
207158

208159
def do_exit(self, arg):
209-
"""Exit the shell."""
160+
"""exit: leave the shell"""
210161
return True
211162

212163
def do_EOF(self, arg):
213-
"""Exit on Ctrl-D."""
164+
"""Ctrl-D: leave the shell"""
214165
print()
215166
return True
216167

test.py

Lines changed: 8 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -256,62 +256,24 @@ def test_cli_main_requires_root_argument(self) -> None:
256256
self.assertEqual(rc, 1)
257257
self.assertIn("Usage: python -m jsondocstore /path/to/db", stderr.getvalue())
258258

259-
def test_shell_completes_document_keys(self) -> None:
260-
_, store = self.make_store(
261-
docs=[
262-
("user-1", {"role": "admin"}),
263-
("user-2", {"role": "user"}),
264-
],
265-
)
266-
shell = cli.JsonDocStoreShell(store)
267-
268-
matches = shell.complete_delete("user-", "delete user-", 7, 12)
269-
270-
self.assertEqual(matches, ["user-1", "user-2"])
271-
272-
def test_shell_completes_index_fields_for_query(self) -> None:
273-
_, store = self.make_store(
274-
index_fields=["role", "region"],
275-
docs=[
276-
("user-1", {"role": "admin", "region": "eu"}),
277-
],
278-
)
259+
def test_shell_completes_command_names(self) -> None:
260+
_, store = self.make_store()
279261
shell = cli.JsonDocStoreShell(store)
280262

281-
matches = shell.complete_queryby("r", "queryby r", 8, 9)
263+
matches = shell.completenames("cr")
282264

283-
self.assertEqual(matches, ["region", "role"])
265+
self.assertEqual(matches, ["createindex"])
284266

285-
def test_shell_completes_unindexed_fields_for_createindex(self) -> None:
267+
def test_shell_does_not_complete_command_arguments(self) -> None:
286268
_, store = self.make_store(
287269
index_fields=["role"],
288-
docs=[
289-
("user-1", {"role": "admin", "region": "eu", "username": "alice"}),
290-
],
270+
docs=[("user-1", {"role": "admin"})],
291271
)
292272
shell = cli.JsonDocStoreShell(store)
293273

294-
matches = shell.complete_createindex("r", "createindex r", 12, 13)
295-
296-
self.assertEqual(matches, ["region"])
297-
298-
def test_shell_completes_deleteindex_from_existing_indexes(self) -> None:
299-
_, store = self.make_store(index_fields=["role", "region"])
300-
shell = cli.JsonDocStoreShell(store)
301-
302-
matches = shell.complete_deleteindex("r", "deleteindex r", 12, 13)
303-
304-
self.assertEqual(matches, ["region", "role"])
305-
306-
def test_shell_query_completion_without_index_uses_document_fields(self) -> None:
307-
root = Path(tempfile.mkdtemp())
308-
(root / "user-1.json").write_text(json.dumps({"role": "admin", "username": "alice"}), encoding="utf-8")
309-
store = JsonDocStore(root, create=True)
310-
shell = cli.JsonDocStoreShell(store)
311-
312-
matches = shell.complete_queryby("r", "queryby r", 8, 9)
274+
matches = shell.completedefault("r", "queryby r", 8, 9)
313275

314-
self.assertEqual(matches, ["role"])
276+
self.assertEqual(matches, [])
315277

316278

317279
class JsonDocStoreShellTests(unittest.TestCase):

0 commit comments

Comments
 (0)