changeset 84:95820b36d7e3

cli client
author Jeff Hammel <jhammel@mozilla.com>
date Sat, 28 Dec 2013 18:12:16 -0800
parents 78139c3cecfa
children 3262010f7f79
files decoupage/cli.py decoupage/web.py setup.py
diffstat 3 files changed, 95 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/decoupage/cli.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+
+"""
+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 sys
+
+from .web import Decoupage
+from wsgiref import simple_server
+
+here = os.path.dirname(os.path.realpath(__file__))
+
+def DecoupageServer(object):
+    """serve locally with decoupage"""
+    def __init__(self):
+        raise NotImplementedError("Do I need this?")
+
+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('--no-reload', dest='auto_reload',
+                      action='store_false', default=True,
+                      help="do not dynamically refresh indices")
+    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 ("'%s' is not a directory" % directory)
+
+    # TODO:
+    # - allow CLI specification of formatters
+    # - template specification
+
+    # create WSGI app
+    app = Decoupage(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)
+    print 'serving directory %s ( %s ) at \nhttp://%s:%d/' % (directory,
+                                                              'file://' + directory, # XXX
+                                                              address,
+                                                              options.port)
+
+    try:
+        server.serve_forever()
+    except KeyboardInterrupt:
+        pass
+
+if __name__ == '__main__':
+    main()
--- a/decoupage/web.py
+++ b/decoupage/web.py
@@ -25,38 +25,48 @@ from martini.config import ConfigMunger
 from paste.fileapp import FileApp
 from pkg_resources import iter_entry_points
 from pkg_resources import load_entry_point
 from pkg_resources import resource_filename
 from webob import Request, Response, exc
 
 transformers = transformers()
 
+string = (str, unicode)
+
 class Decoupage(object):
 
     ### class level variables
-    defaults = { 'auto_reload': 'False',
+    defaults = { 'auto_reload': False,
                  'configuration': None,
                  'directory': None, # directory to serve
-                 'cascade': 'True', # whether to cascade configuration
+                 'cascade': True, # whether to cascade configuration
                  'template': 'index.html', # XXX see below
                  'template_directories': '', # list of directories to look for templates
                  'charset': 'utf-8', # content encoding for index.html files; -> `Content-Type: text/html; charset=ISO-8859-1`
                  }
 
     def __init__(self, **kw):
 
         # set defaults from app configuration
-        for key in self.defaults:
-            setattr(self, key, kw.get(key, self.defaults[key]))
+        for key, default_value in self.defaults.items():
+
+            value = kw.get(key, default_value)
+
+            # handle non-string bools
+            if isinstance(default_value, bool) and isinstance(value, string):
+                value = {'true': True,
+                         'false': False}[value.lower()]
+                # TODO: error handling for bad strings
+
+            setattr(self, key, value)
+
 
         # configure defaults
         assert self.directory, "Decoupage: directory not specified"
-        self.auto_reload = self.auto_reload.lower() == 'true'
-        self.cascade = self.cascade.lower() == 'true'
         self.directory = self.directory.rstrip(os.path.sep)
         assert os.path.isdir(self.directory), "'%s' is not a directory" % self.directory
         self.template_directories = self.template_directories.split() # no spaces in directory names, for now
 
         for directory in self.template_directories:
             assert os.path.isdir(directory), "Decoupage template directory %s does not exist!" % directory
 
         # static file server
--- a/setup.py
+++ b/setup.py
@@ -31,16 +31,17 @@ setup(name='decoupage',
           'PyRSS2Gen',
          ],
       dependency_links=['http://www.dalkescientific.com/Python/PyRSS2Gen-1.0.0.tar.gz#egg=PyRSS2Gen'],
       entry_points="""
       # -*- Entry points: -*-
       [console_scripts]
       decoupage-templates = decoupage.templates:main
       decoupage-formatters = decoupage.formatters:main
+      decoupage = decoupage.cli:main
 
       [paste.app_factory]
       main = decoupage.factory:factory
 
       [decoupage.formats]
       json = decoupage.formats:JSON
       rss = decoupage.formats:RSS