Merge lp:~david4dev/dmedia/fix-759195 into lp:dmedia

Proposed by David Green
Status: Merged
Merged at revision: 211
Proposed branch: lp:~david4dev/dmedia/fix-759195
Merge into: lp:dmedia
Diff against target: 367 lines (+235/-123)
1 file modified
dmedia-cli (+235/-123)
To merge this branch: bzr merge lp:~david4dev/dmedia/fix-759195
Reviewer Review Type Date Requested Status
Jason Gerard DeRose Pending
Review via email: mp+68179@code.launchpad.net

Description of the change

Fixed bug #759195.

dmedi-cli now looks like this:

Run a DBus method:

    $ dmedia-cli Version

Run a DBus method using a custom bus:

    $ dmedia-cli bus com.novacut.DMedia Version

Get documentation for a DBus method:

    $ dmedia-cli help Version

To post a comment you must log in.
Revision history for this message
Jason Gerard DeRose (jderose) wrote :

Thanks, David! Definitely an improvement. Sorry it took me a while to get to this... Kickstarter melted my brain!

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'dmedia-cli'
2--- dmedia-cli 2011-06-26 21:35:04 +0000
3+++ dmedia-cli 2011-07-17 17:17:24 +0000
4@@ -27,128 +27,240 @@
5 Command line tool for talking to dmedia DBus services.
6 """
7
8-from __future__ import print_function
9-
10-import argparse
11-import json
12-
13-import dbus
14-
15+from sys import argv, stderr
16 import dmedia
17 from dmedia.constants import BUS, IFACE
18-
19-
20-parser = argparse.ArgumentParser(
21- description='Execute methods on dmedia DBus services',
22-)
23-parser.add_argument('--version', action='version', version=dmedia.__version__)
24-parser.add_argument('--bus',
25- help='DBus bus name; default is %(default)r',
26- default=BUS,
27-)
28-
29-
30-sub = parser.add_subparsers(
31- dest='cmd',
32- title='Methods on {}'.format(BUS)
33-)
34-
35-
36-# DMedia.Version()
37-sub.add_parser('Version',
38- help='version of running `dmedia-service`',
39-)
40-
41-
42-# DMedia.Kill()
43-sub.add_parser('Kill',
44- help='shutdown `dmedia-service`',
45-)
46-
47-
48-# DMedia.AddFileStore()
49-p = sub.add_parser('AddFileStore',
50- help='add a new FileStore',
51-)
52-p.add_argument('parentdir',
53- help="parent directory, like '/home/username'"
54-)
55-def AddFileStore(method, args):
56- print(method(args.parentdir))
57-p.set_defaults(func=AddFileStore)
58-
59-
60-# DMedia.GetDoc()
61-p = sub.add_parser('GetDoc',
62- help='get document from CouchDB by doc_id',
63-)
64-p.add_argument('doc_id',
65- help='the _id of document',
66-)
67-def GetDoc(method, args):
68- print(method(args.doc_id))
69-p.set_defaults(func=GetDoc)
70-
71-
72-# DMedia.GetFile()
73-p = sub.add_parser('GetFile',
74- help='get full path of file by file_id',
75-)
76-p.add_argument('file_id',
77- help='the _id of file',
78-)
79-def GetFile(method, args):
80- print(method(args.file_id))
81-p.set_defaults(func=GetFile)
82-
83-
84-# DMedia.Upload()
85-p = sub.add_parser('Upload',
86- help='upload file to specified remote store',
87-)
88-p.add_argument('file_id',
89- help="ID of file"
90-)
91-p.add_argument('store_id',
92- help="ID of remote store"
93-)
94-def Upload(method, args):
95- print(method(args.file_id, args.store_id))
96-p.set_defaults(func=Upload)
97-
98-
99-# DMedia.Download()
100-p = sub.add_parser('Download',
101- help='download file from specified remote store',
102-)
103-p.add_argument('file_id',
104- help="ID of file"
105-)
106-p.add_argument('store_id',
107- help="ID of remote store"
108-)
109-def Download(method, args):
110- print(method(args.file_id, args.store_id))
111-p.set_defaults(func=Download)
112-
113-
114-# DMedia.ListTransferes()
115-p = sub.add_parser('ListTransfers',
116- help='list active uploads and downloads',
117-)
118-def ListTransfers(method, args):
119- for t in method():
120- print(t)
121-p.set_defaults(func=ListTransfers)
122-
123-
124-
125-def default_func(method, args):
126- print(method())
127-
128-args = parser.parse_args()
129-func = getattr(args, 'func', default_func)
130-conn = dbus.SessionBus()
131-proxy = conn.get_object(args.bus, '/')
132-method = proxy.get_dbus_method(args.cmd, dbus_interface=IFACE)
133-func(method, args)
134+import dbus
135+
136+class DBusCLI (object):
137+ """
138+ DBusCLI allows you to easily create command line interfaces from
139+ DBus APIs.
140+
141+ Example usage:
142+ my_cli = DBusCLI(
143+ 'mycli', '1.0', 'org.freedesktop.My',
144+ '/', 'org.freedesktop.My'
145+ )
146+ my_cli.add_action("name1", function1, "usage of name1", "description of name1")
147+ my_cli.add_dbus_action("MethodName", "usage of MethodName", "description of MethodName")
148+ if __name__ == '__main__':
149+ my_cli.main(*sys.argv[1:])
150+ """
151+ def __init__(self, exec_name, version, dbus_bus, dbus_object, dbus_iface):
152+ """
153+ `exec_name` is the name of the executable of the command line
154+ program. It should be what the user needs to run to use the
155+ program.
156+
157+ `version` is the version string of the command line program.
158+
159+ `dbus_bus` is the dbus bus name of the dbus API.
160+
161+ `dbus_object` is the dbus object of the dbus API.
162+
163+ `dbus_iface` is the dbus interface of the dbus API.
164+ """
165+ self._exec = exec_name
166+ self._version = version
167+ self._default_bus = dbus_bus
168+ self._bus = self._default_bus
169+ self._obj = dbus_object
170+ self._iface = dbus_iface
171+ self._action_methods = {}
172+ self._action_help = {}
173+ self._conn = dbus.SessionBus()
174+ self._add_default_actions()
175+
176+ def has_action(self, name):
177+ """
178+ Return true if the action `name` exists,
179+ false otherwise.
180+ """
181+ return (self._action_methods.has_key(name) and
182+ self._action_help.has_key(name))
183+
184+ def get_action_method(self, name):
185+ """
186+ Return the function for action `name`.
187+ """
188+ return self._action_methods[name]
189+
190+ def get_usage(self):
191+ """
192+ Return the usage string for this program.
193+ """
194+ return """Usage:
195+\t%s ACTION [ARGUMENTS]
196+
197+Run:
198+\t%s help ACTION
199+for help with a specific action.
200+
201+Run:
202+\t%s actions
203+for a list of available actions.
204+""" % (self._exec, self._exec, self._exec)
205+
206+ def get_help(self, *names):
207+ """
208+ Return the help string for action(s) `names`.
209+ If no names are specified, the help string will show
210+ help for all available actions.
211+ """
212+ if len(names) == 0:
213+ names = self.get_actions()
214+ return "\n".join(map(
215+ lambda name: "Action `%s`. Usage:\n\t%s %s %s\n%s\n" % (
216+ (name, self._exec, name) + self._action_help[name]
217+ ),
218+ names
219+ ))
220+
221+ def get_actions(self):
222+ """
223+ Return all available actions as a tuple.
224+ """
225+ return tuple(self._action_methods)
226+
227+ def show_usage(self):
228+ """
229+ Display the usage string for this program.
230+ """
231+ print(self.get_usage())
232+
233+ def show_help(self, *names):
234+ """
235+ Display the help string for `names`. See `get_help`.
236+ """
237+ print(self.get_help(*names))
238+
239+ def show_actions(self):
240+ """
241+ Display all available actions, each on a separate line.
242+ """
243+ print("\n".join(self.get_actions()))
244+
245+ def show_version(self):
246+ """
247+ Display the version of this program.
248+ """
249+ print(self._version)
250+
251+ def dbus_run(self, name, *args):
252+ """
253+ Run the dbus method `name` with arguments `args`.
254+ Catch any errors and print them to stderr.
255+ """
256+ proxy = self._conn.get_object(self._bus, self._obj)
257+ method = proxy.get_dbus_method(name, dbus_interface=self._iface)
258+ try:
259+ print(str(method(*args)))
260+ except Exception, e:
261+ stderr.write("Error: %s\n"%e.message)
262+
263+ def run_action(self, name, *args):
264+ """
265+ Run the action `name` with arguments `args`.
266+ """
267+ if self.has_action(name):
268+ self.get_action_method(name)(*args)
269+ else:
270+ stderr.write("No such action `%s`\n" % name)
271+ print(self.get_usage())
272+
273+ def run_action_with_bus(self, bus, name, *args):
274+ """
275+ Run the action `name` with arguments `args`.
276+ Use a custom dbus bus name `bus`.
277+ """
278+ self._bus = bus
279+ self.run_action(name, *args)
280+
281+ def add_action(self, name, function=lambda *a:None, usage="", description=""):
282+ """
283+ Add an action to the command line program.
284+ `name` is the name of the action.
285+ `function` is the function to be called for this action.
286+ `usage` is a string describing the arguments of the action to the user.
287+ `description` is a string describing what the action does.
288+ """
289+ self._action_methods[name] = function
290+ self._action_help[name] = (usage, description)
291+
292+ def add_dbus_action(self, name, usage="", description=""):
293+ """
294+ Add an action to the command line program based upon a dbus method.
295+ `name` is the name of the action and the name of the dbus method.
296+ `usage` is a string describing the arguments of the action to the user.
297+ `description` is a string describing what the action does.
298+ """
299+ self.add_action(
300+ name,
301+ lambda *args:self.dbus_run(name, *args),
302+ usage,
303+ description
304+ )
305+
306+ def _add_default_actions(self):
307+ #version
308+ self.add_action(
309+ "version",
310+ self.show_version,
311+ "",
312+ "Display the version of this program."
313+ )
314+ #help
315+ self.add_action(
316+ "help",
317+ self.show_help,
318+ "[ACTION_1] [ACTION_2] ... [ACTION_N]",
319+ "Display help information for each of the listed actions. If no actions are listed, help will be displayed for all available actions."
320+ )
321+ #usage
322+ self.add_action(
323+ "usage",
324+ self.show_usage,
325+ "",
326+ "Display a usage message."
327+ )
328+ #actions
329+ self.add_action(
330+ "actions",
331+ self.show_actions,
332+ "",
333+ "Display the list of available actions."
334+ )
335+ #bus
336+ self.add_action(
337+ "bus",
338+ self.run_action_with_bus,
339+ "BUS_NAME ACTION [ARGUMENTS]",
340+ "Run an action using the dbus bus `BUS_NAME` for any dbus interaction."
341+ )
342+
343+ def main(self, *args):
344+ """
345+ Run the command line program with the specified arguments.
346+ """
347+ args = list(args)
348+ if len(args) < 1:
349+ self.run_action("usage")
350+ else:
351+ self.run_action(args.pop(0), *args)
352+
353+dmedia_cli = DBusCLI("dmedia-cli", dmedia.__version__, BUS, '/', IFACE)
354+
355+dmedia_cli.add_dbus_action("Version", "", "Display the version of running `dmedia-service`.")
356+dmedia_cli.add_dbus_action("Kill", "", "Shutdown `dmedia-service`.")
357+dmedia_cli.add_dbus_action("AddFileStore", "PARENT_DIR", "Add a new dmedia file store. Create it in the `PARENT_DIR` directory (eg. /home/username).")
358+dmedia_cli.add_dbus_action("GetDoc", "DOC_ID", "Get a document from CouchDB. `DOC_ID` is the _id of the document.")
359+dmedia_cli.add_dbus_action("Upload", "FILE_ID STORE_ID", "Upload the file with id `FILE_ID` to the remote store with id `STORE_ID`.")
360+dmedia_cli.add_dbus_action("Download", "FILE_ID STORE_ID", "Download the file with id `FILE_ID` from the remote store with id `STORE_ID`.")
361+dmedia_cli.add_dbus_action("ListTransfers", "", "List active uploads and downloads.")
362+dmedia_cli.add_dbus_action("GetEnv", "", "Display dmedia env as JSON.")
363+dmedia_cli.add_dbus_action("GetAuthURL", "", "Get URL with basic auth user and password.")
364+dmedia_cli.add_dbus_action("HasApp", "", "")
365+
366+if __name__ == '__main__':
367+ dmedia_cli.main(*argv[1:])

Subscribers

People subscribed via source and target branches

to all changes: