diff 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
line wrap: on
line diff
--- a/smartopen/smartopen.py	Thu Apr 04 17:13:18 2013 -0700
+++ b/smartopen/smartopen.py	Wed May 01 06:56:12 2013 -0700
@@ -1,31 +1,110 @@
 #!/usr/bin/env python
 
-""" smart open the data passed in """
+"""
+open what you give in your browser
+"""
 
 import os
+import subprocess
 import sys
 
 from optparse import OptionParser
 from pkg_resources import iter_entry_points
 from ConfigParser import ConfigParser
 
-def locations(names=None, config=None):
+
+### method to open a browser
+
+### It turns out finding the default browser is very hard :(
+# webbrowser is standard...just...what does that mean?  for gnome-terminal,
+# I get firefox;  however, despite what
+# http://stackoverflow.com/questions/4216985/python-call-to-operating-system-to-open-url
+# says (and also because of it), I actually get `links` from xdg-open(!)
+# (which of course it doesn't even tell me)
+
+# xdg-settings --list
+# Known properties:
+#   default-web-browser           Default web browser
+# xdg-settings get default-web-browser
+# xdg-settings: unknown desktop environment
+
+# The solution....is here:
+# http://askubuntu.com/questions/96080/how-to-set-google-chrome-as-the-default-browser/96081#96081
+# and indeed in ~/.local/share/applications/mimeapps.list
+# I do have::
+
+#  text/html=firefox.desktop;thunderbird.desktop;
+
+# in `[Added Associations]`
+# (fwiw, ``pprint(mailcap.getcaps())`` was interesting but not very useful, 
+# referencing /usr/bin/sensible-browser which is in fact links (!)
+
+# TODO:
+
+# probably some fancy module should be written that *actually* does what
+# its supposed to here; maybe https://pypi.python.org/pypi/desktop is that
+# fancy pants module; I don't know! pypi doesn't let me browse tarballs!
+# in any case, this is mostly for me and it must be portable;
+
+# So what we'll actually do:
+# (Not surprisingly, explicit wins)
+# - if specified on the command line (which it can't be yet) that wins
+# - use browser/BROWSER if set in config
+# - if not...you're back to guessing
+# - if `which` was stdlib I'd probably guess on that since I know I can't rely
+#   on `xdg-open`...
+# - ...and there are other platforms; again, I don't care....
+# - so we're back to Firefox being #1
+
+# Which despite my love for Firefox, I'd love to help with autoselection if
+# anyone would throw their hat in
+
+# * let's not forget http://freedesktop.org/wiki/Software/pyxdg if we're
+# talking about non-stdlib solutions for linux only
+
+def open_in_browser(url, browser=None):
+    """yeah, that's right..."""
+
+    if browser is None:
+        browser = 'firefox' # we'll cheat because of course its easy
+
+    # control via env variable; might as well keep it set o_O
+    os.environ.setdefault('BROWSER', browser)
+    # see e.g. http://hg.python.org/cpython/file/2.7/Lib/webbrowser.py
+    # sadly these are different::
+    # xdg-open http://k0s.org/
+    # BROWSER=firefox xdg-open http://k0s.org/
+
+    # now invoke the damn thing
+    import webbrowser
+    return webbrowser.open(url) # XXX lord almighty
+    # why bother returning it even?
+
+
+### methods for inspecting the locations
+
+def locations(names=None, config=None, verbose=False):
     """
     list of 2-tuples of location handlers;
     * names: order names of handlers
     * config: nested dictionary of configuration from names
     """
 
+    # setup
     _handlers = {}
     _names = []
     if config is None:
         config = {}
-    
+
+    # load the handlers
+    # (really, these should come of a dict that is shared with an entry point)
     for i in iter_entry_points('smartopen.locations'):
         try:
             handler = i.load()
-        except:
-            continue # TODO: warn/debug with --verbose flag
+        except Exception, e:
+            if verbose:
+                print >> sys.stderr,  "Error loading handler:\n%s" % e
+            continue
         _handlers[i.name] = handler
         if not names:
             _names.append(i.name)
@@ -41,12 +120,17 @@
         if _name in _handlers:
             try:
                 handler = _handlers[_name](**config.get(name, {}))
-            except:
+            except Exception, e:
+                if verbose:
+                    print >> sys.stderr, "blah  blah blah"
                 continue
             handlers.append((name, handler))
     return handlers
 
+
 def urls(query, handlers=None):
+    """returns available urls in order of preference for a query"""
+
     if handlers is None:
         handlers = locations()
     urls = []
@@ -55,28 +139,41 @@
             urls.append((name, handler.url(query)))
     return urls
 
+
 def url(query, handlers=None):
+    """return the top url for a query"""
+
     if handlers is None:
         handlers = locations()
     for name, handler in handlers:
         if handler.test(query):
             return handler.url(query)
 
+### command line entry point
+
 def main(args=sys.argv[1:]):
 
     # parse command line optioins
-    parser = OptionParser()
+    default_browser = os.environ.get('BROWSER', 'firefox') # ^ see LONG note above
+    usage = '%prog [options] query'
+    parser = OptionParser(usage=usage)
+    parser.add_option('-b', '--browser', dest='browser',
+                      default=None,
+                      help="browser to use; can also be set in config and BROWSER [default: %default]")
     parser.add_option('-c', '--config', dest="config",
                       help="config file to read")
-    parser.add_option('-u', '--url', dest="url", 
+    parser.add_option('-u', '--url', dest="url",
                       action='store_true', default=False,
                       help="print the first url handled")
-    parser.add_option('-a', '--all', dest="all", 
+    parser.add_option('-a', '--all', dest="all",
                       action='store_true', default=False,
                       help="print all handlers that match the query")
     parser.add_option('-H', '--handler', dest="handlers",
                       action='append',
                       help="name of the handler to use, in order")
+    parser.add_option('-v', '--verbose', dest='verbose',
+                      action='store_true', default=True,
+                      help="be verbose with output")
     parser.add_option('--print-handlers', dest="print_handlers",
                       action='store_true',
                       help="print all handlers in order they would be tried")
@@ -87,23 +184,34 @@
     if not options.handlers:
         options.handlers = None
 
-    # config
+    # read config, if available
     config = ConfigParser()
     if not options.config:
         options.config = os.path.join(os.environ.get('HOME', ''), '.smartopen.ini')
     if os.path.exists(options.config):
         config.read(options.config)
+
+        # select handlers
         if not options.handlers and config.has_option('DEFAULTS', 'handlers'):
-            options.handlers = [ i.strip() for i in config.get('DEFAULTS', 'handlers').split(',') ]
+            options.handlers = [i.strip() for i in config.get('DEFAULTS', 'handlers').split(',')]
+
+        # select browser
+        if not options.browser:
+            for key in 'BROWSER', 'browser':
+                if config.has_option('DEFAULTS', key):
+                    options.browser = config.get('DEFAULTS', key)
+                    break
+
+    # the remaining config is arguments to the handlers
     _config = {}
     for section in config.sections():
         _config[section] = dict(config.items(section))
 
     # get the handlers
-    _locations = locations(options.handlers, _config)
+    _locations = locations(options.handlers, _config, verbose=options.verbose)
 
-    # print the handlers
     if options.print_handlers:
+        # print the handlers
         for name, loc in _locations:
             print name
         sys.exit(0)
@@ -112,26 +220,29 @@
     if args:
         data = ' '.join(args)
     else:
+        # read from stdin
         data = sys.stdin.read()
 
-    # print the URLs
     if options.all:
+        # print the URLs
         _urls = urls(data, _locations)
         for name, _url in _urls:
             print '%s: %s' % (name, _url)
         sys.exit(0)
 
+    # select the url
     _url = url(data, _locations)
 
-    # print a URL
     if options.url:
-        print _url
+        # print a URL
+        print _url or 'No handler found for your query'
         sys.exit(0)
 
     # open the URL in a browser
-    os.system("firefox '%s'" % _url)
-    sys.exit(0)
-
+    if _url:
+        open_in_browser(_url)
+    else:
+        parser.error("No handler found")
 
 if __name__ == '__main__':
     main()