changeset 85:3262010f7f79

add command line serving
author Jeff Hammel <jhammel@mozilla.com>
date Sat, 28 Dec 2013 21:56:06 -0800
parents 95820b36d7e3
children a9f5b60006ba
files decoupage/cli.py decoupage/formatters.py decoupage/web.py setup.py
diffstat 4 files changed, 57 insertions(+), 34 deletions(-) [+]
line wrap: on
line diff
--- a/decoupage/cli.py
+++ b/decoupage/cli.py
@@ -5,74 +5,97 @@ serve directory with decoupage, e.g.
 
 ``decoupage --port 8080 /home/jhammel/tmp``
 
 If the directory is not specified, the current working directory is used
 """
 
 import optparse
 import os
+import socket
 import sys
 
+from .formatters import Sort, Up
 from .web import Decoupage
 from wsgiref import simple_server
 
 here = os.path.dirname(os.path.realpath(__file__))
 
-def DecoupageServer(object):
+class DecoupageServer(Decoupage):
     """serve locally with decoupage"""
-    def __init__(self):
-        raise NotImplementedError("Do I need this?")
+    # TODO: deprecate; move Decoupage to a few classes
+    # with more flexible formatters
+    def __init__(self, *args, **kwargs):
+        Decoupage.__init__(self, **kwargs)
+        # default formatters
+        # TODO: make configurable
+        self._formatters = [Sort(), Up('..')]
+    def get_formatters(self, path):
+        return self._formatters
+
 
 def main(args=sys.argv[1:]):
 
     # parse command line options
     usage = '%prog [options]'
     parser = optparse.OptionParser(usage=usage, description=__doc__)
     parser.add_option('-p', '--port', dest='port',
                       type='int', default=1977,
                       help="port to serve on [DEFAULT: %default]")
+    parser.add_option('-a', '--address', dest='address',
+                      default='0.0.0.0',
+                      help="address to serve on [DEFAULT: %default]")
     parser.add_option('--no-reload', dest='auto_reload',
                       action='store_false', default=True,
                       help="do not dynamically refresh indices")
+    parser.add_option('--no-print-ip', dest='print_ip',
+                      action='store_false', default=True,
+                      help="do not print resolvable IP address")
     options, args = parser.parse_args(args)
     if not args:
         directory = os.getcwd()
     elif len(args) == 1:
         directory = args[0]
     if len(args) > 1:
             # TODO:
             # allow multiple directories with mount points
             #   e.g. `decoupage [options] directory [directory2=/foo] [...]`
             #   This may be done by creating a temporary directory with appropriate
             #   symbolic links (on OSes that allow them)
 
         parser.print_help()
         parser.exit(1)
+    if not os.path.isdir(directory):
+        raise OSError("'%s' is not a directory" % directory)
 
-    if not os.path.isdir(directory):
-        raise ("'%s' is not a directory" % directory)
-
+    # create WSGI app
     # TODO:
     # - allow CLI specification of formatters
     # - template specification
-
-    # create WSGI app
-    app = Decoupage(directory=directory,
-                    auto_reload=options.auto_reload)
+    app = DecoupageServer(directory=directory,
+                          auto_reload=options.auto_reload)
 
 
     # create server
     # TODO: allow choice amongst server classes
-    address = '127.0.0.1'
-    server = simple_server.make_server(address, options.port, app)
+    printable_address = '127.0.0.1' if options.address == '0.0.0.0' else options.address
+    server = simple_server.make_server(options.address, options.port, app)
     print 'serving directory %s ( %s ) at \nhttp://%s:%d/' % (directory,
                                                               'file://' + directory, # XXX
-                                                              address,
+                                                              printable_address,
                                                               options.port)
+    if options.print_ip:
+        # from http://stackoverflow.com/questions/166506/finding-local-ip-addresses-using-pythons-stdlib
+        hostname = "google.com"
+        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+        s.connect((hostname,80))
+        hostname = s.getsockname()[0]
+        print "http://%s:%s/" % (hostname, options.port)
+        s.close()
 
+    # serve directory contents via decoupage
     try:
         server.serve_forever()
     except KeyboardInterrupt:
         pass
 
 if __name__ == '__main__':
     main()
--- a/decoupage/formatters.py
+++ b/decoupage/formatters.py
@@ -86,17 +86,17 @@ class All(object):
 class Sort(FormatterBase):
     """
     determines how to sort the files in a directory;
     right now only by case-insensitive alphabetically
     * reverse : reverse the order of the sorting
     """
     defaults = {'order': 'name'}
 
-    def __init__(self, pattern):
+    def __init__(self, pattern=''):
         FormatterBase.__init__(self, pattern)
         self.orders = {'name': self.name,
                        'random': self.random,
                        }
         # TODO: date
 
     def __call__(self, request, data):
 
--- a/decoupage/web.py
+++ b/decoupage/web.py
@@ -184,30 +184,18 @@ class Decoupage(object):
         data['filepath'] = lambda *segments: os.path.join(*([directory] + list(segments)))
 
         # defaults
         data['directory'] = directory
         data['css'] = ()
         data['scripts'] = ()
 
         # apply formatters
-        # XXX this should be cached if not self.auto_reload
-        if '/formatters' in conf:
-            # ordered list of formatters to be applied first
-            formatters = [ i for i in conf['/formatters'].split()
-                           if i in self.formatters ]
-        else:
-            formatters = []
-        for key in conf:
-            if key.startswith('/'):
-                key = key[1:]
-                if key in self.formatters and key not in formatters:
-                    formatters.append(key)
-        for name in formatters:
-            formatter = self.formatters[name](conf.get('/%s' % name, ''))
+        formatters = self.get_formatters(path)
+        for formatter in formatters:
             formatter(request, data)
 
         # return an alternate format if specified
         # decoupage.formats should return a 2-tuple:
         # (content_type, body)
         if 'format' in request.GET:
             format_name = request.GET['format']
             if format_name in self.formats:
@@ -347,16 +335,28 @@ class Decoupage(object):
         # cache configuration
         if not self.auto_reload:
             if not hasattr(self, '_conf'):
                 self._conf = {}
             self._conf[path_tuple] = conf
 
         return conf
 
-    def fmtrs(self, path):
-        formatters = []
-        for key, value in self.conf(path).items():
+    def get_formatters(self, path):
+        """return formatters for a path"""
+        retval = []
+        conf = self.conf(path)
+        # apply formatters
+        # XXX this should be cached if not self.auto_reload
+        if '/formatters' in conf:
+            # ordered list of formatters to be applied first
+            formatters = [ i for i in conf['/formatters'].split()
+                           if i in self.formatters ]
+        else:
+            formatters = []
+        for key in conf:
             if key.startswith('/'):
                 key = key[1:]
-                if key in self.formatters:
-                    formatter = self.formatters[key](value)
+                if key in self.formatters and key not in formatters:
+                    formatters.append(key)
+        for name in formatters:
+            retval.append(self.formatters[name](conf.get('/%s' % name, '')))
 
--- a/setup.py
+++ b/setup.py
@@ -1,17 +1,17 @@
 from setuptools import setup, find_packages
 
 # use README as long_description
 try:
     description = file("README.txt").read()
 except IOError:
     description = ''
 
-version = '0.12.2'
+version = '0.13.0'
 
 setup(name='decoupage',
       version=version,
       description="Decoupage is the art of decorating an object by gluing colored paper cutouts onto it in combination with special paint effects ... The software decoupage lets you stitch together index pages from filesystem content",
       long_description=description,
       classifiers=[], # Get strings from http://www.python.org/pypi?%3Aaction=list_classifiers
       author='Jeff Hammel',
       author_email='k0scist@gmail.com',