Tools and scripts for creating development sandboxes for web applications that primarily use Zope
- Command line options for zopeproject
- The sandbox
- Reporting bugs or asking questions about zopeproject
You can start a new Zope-based web application from scratch with just two commands:
$ easy_install zopeproject $ zopeproject HelloWorld
The second command will ask you for a name and password for an initial administrator user. It will also ask you where to put the Python packages (“eggs”) that it downloads. This way multiple projects created with zopeproject can share the same packages and won’t have to download them each time (see also Sharing eggs among sandboxes below).
(Note: Depending on how they installed Python, Unix/Linux users may have to invoke easy_install with sudo. If that’s not wanted or possible, easy_install can be invoked with normal privileges inside a virtual-python or workingenv).
After asking these questions, zopeproject will download the zc.buildout package that will be used to build the sandbox, unless it is already installed locally. Then it will invoke buildout to download Zope and its dependencies. If you’re doing this for the first time or not sharing packages between different projects, this may take a while.
When zopeproject is done, you will find a typical Python package development environment in the HelloWorld directory: the package itself (helloworld) and a setup.py script. There’s also a bin directory that contains scripts, such as paster which can be used to start the application:
$ cd HelloWorld $ bin/paster serve deploy.ini
You may also use the helloworld-ctl script which works much like the zopectl script from Zope instances:
$ bin/helloworld-ctl foreground
After starting the application, you should now be able to go to http://localhost:8080 and see the default start screen of Zope. You will also be able to log in with the administrator user account that you specified earlier.
Some packages required by Zope contain C extension modules. There may not always be binary Windows distributions available for these packages. In this case, setuptools will try to compile them from source which will likely fail if you don’t have a compiler such as the Microsoft Visual C compiler installed. Alternatively, you can install the free MinGW compiler:
Download MinGW-x.y.z.exe from http://www.mingw.org/ and run it to do a full install into the standard location (ie. C:\MinGW).
Tell Python to use the MinGW compiler by creating C:\Documents and Settings\YOUR USER\pydistutils.cfg with the following contents:
Let Python know about the MinGW installation and the pydistutils.cfg file. To do that, go to the Control Panel, System section, Advanced tab and click on the Environment variables button. Add the C:\MinGW\bin directory to your Path environment variable (individual paths are delimited by semicolons). Also add another environment variable called HOME with the following value:
C:\Documents and Settings\YOUR USER
When installing packages from source, Python should now automatically use the MinGW compiler to build binaries.
- When invoked with this option, zopeproject will only create the project directory with the standard files in it, but it won’t download and invoke zc.buildout.
- This option enables the newest = true setting in buildout.cfg. That way, buildout will always check for newer versions of eggs online. If, for example, you have outdated versions of your dependencies in your shared eggs directory, this switch will force the download of newer versions. Note that you can always edit buildout.cfg to change this behaviour in an existing project area, or you can invoke bin/buildout with the -n option.
- This option will import the project directory and the files in it into the given subversion repository and provide you with a checkout of the trunk. REPOS is supposed to be a repository path that is going to be created, along with tags, branches and trunk below that. This checkin ignores any files and directories created by zc.buildout.
- -v, --verbose
- When this option is enabled, zopeproject won’t hide the output of easy_install (used to install zc.buildout) and the buildout command.
- Configuration file for PasteDeploy. It defines which server software to launch and which WSGI application to invoke upon each request (which is defined in src/helloworld/startup.py). You may also define WSGI middlewares here. Invoke bin/paster serve with this file as an argument.
- Alternate configuration for PasteDeploy that configures middleware which intercepts exceptions for interactive debugging. See Debugging exceptions below.
- This file will be read by the application factory in src/helloworld/startup.py. Here you can define which ZCML file the application factory should load upon startup, the ZODB database instance, an event log as well as whether developer mode is switched on or not.
- This file is referred to by zope.conf and will be loaded by the application factory. It is the root ZCML file and includes everything else that needs to be loaded. ‘Everything else’ typically is just the application package itself, helloworld, which then goes on to include its dependencies. Apart from this, site.zcml also defines the anonymous principal and the initial admin principal.
- This file defines the egg of your application. That definition includes listing the package’s dependencies (mostly Zope eggs) and the entry point for the PasteDeploy application factory.
- This file tells zc.buildout what to do when the buildout is executed. This mostly involves executing setup.py to enable the HelloWorld egg (which also includes downloading its dependencies), as well as installing PasteDeploy for the server. This files also refers to the shared eggs directory (eggs-directory) and determines whether buildout should check whether newer eggs are available online or not (newest).
- This directory contains all executable scripts, e.g for starting the application (paster), installing or reinstalling dependencies (buildout), or invoking the debug prompt (helloworld-debug). It also contains a script (python) that invokes the standard interpreter prompt with all packages on the module search path.
- This directory contains the Python package(s) of your application. Normally there’s just one package (helloworld), but you may add more to this directory if you like. The src directory will be placed on the interpreter’s search path by zc.buildout.
- The ZODB filestorage will place its files (Data.fs, lock files, etc.) here.
You can rename or move the sandbox directory any time you like. Just be sure to run bin/buildout again after doing so. Renaming the sandbox directory won’t change the name of the egg, however. To do that, you’ll have to change the name parameter in setup.py.
After having started up Zope for the first time, you’ll likely want to start developing your web application. Code for your application goes into the helloworld package that was created by zopeproject in the src directory.
For example, to get a simple “Hello world!” message displayed, create src/helloworld/browser.py with the following contents:
from zope.publisher.browser import BrowserPage class HelloPage(BrowserPage): def __call__(self): return "<html><body><h1>Hello World!</h1></body></html>"
Then all you need to do is hook up the page in ZCML. To do that, add the following directive towards the end of src/helloworld/configure.zcml:
<browser:page for="*" name="hello" class=".browser.HelloPage" permission="zope.Public" />
Note that you’ll likely need to define the browser namespace prefix at the top of the file:
<configure xmlns="http://namespaces.zope.org/zope" xmlns:browser="http://namespaces.zope.org/browser" >
After having restarted the application using paster serve, you can visit http://localhost:8080/hello to see the page in action.
The standard setup.py and configure.zcml files list a set of standard dependencies that are typical for most Zope applications. You may obviously remove things from this list, but typically you’ll want to re-use more libraries that others have written. Many, if not most, of additional Zope and third party libraries are listed on the Python Cheeseshop.
Let’s say you wanted to reuse the some.library package in your application. The first step would be to add it to the list of dependencies in setup.py (install_requires). If this package defined any Zope components, you would probably also have to load its ZCML configuration by adding the following line to src/helloworld/configure.zcml:
<include package="some.library" />
After having changed setup.py, you would want the newly added dependency to be downloaded and added to the search path of bin/paster. To do that, simply invoke the buildout:
Automated tests should be placed in Python modules. If they all fit in one module, the module should simply be named tests. If you need many modules, create a tests package and put the modules in there. Each module should start with test (for example, the full dotted name of a test module could be helloworld.tests.test_app).
If you prefer to separate functional tests from unit tests, you can put functional tests in an ftests module or package. Note that this doesn’t matter to the test runner whatsoever, it doesn’t care about the location of a test case.
Each test module should define a test_suite function that constructs the test suites for the test runner, e.g.:
def test_suite(): return unittest.TestSuite([ unittest.makeSuite(ClassicTestCase), DocTestSuite(), DocFileSuite('README.txt', package='helloworld'), ])
To run all tests in your application’s packages, simply invoke the bin/test script:
While unit test typically require no or very little test setup, functional tests normally bootstrap the whole application’s configuration to create a real-life test harness. The configuration file that’s responsible for this test harness is ftesting.zcml. You can add more configuration directives to it if you have components that are specific to functional tests (e.g. mockup components).
To let a particular test run inside this test harness, simply apply the helloworld.testing.FunctionalLayer layer to it:
from helloworld.testing import FunctionalLayer suite.layer = FunctionalLayer
You can also simply use one of the convenience test suites in helloworld.testing:
- FunctionalDocTestSuite (based on doctest.DocTestSuite)
- FunctionalDocFileSuite (based on doctest.DocFileSuite)
- FunctionalTestCase (based on unittest.TestCase)
Use the bin/python script if you’d like test some components from the interpreter prompt and don’t need the component registrations nor access to the ZODB. If you do need those, go on to the next section.
Occasionally, it is useful to be able to interactively debug the state of the application, such as walking the object hierarchy in the ZODB or looking up components manually. This can be done with the interactive debug prompt, which is available under bin/helloworld-debug:
$ bin/helloworld-debug Welcome to the interactive debug prompt. The 'root' variable contains the ZODB root folder. The 'app' variable contains the Debugger, 'app.publish(path)' simulates a request. >>>
You can now get a folder listing of the root folder, for example:
>>> list(root.keys()) [u'folder', u'file']
In case your application fails with an exception, it can be useful to inspect the circumstances with a debugger. This is possible with the z3c.evalexception WSGI middleware. When an exception occurs in your application, stop the process and start it up again, now using the debug.ini configuration file:
$ bin/paster serve debug.ini
When you then repeat the steps that led to the exception, you will see the relevant traceback in your browser, along with the ability to view the corresponding source code and to issue Python commands for inspection.
If you prefer the Python debugger pdb, replace ajax with pdb in the WSGI middleware definition in debug.ini:
[filter-app:main] use = egg:z3c.evalexception#pdb next = zope
Note: Even exceptions such as Unauthorized (which normally leads to a login screen) or NotFound (which normally leads to an HTTP 404 response) will trigger the debugger.
Before deploying a zopeproject-based application, you should make sure that any debugging tools are disabled. In particular, this includes
- making sure there’s no debugging middleware in deploy.ini (normally these should be configured in debug.ini anyway),
- switching off developer-mode in zope.conf,
- disabling the APIDoc tool in site.zcml,
- disabling the bootstrap administrator principal in site.zcml.
You can use the helloworld-ctl script to start the server process in daemon mode. It works much like the apachectl tool as known from the Apache HTTPD server or INIT scripts known from Linux:
$ bin/helloworld-ctl start
To stop the server, issue:
$ bin/helloworld-ctl stop
Other commands, such as status and restart are supported as well.
There’s currently no particular support for deployment on Windows other than what paster provides. Integration with Windows services, much like what could be found in older versions of Zope, is planned.
zopeproject maintains a bugtracker and help desk on Launchpad: https://launchpad.net/zopeproject
Questions can also be directed to the zope3-users mailinglist: http://mail.zope.org/mailman/listinfo/zope3-users
zopeproject is written and maintained by Philipp von Weitershausen. It was inspired by the similar grokproject tool from the same author.
James Gardner, Martijn Faassen, Jan-Wijbrand Kolman and others gave valuable feedback on the early prototype presented at EuroPython 2007.
Michael Bernstein gave valuable feedback and made many suggestions for improvements.
zopeproject is distributed under the Zope Public License, v2.1. Copyright (c) by Zope Corporation and Contributors.
- Use Zope 3.4.0 KGS in the default deployment buildout.cfg.
- Added blobstorage proxy to default ZODB config in zope.conf.
- Use ZopeSecurityPolicy from zope.securitypolicy instead of zope.app.securitypolicy, which is a deprecated place for that.
- The zope.securitypolicy uses a special role name, the zope.Anonymous that every user has, define it in site.zcml and grant view permissions to it, instead of zope.Anybody unauthenticated group.
- Improvements to README.txt and var/README.txt (it was pointing to the wrong configuration file). Moved changelog from README.txt into separate CHANGES.txt file.
- The --no-buildout option is no longer ignored now.
- Added a bin/python script that mimicks an interpreter.
- Enabled the APIDoc tool by default. You may access it under http://localhost:8080/++apidoc++.
- Simplified *package*/testing.py.
- Added a zdaemon controller script much like zopectl called *package*-ctl (where *package* is the name of the package created with zopeproject).
- Added a debug script called *package*-debug that configures the application and drops into an interpreter session. It is also available via *package*-ctl debug.
- Added debug.ini which configures a WSGI middleware for intercepting exceptions and live debugging (either using Paste’s evalexception middleware or the Python debugger pdb).
- Added a functional test layer in *package*.testing which loads the new ftesting.zcml. Convenience definitions of test suites pre-configured for that layer are available in *package*.testing as well.
- More improvements to the README.txt file.
- Make use of zope.app.wsgi.getApplication() to reduce the startup boiler-plate in startup.py (formerly application.py).
- The package that zopeproject creates is now located in a src directory, where it’s easier to single out among the other files and directories.
- Fixed a bug when guessing the default eggs-directory: When ~/.buildout/default.cfg did not contain an eggs-directory option, zopeproject failed with a ConfigParser.NoOptionError.
- Renamed application.py to startup.py to make the intent of the module much clearer, and to avoid clashes with e.g. Grok (where “application” means something else, and app.py is commonly used for the application object).
- The eggs directory will no longer be written to buildout.cfg if it is the same as the buildout default in ~/.buidout/default.cfg.
- Cleaned up and enhanced the dependencies of the generated application. It no longer depends on zope.app.securitypolicy, only the deployment (site.zcml) does. Obsolete dependencies (and their include statements in ZCML) have been removed. zope.app.catalog and friends have been added as a convenience.
- If the user already has a default eggs directory set in ~/.buildout/default.cfg, it is used as the default value for the eggs directory.
- Greatly improved the README.txt file.
- The buildout.cfg template was missing settings for the shared eggs directory and thew newest flag.
- Assemble the default path for the eggs directory in a Windows-friendly way.
- Renamed to zopeproject.
- Incorporated much of the grokproject 0.5.x infrastructure. This makes it much more robust, especially when launching zc.buildout.
- Merged make-zope-app and deploy-zope-app back into one command: zopeproject.
- Renamed to make-zope-app.
- Split mkzopeapp into two commands: make-zope-app and deploy-zope-app.
- No longer use zope.paste for the application factory. Instead, each application that’s created from the skeleton defines its own factory (which is reasonably small and gains flexibility).
- Get rid of the start<<Project>> script. Simply use bin/paster serve deploy.ini for starting the server.
- Use the Paste#http server by default.
Initial release as mkzopeapp