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