Mercurial > hg > smartopen
comparison smartopen/smartopen.py @ 14:a62fbff067f8
start bringing this whole thing up to speed
author | Jeff Hammel <jhammel@mozilla.com> |
---|---|
date | Wed, 01 May 2013 06:56:12 -0700 |
parents | 4dd12cf64c0e |
children |
comparison
equal
deleted
inserted
replaced
13:f11ce7b1a349 | 14:a62fbff067f8 |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 | 2 |
3 """ smart open the data passed in """ | 3 """ |
4 open what you give in your browser | |
5 """ | |
4 | 6 |
5 import os | 7 import os |
8 import subprocess | |
6 import sys | 9 import sys |
7 | 10 |
8 from optparse import OptionParser | 11 from optparse import OptionParser |
9 from pkg_resources import iter_entry_points | 12 from pkg_resources import iter_entry_points |
10 from ConfigParser import ConfigParser | 13 from ConfigParser import ConfigParser |
11 | 14 |
12 def locations(names=None, config=None): | 15 |
16 ### method to open a browser | |
17 | |
18 ### It turns out finding the default browser is very hard :( | |
19 # webbrowser is standard...just...what does that mean? for gnome-terminal, | |
20 # I get firefox; however, despite what | |
21 # http://stackoverflow.com/questions/4216985/python-call-to-operating-system-to-open-url | |
22 # says (and also because of it), I actually get `links` from xdg-open(!) | |
23 # (which of course it doesn't even tell me) | |
24 | |
25 # xdg-settings --list | |
26 # Known properties: | |
27 # default-web-browser Default web browser | |
28 # xdg-settings get default-web-browser | |
29 # xdg-settings: unknown desktop environment | |
30 | |
31 # The solution....is here: | |
32 # http://askubuntu.com/questions/96080/how-to-set-google-chrome-as-the-default-browser/96081#96081 | |
33 # and indeed in ~/.local/share/applications/mimeapps.list | |
34 # I do have:: | |
35 | |
36 # text/html=firefox.desktop;thunderbird.desktop; | |
37 | |
38 # in `[Added Associations]` | |
39 # (fwiw, ``pprint(mailcap.getcaps())`` was interesting but not very useful, | |
40 # referencing /usr/bin/sensible-browser which is in fact links (!) | |
41 | |
42 # TODO: | |
43 | |
44 # probably some fancy module should be written that *actually* does what | |
45 # its supposed to here; maybe https://pypi.python.org/pypi/desktop is that | |
46 # fancy pants module; I don't know! pypi doesn't let me browse tarballs! | |
47 # in any case, this is mostly for me and it must be portable; | |
48 | |
49 # So what we'll actually do: | |
50 # (Not surprisingly, explicit wins) | |
51 # - if specified on the command line (which it can't be yet) that wins | |
52 # - use browser/BROWSER if set in config | |
53 # - if not...you're back to guessing | |
54 # - if `which` was stdlib I'd probably guess on that since I know I can't rely | |
55 # on `xdg-open`... | |
56 # - ...and there are other platforms; again, I don't care.... | |
57 # - so we're back to Firefox being #1 | |
58 | |
59 # Which despite my love for Firefox, I'd love to help with autoselection if | |
60 # anyone would throw their hat in | |
61 | |
62 # * let's not forget http://freedesktop.org/wiki/Software/pyxdg if we're | |
63 # talking about non-stdlib solutions for linux only | |
64 | |
65 def open_in_browser(url, browser=None): | |
66 """yeah, that's right...""" | |
67 | |
68 if browser is None: | |
69 browser = 'firefox' # we'll cheat because of course its easy | |
70 | |
71 # control via env variable; might as well keep it set o_O | |
72 os.environ.setdefault('BROWSER', browser) | |
73 # see e.g. http://hg.python.org/cpython/file/2.7/Lib/webbrowser.py | |
74 # sadly these are different:: | |
75 # xdg-open http://k0s.org/ | |
76 # BROWSER=firefox xdg-open http://k0s.org/ | |
77 | |
78 # now invoke the damn thing | |
79 import webbrowser | |
80 return webbrowser.open(url) # XXX lord almighty | |
81 # why bother returning it even? | |
82 | |
83 | |
84 ### methods for inspecting the locations | |
85 | |
86 def locations(names=None, config=None, verbose=False): | |
13 """ | 87 """ |
14 list of 2-tuples of location handlers; | 88 list of 2-tuples of location handlers; |
15 * names: order names of handlers | 89 * names: order names of handlers |
16 * config: nested dictionary of configuration from names | 90 * config: nested dictionary of configuration from names |
17 """ | 91 """ |
18 | 92 |
93 # setup | |
19 _handlers = {} | 94 _handlers = {} |
20 _names = [] | 95 _names = [] |
21 if config is None: | 96 if config is None: |
22 config = {} | 97 config = {} |
23 | 98 |
99 # load the handlers | |
100 # (really, these should come of a dict that is shared with an entry point) | |
24 for i in iter_entry_points('smartopen.locations'): | 101 for i in iter_entry_points('smartopen.locations'): |
25 try: | 102 try: |
26 handler = i.load() | 103 handler = i.load() |
27 except: | 104 except Exception, e: |
28 continue # TODO: warn/debug with --verbose flag | 105 if verbose: |
106 print >> sys.stderr, "Error loading handler:\n%s" % e | |
107 continue | |
29 _handlers[i.name] = handler | 108 _handlers[i.name] = handler |
30 if not names: | 109 if not names: |
31 _names.append(i.name) | 110 _names.append(i.name) |
32 | 111 |
33 if not names: | 112 if not names: |
39 else: | 118 else: |
40 _name = name | 119 _name = name |
41 if _name in _handlers: | 120 if _name in _handlers: |
42 try: | 121 try: |
43 handler = _handlers[_name](**config.get(name, {})) | 122 handler = _handlers[_name](**config.get(name, {})) |
44 except: | 123 except Exception, e: |
124 if verbose: | |
125 print >> sys.stderr, "blah blah blah" | |
45 continue | 126 continue |
46 handlers.append((name, handler)) | 127 handlers.append((name, handler)) |
47 return handlers | 128 return handlers |
48 | 129 |
130 | |
49 def urls(query, handlers=None): | 131 def urls(query, handlers=None): |
132 """returns available urls in order of preference for a query""" | |
133 | |
50 if handlers is None: | 134 if handlers is None: |
51 handlers = locations() | 135 handlers = locations() |
52 urls = [] | 136 urls = [] |
53 for name, handler in handlers: | 137 for name, handler in handlers: |
54 if handler.test(query): | 138 if handler.test(query): |
55 urls.append((name, handler.url(query))) | 139 urls.append((name, handler.url(query))) |
56 return urls | 140 return urls |
57 | 141 |
142 | |
58 def url(query, handlers=None): | 143 def url(query, handlers=None): |
144 """return the top url for a query""" | |
145 | |
59 if handlers is None: | 146 if handlers is None: |
60 handlers = locations() | 147 handlers = locations() |
61 for name, handler in handlers: | 148 for name, handler in handlers: |
62 if handler.test(query): | 149 if handler.test(query): |
63 return handler.url(query) | 150 return handler.url(query) |
64 | 151 |
152 ### command line entry point | |
153 | |
65 def main(args=sys.argv[1:]): | 154 def main(args=sys.argv[1:]): |
66 | 155 |
67 # parse command line optioins | 156 # parse command line optioins |
68 parser = OptionParser() | 157 default_browser = os.environ.get('BROWSER', 'firefox') # ^ see LONG note above |
158 usage = '%prog [options] query' | |
159 parser = OptionParser(usage=usage) | |
160 parser.add_option('-b', '--browser', dest='browser', | |
161 default=None, | |
162 help="browser to use; can also be set in config and BROWSER [default: %default]") | |
69 parser.add_option('-c', '--config', dest="config", | 163 parser.add_option('-c', '--config', dest="config", |
70 help="config file to read") | 164 help="config file to read") |
71 parser.add_option('-u', '--url', dest="url", | 165 parser.add_option('-u', '--url', dest="url", |
72 action='store_true', default=False, | 166 action='store_true', default=False, |
73 help="print the first url handled") | 167 help="print the first url handled") |
74 parser.add_option('-a', '--all', dest="all", | 168 parser.add_option('-a', '--all', dest="all", |
75 action='store_true', default=False, | 169 action='store_true', default=False, |
76 help="print all handlers that match the query") | 170 help="print all handlers that match the query") |
77 parser.add_option('-H', '--handler', dest="handlers", | 171 parser.add_option('-H', '--handler', dest="handlers", |
78 action='append', | 172 action='append', |
79 help="name of the handler to use, in order") | 173 help="name of the handler to use, in order") |
174 parser.add_option('-v', '--verbose', dest='verbose', | |
175 action='store_true', default=True, | |
176 help="be verbose with output") | |
80 parser.add_option('--print-handlers', dest="print_handlers", | 177 parser.add_option('--print-handlers', dest="print_handlers", |
81 action='store_true', | 178 action='store_true', |
82 help="print all handlers in order they would be tried") | 179 help="print all handlers in order they would be tried") |
83 options, args = parser.parse_args(args) | 180 options, args = parser.parse_args(args) |
84 | 181 |
85 # sanity check | 182 # sanity check |
86 assert not (options.url and options.all) | 183 assert not (options.url and options.all) |
87 if not options.handlers: | 184 if not options.handlers: |
88 options.handlers = None | 185 options.handlers = None |
89 | 186 |
90 # config | 187 # read config, if available |
91 config = ConfigParser() | 188 config = ConfigParser() |
92 if not options.config: | 189 if not options.config: |
93 options.config = os.path.join(os.environ.get('HOME', ''), '.smartopen.ini') | 190 options.config = os.path.join(os.environ.get('HOME', ''), '.smartopen.ini') |
94 if os.path.exists(options.config): | 191 if os.path.exists(options.config): |
95 config.read(options.config) | 192 config.read(options.config) |
193 | |
194 # select handlers | |
96 if not options.handlers and config.has_option('DEFAULTS', 'handlers'): | 195 if not options.handlers and config.has_option('DEFAULTS', 'handlers'): |
97 options.handlers = [ i.strip() for i in config.get('DEFAULTS', 'handlers').split(',') ] | 196 options.handlers = [i.strip() for i in config.get('DEFAULTS', 'handlers').split(',')] |
197 | |
198 # select browser | |
199 if not options.browser: | |
200 for key in 'BROWSER', 'browser': | |
201 if config.has_option('DEFAULTS', key): | |
202 options.browser = config.get('DEFAULTS', key) | |
203 break | |
204 | |
205 # the remaining config is arguments to the handlers | |
98 _config = {} | 206 _config = {} |
99 for section in config.sections(): | 207 for section in config.sections(): |
100 _config[section] = dict(config.items(section)) | 208 _config[section] = dict(config.items(section)) |
101 | 209 |
102 # get the handlers | 210 # get the handlers |
103 _locations = locations(options.handlers, _config) | 211 _locations = locations(options.handlers, _config, verbose=options.verbose) |
104 | 212 |
105 # print the handlers | |
106 if options.print_handlers: | 213 if options.print_handlers: |
214 # print the handlers | |
107 for name, loc in _locations: | 215 for name, loc in _locations: |
108 print name | 216 print name |
109 sys.exit(0) | 217 sys.exit(0) |
110 | 218 |
111 # get data to be operated on | 219 # get data to be operated on |
112 if args: | 220 if args: |
113 data = ' '.join(args) | 221 data = ' '.join(args) |
114 else: | 222 else: |
223 # read from stdin | |
115 data = sys.stdin.read() | 224 data = sys.stdin.read() |
116 | 225 |
117 # print the URLs | |
118 if options.all: | 226 if options.all: |
227 # print the URLs | |
119 _urls = urls(data, _locations) | 228 _urls = urls(data, _locations) |
120 for name, _url in _urls: | 229 for name, _url in _urls: |
121 print '%s: %s' % (name, _url) | 230 print '%s: %s' % (name, _url) |
122 sys.exit(0) | 231 sys.exit(0) |
123 | 232 |
233 # select the url | |
124 _url = url(data, _locations) | 234 _url = url(data, _locations) |
125 | 235 |
126 # print a URL | |
127 if options.url: | 236 if options.url: |
128 print _url | 237 # print a URL |
238 print _url or 'No handler found for your query' | |
129 sys.exit(0) | 239 sys.exit(0) |
130 | 240 |
131 # open the URL in a browser | 241 # open the URL in a browser |
132 os.system("firefox '%s'" % _url) | 242 if _url: |
133 sys.exit(0) | 243 open_in_browser(_url) |
134 | 244 else: |
245 parser.error("No handler found") | |
135 | 246 |
136 if __name__ == '__main__': | 247 if __name__ == '__main__': |
137 main() | 248 main() |