[z3-checkins] r37244 - in z3/deliverance/DeliveranceVHoster/trunk: . dvhoster tests

ianb at codespeak.net ianb at codespeak.net
Wed Jan 24 02:12:39 CET 2007


Author: ianb
Date: Wed Jan 24 02:12:31 2007
New Revision: 37244

Modified:
   z3/deliverance/DeliveranceVHoster/trunk/dvhoster/dataprovider.py
   z3/deliverance/DeliveranceVHoster/trunk/dvhoster/dispatcher.py
   z3/deliverance/DeliveranceVHoster/trunk/setup.cfg
   z3/deliverance/DeliveranceVHoster/trunk/tests/   (props changed)
   z3/deliverance/DeliveranceVHoster/trunk/tests/test_functional_api.py
Log:
Started the API, using OHM.  Also functional tests (not all passing) for the API

Modified: z3/deliverance/DeliveranceVHoster/trunk/dvhoster/dataprovider.py
==============================================================================
--- z3/deliverance/DeliveranceVHoster/trunk/dvhoster/dataprovider.py	(original)
+++ z3/deliverance/DeliveranceVHoster/trunk/dvhoster/dataprovider.py	Wed Jan 24 02:12:31 2007
@@ -1,5 +1,13 @@
 import re
 import os
+from ohm import persist
+from ohm import server
+from ohm import descriptors
+from ohm import lildav
+from ohm.validators import LineConverter
+from formencode import validators
+from formencode.foreach import ForEach
+from formencode.compound import All
 
 domain_re = re.compile(r'^localhost|[a-z][a-z0-9.\-]+\.[a-z]+$', re.I)
 
@@ -48,7 +56,7 @@
         dir = os.path.join(self.dir, domain_name)
         if not os.path.exists(dir):
             os.mkdir(dir)
-        return DomainDataProvider(domain_name, dir)
+        return DomainDataProvider(self, domain_name, dir)
 
     def normalize(self, domain_name):
         domain_name = domain_name.strip().lower()
@@ -57,51 +65,22 @@
                 'Bad domain name: %r' % domain_name)
         return domain_name
 
-class file_property(object):
-
-    def __init__(self, basename):
-        self.basename = basename
-
-    def filename(self, obj):
-        return os.path.join(obj.dir, self.basename)
-
-    def __get__(self, obj, type=None):
-        if obj is None:
-            return self
-        if not os.path.exists(self.filename(obj)):
-            return None
-        f = open(self.filename(obj), 'rb')
-        c = f.read()
-        f.close()
-        return c
-
-    def __set__(self, obj, value):
-        f = open(self.filename(obj), 'wb')
-        f.write(value)
-        f.close()
-
-    def __del__(self, obj):
-        os.unlink(self.filename(obj))
-
-    def __repr__(self):
-        return '%s(%r)' % (
-            self.__class__.__name__, self.basename)
-
 class DomainDataProvider(object):
 
-    def __init__(self, domain_name, dir):
-        self.domain_name = domain_name
-        self.dir = dir
+    def __init__(self, provider, domain_name, base_dir):
+        self._domain_name = domain_name
+        self.base_dir = base_dir
+        self.provider = provider
 
     def __repr__(self):
         return '<%s %s for %s in %r>' % (
             self.__class__.__name__,
             hex(id(self)),
-            self.domain_name,
-            self.dir)
+            self._domain_name,
+            self.base_dir)
 
     def initialize(self):
-        for dir in [self.dir, self.rule_dir,
+        for dir in [self.base_dir, self.rule_dir,
                     self.static_dir]:
             if not os.path.exists(dir):
                 os.mkdir(dir)
@@ -116,25 +95,99 @@
 
     @property
     def initialized(self):
-        return bool(self.remote)
+        return hasattr(self, 'remote')
 
     @property
     def rule_dir(self):
-        return os.path.join(self.dir, 'rules')
+        return os.path.join(self.base_dir, 'rules')
 
     @property
     def static_dir(self):
-        return os.path.join(self.dir, 'static')
-    
-    remote = file_property('remote.txt')
-    theme_uri = file_property('theme_uri.txt')
-    theme_id = file_property('theme_id.txt')
-    rule_ids = file_property('rule_ids.txt')
+        return os.path.join(self.base_dir, 'static')
+
+    remote = persist.file_property('remote.txt')
+    theme_uri = persist.file_property('theme_uri.txt')
+    theme_id = persist.file_property('theme_id.txt')
+    rule_ids = persist.file_property('rule_ids.txt')
+    aliases = descriptors.line_converter(persist.file_property('aliases.txt'))
 
     def set_rule_file(self, filename, content):
         filename = os.path.join(self.rule_dir, filename)
         f = open(filename, 'wb')
         f.write(content)
         f.close()
-        
 
+    def domain__get(self):
+        return self._domain_name
+
+    def domain__set(self, value):
+        new_obj = self.provider.rename_domain(self.domain, value)
+        # Clone new object as self:
+        self.__dict__.update(new_obj.__dict__)
+
+    domain = property(domain__get, domain__set)
+
+class DomainValidator(validators.Regex):
+    regex = r'^[a-z0-9\-]+|[a-z0-9][a-z0-9\-\.\_]*\.[a-z]+$'
+
+class ListDictValidator(validators.FancyValidator):
+    required_keys = ()
+    optional_keys = ()
+    
+    def validate_python(self, value, state=None):
+        # Should be like [{'path': path, 'remote_uri': uri, 'comment': str}]
+        # comment is optional
+        try:
+            assert isinstance(value, list), 'Must be list'
+            for d in value:
+                keys = d.keys()
+                for key in self.required_keys:
+                    assert key in d, "Must have key %r in %r" % d
+                    assert isinstance(d[key], basestring), (
+                        '%s must be string (not %r)' % (key, d[key]))
+                    keys.remove(key)
+                for key in self.optional_keys:
+                    if key not in d:
+                        continue
+                    assert isinstance(d[key], basestring), (
+                        '%s must be string (not %r)' % (key, d[key]))
+                    keys.remove(key)
+                assert not keys, (
+                    "Key(s) not allowed: %s in %r"
+                    % (', '.join(map(repr, keys)), d))
+        except AssertionError, e:
+            raise validators.Invalid(str(e), value, state)
+
+class RemoteURIValidator(ListDictValidator):
+    required_keys = ('path', 'remote_uri')
+    optional_keys = ('comment', )
+
+class RewriteValidator(ListDictValidator):
+    required_keys = ('rewrite', )
+    # @@: Really we should require one of path or prefix
+    optional_keys = ('path', 'prefix', 'comment')
+
+class ProviderApp(server.ApplicationWrapper):
+
+    theme_uri = server.Setter(
+        validator=validators.URL())
+    domain = server.Setter(
+        validator=DomainValidator())
+    aliases = server.Setter(
+        validator=All(ForEach(DomainValidator()), LineConverter()))
+    remote_uris = server.JSONSetter(
+        validator=RemoteURIValidator())
+    redirects = server.JSONSetter(
+        validator=RewriteValidator())
+
+    @server.appfactory()
+    def rules(self):
+        if not os.path.exists(self.rule_dir):
+            os.makedirs(self.rule_dir)
+        return lildav.LilDAV(self.rule_dir)
+
+    @server.appfactory()
+    def static(self):
+        if not os.path.exists(self.static_dir):
+            os.makedirs(self.static_dir)
+        return lildav.LilDAV(self.rule_dir)

Modified: z3/deliverance/DeliveranceVHoster/trunk/dvhoster/dispatcher.py
==============================================================================
--- z3/deliverance/DeliveranceVHoster/trunk/dvhoster/dispatcher.py	(original)
+++ z3/deliverance/DeliveranceVHoster/trunk/dvhoster/dispatcher.py	Wed Jan 24 02:12:31 2007
@@ -8,7 +8,7 @@
 from wsgifilter import proxyapp
 from wsgifilter import relocateresponse
 from deliverance.wsgimiddleware import DeliveranceMiddleware
-from dvhoster.dataprovider import DataProvider
+from dvhoster.dataprovider import DataProvider, ProviderApp
 from dvhoster import current_environ
 from dvhoster.debuginterp import Renderer
 
@@ -38,20 +38,16 @@
             environ, with_query_string=False,
             path_info='')
         path_info = norm_path(environ.get('PATH_INFO', ''))
-        if (not domain_info.initialized
-            or not domain_info.remote
-            or not domain_info.theme_uri):
-            new_url = construct_url(
-                environ, with_query_string=False,
-                path_info='/_deliverance')
-            exc = httpexceptions.HTTPTemporaryRedirect(
-                headers=[('location', new_url)])
-            return exc(environ, start_response)
+        if path_info.startswith('/.deliverance'):
+            path_info_pop(environ)
+            subapp = ProviderApp(domain_info)
+            return subapp(environ, start_response)
         if path_info.startswith('/_rules'):
             path_info_pop(environ)
             subapp = StaticURLParser(
                 domain_info.rule_dir)
             return subapp(environ, start_response)
+            
         static_path = os.path.join(domain_info.static_dir,
                                    path_info.lstrip('/'))
         static_fn = self.find_file(static_path)

Modified: z3/deliverance/DeliveranceVHoster/trunk/setup.cfg
==============================================================================
--- z3/deliverance/DeliveranceVHoster/trunk/setup.cfg	(original)
+++ z3/deliverance/DeliveranceVHoster/trunk/setup.cfg	Wed Jan 24 02:12:31 2007
@@ -1,6 +1,6 @@
 [egg_info]
 tag_build = dev
-tag_svn_revision = true
+#tag_svn_revision = true
 
 [easy_install]
 find_links = http://www.pylonshq.com/download/

Modified: z3/deliverance/DeliveranceVHoster/trunk/tests/test_functional_api.py
==============================================================================
--- z3/deliverance/DeliveranceVHoster/trunk/tests/test_functional_api.py	(original)
+++ z3/deliverance/DeliveranceVHoster/trunk/tests/test_functional_api.py	Wed Jan 24 02:12:31 2007
@@ -1,15 +1,16 @@
+import os
+import shutil
 from paste.fixture import TestApp
 from paste.urlparser import StaticURLParser
 from dvhoster.wsgiapp import make_app
 import wsgi_intercept
 
-wsgi_app = make_app(
-    {},
-    data_dir=os.path.join(os.path.dirname(__file__), 'test-data'))
+data_filename = os.path.join(os.path.dirname(__file__), 'test-data')
+wsgi_app = make_app({}, data_dir=data_filename)
 app = TestApp(wsgi_app)
 
 def put(uri, data):
-    app.post(uri, data, extra_environ={'REQUEST_METHOD': 'PUT'})
+    app.post(uri, data, extra_environ={'REQUEST_METHOD': 'PUT'}, status=(201, 204))
 
 rule_data = '''
 <?xml version="1.0" encoding="UTF-8"?>
@@ -32,14 +33,19 @@
 
 
 def test_everything():
+    yield (reset_env,)
     yield (create_api,)
     yield (use_api, 'localhost')
     yield (rename_site,)
     yield (use_api, 'localhost2')
 
-def create_api():
-    app = make_test_app()
+def reset_env():
+    if os.path.exists(data_filename):
+        shutil.rmtree(data_filename)
+    os.mkdir(data_filename)
+            
 
+def create_api():
     # Theme:
     uri = 'http://wsgify.org/theme.html'
     put('/.deliverance/theme_uri', uri)
@@ -54,13 +60,13 @@
     assert res.body == 'localhost'
 
     # Aliases:
-    data = "localhost.localhost\nexample.com"
+    data = "localhost.localhost\nexample.com\n"
     put('/.deliverance/aliases', data)
     assert app.get('/.deliverance/aliases').body == data
 
     data = '''
     [{"path": "/", "remote_uri": "http://wsgify.org/"},
-     {"path": "/bar", "remote_uri": "http://wsgify.org/blah", comment="x"}]
+     {"path": "/bar", "remote_uri": "http://wsgify.org/blah", "comment": "x"}]
      '''
 
     put('/.deliverance/remote_uris', data)
@@ -76,9 +82,9 @@
     data = '<html>some data!</html>'
     put('/.deliverance/static/data.html', data)
     res = app.get('/.deliverance/static/data.html')
-    assert res.data == data
+    assert res.body == data
     app.get('/.deliverance/static/subdir',
-            extra_environ={'REQUEST_METHOD', 'MKCOL'})
+            extra_environ={'REQUEST_METHOD': 'MKCOL'}, status=201)
     put('/.deliverance/static/subdir/foo.html', 'blah')
     res = app.get('/.deliverance/static/subdir/foo.html')
     assert res.body == 'blah'
@@ -88,7 +94,6 @@
     res = app.get('/subdir/', status=404)
     
 def use_api(hostname):
-    app.extra_environ['HTTP_HOST'] = hostname
     res = app.get('/index.html')
     res.mustcontain(
         # From the theme:
@@ -117,4 +122,10 @@
     res = app.get('/subdir/foo.html')
     assert res.body == 'blah'
     
-        
+def rename_site():
+    put('/.deliverance/domain', 'localhost2')
+    app.extra_environ['HTTP_HOST'] = 'localhost2'
+    res = app.get('/.deliverance/aliases')
+    res.mustcontain('localhost')
+    assert app.get('/.deliverance/domain').body == 'localhost2'
+    


More information about the z3-checkins mailing list