[Checkins] SVN: zc.zope3recipes/dev/ Initial versions of new app and instance recipes.

Jim Fulton jim at zope.com
Wed Jan 10 14:56:28 EST 2007


Log message for revision 71896:
  Initial versions of new app and instance recipes.
  

Changed:
  _U  zc.zope3recipes/dev/
  A   zc.zope3recipes/dev/buildout.cfg
  A   zc.zope3recipes/dev/setup.py
  A   zc.zope3recipes/dev/zc/
  A   zc.zope3recipes/dev/zc/__init__.py
  A   zc.zope3recipes/dev/zc/zope3recipes/
  A   zc.zope3recipes/dev/zc/zope3recipes/README.txt
  A   zc.zope3recipes/dev/zc/zope3recipes/__init__.py
  A   zc.zope3recipes/dev/zc/zope3recipes/ctl.py
  A   zc.zope3recipes/dev/zc/zope3recipes/debugzope.py
  A   zc.zope3recipes/dev/zc/zope3recipes/recipes.py
  A   zc.zope3recipes/dev/zc/zope3recipes/tests.py

-=-

Property changes on: zc.zope3recipes/dev
___________________________________________________________________
Name: svn:ignore
   + develop-eggs
bin
parts
.installed.cfg


Added: zc.zope3recipes/dev/buildout.cfg
===================================================================
--- zc.zope3recipes/dev/buildout.cfg	2007-01-10 19:43:48 UTC (rev 71895)
+++ zc.zope3recipes/dev/buildout.cfg	2007-01-10 19:56:27 UTC (rev 71896)
@@ -0,0 +1,8 @@
+[buildout]
+parts = test
+develop = .
+find-links = http://download.zope.org/distribution
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = zc.zope3recipes [tests]


Property changes on: zc.zope3recipes/dev/buildout.cfg
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zc.zope3recipes/dev/setup.py
===================================================================
--- zc.zope3recipes/dev/setup.py	2007-01-10 19:43:48 UTC (rev 71895)
+++ zc.zope3recipes/dev/setup.py	2007-01-10 19:56:27 UTC (rev 71896)
@@ -0,0 +1,31 @@
+from setuptools import setup, find_packages
+
+name = "zc.zope3recipes"
+setup(
+    name = name,
+    version = "0.1a1",
+    author = "Jim Fulton",
+    author_email = "jim at zope.com",
+    description = "ZC Buildout recipe for defining Zope 3 applications",
+    #long_description = open('README.txt').read(),
+    license = "ZPL 2.1",
+    keywords = "zope3 buildout",
+    url='http://svn.zope.org/'+name,
+
+    packages = find_packages('.'),
+    include_package_data = True,
+    #package_dir = {'':'src'},
+    namespace_packages = ['zc'],
+    install_requires = ['zc.buildout', 'zope.testing', 'setuptools',
+                        'zc.recipe.egg', 'ZConfig'],
+    dependency_links = ['http://download.zope.org/distribution/'],
+    entry_points = {
+        'zc.buildout': [
+             'app = %s.recipes:App' % name,
+             'instance = %s.recipes:Instance' % name,
+             ]
+        },
+    extras_require = dict(
+        tests = ['zdaemon', 'zc.recipe.filestorage'],
+        ),
+    )


Property changes on: zc.zope3recipes/dev/setup.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: zc.zope3recipes/dev/zc/__init__.py
===================================================================
--- zc.zope3recipes/dev/zc/__init__.py	2007-01-10 19:43:48 UTC (rev 71895)
+++ zc.zope3recipes/dev/zc/__init__.py	2007-01-10 19:56:27 UTC (rev 71896)
@@ -0,0 +1 @@
+__import__('pkg_resources').declare_namespace(__name__)


Property changes on: zc.zope3recipes/dev/zc/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: zc.zope3recipes/dev/zc/zope3recipes/README.txt
===================================================================
--- zc.zope3recipes/dev/zc/zope3recipes/README.txt	2007-01-10 19:43:48 UTC (rev 71895)
+++ zc.zope3recipes/dev/zc/zope3recipes/README.txt	2007-01-10 19:56:27 UTC (rev 71896)
@@ -0,0 +1,705 @@
+=============
+Zope3 Recipes
+=============
+
+The Zope 3 recipes allow one to define Zope applications and instances
+of those applications.  A Zope application is a collection of software
+and software configuration, expressed as ZCML.  A Zope instance
+invokes the application with a specific instance configuration.  A
+single application may have many instances.
+
+Building Zope 3 Applications
+============================
+
+The "app" recipe is used to define a Zope application.  It is designed
+to work with classic Zope releases. (In the future, there will be an
+"application" recipe that will work with Zope soley as eggs.)  The app
+recipe causes a part to be created. The part will contain the scripts
+runzope and debugzope and the application's site.zcml.  Both of the
+scripts will require providing a -C option and the path to a zope.conf
+file when run.  The debugzope script can be run with a script name and
+arguments, in which case it will run the script, rather than starting
+an interactive session.
+
+The app recipe accepts the following options:
+
+zope3
+  The name of a section defining a location option that gives the
+  location of a Zope installation.  This can be either a checkout or a
+  distribution.  If the location has a lib/python subdirectory, it is
+  treated as a distribution, othwerwise, it must have a src
+  subdirectory and will be treated as a checkout. This option defaults
+  to "zope3".
+
+site.zcml
+  The contents of site.zcml.
+
+eggs
+  The names of one or more eggs, with their dependencies that should
+  be included in the Python path of the generated scripts. 
+
+Let's look at an example.  We'll make a faux zope installation:
+
+    >>> zope3 = tmpdir('zope3')
+    >>> mkdir(zope3, 'src')
+
+We'll also define some (bogus) eggs that we can use in our
+application:
+
+    >>> mkdir('demo1')
+    >>> write('demo1', 'setup.py', 
+    ... '''
+    ... from setuptools import setup
+    ... setup(name = 'demo1')
+    ... ''')
+
+    >>> mkdir('demo2')
+    >>> write('demo2', 'setup.py', 
+    ... '''
+    ... from setuptools import setup
+    ... setup(name = 'demo2', install_requires='demo1')
+    ... ''')
+
+
+Now we'll create a buildout.cfg file that defines our application:
+
+    >>> write('buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... develop = demo1 demo2
+    ... parts = myapp
+    ... 
+    ... [zope3]
+    ... location = %(zope3)s
+    ...
+    ... [myapp]
+    ... recipe = zc.zope3recipes:app
+    ... 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
+    ... ''' % globals())
+
+Note that our site.zcml file is very small.  It expect the application
+zcml to define amost everything.  In fact, a site.zcml file will often
+include just a single include directive.  We don't need to include the
+surrounding configure element, unless we want a namespace other than
+the zope namespace.  A configure directive will be included for us.
+
+Let's run the buildout and see what we get:
+
+    >>> print system(join('bin', 'buildout')),
+    buildout: Develop: /sample-buildout/demo1
+    buildout: Develop: /sample-buildout/demo2
+    buildout: Installing myapp
+
+A directory is created in the parts directory for out application files:
+
+    >>> ls('parts')
+    d  myapp
+
+    >>> ls('parts', 'myapp')
+    -  debugzope
+    -  runzope
+    -  site.zcml
+
+We get 3 files, two scripts and a site.zcml file.  The site.zcml file
+is just what we had in the buildout configuration:
+
+    >>> cat('parts', 'myapp', 'site.zcml')
+    <configure xmlns='http://namespaces.zope.org/zope'>
+    <include package="demo2" />
+    <principal
+    id="zope.manager"
+    title="Manager"
+    login="jim"
+    password_manager="SHA1"
+    password="40bd001563085fc35165329ea1ff5c5ecbdbbeef"
+    />
+    <grant
+    role="zope.Manager"
+    principal="zope.manager"
+    />
+    </configure>
+
+Unfortunately, the leading whitespace is stripped from the
+configuration file lines.  This is a consequence of the way
+ConfigParser works.
+
+The runzope script runs the Web server:
+
+    >>> cat('parts', 'myapp', 'runzope')
+    #!/usr/local/bin/python2.4
+    <BLANKLINE>
+    import sys
+    sys.path[0:0] = [
+      '/sample-buildout/demo2',
+      '/sample-buildout/demo1',
+      '/zope3/src',
+      ]
+    <BLANKLINE>
+    import zope.app.twisted.main
+    <BLANKLINE>
+    if __name__ == '__main__':
+        zope.app.twisted.main.main()
+
+It includes in it's path the eggs we specified in the configuration
+file, along with their dependencies. Note that we haven't specified a
+configuration file.  When runzope is run, a -C option must be used to
+provide a configuration file.  -X options can also be provided to 
+override configuration file options.
+
+The debugzope script provides access to the object system.  When
+debugzope is run, a -C option must be used to provide a configuration
+file.  -X options can also be provided to override configuration file
+options.  If run without any additional arguments, then an interactive
+interpreter will be started with databases specified in the
+configuration file opened and with the variable root set to the
+application root object.  The debugger variable is set to a Zope 3
+debugger.  If additional arguments are provided, then the first
+argument should be a script name and the remaining arguments are
+script arguments.  The script will be run with the root and debugger
+variables available as global variables.
+
+..
+
+    >>> cat('parts', 'myapp', 'debugzope')
+    #!/usr/local/bin/python2.4
+    <BLANKLINE>
+    import sys
+    sys.path[0:0] = [
+      '/sample-buildout/demo2',
+      '/sample-buildout/demo1',
+      '/zope3/src',
+      '/zope3recipes',
+      ]
+    <BLANKLINE>
+    import zc.zope3recipes.debugzope
+    <BLANKLINE>
+    if __name__ == '__main__':
+        zc.zope3recipes.debugzope.main()
+    
+Defining Zope3 instances
+========================
+
+Having defined an application, we can define one or more instances of
+the application.  We do this using the zc.zope3recipes instance
+recipe.  The instance recipe has 2 modes, a development and a
+production mode.  We'll start with the development mode.  In
+development mode, a part directory will be created for each instance
+containing the instance's configuration files. This directory will
+also contain run-time files created by the instances, such as log
+files or zdaemon socket files.
+
+When defining an instance, we need to specify a zope.conf file.  The
+recipe can do most of the work for us.  Let's look at a a basic
+example:
+
+    >>> write('buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... develop = demo1 demo2
+    ... parts = instance
+    ... 
+    ... [zope3]
+    ... location = %(zope3)s
+    ...
+    ... [myapp]
+    ... recipe = zc.zope3recipes:app
+    ... 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 = ${database:zconfig}
+    ...
+    ... [database]
+    ... recipe = zc.recipe.filestorage
+    ... ''' % globals())
+
+The application option names an application part.  The application
+part will be used to determine the location of the site.zcml file and
+the name of the control script to run.
+
+We specified a zope.conf option which contains a start at our final
+zope.conf file.  The recipe will add some bits we leave out.  The one
+thing we really need to have is a database definition.  We simply
+include the zconfig option from the database section, which we provide
+as a file storage part using the zc.recipe.filestorage recipe.  The
+filestorage recipe will create a directory to hold our database and
+compute a zconfig option that we can use in our instance section.
+
+Note that we've replaced the myapp part with the instance part.  The
+myapp part will be included by virtue of the reference from the
+instance part.
+
+Let's run the buildout, and see what we get:
+
+    >>> print system(join('bin', 'buildout')),
+    buildout: Develop: /sample-buildout/demo1
+    buildout: Develop: /sample-buildout/demo2
+    buildout: Installing database
+    buildout: Updating myapp
+    buildout: Installing instance
+
+We see thatthe database and myapp parts were included by virtue of
+being referenced from the instance part.
+
+We get new directories for our database and instance:
+
+    >>> ls('parts')
+    d  database
+    d  instance
+    d  myapp
+
+The instance directory contains zdaemon.conf and zope.conf files:
+
+    >>> ls('parts', 'instance')
+    -  zdaemon.conf
+    -  zope.conf
+
+Let's look at the zope.conf file that was generated:
+
+    >>> cat('parts', 'instance', 'zope.conf')
+    site-definition /sample-buildout/parts/myapp/site.zcml
+    <BLANKLINE>
+    <zodb>
+      <filestorage>
+        path /sample-buildout/parts/database/Data.fs
+      </filestorage>
+    <BLANKLINE>
+    </zodb>
+    <BLANKLINE>
+    <server>
+      address 8080
+      type HTTP
+    </server>
+    <BLANKLINE>
+    <accesslog>
+      <logfile>
+        path /sample-buildout/parts/instance/access.log
+      </logfile>
+    <BLANKLINE>
+    </accesslog>
+    <BLANKLINE>
+    <eventlog>
+      <logfile>
+        formatter zope.exceptions.log.Formatter
+        path STDOUT
+      </logfile>
+    <BLANKLINE>
+    </eventlog>
+
+It includes the database definition that we provided in the zope.conf
+option.  It has a site-definition option that names the site.zcml file
+from our application directory.
+
+We didn't specify any server or logging ZConfig sections, so some were
+generated for us.  
+
+Note that, by default, the event-log output goes to standard output.
+We'll say more about that when we talk about the zdaemon
+configuration later.
+
+If we specify a server section ourselves:
+
+    >>> write('buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... develop = demo1 demo2
+    ... parts = instance
+    ... 
+    ... [zope3]
+    ... location = %(zope3)s
+    ...
+    ... [myapp]
+    ... recipe = zc.zope3recipes:app
+    ... 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 = ${database:zconfig}
+    ...    <server>
+    ...        type PostmortemDebuggingHTTP
+    ...        address 8080
+    ...    </server>
+    ...
+    ... [database]
+    ... recipe = zc.recipe.filestorage
+    ... ''' % globals())
+
+    >>> print system(join('bin', 'buildout')),
+    buildout: Develop: /sample-buildout/demo1
+    buildout: Develop: /sample-buildout/demo2
+    buildout: Uninstalling instance
+    buildout: Updating database
+    buildout: Updating myapp
+    buildout: Installing instance
+
+Then the section (or sections) we provide will be used and new ones
+won't be added:
+
+    >>> cat('parts', 'instance', 'zope.conf')
+    site-definition /sample-buildout/parts/myapp/site.zcml
+    <BLANKLINE>
+    <zodb>
+      <filestorage>
+        path /sample-buildout/parts/database/Data.fs
+      </filestorage>
+    <BLANKLINE>
+    </zodb>
+    <BLANKLINE>
+    <server>
+      address 8080
+      type PostmortemDebuggingHTTP
+    </server>
+    <BLANKLINE>
+    <accesslog>
+      <logfile>
+        path /sample-buildout/parts/instance/access.log
+      </logfile>
+    <BLANKLINE>
+    </accesslog>
+    <BLANKLINE>
+    <eventlog>
+      <logfile>
+        formatter zope.exceptions.log.Formatter
+        path STDOUT
+      </logfile>
+    <BLANKLINE>
+    </eventlog>
+
+If we just want to specify alternate ports or addresses, we can use
+the address option which accepts zero or more address specifications:
+
+    >>> write('buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... develop = demo1 demo2
+    ... parts = instance
+    ... 
+    ... [zope3]
+    ... location = %(zope3)s
+    ...
+    ... [myapp]
+    ... recipe = zc.zope3recipes:app
+    ... 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 = ${database:zconfig}
+    ... address = 8081 foo.com:8082
+    ...
+    ... [database]
+    ... recipe = zc.recipe.filestorage
+    ... ''' % globals())
+
+    >>> print system(join('bin', 'buildout')),
+    buildout: Develop: /sample-buildout/demo1
+    buildout: Develop: /sample-buildout/demo2
+    buildout: Uninstalling instance
+    buildout: Updating database
+    buildout: Updating myapp
+    buildout: Installing instance
+
+    >>> cat('parts', 'instance', 'zope.conf')
+    site-definition /sample-buildout/parts/myapp/site.zcml
+    <BLANKLINE>
+    <zodb>
+      <filestorage>
+        path /sample-buildout/parts/database/Data.fs
+      </filestorage>
+    <BLANKLINE>
+    </zodb>
+    <BLANKLINE>
+    <server>
+      address 8081
+      type HTTP
+    </server>
+    <BLANKLINE>
+    <server>
+      address foo.com:8082
+      type HTTP
+    </server>
+    <BLANKLINE>
+    <accesslog>
+      <logfile>
+        path /sample-buildout/parts/instance/access.log
+      </logfile>
+    <BLANKLINE>
+    </accesslog>
+    <BLANKLINE>
+    <eventlog>
+      <logfile>
+        formatter zope.exceptions.log.Formatter
+        path STDOUT
+      </logfile>
+    <BLANKLINE>
+    </eventlog>
+
+We can specify our own accesslog and eventlog configuration.  For
+example, to send the event-log output to a file and suppress the
+access log:
+
+    >>> write('buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... develop = demo1 demo2
+    ... parts = instance
+    ... 
+    ... [zope3]
+    ... location = %(zope3)s
+    ...
+    ... [myapp]
+    ... recipe = zc.zope3recipes:app
+    ... 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 = ${database:zconfig}
+    ...    <eventlog>
+    ...      <logfile>
+    ...        path ${buildout:parts-directory}/instance/event.log
+    ...        formatter zope.exceptions.log.Formatter
+    ...      </logfile>
+    ...    </eventlog>
+    ...    <accesslog>
+    ...    </accesslog>
+    ...
+    ... address = 8081
+    ...
+    ... [database]
+    ... recipe = zc.recipe.filestorage
+    ... ''' % globals())
+
+    >>> print system(join('bin', 'buildout')),
+    buildout: Develop: /sample-buildout/demo1
+    buildout: Develop: /sample-buildout/demo2
+    buildout: Uninstalling instance
+    buildout: Updating database
+    buildout: Updating myapp
+    buildout: Installing instance
+
+    >>> cat('parts', 'instance', 'zope.conf')
+    site-definition /sample-buildout/parts/myapp/site.zcml
+    <BLANKLINE>
+    <zodb>
+      <filestorage>
+        path /sample-buildout/parts/database/Data.fs
+      </filestorage>
+    <BLANKLINE>
+    </zodb>
+    <BLANKLINE>
+    <eventlog>
+      <logfile>
+        formatter zope.exceptions.log.Formatter
+        path /sample-buildout/parts/instance/event.log
+      </logfile>
+    <BLANKLINE>
+    </eventlog>
+    <BLANKLINE>
+    <accesslog>
+    </accesslog>
+    <BLANKLINE>
+    <server>
+      address 8081
+      type HTTP
+    </server>
+
+Let's look at the zdaemon.conf file:
+
+    >>> cat('parts', 'instance', 'zdaemon.conf') 
+    <runner>
+      daemon on
+      program /sample-buildout/parts/myapp/runzope -C /sample-buildout/parts/instance/zope.conf
+      socket-name /sample-buildout/parts/instance/zopectl.sock
+      transcript /sample-buildout/parts/instance/z3.log
+    </runner>
+    <BLANKLINE>
+    <eventlog>
+      <logfile>
+        path z3.log
+      </logfile>
+    <BLANKLINE>
+    </eventlog>
+
+Here we see a fairly ordinary zdaemon.conf file.  The program option
+refers to the runzope script in our application directory.  The socket
+file, used for communication between the zdaemon command-line script
+and the zademon manager is placed in the instance directory.
+
+If you want to overrise any part of the generated zdaemon output,
+simply provide a zdaemon.conf option in your instance section:
+
+
+    >>> write('buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... develop = demo1 demo2
+    ... parts = instance
+    ... 
+    ... [zope3]
+    ... location = %(zope3)s
+    ...
+    ... [myapp]
+    ... recipe = zc.zope3recipes:app
+    ... 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 = ${database:zconfig}
+    ... address = 8081
+    ... zdaemon.conf = 
+    ...     <runner>
+    ...       daemon off
+    ...       socket-name /sample-buildout/parts/instance/sock
+    ...       transcript /dev/null
+    ...     </runner>
+    ...     <eventlog>
+    ...     </eventlog>
+    ...
+    ... [database]
+    ... recipe = zc.recipe.filestorage
+    ... ''' % globals())
+
+    >>> print system(join('bin', 'buildout')),
+    buildout: Develop: /sample-buildout/demo1
+    buildout: Develop: /sample-buildout/demo2
+    buildout: Uninstalling instance
+    buildout: Updating database
+    buildout: Updating myapp
+    buildout: Installing instance
+
+    >>> cat('parts', 'instance', 'zdaemon.conf')
+    <runner>
+      daemon off
+      program /sample-buildout/parts/myapp/runzope -C /sample-buildout/parts/instance/zope.conf
+      socket-name /sample-buildout/parts/instance/sock
+      transcript /dev/null
+    </runner>
+    <BLANKLINE>
+    <eventlog>
+    </eventlog>
+
+In addition to the configuration files, a control script is generated
+in the buildout bin directory:
+
+    >>> ls('bin')
+    -  buildout
+    -  instance
+
+..
+
+    >>> cat('bin', 'instance')
+    #!/usr/local/bin/python2.4
+    <BLANKLINE>
+    import sys
+    sys.path[0:0] = [
+      '/sample-buildout/eggs/zdaemon-2.0-py2.4.egg',
+      '/sample-buildout/eggs/setuptools-0.6-py2.4.egg',
+      '/sample-buildout/eggs/ZConfig-2.3-py2.4.egg',
+      '/zope3recipes',
+      ]
+    <BLANKLINE>
+    import zc.zope3recipes.ctl
+    <BLANKLINE>
+    if __name__ == '__main__':
+        zc.zope3recipes.ctl.main([
+            '/sample-buildout/parts/myapp/debugzope',
+            '/sample-buildout/parts/instance/zope.conf',
+            '-C', '/sample-buildout/parts/instance/zdaemon.conf',
+            ]+sys.argv[1:]
+            )
+
+Log files
+---------
+
+The log file settings deserver some explanation.  The Zope event log
+only captures output from logging calls.  In particular, it doesn't
+capture startup errors written to standard error.  The zdaemon
+transcript log is very useful for capturing this output.  Without it,
+error written to standard error are lost when running as a deamon.
+The default Zope 3 configuration in the past was to write the Zope
+access and event log output to both files and standard output and to
+define a transcript log.  This had the effect that the transacript
+duplicated the contents of the event log and access logs, in addition
+to capturing other output.  This was space inefficient.
+
+This recipe's approach is to combine the zope and zdaemon event-log
+information as well as Zope error output into a single log file.  We
+do this by directing Zope's event log to standard output, where it is
+useful when running Zope in foreground mode and where it can be
+captured by the zdaemon transscript log.


Property changes on: zc.zope3recipes/dev/zc/zope3recipes/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zc.zope3recipes/dev/zc/zope3recipes/__init__.py
===================================================================
--- zc.zope3recipes/dev/zc/zope3recipes/__init__.py	2007-01-10 19:43:48 UTC (rev 71895)
+++ zc.zope3recipes/dev/zc/zope3recipes/__init__.py	2007-01-10 19:56:27 UTC (rev 71896)
@@ -0,0 +1 @@
+#


Property changes on: zc.zope3recipes/dev/zc/zope3recipes/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: zc.zope3recipes/dev/zc/zope3recipes/ctl.py
===================================================================
--- zc.zope3recipes/dev/zc/zope3recipes/ctl.py	2007-01-10 19:43:48 UTC (rev 71895)
+++ zc.zope3recipes/dev/zc/zope3recipes/ctl.py	2007-01-10 19:56:27 UTC (rev 71896)
@@ -0,0 +1,47 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation 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.
+#
+##############################################################################
+"""Top-level controller for 'zopectl'.
+"""
+
+import os, sys
+import zdaemon.zdctl
+
+class ZopectlCmd(zdaemon.zdctl.ZDCmd):
+
+    def do_debug(self, ignored_because_its_stupid):
+        os.spawnlp(os.P_WAIT, self._debugzope, self._debugzope,
+                   '-C', self._zope_conf,
+                   *self.options.args[1:])
+
+    def help_debug(self):
+        print "debug -- Initialize the Zope application, providing a"
+        print "         debugger object at an interactive Python prompt."
+
+    do_run = do_debug
+
+    def help_run(self):
+        print "run <script> [args] -- run a Python script with the Zope "
+        print "                       environment set up.  The script has "
+        print "                       'root' exposed as the root container."
+
+
+def main(args=None):
+    if args is None:
+        args = sys.argv[1:]
+    
+    class Cmd(ZopectlCmd):
+        _debugzope = args.pop(0)
+        _zope_conf = args.pop(0)
+
+    zdaemon.zdctl.main(args, None, Cmd)


Property changes on: zc.zope3recipes/dev/zc/zope3recipes/ctl.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: zc.zope3recipes/dev/zc/zope3recipes/debugzope.py
===================================================================
--- zc.zope3recipes/dev/zc/zope3recipes/debugzope.py	2007-01-10 19:43:48 UTC (rev 71895)
+++ zc.zope3recipes/dev/zc/zope3recipes/debugzope.py	2007-01-10 19:56:27 UTC (rev 71896)
@@ -0,0 +1,59 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation 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.
+#
+##############################################################################
+"""Zope 3 program entry points
+
+$Id$
+"""
+
+import os, sys
+import zope.app.debug
+import zope.app.twisted.main
+import zope.app.appsetup.interfaces
+from zope.event import notify
+
+def zglobals(args):
+    options = zope.app.twisted.main.load_options(args)
+    zope.app.appsetup.config(options.site_definition)
+    db = zope.app.appsetup.appsetup.multi_database(options.databases)[0][0]
+    notify(zope.app.appsetup.interfaces.DatabaseOpened(db))
+    
+    db = zope.app.twisted.main.debug(args)
+    if "PYTHONSTARTUP" in os.environ:
+        execfile(os.environ["PYTHONSTARTUP"])
+    
+    app = zope.app.debug.Debugger.fromDatabase(db)
+    return options, dict(
+        app = app,
+        debugger = app,
+        root = app.root(),
+        __name__ = '__main__',
+        )
+
+def debug(args):
+    options, globs = zglobals(args[:2])
+    args = options.args
+
+    if args:
+        sys.argv[:] = args
+        globs['__file__'] = sys.argv[0]
+        execfile(sys.argv[0], globs)
+        sys.exit()
+    else:
+        import code
+        code.interact(banner=banner, local=globs)
+
+banner = """Welcome to the Zope 3 "debugger".
+The application root object is available as the root variable.
+A Zope debugger instance is available as the debugger (aka app) variable.
+"""


Property changes on: zc.zope3recipes/dev/zc/zope3recipes/debugzope.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: zc.zope3recipes/dev/zc/zope3recipes/recipes.py
===================================================================
--- zc.zope3recipes/dev/zc/zope3recipes/recipes.py	2007-01-10 19:43:48 UTC (rev 71895)
+++ zc.zope3recipes/dev/zc/zope3recipes/recipes.py	2007-01-10 19:56:27 UTC (rev 71896)
@@ -0,0 +1,368 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation 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.
+#
+##############################################################################
+"""Collected Zope 3 recipes
+"""
+
+import os, shutil
+import zc.buildout
+import zc.recipe.egg
+import pkg_resources
+import ZConfig.cfgparser
+import cStringIO
+
+this_loc = pkg_resources.working_set.find(
+    pkg_resources.Requirement.parse('zc.zope3recipes')).location
+
+class App:
+
+    def __init__(self, buildout, name, options):
+        self.name, self.options = name, options
+
+        options['location'] = os.path.join(
+            buildout['buildout']['parts-directory'],
+            self.name,
+            )
+
+        z3path = options['zope3-location'] = os.path.join(
+            buildout['buildout']['directory'],
+            buildout[options.get('zope3', 'zope3')]['location'],
+            )
+
+        if not os.path.exists(z3path):
+            raise zc.buildout.UserError("No directory:", z3path)
+
+        path = os.path.join(z3path, 'lib', 'python')
+        if not os.path.exists(path):
+            path = os.path.join(z3path, 'src')
+            if not os.path.exists(path):
+                logger.error(
+                    "The directory, %r, isn't a valid checkout or release."
+                    % z3)
+                raise zc.buildout.UserError(
+                    "Invalid Zope 3 installation:", z3path)
+
+        self.zpath = path
+        options['scripts'] = ''
+        self.egg = zc.recipe.egg.Egg(buildout, name, options)
+
+
+    def install(self):
+        options = self.options
+        dest = options['location']
+        if not os.path.exists(dest):
+            os.mkdir(dest)
+            created = True
+        else:
+            created = False
+            
+        try:
+            open(os.path.join(dest, 'site.zcml'), 'w').write(
+                site_zcml_template % self.options['site.zcml']
+                )
+
+            extra = options.get('extra-paths')
+            zpath = self.zpath
+            if extra:
+                extra += '\n' + zpath
+            else:
+                extra = zpath
+            options['extra-paths'] = extra
+
+            self.egg.install()
+            requirements, ws = self.egg.working_set()
+
+            # install subprograms and ctl scripts
+            zc.buildout.easy_install.scripts(
+                [('runzope', 'zope.app.twisted.main', 'main')],
+                ws, options['executable'], dest,
+                extra_paths = options['extra-paths'].split(),
+                )
+
+            options['extra-paths'] = extra + '\n' + this_loc
+
+            zc.buildout.easy_install.scripts(
+                [('debugzope', 'zc.zope3recipes.debugzope', 'main')],
+                ws, options['executable'], dest,
+                extra_paths = options['extra-paths'].split(),
+                )
+
+            return dest
+
+        except:
+            if created:
+                shutil.rmtree(dest)
+            raise
+        
+        return dest
+
+    update = install
+
+site_zcml_template = """<configure xmlns='http://namespaces.zope.org/zope'>
+%s
+</configure>
+"""
+
+class Instance:
+    
+    def __init__(self, buildout, name, options):
+        self.name, self.options = name, options
+
+        options['location'] = os.path.join(
+            buildout['buildout']['parts-directory'],
+            self.name,
+            )
+
+        options['application-location'] = buildout[options['application']
+                                                   ]['location']
+
+        options['bin-directory'] = buildout['buildout']['bin-directory']
+
+        options['scripts'] = ''
+        options['eggs'] = options.get('eggs', 'zdaemon\nsetuptools')
+        self.egg = zc.recipe.egg.Egg(buildout, name, options)
+            
+
+    def install(self):
+        options = self.options
+        dest = conf_dir = log_dir = run_dir = options['location']
+        app_loc = options['application-location']
+
+        zope_conf_path = os.path.join(dest, 'zope.conf')
+        zdaemon_conf_path = os.path.join(dest, 'zdaemon.conf')
+
+        zope_conf = options.get('zope.conf', '')+'\n'
+        zope_conf = ZConfigParse(cStringIO.StringIO(zope_conf))
+        
+        zope_conf['site-definition'] = os.path.join(app_loc, 'site.zcml')
+
+        for address in options.get('address', '').split():
+            zope_conf.sections.append(
+                ZConfigSection('server',
+                               data=dict(type='HTTP',
+                                         address=address,
+                                         ),
+                               )
+                )
+        if not [s for s in zope_conf.sections
+                if ('server' in s.type)]:
+            zope_conf.sections.append(
+                ZConfigSection('server',
+                               data=dict(type='HTTP',
+                                         address='8080',
+                                         ),
+                               )
+                )
+
+        if not [s for s in zope_conf.sections if s.type == 'zodb']:
+            raise zc.buildout.UserError(
+                'No database sections have been defined.')
+
+        if not [s for s in zope_conf.sections
+                if s.type == 'accesslog']:
+            zope_conf.sections.append(
+                access_log(os.path.join(log_dir, 'access.log')))
+
+                
+        if not [s for s in zope_conf.sections
+                if s.type == 'eventlog']:
+            zope_conf.sections.append(event_log('STDOUT'))
+
+
+        zdaemon_conf = options.get('zdaemon.conf', '')+'\n'
+        zdaemon_conf = ZConfigParse(cStringIO.StringIO(zdaemon_conf))
+
+        defaults = {
+            'program': "%s -C %s" % (os.path.join(app_loc, 'runzope'),
+                                     zope_conf_path,
+                                     ),
+            'daemon': 'on',
+            'transcript': os.path.join(log_dir, 'z3.log'),
+            'socket-name': os.path.join(run_dir, 'zopectl.sock'),
+            }
+        runner = [s for s in zdaemon_conf.sections
+                  if s.type == 'runner']
+        if runner:
+            runner = runner[0]
+        else:
+            runner = ZConfigSection('runner')
+            zdaemon_conf.sections.insert(0, runner)
+        for name, value in defaults.items():
+            if name not in runner:
+                runner[name] = value
+        
+        if not [s for s in zdaemon_conf.sections
+                if s.type == 'eventlog']:
+            # No database, specify a default one
+            zdaemon_conf.sections.append(event_log2('z3.log'))
+
+        zdaemon_conf = str(zdaemon_conf)
+
+        self.egg.install()
+        requirements, ws = self.egg.working_set()
+
+        if not os.path.exists(dest):
+            os.mkdir(dest)
+            created = True
+        else:
+            created = False
+            
+        try:
+            open(zope_conf_path, 'w').write(str(zope_conf))
+            open(zdaemon_conf_path, 'w').write(str(zdaemon_conf))
+
+            zc.buildout.easy_install.scripts(
+                [(self.name, 'zc.zope3recipes.ctl', 'main')],
+                ws, options['executable'], options['bin-directory'],
+                extra_paths = [this_loc],
+                arguments = ('['
+                             '\n        %r,'
+                             '\n        %r,'
+                             '\n        %r, %r,'
+                             '\n        ]+sys.argv[1:]'
+                             '\n        '
+                             % (os.path.join(app_loc, 'debugzope'),
+                                zope_conf_path,
+                                '-C', zdaemon_conf_path,
+                                )
+                             ),
+                )
+
+        except:
+            if created:
+                shutil.rmtree(dest)
+            raise
+
+        return dest, os.path.join(options['bin-directory'], self.name)
+
+    def update(self):
+        pass
+
+
+def access_log(path):
+    return ZConfigSection(
+        'accesslog', '',
+        sections=[ZConfigSection('logfile', '', dict(path=path))]
+        )
+
+def event_log(path, *data):
+    return ZConfigSection(
+        'eventlog', '', None,
+        [ZConfigSection('logfile', '',
+                        dict(path=path,
+                             formatter='zope.exceptions.log.Formatter',
+                             )
+                        )
+         ],
+        )
+
+def event_log2(path, *data):
+    return ZConfigSection(
+        'eventlog', '', None,
+        [ZConfigSection('logfile', '',
+                        dict(path=path,
+                             )
+                        )
+         ],
+        )
+
+   
+server_template = """
+<server>
+  type HTTP
+  address %s
+</server>
+"""
+
+access_log_template = """
+<accesslog>
+  <logfile>
+    path %s
+  </logfile>
+</accesslog>
+"""
+
+event_log_template = """
+<eventlog>
+  <logfile>
+    path %s
+    formatter zope.exceptions.log.Formatter
+  </logfile>
+</eventlog>
+"""
+
+class ZConfigResource:
+
+    def __init__(self, file, url=''):
+        self.file, self.url = file, url
+
+class ZConfigSection(dict):
+
+    def __init__(self, type='', name='', data=None, sections=None):
+        dict.__init__(self)
+        if data:
+            self.update(data)
+        self.sections = sections or []
+        self.type, self.name = type, name
+
+    def addValue(self, key, value, *args):
+        self[key] = value
+
+    def __str__(self, pre=''):
+        result = []
+        if self.type:
+            if self.name:
+                result = ['%s<%s %s>' % (pre, self.type, self.name)]
+            else:
+                result = ['%s<%s>' % (pre, self.type)]
+            pre += '  '
+
+        for name, value in sorted(self.items()):
+            result.append('%s%s %s' % (pre, name, value))
+
+        if self.sections and self:
+            result.append('')
+
+        for section in self.sections:
+            result.append(section.__str__(pre))
+        
+        if self.type:
+            result.append('%s</%s>' % (pre[:-2], self.type))
+            result.append('')
+                          
+        return '\n'.join(result).rstrip()+'\n'
+  
+class ZConfigContext:
+
+    def __init__(self):
+        self.top = ZConfigSection()
+        self.sections = []
+
+    def startSection(self, container, type, name):
+        newsec = ZConfigSection(type, name)
+        container.sections.append(newsec)
+        return newsec
+
+    def endSection(self, container, type, name, newsect):
+        pass
+
+    def importSchemaComponent(self, pkgname):
+        pass
+
+    def includeConfiguration(self, section, newurl, defines):
+        raise NotImplementedError('includes are not supported')
+
+def ZConfigParse(file):
+    c = ZConfigContext()
+    ZConfig.cfgparser.ZConfigParser(ZConfigResource(file), c).parse(c.top)
+    return c.top


Property changes on: zc.zope3recipes/dev/zc/zope3recipes/recipes.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: zc.zope3recipes/dev/zc/zope3recipes/tests.py
===================================================================
--- zc.zope3recipes/dev/zc/zope3recipes/tests.py	2007-01-10 19:43:48 UTC (rev 71895)
+++ zc.zope3recipes/dev/zc/zope3recipes/tests.py	2007-01-10 19:56:27 UTC (rev 71896)
@@ -0,0 +1,128 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation 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.
+#
+##############################################################################
+
+import os, re, shutil, sys, tempfile
+import pkg_resources
+
+import zc.buildout.testing
+
+import unittest
+import zope.testing
+from zope.testing import doctest, renormalizing
+
+
+def test_ctl():
+    """
+The ctl script is an extended version of zdaemon that provides an
+extra command, run.  Let's create a buildout that installs it as an
+ordinary script:
+
+    >>> write('buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... parts = ctl
+    ...
+    ... [ctl]
+    ... recipe = zc.recipe.egg
+    ... eggs = zc.zope3recipes
+    ...        zdaemon
+    ... entry-points = ctl=zc.zope3recipes.ctl:main
+    ... scripts = ctl
+    ... ''')
+
+    >>> print system(join('bin', 'buildout')),
+    buildout: Installing ctl
+
+We'll create a configuration file:
+
+    >>> write('conf',
+    ... '''
+    ... <runner>
+    ...   program echo hi
+    ... </runner>
+    ... ''')
+
+The configuration doesn't matter much. :)
+
+Unlike a normal zdaemon script, we have to pass two extra arguments, a
+script to run the zope debugger with, and the name of a zope
+configuration file. For demonstration purposes, we'll just use echo.
+
+    >>> print system(join('bin', 'ctl')+' echo zope.conf -Cconf fg there'),
+    echo hi there
+    hi there
+
+Notice:
+
+  - The first 2 arguments were ignored.
+
+  - It got the program, 'echo hi', from the configuration file.
+
+  - We ran the program in the foreground, passing the extra argument, there.
+
+Now, if we use the run command, it will run the script we passed as
+the first argument:
+
+    >>> print system(join('bin', 'ctl')+' echo zope.conf -Cconf run there'),
+    -C zope.conf there
+
+debug is another name for run:
+
+    >>> print system(join('bin', 'ctl')+' echo zope.conf -Cconf debug there'),
+    -C zope.conf there
+
+
+"""
+
+def setUp(test):
+    zc.buildout.testing.buildoutSetUp(test)
+    zc.buildout.testing.install_develop('zc.zope3recipes', test)
+    zc.buildout.testing.install('zope.testing', test)
+    zc.buildout.testing.install('zc.recipe.egg', test)
+    zc.buildout.testing.install('zdaemon', test)
+    zc.buildout.testing.install('ZConfig', test)
+    zc.buildout.testing.install('zc.recipe.filestorage', test)
+
+
+checker = renormalizing.RENormalizing([
+    zc.buildout.testing.normalize_path,
+    (re.compile(
+    "Couldn't find index page for '[a-zA-Z0-9.]+' "
+    "\(maybe misspelled\?\)"
+    "\n"
+    ), ''),
+    (re.compile("""['"][^\n]+zope3recipes[^\n]+['"],"""), #'
+                                          "'/zope3recipes',"),
+    (re.compile('#![^\n]+\n'), ''),                
+    (re.compile('-\S+-py\d[.]\d(-\S+)?.egg'),
+     '-pyN.N.egg',
+    ),
+    ])
+
+def test_suite():
+    return unittest.TestSuite((
+        doctest.DocTestSuite(
+            setUp=setUp, tearDown=zc.buildout.testing.buildoutTearDown,
+            checker=checker,
+            ),
+        doctest.DocFileSuite(
+            'README.txt',
+            setUp=setUp, tearDown=zc.buildout.testing.buildoutTearDown,
+            checker=checker,
+            ),
+        
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')


Property changes on: zc.zope3recipes/dev/zc/zope3recipes/tests.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native



More information about the Checkins mailing list