--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/pvl.verkko-dhcp Sun Jan 20 18:26:12 2013 +0200
@@ -0,0 +1,67 @@
+#!/usr/bin/python
+
+from werkzeug.serving import run_simple
+
+from pvl import __version__
+import pvl.args
+import pvl.verkko.wsgi
+
+import optparse
+import logging; log = logging.getLogger('main')
+
+def parse_argv (argv, doc = __doc__) :
+ """
+ Parse command-line argv, returning (options, args).
+ """
+
+ prog = argv.pop(0)
+ args = argv
+
+ # optparse
+ parser = optparse.OptionParser(
+ prog = prog,
+ usage = '%prog: [options] [<user> [...]]',
+ version = __version__,
+ description = doc,
+ )
+
+ # common
+ parser.add_option_group(pvl.args.parser(parser))
+
+ parser.add_option('-d', '--database-read', metavar='URI', default='sqlite:///var/verkko.db',
+ help="Database to use (readonly)")
+
+ # parse
+ options, args = parser.parse_args(args)
+
+ # apply
+ pvl.args.apply(options)
+
+ return options, args
+
+def main (argv) :
+ """
+ pvl.verkko wsgi development server.
+ """
+
+ # parse cmdline
+ options, args = parse_argv(argv, doc=__doc__)
+
+ # app
+ application = pvl.verkko.wsgi.Application(options.database_read)
+
+ # wsgi wrapper
+ run_simple('0.0.0.0', 8080, application,
+ #use_reloader = True,
+ use_debugger = (options.loglevel == logging.DEBUG),
+ static_files = {
+ '/static': 'static',
+ },
+ )
+
+if __name__ == '__main__' :
+ import sys
+
+ sys.exit(main(sys.argv))
+
+
--- a/pvl/html.py Sun Jan 20 18:19:03 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,607 +0,0 @@
-"""
- Generate XHTML output from python code.
-
- >>> from html import tags
- >>> unicode(tags.a(href="http://www.google.com")("Google <this>!"))
- u'<a href="http://www.google.com">\\n\\tGoogle <this>!\\n</a>'
-"""
-
-import itertools as itertools
-import types as types
-from xml.sax import saxutils
-
-class Renderable (object) :
- """
- Structured data that's flattened into indented lines of text.
- """
-
- def flatten (self) :
- """
- Flatten this object into a series of (identlevel, line) tuples.
- """
-
- raise NotImplementedError()
-
- def iter (self, indent='\t') :
- """
- Yield a series of lines for this render.
- """
-
- for indent_level, line in self.flatten() :
- yield (indent * indent_level) + line
-
- def unicode (self, newline=u'\n', **opts) :
- """
- Render as a single unicode string.
-
- No newline is returned at the end of the string.
-
- >>> Tag.build('a', 'b').unicode(newline='X', indent='Y')
- u'<a>XYbX</a>'
- """
-
- return newline.join(self.iter(**opts))
-
- # required for print
- def str (self, newline='\n', encoding='ascii', **opts) :
- """
- Render as a single string.
- """
-
- # XXX: try and render as non-unicode, i.e. binary data in the tree?
- return newline.join(line.encode(encoding) for line in self.iter(**opts))
-
- # formal interface using defaults
- __iter__ = iter
- __unicode__ = unicode
- __str__ = str
-
-class Text (Renderable) :
- """
- Plain un-structured/un-processed HTML text for output
-
- >>> Text('foo')
- Text(u'foo')
- >>> list(Text('<foo>'))
- [u'<foo>']
- >>> list(tag('a', Text('<foo>')))
- [u'<a>', u'\\t<foo>', u'</a>']
- """
-
- def __init__ (self, text) :
- self.text = unicode(text)
-
- def flatten (self) :
- yield (0, self.text)
-
- def __repr__ (self) :
- return "Text(%r)" % (self.text, )
-
-class Tag (Renderable) :
- """
- An immutable HTML tag structure, with the tag's name, attributes and contents.
- """
-
- # types of nested items to flatten
- CONTAINER_TYPES = (types.TupleType, types.ListType, types.GeneratorType)
-
- # types of items to keep as-is
- ITEM_TYPES = (Renderable, )
-
- @classmethod
- def process_contents (cls, *args) :
- """
- Yield the HTML tag's contents from the given sequence of positional arguments as a series of flattened
- items, eagerly converting them to unicode.
-
- If no arguments are given, we don't have any children:
-
- >>> bool(list(Tag.process_contents()))
- False
-
- Items that are None will be ignored:
-
- >>> list(Tag.process_contents(None))
- []
-
- Various Python container types are recursively flattened:
-
- >>> list(Tag.process_contents([1, 2]))
- [u'1', u'2']
- >>> list(Tag.process_contents([1], [2]))
- [u'1', u'2']
- >>> list(Tag.process_contents([1, [2]]))
- [u'1', u'2']
- >>> list(Tag.process_contents(n + 1 for n in xrange(2)))
- [u'1', u'2']
- >>> list(Tag.process_contents((1, 2)))
- [u'1', u'2']
- >>> list(Tag.process_contents((1), (2, )))
- [u'1', u'2']
-
- Our own HTML-aware objects are returned as-is:
-
- >>> list(Tag.process_contents(Tag.build('foo')))
- [tag('foo')]
- >>> list(Tag.process_contents(Text('bar')))
- [Text(u'bar')]
-
- All other objects are converted to unicode:
-
- >>> list(Tag.process_contents('foo', u'bar', 0.123, False))
- [u'foo', u'bar', u'0.123', u'False']
-
- """
-
- for arg in args :
- if arg is None :
- # skip null: None
- continue
-
- elif isinstance(arg, cls.CONTAINER_TYPES) :
- # flatten nested container: tuple/list/generator
- for node in arg :
- # recurse
- for item in cls.process_contents(node) :
- yield item
-
- elif isinstance(arg, cls.ITEM_TYPES) :
- # yield item: Renderable
- yield arg
-
- else :
- # as unicode
- yield unicode(arg)
-
- @classmethod
- def process_attrs (cls, **kwargs) :
- """
- Yield the HTML tag attributes from the given set of keyword arguments as a series of (name, value) tuples.
-
- Keyword-only options (`_key=value`) are filtered out:
-
- >>> dict(Tag.process_attrs(_opt=True))
- {}
-
- Attributes with a value of None/False are filtered out:
-
- >>> dict(Tag.process_attrs(foo=None, bar=False))
- {}
-
- A value given as True is returned as the key's value:
-
- >>> dict(Tag.process_attrs(quux=True))
- {'quux': u'quux'}
-
- A (single) trailing underscore in the attribute name is removed:
-
- >>> dict(Tag.process_attrs(class_='foo'))
- {'class': u'foo'}
- >>> dict(Tag.process_attrs(data__='foo'))
- {'data_': u'foo'}
- """
-
- for key, value in kwargs.iteritems() :
- # keyword arguments are always pure strings
- assert type(key) is str
-
- if value is None or value is False:
- # omit
- continue
-
- if key.startswith('_') :
- # option
- continue
-
- if key.endswith('_') :
- # strip underscore
- key = key[:-1]
-
- if value is True :
- # flag attr
- value = key
-
- yield key, unicode(value)
-
- @classmethod
- def process_opts (cls, **kwargs) :
- """
- Return a series of of the keyword-only _options, extracted from the given dict of keyword arguments, as
- (k, v) tuples.
-
- >>> Tag.process_opts(foo='bar', _bar=False)
- (('bar', False),)
- """
-
- return tuple((k.lstrip('_'), v) for k, v in kwargs.iteritems() if k.startswith('_'))
-
- @classmethod
- def build (cls, _name, *args, **kwargs) :
- """
- Factory function for constructing Tags by directly passing in contents/attributes/options as Python function
- arguments/keyword arguments.
-
- The first positional argument is the tag's name:
-
- >>> Tag.build('foo')
- tag('foo')
-
- Further positional arguments are the tag's contents:
-
- >>> Tag.build('foo', 'quux', 'bar')
- tag('foo', u'quux', u'bar')
-
- All the rules used by process_contents() are available:
-
- >>> Tag.build('foo', [1, None], None, (n for n in xrange(2)))
- tag('foo', u'1', u'0', u'1')
-
- The special-case for a genexp as the only argument works:
-
- >>> f = lambda *args: Tag.build('foo', *args)
- >>> f('hi' for n in xrange(2))
- tag('foo', u'hi', u'hi')
-
- Attributes are passed as keyword arguments, with or without contents:
-
- >>> Tag.build('foo', id=1)
- tag('foo', id=u'1')
- >>> Tag.build('foo', 'quux', bar=5)
- tag('foo', u'quux', bar=u'5')
- >>> Tag.build('foo', class_='ten')
- tag('foo', class=u'ten')
-
- The attribute names don't conflict with positional argument names:
-
- >>> Tag.build('bar', name='foo')
- tag('bar', name=u'foo')
-
- Options are handled as the 'real' keyword arguments:
-
- >>> print Tag.build('foo', _selfclosing=False)
- <foo></foo>
- >>> print Tag.build('foo', _foo='bar')
- Traceback (most recent call last):
- ...
- TypeError: __init__() got an unexpected keyword argument 'foo'
- """
-
- # pre-process incoming user values
- contents = list(cls.process_contents(*args))
- attrs = dict(cls.process_attrs(**kwargs))
-
- # XXX: use Python 2.6 keyword-only arguments instead?
- options = dict(cls.process_opts(**kwargs))
-
- return cls(_name, contents, attrs, **options)
-
- def __init__ (self, name, contents, attrs, selfclosing=None, whitespace_sensitive=None) :
- """
- Initialize internal Tag state with the given tag identifier, flattened list of content items, dict of
- attributes and dict of options.
-
- selfclosing - set to False to render empty tags as <foo></foo> instead of <foo />
- (for XHTML -> HTML compatibility)
-
- whitespace_sensitive - do not indent tag content onto separate rows, render the full tag as a single
- row
-
- Use the build() factory function to build Tag objects using Python's function call argument semantics.
-
- The tag name is used a pure string identifier:
-
- >>> Tag(u'foo', [], {})
- tag('foo')
- >>> Tag(u'\\xE4', [], {})
- Traceback (most recent call last):
- ...
- UnicodeEncodeError: 'ascii' codec can't encode character u'\\xe4' in position 0: ordinal not in range(128)
-
- Contents have their order preserved:
-
- >>> Tag('foo', [1, 2], {})
- tag('foo', 1, 2)
- >>> Tag('foo', [2, 1], {})
- tag('foo', 2, 1)
-
- Attributes can be given:
-
- >>> Tag('foo', [], dict(foo='bar'))
- tag('foo', foo='bar')
-
- Options can be given:
-
- >>> print Tag('foo', [], {}, selfclosing=False)
- <foo></foo>
- """
-
- self.name = str(name)
- self.contents = contents
- self.attrs = attrs
-
- # options
- self.selfclosing = selfclosing
- self.whitespace_sensitive = whitespace_sensitive
-
- def __call__ (self, *args, **kwargs) :
- """
- Return a new Tag as a copy of this tag, but with the given additional attributes/contents.
-
- The same rules for function positional/keyword arguments apply as for build()
-
- >>> Tag.build('foo')('bar')
- tag('foo', u'bar')
- >>> Tag.build('a', href='index.html')("Home")
- tag('a', u'Home', href=u'index.html')
-
- New contents and attributes can be given freely, using the same rules as for Tag.build:
-
- >>> Tag.build('bar', None)(5, foo=None, class_='bar')
- tag('bar', u'5', class=u'bar')
-
- Tag contents accumulate in order:
-
- >>> Tag.build('a')('b', ['c'])('d')
- tag('a', u'b', u'c', u'd')
-
- Each Tag is immutable, so the called Tag isn't changed, but rather a copy is returned:
-
- >>> t1 = Tag.build('a'); t2 = t1('b'); t1
- tag('a')
-
- Attribute values are replaced:
-
- >>> Tag.build('foo', a=2)(a=3)
- tag('foo', a=u'3')
-
- Options are also supported:
-
- >>> list(Tag.build('foo')(bar='quux', _selfclosing=False))
- [u'<foo bar="quux"></foo>']
- """
-
- # accumulate contents
- contents = self.contents + list(self.process_contents(*args))
-
- # merge attrs
- attrs = dict(self.attrs)
- attrs.update(self.process_attrs(**kwargs))
-
- # options
- opts = dict(
- selfclosing = self.selfclosing,
- whitespace_sensitive = self.whitespace_sensitive,
- )
- opts.update(self.process_opts(**kwargs))
-
- # build updated tag
- return Tag(self.name, contents, attrs, **opts)
-
- def render_attrs (self) :
- """
- Return the HTML attributes string
-
- >>> Tag.build('x', foo=5, bar='<', quux=None).render_attrs()
- u'foo="5" bar="<"'
- >>> Tag.build('x', foo='a"b').render_attrs()
- u'foo=\\'a"b\\''
- """
-
- return " ".join(
- (
- u'%s=%s' % (name, saxutils.quoteattr(value))
- ) for name, value in self.attrs.iteritems()
- )
-
- def flatten_items (self, indent=1) :
- """
- Flatten our content into a series of indented lines.
-
- >>> list(Tag.build('tag', 5).flatten_items())
- [(1, u'5')]
- >>> list(Tag.build('tag', 'line1', 'line2').flatten_items())
- [(1, u'line1'), (1, u'line2')]
-
- Nested :
- >>> list(Tag.build('tag', 'a', Tag.build('b', 'bb'), 'c').flatten_items())
- [(1, u'a'), (1, u'<b>'), (2, u'bb'), (1, u'</b>'), (1, u'c')]
- >>> list(Tag.build('tag', Tag.build('hr'), Tag.build('foo')('bar')).flatten_items())
- [(1, u'<hr />'), (1, u'<foo>'), (2, u'bar'), (1, u'</foo>')]
- """
-
- for item in self.contents :
- if isinstance(item, Renderable) :
- # recursively flatten items
- for line_indent, line in item.flatten() :
- # indented
- yield indent + line_indent, line
-
- else :
- # render HTML-escaped raw value
- # escape raw values
- yield indent, saxutils.escape(item)
-
- def flatten (self) :
- """
- Render the tag and all content as a flattened series of indented lines.
-
- Empty tags collapse per default:
-
- >>> list(Tag.build('foo').flatten())
- [(0, u'<foo />')]
- >>> list(Tag.build('bar', id=5).flatten())
- [(0, u'<bar id="5" />')]
-
- Values are indented inside the start tag:
-
- >>> list(Tag.build('foo', 'bar', a=5).flatten())
- [(0, u'<foo a="5">'), (1, u'bar'), (0, u'</foo>')]
-
- Nested tags are further indented:
-
- >>> list(Tag.build('1', '1.1', Tag.build('1.2', '1.2.1'), '1.3', a=5).flatten())
- [(0, u'<1 a="5">'), (1, u'1.1'), (1, u'<1.2>'), (2, u'1.2.1'), (1, u'</1.2>'), (1, u'1.3'), (0, u'</1>')]
-
- Empty tags are rendered with a separate closing tag on the same line, if desired:
-
- >>> list(Tag.build('foo', _selfclosing=False).flatten())
- [(0, u'<foo></foo>')]
- >>> list(Tag.build('foo', src='asdf', _selfclosing=False).flatten())
- [(0, u'<foo src="asdf"></foo>')]
-
- Tags that are declared as whitespace-sensitive are collapsed onto the same line:
-
- >>> list(Tag.build('foo', _whitespace_sensitive=True).flatten())
- [(0, u'<foo />')]
- >>> list(Tag.build('foo', _whitespace_sensitive=True, _selfclosing=False).flatten())
- [(0, u'<foo></foo>')]
- >>> list(Tag.build('foo', 'bar', _whitespace_sensitive=True).flatten())
- [(0, u'<foo>bar</foo>')]
- >>> list(Tag.build('foo', 'bar\\nasdf\\tx', _whitespace_sensitive=True).flatten())
- [(0, u'<foo>bar\\nasdf\\tx</foo>')]
- >>> list(Tag.build('foo', 'bar', Tag.build('quux', 'asdf'), 'asdf', _whitespace_sensitive=True).flatten())
- [(0, u'<foo>bar<quux>asdf</quux>asdf</foo>')]
-
- Embedded HTML given as string values is escaped:
-
- >>> list(Tag.build('foo', '<asdf>'))
- [u'<foo>', u'\\t<asdf>', u'</foo>']
-
- Embedded quotes in attribute values are esacaped:
-
- >>> list(Tag.build('foo', style='ok;" onload="...'))
- [u'<foo style=\\'ok;" onload="...\\' />']
- >>> list(Tag.build('foo', style='ok;\\'" onload=..."\\''))
- [u'<foo style="ok;\\'" onload=..."\\'" />']
- """
-
- # optional attr spec
- if self.attrs :
- attrs = " " + self.render_attrs()
-
- else :
- attrs = ""
-
- if not self.contents and self.selfclosing is False :
- # empty tag, but don't use the self-closing syntax..
- yield 0, u"<%s%s></%s>" % (self.name, attrs, self.name)
-
- elif not self.contents :
- # self-closing xml tag
- # do note that this is invalid HTML, and the space before the / is relevant for parsing it as HTML
- yield 0, u"<%s%s />" % (self.name, attrs)
-
- elif self.whitespace_sensitive :
- # join together each line for each child, discarding the indent
- content = u''.join(line for indent, line in self.flatten_items())
-
- # render full tag on a single line
- yield 0, u"<%s%s>%s</%s>" % (self.name, attrs, content, self.name)
-
- else :
- # start tag
- yield 0, u"<%s%s>" % (self.name, attrs)
-
- # contents, indented one level below the start tag
- for indent, line in self.flatten_items(indent=1) :
- yield indent, line
-
- # close tag
- yield 0, u"</%s>" % (self.name, )
-
- def __repr__ (self) :
- return 'tag(%s)' % ', '.join(
- [
- repr(self.name)
- ] + [
- repr(c) for c in self.contents
- ] + [
- '%s=%r' % (name, value) for name, value in self.attrs.iteritems()
- ]
- )
-
-# factory function for Tag
-tag = Tag.build
-
-
-class Document (Renderable) :
- """
- A full XHTML 1.0 document with optional XML header, doctype, html[@xmlns].
-
- >>> list(Document(tags.html('...')))
- [u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">', u'<html xmlns="http://www.w3.org/1999/xhtml">', u'\\t...', u'</html>']
- """
-
- def __init__ (self, root,
- doctype='html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"',
- html_xmlns='http://www.w3.org/1999/xhtml',
- xml_version=None, xml_encoding=None,
- ) :
- # add xmlns attr to root node
- self.root = root(xmlns=html_xmlns)
-
- # store
- self.doctype = doctype
- self.xml_declaration = {}
-
- if xml_version :
- self.xml_declaration['version'] = xml_version
-
- if xml_encoding :
- self.xml_declaration['encoding'] = xml_encoding
-
- def flatten (self) :
- """
- Return the header lines along with the normally formatted <html> tag
- """
-
- if self.xml_declaration :
- yield 0, u'<?xml %s ?>' % (' '.join('%s="%s"' % kv for kv in self.xml_declaration.iteritems()))
-
- if self.doctype :
- yield 0, u'<!DOCTYPE %s>' % (self.doctype)
-
- # <html>
- for indent, line in self.root.flatten() :
- yield indent, line
-
-class TagFactory (object) :
- """
- Build Tags with names give as attribute names
-
- >>> list(TagFactory().a(href='#')('Yay'))
- [u'<a href="#">', u'\\tYay', u'</a>']
-
- >>> list(TagFactory()("><"))
- [u'><']
- """
-
- # full XHTML document
- document = Document
-
- def __getattr__ (self, name) :
- """
- Get a Tag object with the given name, but no contents
-
- >>> TagFactory().a
- tag('a')
- """
-
- return Tag(name, [], {})
-
- def __call__ (self, value) :
- """
- Raw HTML.
- """
-
- return Text(value)
-
-# static instance
-tags = TagFactory()
-
-# testing
-if __name__ == '__main__' :
- import doctest
-
- doctest.testmod()
-
--- a/pvl/verkko/hosts.py Sun Jan 20 18:19:03 2013 +0200
+++ b/pvl/verkko/hosts.py Sun Jan 20 18:26:12 2013 +0200
@@ -1,7 +1,7 @@
from pvl.verkko import db, web
from pvl.verkko.utils import parse_timedelta, IPv4Network
-from pvl.html import tags as html
+from pvl.web import html
import re
import datetime
--- a/pvl/verkko/urls.py Sun Jan 20 18:19:03 2013 +0200
+++ b/pvl/verkko/urls.py Sun Jan 20 18:26:12 2013 +0200
@@ -8,6 +8,7 @@
# index page here :)
from pvl.verkko import web
+
class Index (web.Handler) :
def render (self) :
html = web.html
--- a/pvl/verkko/web.py Sun Jan 20 18:19:03 2013 +0200
+++ b/pvl/verkko/web.py Sun Jan 20 18:26:12 2013 +0200
@@ -1,14 +1,9 @@
# encoding: utf-8
-
-def merge (*dicts) :
- dict = {}
+import pvl.web.application
- for d in dicts :
- dict.update(d)
+# view
+from pvl.web.html import tags as html
- return dict
-
-# response types
from werkzeug.wrappers import Response
from werkzeug.exceptions import (
HTTPException,
@@ -17,15 +12,7 @@
)
from werkzeug.utils import redirect
-# view
-from pvl.html import tags as html
-
-class Handler (object) :
- """
- Per-Request controller/view, containing the request context and generating the response.
- """
-
- TITLE = None
+class Handler (pvl.web.application.Handler) :
CSS = (
#"/static/layout.css",
"/static/style.css",
@@ -35,32 +22,11 @@
)
def __init__ (self, app, request, urls, params) :
- """
- app - wsgi.Application
- request - werkzeug.Request
- urls - werkzeug.routing.MapAdapter
- """
+ super(Handler, self).__init__(app, request, urls, params)
- self.app = app
- self.request = request
- self.urlmap = urls
- self.params = params
-
# new ORM session per request
self.db = app.db.session()
- def url (self, handler=None, **params) :
- """
- Return an URL for given endpoint, with parameters,
- """
-
- if not handler :
- # XXX: just generate a plain-relative '?foo=...' url instead?
- handler = self.__class__
- params = merge(self.params, params)
-
- return self.urlmap.build(handler, params)
-
def title (self) :
"""
Render site/page title as text.
@@ -71,65 +37,9 @@
else :
return u"Päivölä Verkko"
- def render (self) :
- """
- Render page content (as <body>...</body>).
- """
-
- raise NotImplementedError()
-
- def render_html (self) :
- """
- Render page layout (as <html>).
- """
-
- title = self.title()
-
- return html.html(
- html.head(
- html.title(title),
- (
- html.link(rel='Stylesheet', type="text/css", href=src) for src in self.CSS
- ),
- (
- html.script(src=src, type='text/javascript', _selfclosing=False) for src in self.JS
- ),
- ),
- html.body(
- html.h1(title),
- self.render()
- )
- )
-
- def process (self, **params) :
- """
- Process request args to build internal request state.
- """
-
- pass
-
- def respond (self) :
- """
- Generate a response, or raise an HTTPException
-
- Does an HTML layout'd response per default.
- """
-
- # returning e.g. redirect?
- response = self.process(**self.params)
-
- if response :
- return response
-
- # render as html per default
- render = self.render_html()
- text = unicode(html.document(render))
-
- return Response(text, mimetype='text/html')
-
def cleanup (self) :
"""
- After request processing. Do not fail :)
+ After request processing.
"""
# XXX: SQLAlchemy doesn't automatically close these...?
--- a/pvl/verkko/wsgi.py Sun Jan 20 18:19:03 2013 +0200
+++ b/pvl/verkko/wsgi.py Sun Jan 20 18:26:12 2013 +0200
@@ -1,51 +1,18 @@
-import werkzeug
-from werkzeug.wrappers import Request, Response
-from werkzeug.exceptions import HTTPException
+import pvl.web
import logging; log = logging.getLogger('pvl.verkko.wsgi')
-from pvl.verkko import db as database, urls, web
+from pvl.verkko import urls, db as database
-class Application (object) :
- urls = urls.urls
+class Application (pvl.web.Application) :
+ URLS = urls.urls
def __init__ (self, db) :
"""
Initialize app with db.
"""
+ super(Application, self).__init__()
+
self.db = database.Database(db)
- def respond (self, request) :
- """
- Lookup Request -> web.Handler, params
- """
-
- # bind to request
- urls = self.urls.bind_to_environ(request)
-
- # lookup
- handler, params = urls.match()
-
- # handler instance
- handler = handler(self, request, urls, params)
-
- try :
- # apply
- return handler.respond()
-
- finally :
- handler.cleanup()
-
- @Request.application
- def __call__ (self, request) :
- """
- WSGI entry point, werkzeug Request -> Response
- """
-
- try :
- return self.respond(request)
-
- except HTTPException as ex :
- return ex
-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pvl/web/__init__.py Sun Jan 20 18:26:12 2013 +0200
@@ -0,0 +1,15 @@
+"""
+ Werkzeug-based web application.
+"""
+
+from pvl.web.html import tags as html
+from pvl.web.application import Application, Handler
+
+# werkzeug
+from werkzeug.exceptions import (
+ HTTPException,
+ BadRequest, # 400
+ NotFound, # 404
+)
+from werkzeug.utils import redirect
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pvl/web/application.py Sun Jan 20 18:26:12 2013 +0200
@@ -0,0 +1,172 @@
+"""
+ WSGI Application.
+"""
+
+import werkzeug
+from werkzeug.wrappers import Request, Response
+from werkzeug.exceptions import HTTPException
+
+import logging; log = logging.getLogger('pvl.web.application')
+
+class Application (object) :
+ """
+ Main state.
+ """
+
+ URLS = None
+
+ def __init__ (self, urls=None) :
+ """
+ urls - werkzeug.routing.Map -> Handler
+ """
+
+ if not urls :
+ urls = self.URLS
+
+ if not urls :
+ raise ValueError("No URLS/urls=... given")
+
+ self.urls = urls
+
+ def respond (self, request) :
+ """
+ Lookup Request -> web.Handler, params
+ """
+
+ # bind to request
+ urls = self.urls.bind_to_environ(request)
+
+ # lookup
+ handler, params = urls.match()
+
+ # handler instance
+ handler = handler(self, request, urls, params)
+
+ try :
+ # apply
+ return handler.respond()
+
+ finally :
+ handler.cleanup()
+
+ @Request.application
+ def __call__ (self, request) :
+ """
+ WSGI entry point, werkzeug Request -> Response
+ """
+
+ try :
+ return self.respond(request)
+
+ except HTTPException as ex :
+ return ex
+
+from pvl.invoke import merge # XXX
+from pvl.web.html import tags as html
+
+class Handler (object) :
+ """
+ Per-Request controller/view, containing the request context and generating the response.
+ """
+
+ TITLE = None
+ CSS = (
+ #"/static/...css",
+ )
+ JS = (
+ #"/static/....js"
+ )
+
+ def __init__ (self, app, request, urls, params) :
+ """
+ app - wsgi.Application
+ request - werkzeug.Request
+ urls - werkzeug.routing.Map.bind_to_environ()
+ """
+
+ self.app = app
+ self.request = request
+ self.urls = urls
+ self.params = params
+
+ def url (self, handler=None, **params) :
+ """
+ Return an URL for given endpoint, with parameters,
+ """
+
+ if not handler :
+ # XXX: just generate a plain-relative '?foo=...' url instead?
+ handler = self.__class__
+ params = merge(self.params, params)
+
+ return self.urls.build(handler, params)
+
+ def title (self) :
+ """
+ Render site/page title as text.
+ """
+
+ return self.TITLE
+
+ def render (self) :
+ """
+ Render page content (as <body>...</body>).
+ """
+
+ raise NotImplementedError()
+
+ def render_html (self) :
+ """
+ Render page layout (as <html>).
+ """
+
+ title = self.title()
+
+ return html.html(
+ html.head(
+ html.title(title),
+ (
+ html.link(rel='Stylesheet', type="text/css", href=src) for src in self.CSS
+ ),
+ (
+ html.script(src=src, type='text/javascript', _selfclosing=False) for src in self.JS
+ ),
+ ),
+ html.body(
+ html.h1(title),
+ self.render()
+ )
+ )
+
+ def process (self, **params) :
+ """
+ Process request args to build internal request state.
+ """
+
+ pass
+
+ def respond (self) :
+ """
+ Generate a response, or raise an HTTPException
+
+ Does an HTML layout'd response per default.
+ """
+
+ # returning e.g. redirect?
+ response = self.process(**self.params)
+
+ if response :
+ return response
+
+ # render as html per default
+ render = self.render_html()
+ text = unicode(html.document(render))
+
+ return Response(text, mimetype='text/html')
+
+ def cleanup (self) :
+ """
+ After request processing. Do not fail :)
+ """
+
+ pass
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pvl/web/html.py Sun Jan 20 18:26:12 2013 +0200
@@ -0,0 +1,607 @@
+"""
+ Generate XHTML output from python code.
+
+ >>> from html import tags
+ >>> unicode(tags.a(href="http://www.google.com")("Google <this>!"))
+ u'<a href="http://www.google.com">\\n\\tGoogle <this>!\\n</a>'
+"""
+
+import itertools as itertools
+import types as types
+from xml.sax import saxutils
+
+class Renderable (object) :
+ """
+ Structured data that's flattened into indented lines of text.
+ """
+
+ def flatten (self) :
+ """
+ Flatten this object into a series of (identlevel, line) tuples.
+ """
+
+ raise NotImplementedError()
+
+ def iter (self, indent='\t') :
+ """
+ Yield a series of lines for this render.
+ """
+
+ for indent_level, line in self.flatten() :
+ yield (indent * indent_level) + line
+
+ def unicode (self, newline=u'\n', **opts) :
+ """
+ Render as a single unicode string.
+
+ No newline is returned at the end of the string.
+
+ >>> Tag.build('a', 'b').unicode(newline='X', indent='Y')
+ u'<a>XYbX</a>'
+ """
+
+ return newline.join(self.iter(**opts))
+
+ # required for print
+ def str (self, newline='\n', encoding='ascii', **opts) :
+ """
+ Render as a single string.
+ """
+
+ # XXX: try and render as non-unicode, i.e. binary data in the tree?
+ return newline.join(line.encode(encoding) for line in self.iter(**opts))
+
+ # formal interface using defaults
+ __iter__ = iter
+ __unicode__ = unicode
+ __str__ = str
+
+class Text (Renderable) :
+ """
+ Plain un-structured/un-processed HTML text for output
+
+ >>> Text('foo')
+ Text(u'foo')
+ >>> list(Text('<foo>'))
+ [u'<foo>']
+ >>> list(tag('a', Text('<foo>')))
+ [u'<a>', u'\\t<foo>', u'</a>']
+ """
+
+ def __init__ (self, text) :
+ self.text = unicode(text)
+
+ def flatten (self) :
+ yield (0, self.text)
+
+ def __repr__ (self) :
+ return "Text(%r)" % (self.text, )
+
+class Tag (Renderable) :
+ """
+ An immutable HTML tag structure, with the tag's name, attributes and contents.
+ """
+
+ # types of nested items to flatten
+ CONTAINER_TYPES = (types.TupleType, types.ListType, types.GeneratorType)
+
+ # types of items to keep as-is
+ ITEM_TYPES = (Renderable, )
+
+ @classmethod
+ def process_contents (cls, *args) :
+ """
+ Yield the HTML tag's contents from the given sequence of positional arguments as a series of flattened
+ items, eagerly converting them to unicode.
+
+ If no arguments are given, we don't have any children:
+
+ >>> bool(list(Tag.process_contents()))
+ False
+
+ Items that are None will be ignored:
+
+ >>> list(Tag.process_contents(None))
+ []
+
+ Various Python container types are recursively flattened:
+
+ >>> list(Tag.process_contents([1, 2]))
+ [u'1', u'2']
+ >>> list(Tag.process_contents([1], [2]))
+ [u'1', u'2']
+ >>> list(Tag.process_contents([1, [2]]))
+ [u'1', u'2']
+ >>> list(Tag.process_contents(n + 1 for n in xrange(2)))
+ [u'1', u'2']
+ >>> list(Tag.process_contents((1, 2)))
+ [u'1', u'2']
+ >>> list(Tag.process_contents((1), (2, )))
+ [u'1', u'2']
+
+ Our own HTML-aware objects are returned as-is:
+
+ >>> list(Tag.process_contents(Tag.build('foo')))
+ [tag('foo')]
+ >>> list(Tag.process_contents(Text('bar')))
+ [Text(u'bar')]
+
+ All other objects are converted to unicode:
+
+ >>> list(Tag.process_contents('foo', u'bar', 0.123, False))
+ [u'foo', u'bar', u'0.123', u'False']
+
+ """
+
+ for arg in args :
+ if arg is None :
+ # skip null: None
+ continue
+
+ elif isinstance(arg, cls.CONTAINER_TYPES) :
+ # flatten nested container: tuple/list/generator
+ for node in arg :
+ # recurse
+ for item in cls.process_contents(node) :
+ yield item
+
+ elif isinstance(arg, cls.ITEM_TYPES) :
+ # yield item: Renderable
+ yield arg
+
+ else :
+ # as unicode
+ yield unicode(arg)
+
+ @classmethod
+ def process_attrs (cls, **kwargs) :
+ """
+ Yield the HTML tag attributes from the given set of keyword arguments as a series of (name, value) tuples.
+
+ Keyword-only options (`_key=value`) are filtered out:
+
+ >>> dict(Tag.process_attrs(_opt=True))
+ {}
+
+ Attributes with a value of None/False are filtered out:
+
+ >>> dict(Tag.process_attrs(foo=None, bar=False))
+ {}
+
+ A value given as True is returned as the key's value:
+
+ >>> dict(Tag.process_attrs(quux=True))
+ {'quux': u'quux'}
+
+ A (single) trailing underscore in the attribute name is removed:
+
+ >>> dict(Tag.process_attrs(class_='foo'))
+ {'class': u'foo'}
+ >>> dict(Tag.process_attrs(data__='foo'))
+ {'data_': u'foo'}
+ """
+
+ for key, value in kwargs.iteritems() :
+ # keyword arguments are always pure strings
+ assert type(key) is str
+
+ if value is None or value is False:
+ # omit
+ continue
+
+ if key.startswith('_') :
+ # option
+ continue
+
+ if key.endswith('_') :
+ # strip underscore
+ key = key[:-1]
+
+ if value is True :
+ # flag attr
+ value = key
+
+ yield key, unicode(value)
+
+ @classmethod
+ def process_opts (cls, **kwargs) :
+ """
+ Return a series of of the keyword-only _options, extracted from the given dict of keyword arguments, as
+ (k, v) tuples.
+
+ >>> Tag.process_opts(foo='bar', _bar=False)
+ (('bar', False),)
+ """
+
+ return tuple((k.lstrip('_'), v) for k, v in kwargs.iteritems() if k.startswith('_'))
+
+ @classmethod
+ def build (cls, _name, *args, **kwargs) :
+ """
+ Factory function for constructing Tags by directly passing in contents/attributes/options as Python function
+ arguments/keyword arguments.
+
+ The first positional argument is the tag's name:
+
+ >>> Tag.build('foo')
+ tag('foo')
+
+ Further positional arguments are the tag's contents:
+
+ >>> Tag.build('foo', 'quux', 'bar')
+ tag('foo', u'quux', u'bar')
+
+ All the rules used by process_contents() are available:
+
+ >>> Tag.build('foo', [1, None], None, (n for n in xrange(2)))
+ tag('foo', u'1', u'0', u'1')
+
+ The special-case for a genexp as the only argument works:
+
+ >>> f = lambda *args: Tag.build('foo', *args)
+ >>> f('hi' for n in xrange(2))
+ tag('foo', u'hi', u'hi')
+
+ Attributes are passed as keyword arguments, with or without contents:
+
+ >>> Tag.build('foo', id=1)
+ tag('foo', id=u'1')
+ >>> Tag.build('foo', 'quux', bar=5)
+ tag('foo', u'quux', bar=u'5')
+ >>> Tag.build('foo', class_='ten')
+ tag('foo', class=u'ten')
+
+ The attribute names don't conflict with positional argument names:
+
+ >>> Tag.build('bar', name='foo')
+ tag('bar', name=u'foo')
+
+ Options are handled as the 'real' keyword arguments:
+
+ >>> print Tag.build('foo', _selfclosing=False)
+ <foo></foo>
+ >>> print Tag.build('foo', _foo='bar')
+ Traceback (most recent call last):
+ ...
+ TypeError: __init__() got an unexpected keyword argument 'foo'
+ """
+
+ # pre-process incoming user values
+ contents = list(cls.process_contents(*args))
+ attrs = dict(cls.process_attrs(**kwargs))
+
+ # XXX: use Python 2.6 keyword-only arguments instead?
+ options = dict(cls.process_opts(**kwargs))
+
+ return cls(_name, contents, attrs, **options)
+
+ def __init__ (self, name, contents, attrs, selfclosing=None, whitespace_sensitive=None) :
+ """
+ Initialize internal Tag state with the given tag identifier, flattened list of content items, dict of
+ attributes and dict of options.
+
+ selfclosing - set to False to render empty tags as <foo></foo> instead of <foo />
+ (for XHTML -> HTML compatibility)
+
+ whitespace_sensitive - do not indent tag content onto separate rows, render the full tag as a single
+ row
+
+ Use the build() factory function to build Tag objects using Python's function call argument semantics.
+
+ The tag name is used a pure string identifier:
+
+ >>> Tag(u'foo', [], {})
+ tag('foo')
+ >>> Tag(u'\\xE4', [], {})
+ Traceback (most recent call last):
+ ...
+ UnicodeEncodeError: 'ascii' codec can't encode character u'\\xe4' in position 0: ordinal not in range(128)
+
+ Contents have their order preserved:
+
+ >>> Tag('foo', [1, 2], {})
+ tag('foo', 1, 2)
+ >>> Tag('foo', [2, 1], {})
+ tag('foo', 2, 1)
+
+ Attributes can be given:
+
+ >>> Tag('foo', [], dict(foo='bar'))
+ tag('foo', foo='bar')
+
+ Options can be given:
+
+ >>> print Tag('foo', [], {}, selfclosing=False)
+ <foo></foo>
+ """
+
+ self.name = str(name)
+ self.contents = contents
+ self.attrs = attrs
+
+ # options
+ self.selfclosing = selfclosing
+ self.whitespace_sensitive = whitespace_sensitive
+
+ def __call__ (self, *args, **kwargs) :
+ """
+ Return a new Tag as a copy of this tag, but with the given additional attributes/contents.
+
+ The same rules for function positional/keyword arguments apply as for build()
+
+ >>> Tag.build('foo')('bar')
+ tag('foo', u'bar')
+ >>> Tag.build('a', href='index.html')("Home")
+ tag('a', u'Home', href=u'index.html')
+
+ New contents and attributes can be given freely, using the same rules as for Tag.build:
+
+ >>> Tag.build('bar', None)(5, foo=None, class_='bar')
+ tag('bar', u'5', class=u'bar')
+
+ Tag contents accumulate in order:
+
+ >>> Tag.build('a')('b', ['c'])('d')
+ tag('a', u'b', u'c', u'd')
+
+ Each Tag is immutable, so the called Tag isn't changed, but rather a copy is returned:
+
+ >>> t1 = Tag.build('a'); t2 = t1('b'); t1
+ tag('a')
+
+ Attribute values are replaced:
+
+ >>> Tag.build('foo', a=2)(a=3)
+ tag('foo', a=u'3')
+
+ Options are also supported:
+
+ >>> list(Tag.build('foo')(bar='quux', _selfclosing=False))
+ [u'<foo bar="quux"></foo>']
+ """
+
+ # accumulate contents
+ contents = self.contents + list(self.process_contents(*args))
+
+ # merge attrs
+ attrs = dict(self.attrs)
+ attrs.update(self.process_attrs(**kwargs))
+
+ # options
+ opts = dict(
+ selfclosing = self.selfclosing,
+ whitespace_sensitive = self.whitespace_sensitive,
+ )
+ opts.update(self.process_opts(**kwargs))
+
+ # build updated tag
+ return Tag(self.name, contents, attrs, **opts)
+
+ def render_attrs (self) :
+ """
+ Return the HTML attributes string
+
+ >>> Tag.build('x', foo=5, bar='<', quux=None).render_attrs()
+ u'foo="5" bar="<"'
+ >>> Tag.build('x', foo='a"b').render_attrs()
+ u'foo=\\'a"b\\''
+ """
+
+ return " ".join(
+ (
+ u'%s=%s' % (name, saxutils.quoteattr(value))
+ ) for name, value in self.attrs.iteritems()
+ )
+
+ def flatten_items (self, indent=1) :
+ """
+ Flatten our content into a series of indented lines.
+
+ >>> list(Tag.build('tag', 5).flatten_items())
+ [(1, u'5')]
+ >>> list(Tag.build('tag', 'line1', 'line2').flatten_items())
+ [(1, u'line1'), (1, u'line2')]
+
+ Nested :
+ >>> list(Tag.build('tag', 'a', Tag.build('b', 'bb'), 'c').flatten_items())
+ [(1, u'a'), (1, u'<b>'), (2, u'bb'), (1, u'</b>'), (1, u'c')]
+ >>> list(Tag.build('tag', Tag.build('hr'), Tag.build('foo')('bar')).flatten_items())
+ [(1, u'<hr />'), (1, u'<foo>'), (2, u'bar'), (1, u'</foo>')]
+ """
+
+ for item in self.contents :
+ if isinstance(item, Renderable) :
+ # recursively flatten items
+ for line_indent, line in item.flatten() :
+ # indented
+ yield indent + line_indent, line
+
+ else :
+ # render HTML-escaped raw value
+ # escape raw values
+ yield indent, saxutils.escape(item)
+
+ def flatten (self) :
+ """
+ Render the tag and all content as a flattened series of indented lines.
+
+ Empty tags collapse per default:
+
+ >>> list(Tag.build('foo').flatten())
+ [(0, u'<foo />')]
+ >>> list(Tag.build('bar', id=5).flatten())
+ [(0, u'<bar id="5" />')]
+
+ Values are indented inside the start tag:
+
+ >>> list(Tag.build('foo', 'bar', a=5).flatten())
+ [(0, u'<foo a="5">'), (1, u'bar'), (0, u'</foo>')]
+
+ Nested tags are further indented:
+
+ >>> list(Tag.build('1', '1.1', Tag.build('1.2', '1.2.1'), '1.3', a=5).flatten())
+ [(0, u'<1 a="5">'), (1, u'1.1'), (1, u'<1.2>'), (2, u'1.2.1'), (1, u'</1.2>'), (1, u'1.3'), (0, u'</1>')]
+
+ Empty tags are rendered with a separate closing tag on the same line, if desired:
+
+ >>> list(Tag.build('foo', _selfclosing=False).flatten())
+ [(0, u'<foo></foo>')]
+ >>> list(Tag.build('foo', src='asdf', _selfclosing=False).flatten())
+ [(0, u'<foo src="asdf"></foo>')]
+
+ Tags that are declared as whitespace-sensitive are collapsed onto the same line:
+
+ >>> list(Tag.build('foo', _whitespace_sensitive=True).flatten())
+ [(0, u'<foo />')]
+ >>> list(Tag.build('foo', _whitespace_sensitive=True, _selfclosing=False).flatten())
+ [(0, u'<foo></foo>')]
+ >>> list(Tag.build('foo', 'bar', _whitespace_sensitive=True).flatten())
+ [(0, u'<foo>bar</foo>')]
+ >>> list(Tag.build('foo', 'bar\\nasdf\\tx', _whitespace_sensitive=True).flatten())
+ [(0, u'<foo>bar\\nasdf\\tx</foo>')]
+ >>> list(Tag.build('foo', 'bar', Tag.build('quux', 'asdf'), 'asdf', _whitespace_sensitive=True).flatten())
+ [(0, u'<foo>bar<quux>asdf</quux>asdf</foo>')]
+
+ Embedded HTML given as string values is escaped:
+
+ >>> list(Tag.build('foo', '<asdf>'))
+ [u'<foo>', u'\\t<asdf>', u'</foo>']
+
+ Embedded quotes in attribute values are esacaped:
+
+ >>> list(Tag.build('foo', style='ok;" onload="...'))
+ [u'<foo style=\\'ok;" onload="...\\' />']
+ >>> list(Tag.build('foo', style='ok;\\'" onload=..."\\''))
+ [u'<foo style="ok;\\'" onload=..."\\'" />']
+ """
+
+ # optional attr spec
+ if self.attrs :
+ attrs = " " + self.render_attrs()
+
+ else :
+ attrs = ""
+
+ if not self.contents and self.selfclosing is False :
+ # empty tag, but don't use the self-closing syntax..
+ yield 0, u"<%s%s></%s>" % (self.name, attrs, self.name)
+
+ elif not self.contents :
+ # self-closing xml tag
+ # do note that this is invalid HTML, and the space before the / is relevant for parsing it as HTML
+ yield 0, u"<%s%s />" % (self.name, attrs)
+
+ elif self.whitespace_sensitive :
+ # join together each line for each child, discarding the indent
+ content = u''.join(line for indent, line in self.flatten_items())
+
+ # render full tag on a single line
+ yield 0, u"<%s%s>%s</%s>" % (self.name, attrs, content, self.name)
+
+ else :
+ # start tag
+ yield 0, u"<%s%s>" % (self.name, attrs)
+
+ # contents, indented one level below the start tag
+ for indent, line in self.flatten_items(indent=1) :
+ yield indent, line
+
+ # close tag
+ yield 0, u"</%s>" % (self.name, )
+
+ def __repr__ (self) :
+ return 'tag(%s)' % ', '.join(
+ [
+ repr(self.name)
+ ] + [
+ repr(c) for c in self.contents
+ ] + [
+ '%s=%r' % (name, value) for name, value in self.attrs.iteritems()
+ ]
+ )
+
+# factory function for Tag
+tag = Tag.build
+
+
+class Document (Renderable) :
+ """
+ A full XHTML 1.0 document with optional XML header, doctype, html[@xmlns].
+
+ >>> list(Document(tags.html('...')))
+ [u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">', u'<html xmlns="http://www.w3.org/1999/xhtml">', u'\\t...', u'</html>']
+ """
+
+ def __init__ (self, root,
+ doctype='html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"',
+ html_xmlns='http://www.w3.org/1999/xhtml',
+ xml_version=None, xml_encoding=None,
+ ) :
+ # add xmlns attr to root node
+ self.root = root(xmlns=html_xmlns)
+
+ # store
+ self.doctype = doctype
+ self.xml_declaration = {}
+
+ if xml_version :
+ self.xml_declaration['version'] = xml_version
+
+ if xml_encoding :
+ self.xml_declaration['encoding'] = xml_encoding
+
+ def flatten (self) :
+ """
+ Return the header lines along with the normally formatted <html> tag
+ """
+
+ if self.xml_declaration :
+ yield 0, u'<?xml %s ?>' % (' '.join('%s="%s"' % kv for kv in self.xml_declaration.iteritems()))
+
+ if self.doctype :
+ yield 0, u'<!DOCTYPE %s>' % (self.doctype)
+
+ # <html>
+ for indent, line in self.root.flatten() :
+ yield indent, line
+
+class TagFactory (object) :
+ """
+ Build Tags with names give as attribute names
+
+ >>> list(TagFactory().a(href='#')('Yay'))
+ [u'<a href="#">', u'\\tYay', u'</a>']
+
+ >>> list(TagFactory()("><"))
+ [u'><']
+ """
+
+ # full XHTML document
+ document = Document
+
+ def __getattr__ (self, name) :
+ """
+ Get a Tag object with the given name, but no contents
+
+ >>> TagFactory().a
+ tag('a')
+ """
+
+ return Tag(name, [], {})
+
+ def __call__ (self, value) :
+ """
+ Raw HTML.
+ """
+
+ return Text(value)
+
+# static instance
+tags = TagFactory()
+
+# testing
+if __name__ == '__main__' :
+ import doctest
+
+ doctest.testmod()
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pvl/web/urls.py Sun Jan 20 18:26:12 2013 +0200
@@ -0,0 +1,5 @@
+from werkzeug.routing import Map, Rule
+
+def rule (string, endpoint, **opts) :
+ return Rule(string, endpoint=endpoint, defaults=opts)
+
--- a/test.py Sun Jan 20 18:19:03 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-#!/usr/bin/python
-
-from werkzeug.serving import run_simple
-
-from pvl import __version__
-import pvl.args
-import pvl.verkko.wsgi
-
-import optparse
-import logging; log = logging.getLogger('main')
-
-def parse_argv (argv, doc = __doc__) :
- """
- Parse command-line argv, returning (options, args).
-
- Use pvl.ldap.args.apply() to get the LDAPClient
- """
-
- prog = argv.pop(0)
- args = argv
-
- # optparse
- parser = optparse.OptionParser(
- prog = prog,
- usage = '%prog: [options] [<user> [...]]',
- version = __version__,
- description = doc,
- )
-
- # common
- parser.add_option_group(pvl.args.parser(parser))
-
- parser.add_option('-d', '--database-read', metavar='URI', default='sqlite:///var/verkko.db',
- help="Database to use (readonly)")
-
- # parse
- options, args = parser.parse_args(args)
-
- # apply
- pvl.args.apply(options)
-
- return options, args
-
-def main (argv) :
- """
- Manage LDAP user dirs/symlinks/quotas on system.
- """
-
- # parse cmdline
- options, args = parse_argv(argv, doc=__doc__)
-
- # app
- application = pvl.verkko.wsgi.Application(options.database_read)
-
- # wsgi wrapper
- run_simple('0.0.0.0', 8080, application,
- #use_reloader = True,
- use_debugger = (options.loglevel == logging.DEBUG),
- static_files = {
- '/static': 'static',
- },
- )
-
-if __name__ == '__main__' :
- import sys
-
- sys.exit(main(sys.argv))
-
-