[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