[Checkins] SVN: zc.zope3recipes/trunk/ - Added support for using Paste Deployment to specify a WSGI stack

Jim Fulton jim at zope.com
Sun Jan 15 16:15:14 UTC 2012


Log message for revision 124047:
  - Added support for using Paste Deployment to specify a WSGI stack
    over a zope.app-based application,
  

Changed:
  U   zc.zope3recipes/trunk/README.txt
  U   zc.zope3recipes/trunk/setup.py
  U   zc.zope3recipes/trunk/zc/zope3recipes/README.txt
  U   zc.zope3recipes/trunk/zc/zope3recipes/recipes.py
  U   zc.zope3recipes/trunk/zc/zope3recipes/tests.py

-=-
Modified: zc.zope3recipes/trunk/README.txt
===================================================================
--- zc.zope3recipes/trunk/README.txt	2012-01-15 08:35:49 UTC (rev 124046)
+++ zc.zope3recipes/trunk/README.txt	2012-01-15 16:15:13 UTC (rev 124047)
@@ -21,6 +21,13 @@
 ********
 
 ===================
+0.16.0 (2012/01/15)
+===================
+
+- Added support for using Paste Deployment to specify a WSGI stack
+  over a zope.app-based application,
+
+===================
 0.15.0 (2011/12/12)
 ===================
 

Modified: zc.zope3recipes/trunk/setup.py
===================================================================
--- zc.zope3recipes/trunk/setup.py	2012-01-15 08:35:49 UTC (rev 124046)
+++ zc.zope3recipes/trunk/setup.py	2012-01-15 16:15:13 UTC (rev 124047)
@@ -1,3 +1,18 @@
+##############################################################################
+#
+# Copyright Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+name, version = "zc.zope3recipes", "0"
+
 import os
 from setuptools import setup, find_packages
 
@@ -4,10 +19,9 @@
 def read(*rnames):
     return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
 
-name = "zc.zope3recipes"
 setup(
     name = name,
-    version = '0',
+    version = version,
     author = "Jim Fulton",
     author_email = "jim at zope.com",
     description = "ZC Buildout recipe for defining Zope 3 applications",
@@ -44,7 +58,7 @@
              ],
         },
     extras_require = dict(
-        tests = ['zdaemon', 'zc.recipe.filestorage'],
+        tests = ['zdaemon', 'zc.recipe.filestorage', 'PasteScript'],
         ),
     classifiers=[
         "Development Status :: 5 - Production/Stable",

Modified: zc.zope3recipes/trunk/zc/zope3recipes/README.txt
===================================================================
--- zc.zope3recipes/trunk/zc/zope3recipes/README.txt	2012-01-15 08:35:49 UTC (rev 124046)
+++ zc.zope3recipes/trunk/zc/zope3recipes/README.txt	2012-01-15 16:15:13 UTC (rev 124047)
@@ -3029,3 +3029,219 @@
     <BLANKLINE>
     # print "starting debugzope..."
     execfile(debugzope)
+
+Paste-deployment support
+========================
+
+You can use paste-deployment to control WSGI servers and middleware.
+You indicate this by specifying ``paste`` in the ``servers`` option:
+
+    >>> write('buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... develop = demo1 demo2
+    ... parts = instance
+    ...
+    ... [zope3]
+    ... location = %(zope3)s
+    ...
+    ... [myapp]
+    ... recipe = zc.zope3recipes:application
+    ... servers = paste
+    ... site.zcml = <include package="demo2" />
+    ...             <principal
+    ...                 id="zope.manager"
+    ...                 title="Manager"
+    ...                 login="jim"
+    ...                 password_manager="SHA1"
+    ...                 password="40bd001563085fc35165329ea1ff5c5ecbdbbeef"
+    ...                 />
+    ...             <grant
+    ...                 role="zope.Manager"
+    ...                 principal="zope.manager"
+    ...                 />
+    ... eggs = demo2
+    ...
+    ... [instance]
+    ... recipe = zc.zope3recipes:instance
+    ... application = myapp
+    ... zope.conf =
+    ...    threads 1
+    ...    ${database:zconfig}
+    ...
+    ...
+    ... [database]
+    ... recipe = zc.recipe.filestorage
+    ... ''' % globals())
+
+When we run the buildout, we'll get a paste-based runzope script and
+paste-based instance start scripts.
+
+.. test
+
+    >>> print system(join('bin', 'buildout')),
+    Develop: '/sample-buildout/demo1'
+    Develop: '/sample-buildout/demo2'
+    Uninstalling run-foo.
+    Uninstalling instance.
+    Uninstalling myapp.
+    Installing database.
+    Installing myapp.
+    Generated script '/sample-buildout/parts/myapp/runzope'.
+    Generated script '/sample-buildout/parts/myapp/debugzope'.
+    Installing instance.
+    Generated script '/sample-buildout/bin/instance'.
+
+
+    >>> cat('parts', 'myapp', 'runzope')
+    #!/usr/local/python/2.6/bin/python2.6
+    <BLANKLINE>
+    import sys
+    sys.path[0:0] = [
+        '/sample-buildout/demo2',
+        '/sample-buildout/eggs/PasteScript-1.7.4.2-py2.6.egg',
+        '/sample-buildout/eggs/setuptools-0.6c12dev_r88846-py2.6.egg',
+        '/sample-buildout/eggs/PasteDeploy-1.5.0-py2.6.egg',
+        '/sample-buildout/eggs/Paste-1.7.5.1-py2.6.egg',
+        '/sample-buildout/demo1',
+        ]
+    <BLANKLINE>
+    <BLANKLINE>
+    import paste.script.command
+    <BLANKLINE>
+    if __name__ == '__main__':
+        paste.script.command.run(['serve']+sys.argv[1:])
+
+
+    >>> cat('parts', 'instance', 'zope.conf')
+    site-definition /sample-buildout/parts/myapp/site.zcml
+    <BLANKLINE>
+    <zodb>
+      <filestorage>
+        path /sample-buildout/parts/database/Data.fs
+      </filestorage>
+    </zodb>
+    <BLANKLINE>
+    <logger accesslog>
+      level info
+      name accesslog
+      propagate false
+    <BLANKLINE>
+      <logfile>
+        format %(message)s
+        path /sample-buildout/parts/instance/access.log
+      </logfile>
+    </logger>
+    <BLANKLINE>
+    <eventlog>
+      <logfile>
+        formatter zope.exceptions.log.Formatter
+        path STDOUT
+      </logfile>
+    </eventlog>
+
+    >>> cat('parts', 'instance', 'zdaemon.conf')
+    <runner>
+      daemon on
+      directory /sample-buildout/parts/instance
+      program /sample-buildout/parts/myapp/runzope /sample-buildout/parts/instance/paste.ini
+      socket-name /sample-buildout/parts/instance/zdaemon.sock
+      transcript /sample-buildout/parts/instance/z3.log
+    </runner>
+    <BLANKLINE>
+    <eventlog>
+      <logfile>
+        path /sample-buildout/parts/instance/z3.log
+      </logfile>
+    </eventlog>
+
+We also get a paste.ini file in the instance directory, which defines
+the application and server and is used when running paste::
+
+    >>> cat('parts', 'instance', 'paste.ini')
+    [app:main]
+    use = egg:zope.app.wsgi
+    config_file = /sample-buildout/parts/instance/zope.conf
+    filter-with = translogger
+    <BLANKLINE>
+    [filter:translogger]
+    use = egg:Paste#translogger
+    setup_console_handler = False
+    logger_name = accesslog
+    <BLANKLINE>
+    [server:main]
+    use = egg:zope.server
+    host = 
+    port = 8080
+    threads = 1
+
+Note that the threads setting made in zope.conf was moved to paste.ini
+
+Note too that past:translogger was used to provide an access log.
+
+If you don't want to use zope.server, or if you want to control the
+server configuration yourself, you can provide a paste.init option::
+
+    >>> write('buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... develop = demo1 demo2
+    ... parts = instance
+    ...
+    ... [zope3]
+    ... location = %(zope3)s
+    ...
+    ... [myapp]
+    ... recipe = zc.zope3recipes:application
+    ... servers = paste
+    ... site.zcml = <include package="demo2" />
+    ...             <principal
+    ...                 id="zope.manager"
+    ...                 title="Manager"
+    ...                 login="jim"
+    ...                 password_manager="SHA1"
+    ...                 password="40bd001563085fc35165329ea1ff5c5ecbdbbeef"
+    ...                 />
+    ...             <grant
+    ...                 role="zope.Manager"
+    ...                 principal="zope.manager"
+    ...                 />
+    ... eggs = demo2
+    ...
+    ... [instance]
+    ... recipe = zc.zope3recipes:instance
+    ... application = myapp
+    ... zope.conf =
+    ...    threads 1
+    ...    ${database:zconfig}
+    ... paste.ini = test and not working :)
+    ...
+    ...
+    ... [database]
+    ... recipe = zc.recipe.filestorage
+    ... ''' % globals())
+
+.. test
+
+    >>> print system(join('bin', 'buildout')),
+    Develop: '/sample-buildout/demo1'
+    Develop: '/sample-buildout/demo2'
+    Uninstalling instance.
+    Updating database.
+    Updating myapp.
+    Installing instance.
+    Generated script '/sample-buildout/bin/instance'.
+
+In this example, we gave useless text in the paste.ini option and we
+got a nonsense paste.ini file::
+
+    >>> cat('parts', 'instance', 'paste.ini')
+    [app:main]
+    use = egg:zope.app.wsgi
+    config_file = /sample-buildout/parts/instance/zope.conf
+    <BLANKLINE>
+    test and not working :)
+
+This illustrates that the recipe doesn't care what you provide.  It
+uses it with the application definition instead of supplying
+zope.server and paste.translogger definition.

Modified: zc.zope3recipes/trunk/zc/zope3recipes/recipes.py
===================================================================
--- zc.zope3recipes/trunk/zc/zope3recipes/recipes.py	2012-01-15 08:35:49 UTC (rev 124046)
+++ zc.zope3recipes/trunk/zc/zope3recipes/recipes.py	2012-01-15 16:15:13 UTC (rev 124047)
@@ -14,14 +14,17 @@
 """Collected Zope 3 recipes
 """
 
-import os, sys, shutil
+import cStringIO
 import logging
+import os
+import pkg_resources
 import pprint
+import re
+import shutil
+import sys
 import zc.buildout
+import ZConfig.schemaless
 import zc.recipe.egg
-import pkg_resources
-import ZConfig.schemaless
-import cStringIO
 
 this_loc = pkg_resources.working_set.find(
     pkg_resources.Requirement.parse('zc.zope3recipes')).location
@@ -30,6 +33,7 @@
     # name     (module,                  http-name)
     'twisted': ('zope.app.twisted.main', 'HTTP'),
     'zserver': ('zope.app.server.main',  'WSGI-HTTP'),
+    'paste': ('zope.app.server.main',  ''),
     }
 
 WIN = False
@@ -51,7 +55,11 @@
         options['servers'] = options.get('servers', 'twisted')
         if options['servers'] not in server_types:
             raise ValueError(
-                'servers setting must be one of "twisted" or "zserver"')
+                'servers setting must be one of %s' %
+                repr(sorted(server_types))[1:-1]
+                )
+        if options['servers'] == 'paste':
+            options['eggs'] += '\n  PasteScript\n'
 
         options['scripts'] = ''
         self.egg = zc.recipe.egg.Egg(buildout, name, options)
@@ -74,14 +82,27 @@
             self.egg.install()
             requirements, ws = self.egg.working_set()
 
+            server_module = server_types[options['servers']][0]
+
             # install subprograms and ctl scripts
-            server_module = server_types[options['servers']][0]
+            if options['servers'] == 'paste':
+                reqs = ['PasteScript']
+                scripts = dict(paster='runzope')
+                arguments = "['serve']+sys.argv[1:]"
+            else:
+                reqs = [('runzope', server_module, 'main')]
+                scripts = None
+                arguments = ''
+
             extra_paths = options.get('extra-paths', '')
             initialization = options.get('initialization') or ''
+
             zc.buildout.easy_install.scripts(
-                [('runzope', server_module, 'main')],
+                reqs,
                 ws, options['executable'], dest,
+                scripts=scripts,
                 extra_paths=extra_paths.split(),
+                arguments=arguments,
                 initialization=initialization,
                 relative_paths=self.egg._relative_paths,
                 )
@@ -222,6 +243,8 @@
                                           self.name+'-zope.conf')
             zdaemon_conf_path = os.path.join(options['etc-directory'],
                                              self.name+'-zdaemon.conf')
+            paste_ini_path = os.path.join(options['etc-directory'],
+                                             self.name+'-paste.ini')
             event_log_path = os.path.join(options['log-directory'],
                                           self.name+'-z3.log')
             access_log_path = os.path.join(options['log-directory'],
@@ -250,6 +273,7 @@
         else:
             zope_conf_path = os.path.join(run_directory, 'zope.conf')
             zdaemon_conf_path = os.path.join(run_directory, 'zdaemon.conf')
+            paste_ini_path = os.path.join(run_directory, 'paste.ini')
             event_log_path = os.path.join(run_directory, 'z3.log')
             access_log_path = os.path.join(run_directory, 'access.log')
             socket_path = os.path.join(run_directory, 'zdaemon.sock')
@@ -272,21 +296,76 @@
                     os.path.join(app_loc, 'site.zcml')
                     ]
 
+            threads = None
             server_type = server_types[options['servers']][1]
-            for address in options.get('address', '').split():
-                zope_conf.sections.append(
-                    ZConfig.schemaless.Section(
-                        'server',
-                        data=dict(type=[server_type], address=[address]))
-                    )
-            if not [s for s in zope_conf.sections
-                    if ('server' in s.type)]:
-                zope_conf.sections.append(
-                    ZConfig.schemaless.Section(
-                        'server',
-                        data=dict(type=[server_type], address=['8080']))
-                    )
+            if server_type:
+                for address in options.get('address', '').split():
+                    zope_conf.sections.append(
+                        ZConfig.schemaless.Section(
+                            'server',
+                            data=dict(type=[server_type], address=[address]))
+                        )
+                if not [s for s in zope_conf.sections
+                        if ('server' in s.type)]:
+                    zope_conf.sections.append(
+                        ZConfig.schemaless.Section(
+                            'server',
+                            data=dict(type=[server_type], address=['8080']))
+                        )
+                program_args = '-C '+zope_conf_path
+            else: # paste
+                paste_ini = options.get('paste.ini', '')
+                if not paste_ini:
+                    address = options.get('address', '8080').split()
+                    if not len(address) == 1:
+                        raise zc.buildout.UserError(
+                            "If you don't specify a paste.ini option, "
+                            "you must specify exactly one address.")
+                    [address] = address
+                    if ':' in address:
+                        host, port = address.rsplit(':', 1)
+                        port = int(port)
+                    elif re.match('\d+$', address):
+                        host = ''
+                        port = int(address)
+                    else:
+                        host = address
+                        port = 8080
 
+                    threads = zope_conf.pop('threads', None)
+                    threads = threads and threads[0] or 4
+
+                    paste_ini = (
+                        "filter-with = translogger\n"
+                        "\n"
+                        "[filter:translogger]\n"
+                        "use = egg:Paste#translogger\n"
+                        "setup_console_handler = False\n"
+                        "logger_name = accesslog\n"
+                        "\n"
+                        ""
+                        "[server:main]\n"
+                        "use = egg:zope.server\n"
+                        "host = %s\n"
+                        "port = %s\n"
+                        "threads = %s\n"
+                        % (host, port, threads)
+                        )
+
+                paste_ini = (
+                    "[app:main]\n"
+                    "use = egg:zope.app.wsgi\n"
+                    "config_file = %s\n"
+                    % zope_conf_path) + paste_ini
+
+                creating.append(paste_ini_path)
+                f = open(paste_ini_path, 'w')
+                f.write(paste_ini)
+                f.close()
+
+                program_args = paste_ini_path
+
+
             if not [s for s in zope_conf.sections if s.type == 'zodb']:
                 raise zc.buildout.UserError(
                     'No database sections have been defined.')
@@ -294,6 +373,18 @@
             if not [s for s in zope_conf.sections if s.type == 'accesslog']:
                 zope_conf.sections.append(access_log(access_log_path))
 
+            if not server_type: # paste
+                for s in zope_conf.sections:
+                    if s.type != 'accesslog':
+                        continue
+                    s.type = 'logger'
+                    s.name = 'accesslog'
+                    s['name'] = [s.name]
+                    s['level'] = ['info']
+                    s['propagate'] = ['false']
+                    for formatter in s.sections:
+                        formatter['format'] = ['%(message)s']
+
             if not [s for s in zope_conf.sections if s.type == 'eventlog']:
                 zope_conf.sections.append(event_log('STDOUT'))
 
@@ -303,9 +394,8 @@
                 cStringIO.StringIO(zdaemon_conf))
 
             defaults = {
-                'program': "%s -C %s" % (os.path.join(app_loc, 'runzope'),
-                                         zope_conf_path,
-                                         ),
+                'program': "%s %s" % (os.path.join(app_loc, 'runzope'),
+                                      program_args),
                 'daemon': 'on',
                 'transcript': event_log_path,
                 'socket-name': socket_path,
@@ -436,10 +526,7 @@
     return ZConfig.schemaless.Section(
         'eventlog', '', None,
         [ZConfig.schemaless.Section(
-             'logfile',
-             '',
-             dict(path=[path])),
-         ])
+            'logfile', '', dict(path=[path]))])
 
 
 logrotate_template = """%(logfile)s {

Modified: zc.zope3recipes/trunk/zc/zope3recipes/tests.py
===================================================================
--- zc.zope3recipes/trunk/zc/zope3recipes/tests.py	2012-01-15 08:35:49 UTC (rev 124046)
+++ zc.zope3recipes/trunk/zc/zope3recipes/tests.py	2012-01-15 16:15:13 UTC (rev 124047)
@@ -209,6 +209,9 @@
     zc.buildout.testing.install_develop('zc.zope3recipes', test)
     zc.buildout.testing.install('zope.exceptions', test)
     zc.buildout.testing.install('zope.interface', test)
+    zc.buildout.testing.install('PasteScript', test)
+    zc.buildout.testing.install('PasteDeploy', test)
+    zc.buildout.testing.install('Paste', test)
     zc.buildout.testing.install('zope.testing', test)
     zc.buildout.testing.install('zc.recipe.egg', test)
     zc.buildout.testing.install('zdaemon', test)



More information about the checkins mailing list