[Zope-Checkins] SVN: Zope/trunk/ Merge of my recent changes from the 2.12 branch:

Chris Withers chris at simplistix.co.uk
Mon Oct 5 20:28:03 EDT 2009


Log message for revision 104819:
  Merge of my recent changes from the 2.12 branch:
  - Ignore pyd's when they're built in place.
  - add sphinx section to buildout and make.bat for Windows user
  - de-cruft of tree
  - revamp of docs
  - rework of Windows service stuff to make zopeservice.py in instances unnecessary
  - make zopectl work properly on Windows

Changed:
  D   Zope/trunk/Extensions/
  U   Zope/trunk/buildout.cfg
  D   Zope/trunk/doc/CREDITS.rst
  U   Zope/trunk/doc/INSTALL.rst
  U   Zope/trunk/doc/SECURITY.rst
  U   Zope/trunk/doc/SETUID.rst
  A   Zope/trunk/doc/USERS.rst
  D   Zope/trunk/doc/WINDOWS.rst
  U   Zope/trunk/doc/index.rst
  A   Zope/trunk/doc/make.bat
  _U  Zope/trunk/src/AccessControl/
  _U  Zope/trunk/src/DocumentTemplate/
  _U  Zope/trunk/src/Missing/
  _U  Zope/trunk/src/MultiMapping/
  _U  Zope/trunk/src/Products/ZCTextIndex/
  _U  Zope/trunk/src/Record/
  _U  Zope/trunk/src/ThreadLock/
  D   Zope/trunk/src/Zope2/Startup/nt/
  U   Zope/trunk/src/Zope2/Startup/zopectl.py
  D   Zope/trunk/src/Zope2/utilities/skel/bin/zopeservice.py.in
  _U  Zope/trunk/src/initgroups/
  U   Zope/trunk/src/nt_svcutils/service.py

-=-
Modified: Zope/trunk/buildout.cfg
===================================================================
--- Zope/trunk/buildout.cfg	2009-10-06 00:12:56 UTC (rev 104818)
+++ Zope/trunk/buildout.cfg	2009-10-06 00:28:03 UTC (rev 104819)
@@ -7,6 +7,7 @@
     alltests
     allpy
     dependencies
+    docs
 
 extends = versions.cfg
 unzip = true
@@ -28,7 +29,6 @@
 interpreter = zopepy
 scripts = zopepy
 
-
 [alltests]
 recipe = zc.recipe.testrunner
 eggs =
@@ -122,3 +122,7 @@
     ClientForm
     docutils
     mechanize
+
+[docs]
+recipe = zc.recipe.egg
+eggs =  sphinx

Deleted: Zope/trunk/doc/CREDITS.rst
===================================================================
--- Zope/trunk/doc/CREDITS.rst	2009-10-06 00:12:56 UTC (rev 104818)
+++ Zope/trunk/doc/CREDITS.rst	2009-10-06 00:28:03 UTC (rev 104819)
@@ -1,64 +0,0 @@
-Credits
-=======
-
-The Zope software receives contributions from far and wide.  Here's
-the Zope Hall of Fame:
-
-- Stephen Purcell allows us to distribute his PyUnit unit testing
-  framework with Zope.
-
-- Jeff Bauer is Zope Dude Number One.  Jeff took over PCGI and
-  kept pushing it forward through the years.
-
-- Sam Rushing worked with us at Digital Creations to make Medusa
-  the publishing platform for ZServer and the concurrency of Zope2.
-
-- A subset of windows guru Mark Hammond's win32 extensions are
-  bundled with win32 binary distributions of Zope.
-
-- Martijn Pieters and Brian Hooper contributed the #in reverse
-  attribute.
-
-- Phillip Eby contributed the DTML 'let' tag and many
-  other useful ideas, including the inspiration for the DTML
-  'call', 'with' and 'return'
-  tags.
-
-- The DateTime module was based on work from Ted Horst.
-
-- Jordan Baker contributed the 'try' tag, something we've wanted
-  for a long, long time.
-
-- Martijn Pieters chipped in with a safe range function.
-
-- Michael Hauser came up with the name "Zope".
-
-- Eric Kidd from Userland contributed to ZPublisher's support for
-  XML-RPC.
-
-- Andrew M. Kuchling wrote the initial version of mod_pcgi, making 
-  him extremely cool in our book.
-
-- Oleg Broytmann has taken up the standard of mod_pcgi and moving
-  it to be a really amazing thing, and ready for prime time.
-
-- Jephte CLAIN made some patches to European ZopeTime.
-
-- Thanks to Gregor Hoffleit for his work in getting Zope into the
-  Debian distribution.
-
-- All the other Zopistas far and wide that stuck with us during
-  the Bobo/Principia days and politely push us to make the best damn
-  app server on this or any other planet.
-
-- Of course the list of credits would be quite incomplete without
-  mentioning Guido van Rossum, benevolent dictator of Python and
-  long-time friend of Digital Creations.  Zope Power is Python
-  Power.
-
-- Special thanks to Richard Stallman and the Free Software
-  Foundation for their assistance and feedback on the
-  GPL-compatible 2.0 version of the Zope Public License.
-
-
-

Modified: Zope/trunk/doc/INSTALL.rst
===================================================================
--- Zope/trunk/doc/INSTALL.rst	2009-10-06 00:12:56 UTC (rev 104818)
+++ Zope/trunk/doc/INSTALL.rst	2009-10-06 00:28:03 UTC (rev 104819)
@@ -1,16 +1,16 @@
-========================================
-Building and installing Zope from source
-========================================
+============================
+Building and Installing Zope
+============================
 
-Welcome to Zope!  This document describes building and installing
-Zope on UNIX and Linux.
+.. highlight:: bash
 
-See ``doc/WINDOWS.rst`` for information about Windows.
+This document descibes how to get going with Zope.
 
 Prerequisites
--------------
+=============
 
-System requirements when building from source
+In order to use Zope, you must have the following pre-requisites
+available: 
 
 - A supported version of Python, including the development support if
   installed from system-level packages.  Supported versions include:
@@ -24,55 +24,100 @@
   headers installed which correspond to your system's ``zlib``.
 
 - A C compiler capable of building extension modules for your Python
-  (gcc recommended).
+  (gcc recommended). This is not necessary for Windows as binary
+  releases of the parts that would need compiling are always made
+  available.
 
+- If you wish to install Zope as a Service on Windows, you will need
+  to have the `pywin32`__ package installed.
 
-Building Zope using zc.buildout
--------------------------------
+  __ https://sourceforge.net/projects/pywin32/
 
-Zope is built using the ``zc.buildout`` library, which needs to be
-"bootstrapped" with your Python version.  E.g.::
+Installing Zope
+===============
 
-  $ cd /path/to/zope
-  $ /path/to/your/python bootstrap/bootstrap.py
+Unless using buildout to build a zope instance as described
+:ref:`below <buildout-instances>`, you will need to install Zope
+separately. If you want to create a buildout-based Zope instance,
+please skip directly to that section.
 
-The bootstrap script creates a ``buildout`` script in ``bin``;  run this
-script to finish building Zope::
+Installing Zope using virtualenv
+--------------------------------
 
+Zope can be installed within a virtualized Python environment using 
+``virtualenv`` as follows::
+
+  $ virtualenv --no-site-packages my_zope
+  $ cd my_zope
+  $ source bin/activate
+  $ bin/easy_install -i http://download.zope.org/Zope2/index/<Zope version> Zope2
+
+Using ``virtualenv`` is **highly recommended**. Otherwise, you may encounter
+unexpected conflicts with packages that have already been installed.
+
+Once you've installed Zope, you'll need to :ref:`create an instance <classic-instances>`.
+
+Installing Zope using zc.buildout
+---------------------------------
+
+Unless you are `developing zope`__, you most likely
+want to be creating a :ref:`buildout-based Zope instance <buildout-instances>` rather
+that installing using buildout as described in this section.
+
+__ http://docs.zope.org/developer/
+
+However, if you really just want to create Zope instances using the
+classic ``mkzopeinstance`` but with the software installed by buildout,
+then you need to do the following:
+
+- Download the Zope 2 source distribution from `PyPI`__
+
+  __ http://pypi.python.org/pypi/Zope2
+
+- Bootstrap the buildout
+
+- Run the buildout
+
+On Linux, this can be done as follows::
+
+  $ wget http://pypi.python.org/packages/source/Z/Zope2/Zope2-<Zope version>.tar.gz
+  $ tar xfvz Zope2-2.12.0.tar.gz
+  $ cd Zope2-2.12.0
+  $ /path/to/your/python bootstrap/bootstrap.py
   $ bin/buildout
 
+Once you've installed Zope, you'll need to :ref:`create an instance <classic-instances>`.
+
 Installing Zope using easy_install
 ----------------------------------
 
-Zope can be installed using ``easy_install`` either using a global
-easy_install installation or within a virtualized Python environment
-(using ``virtualenv``)::
+Zope can be installed using ``easy_install``, but it is recommended to
+use ``virtualenv`` as described above to avoid unexpected conflicts
+with other packages installed directly in your python installation.
 
-  $ virtualenv --no-site-packages my_zope
-  $ cd my_zope
-  $ source bin/activate
-  $ bin/easy_install -i http://download.zope.org/Zope2/index/<Zope version> Zope2
+However, if you want to use easy_install globally, all you need to do
+is::
 
-This will create the related scripts like ``mkzopeinstance`` within the
-``bin`` folder of your global or virtualized Python environment.
+  $ easy_install -i http://download.zope.org/Zope2/index/<Zope version> Zope2
 
-Using ``virtualenv`` is **highly recommended**. Otherwise you may encounter
-unexpected conflicts with already installed packages.
+This will create the related scripts such as ``mkzopeinstance`` within the
+scripts folder of your python installation. You can then use them to
+create instances as described below.
 
+.. _classic-instances:
 
-Creating a Zope Instance
-------------------------
+Creating a classic Zope Instance
+================================
 
-Once you've performed the install step, to begin actually using
-Zope, you will need to create an "instance home", which is a
-directory that contains configuration and data for a Zope server
-process.  The instance home is created using the ``mkzopeinstance``
-script::
+Once you've installed Zope, you will need to create an "instance
+home". This is a directory that contains configuration and data for a
+Zope server process.  The instance home is created using the
+``mkzopeinstance`` script::
 
   $ bin/mkzopeinstance
 
-If you use Zope from SVN, you will need to specify the Python interpreter
-to use for the instance explicitly::
+You can specify the Python interpreter to use for the instance
+explicitly:: 
 
   $ bin/mkzopeinstance --python=$PWD/bin/zopepy
 
@@ -82,8 +127,97 @@
 
   $ bin/mkzopeinstance --help
 
+.. note::
+  The traditional "inplace" build is no longer supported. If using
+  ``mkzopeinstance``, always do so outside the buildout/virtualenv
+  environment. If you wish to manage your Zope instance using
+  buildout, please see the section below.
 
-Starting Zope as a Daemon
+.. _buildout-instances:
+
+Creating a buildout-based Zope Instance
+=======================================
+
+If you wish to use buildout to manage your Zope instance, then the
+instance is created as follows:
+
+* Create a directory for your instance. In this directory, create a
+  ``etc``, ``logs`` and ``var`` subdirectories.
+
+* Download the following file into your instance directory:
+
+  `http://svn.zope.org/*checkout*/zc.buildout/trunk/bootstrap/bootstrap.py`__
+    
+  __ http://svn.zope.org/*checkout*/zc.buildout/trunk/bootstrap/bootstrap.py
+
+.. highlight:: none
+
+* Create a buildout configuration as follows:
+
+.. topic:: buildout.cfg
+ :class: file
+
+ ::
+
+   [buildout]
+   parts = instance 
+   extends = http://svn.zope.org/*checkout*/Zope/tags/<Zope version>/versions.cfg
+
+   [instance]
+   recipe = zc.recipe.egg
+   eggs = Zope2
+   interpreter = py
+   scripts = runzope zopectl
+   initialization =
+     import sys
+     sys.argv[1:1] = ['-C',r'${buildout:directory}/etc/zope.conf']
+
+This is the minimum but all the usual buildout techniques can be
+used.
+
+* Create a Zope configuration file starting as follows:
+
+.. topic:: etc/zope.cfg
+ :class: file
+
+ ::
+
+   %define INSTANCE <path to your instance directory>
+
+   python $INSTANCE/bin/py[.exe on Windows]
+ 
+   instancehome $INSTANCE
+
+.. highlight:: bash
+
+* Now, run the following commands::
+
+    $ /path/to/your/python bootstrap.py
+    $ bin/buildout
+
+  In the ``bin`` subdirectory of your instance directory, you will
+  find ``runzope`` and ``zopectl`` scripts that can be used as
+  normal.
+
+Using your Zope instance
+========================
+
+There are various ways to run Zope from your newly created
+instance. They are all described below.
+
+Running Zope in the Foreground
+------------------------------
+
+To run Zope without detaching from the console, use the ``fg``
+command (short for ``foreground``)::
+
+  $ /path/to/zope/instance/bin/zopectl fg
+
+In this mode, Zope emits its log messages to the console, and does not
+detach from terminal.
+
+
+Running Zope as a Daemon
 -------------------------
 
 Once an instance home has been created, the Zope server can now be
@@ -95,23 +229,47 @@
 You can examine it with the usual tools (cat, more, tail)
 and see if there are any errors preventing zope from starting.
 
+.. highlight:: none
+.. note::
 
-Running Zope in the Foreground
-------------------------------
+  For this to work on Windows, the Zope instance must be installed as
+  a Service. This is done with::
 
-By default, ``zopectl start`` will start a background process (a
-"daemon) that manages Zope.  ``zopectl stop`` will stop the background
-process.  To run Zope without detaching from the console, use the ``fg``
-command (short for ``foreground``)::
+    bin\zopectl install
 
-  $ /path/to/zope/instance/bin/zopectl fg
+  If you later want to remove this Service, do the following::
 
-In this mode, Zope emits its log messages to the console, and does not
-detach from terminal.
+    bin\zopectl remove
 
+  For the full list of options available for setting up Zope as a
+  Windows Service, do::
 
+    bin\zopectl install --help
+
+.. highlight:: bash
+
+Integrating with System Startup
+-------------------------------
+
+zopectl can be linked as rc-script in the usual start directories
+on linux or other System V unix variants.
+
+You can use ``zopectl`` interactively as a command shell by just
+calling it without any arguments. Try ``help`` there and ``help <command>``
+to find out about additionally commands of zopectl. These commands
+also work at the command line.
+
+.. note::
+
+  On Windows, a Service can be installed and set to start
+  automatically with the following:
+
+  .. code-block:: none
+
+    bin\zopectl install --startup=auto
+
 Configuring Zope
-----------------
+================
 
 Your Zope instance is configured through a file, either found by
 default::
@@ -127,8 +285,8 @@
   Config file:  /tmp/other.conf
 
 When starting Zope, if you see errors indicating that an address is in
-use, then you will have to supply arguments to runzope to change the ports
-used for HTTP or FTP. The default HTTP and FTP ports used by Zope are
+use, then you may have to change the ports Zope uses for HTTP or FTP. 
+The default HTTP and FTP ports used by Zope are
 8080 and 8021 respectively. You can change the ports used by
 editing ./etc/zope.conf appropriately.
 
@@ -143,21 +301,8 @@
 The address can just be a port number as shown, or a  host:port
 pair to bind only to a specific interface.
 
-
-Integrating with System Startup
--------------------------------
-
-zopectl can be linked as rc-script in the usual start directories
-on linux or other System V unix variants.
-
-You can use ``zopectl`` interactively as a command shell by just
-calling it without any arguments. Try ``help`` there and ``help <command>``
-to find out about additionally commands of zopectl. These commands
-also work at the command line.
-
-
 Logging In To Zope
-------------------
+==================
 
 Once you've started Zope, you can then connect to the Zope webserver
 by directing your browser to::
@@ -172,6 +317,11 @@
 and password you provided in response to the prompts issued during
 the "make instance" process.
 
+If you are using a buildout-based Zope instance, you will need to
+create a user as follows::
+
+  $ bin/zopectl adduser username password
+
 Now you're off and running! You should be looking at the Zope
 management screen which is divided into two frames. On the left you
 can navigate between Zope objects and on the right you can edit them
@@ -180,12 +330,10 @@
 
 If you haven't used Zope before, you should head to the Zope web
 site and read some documentation. The Zope Documentation section is
-a good place to start. You can access it at:
+a good place to start. You can access it at http://docs.zope.org/
 
-http://docs.zope.org/
-
 Troubleshooting
----------------
+===============
 
 - This version of Zope requires Python 2.5.4 or better, including
   2.6.x.  It will *not* run with Python 3.x.
@@ -201,4 +349,4 @@
   you built Python from source all the configuration information
   should already be available.
 
-- See ``doc/CHANGES.rst`` for important notes on this version of Zope.
+- See the :doc:`CHANGES` for important notes on this version of Zope.

Modified: Zope/trunk/doc/SECURITY.rst
===================================================================
--- Zope/trunk/doc/SECURITY.rst	2009-10-06 00:12:56 UTC (rev 104818)
+++ Zope/trunk/doc/SECURITY.rst	2009-10-06 00:28:03 UTC (rev 104819)
@@ -1,87 +1,18 @@
-Setting the initial user name and password
-==========================================
+Filesytem Permissions
+=====================
 
-Because Zope is managed through the web, user names and passwords must be
-used to assure that only authorized people can make changes to a Zope
-installation.
-
-Some user name and password is needed to "bootstrap" the creation of
-normal managers of your Zope site.  This is accomplished through the
-use of the file 'inituser'.  The first time Zope starts, it will detect
-that no users have been defined in the root user folder.  It will search
-for the 'inituser' file and, if it exists, will add the user defined
-in the file to the root user folder.
-
-Normally, 'inituser' is created by the Zope install scripts.  Either
-the installer prompts for the password or a randomly generated
-password is created and displayed at the end of the build script.
-
-You can use the 'zpasswd.py' script to create 'inituser' yourself.
-Execute 'zpasswd.py' like this::
-
-    python zpasswd.py inituser
-
-The script will prompt you for the name, password, and allowed
-domains.  The default is to encode the password with SHA, so please
-remember this password as there is no way to recover it (although
-'zpasswd.py' lets you reset it.)
-
-In some situations you may need to bypass normal security controls
-because you have lost your password or because the security settings
-have been mixed up.  Zope provides a facility called an "emergency
-user" so that you can reset passwords and correct security
-settings.
-
-The emergency user password must be defined outside the application
-user interface.  It is defined in the 'access' file located
-in the Zope directory.  It should be readable only by the user
-as which your web server runs.
-
-To create the emergency user, use 'zpasswd.py' to create the
-'access' file like this::
-
-    python zpasswd.py access
-
-In order to provide a somewhat higher level of security, various
-encoding schemes are supported which provide access to either SHA-1
-encryption or the standard UNIX crypt facility if it has been compiled
-into Python.  Unless you have some special requirements (see below), 
-you should use the SHA-1 facility, which is the default.
-
-Format of 'inituser' and 'access'
----------------------------------
-
-A password file should consist of a single line of the form::
-
-    name:password
-
-Note that you may also add an optional third component to the line in the
-access file to restrict access by domain.  For example, the line::
-
-    mario:nintendoRules:*.mydomain.com
-
-in your 'access' file will only allow permit emergency user access
-from `*.mydomain.com` machines. Attempts to access the system from
-other domains will fail, even if the correct emergency user name
-and password are used.
-
-Please note that if you use the ZServer monitor capability, you will
-need to run with a clear text password.
-
-Setting permissions on the var directory
-----------------------------------------
-
-You need to set permissions on the Zope var directory.
-Zope needs to read and write data from its var directory. Before
+You need to set permissions on the directory Zope uses to store its
+data. This will normally be the `var` directory in the instance home.
+Zope needs to read and write data to this directory. Before
 running Zope you should ensure that you give adequate permissions
-to the Zope var directory for the userid Zope will run under.
+to this directory for the userid Zope will run under.
 
 Depending on how you choose to run Zope you will need to give
-different permissions to the var directory.  If you use Zope with an
+different permissions to the directory.  If you use Zope with an
 existing web server, it will probably run Zope as 'nobody'. In this
 case 'nobody' needs read and write permissions to the var directory.
 
-If you change the way you run Zope you may need to modify the permissions
-of the var directory and the files in it to allow Zope to read and write
+If you change the way you run Zope, you may need to modify the permissions
+of the directory and the files in it to allow Zope to read and write
 under its changed userid.
 

Modified: Zope/trunk/doc/SETUID.rst
===================================================================
--- Zope/trunk/doc/SETUID.rst	2009-10-06 00:12:56 UTC (rev 104818)
+++ Zope/trunk/doc/SETUID.rst	2009-10-06 00:28:03 UTC (rev 104819)
@@ -2,9 +2,9 @@
 ===========================
 
 .. note:: 
-  It is best practice running Zope behind a reverse proxy like
-  Apache, Squid or Varnish. In this case you do not need to run
-  or install Zope with root privileges since the reverse proxy
+  It is best practice to run Zope behind a reverse proxy like
+  Apache, Squid or Varnish. In this case, you do not need to run
+  or install Zope with root privileges, since the reverse proxy
   will bind to port 80 and proxy back all request to Zope running
   on an unpriviledged port.
 

Copied: Zope/trunk/doc/USERS.rst (from rev 104695, Zope/branches/2.12/doc/USERS.rst)
===================================================================
--- Zope/trunk/doc/USERS.rst	                        (rev 0)
+++ Zope/trunk/doc/USERS.rst	2009-10-06 00:28:03 UTC (rev 104819)
@@ -0,0 +1,86 @@
+Special Users
+=============
+
+Because Zope is managed through the web, user names and passwords must be
+used to assure that only authorized people can make changes to a Zope
+installation.
+
+Adding Managers
+---------------
+
+If you need to add a Manager to an existing Zope instance, you can do
+this using `zopectl` as follows::
+
+  zopectl adduser `name` `password`
+
+The Initial User
+----------------
+
+An initial username and password is needed to "bootstrap" the creation of
+normal managers of your Zope site.  This is accomplished through the
+use of the 'inituser' file in the directory specified as the instance
+home. 
+
+The first time Zope starts, it will detect
+that no users have been defined in the root user folder.  It will search
+for the 'inituser' file and, if it exists, will add the user defined
+in the file to the root user folder.
+
+Normally, 'inituser' is created by the Zope install scripts.  Either
+the installer prompts for the password or a randomly generated
+password is created and displayed at the end of the build script.
+
+You can use the 'zpasswd.py' script to create 'inituser' yourself.
+Execute 'zpasswd.py' like this::
+
+    python zpasswd.py inituser
+
+The script will prompt you for the name, password, and allowed
+domains.  The default is to encode the password with SHA, so please
+remember this password as there is no way to recover it (although
+'zpasswd.py' lets you reset it.)
+
+The Emergency User
+------------------
+
+In some situations you may need to bypass normal security controls
+because you have lost your password or because the security settings
+have been mixed up.  Zope provides a facility called an "emergency
+user" so that you can reset passwords and correct security
+settings.
+
+The emergency user password must be defined outside the application
+user interface.  It is defined in the 'access' file located
+in the Zope directory.  It should be readable only by the user
+as which your web server runs.
+
+To create the emergency user, use 'zpasswd.py' to create the
+'access' file like this::
+
+    python zpasswd.py access
+
+In order to provide a somewhat higher level of security, various
+encoding schemes are supported which provide access to either SHA-1
+encryption or the standard UNIX crypt facility if it has been compiled
+into Python.  Unless you have some special requirements (see below), 
+you should use the SHA-1 facility, which is the default.
+
+Format of 'inituser' and 'access'
+---------------------------------
+
+A password file should consist of a single line of the form::
+
+    name:password
+
+Note that you may also add an optional third component to the line in the
+access file to restrict access by domain.  For example, the line::
+
+    mario:nintendoRules:*.mydomain.com
+
+in your 'access' file will only allow permit emergency user access
+from `*.mydomain.com` machines. Attempts to access the system from
+other domains will fail, even if the correct emergency user name
+and password are used.
+
+Please note that if you use the ZServer monitor capability, you will
+need to run with a clear text password.

Deleted: Zope/trunk/doc/WINDOWS.rst
===================================================================
--- Zope/trunk/doc/WINDOWS.rst	2009-10-06 00:12:56 UTC (rev 104818)
+++ Zope/trunk/doc/WINDOWS.rst	2009-10-06 00:28:03 UTC (rev 104819)
@@ -1,58 +0,0 @@
-How to build and install Zope from source code on Windows.
-----------------------------------------------------------
-
-* Ensure you have the correct MSVC version installed for the
-  version of Python you will be using.
-
-* Install (or build from sources) Python
-  http://www.python.org
-
-* Install (or build from sources) the Python for Windows extensions
-  http://sourceforge.net/projects/pywin32/
-
-* Unpack the Zope source distribution. Change to that directory.
-
-* Execute:
-  % python.exe inst\configure.py
-  It should say something like:
-  >
-  > - Zope top-level binary directory will be c:\Zope-2.13.
-  > - Makefile written.
-  >
-  > Next, run the Visual C++ batch file "VCVARS32.bat" and then "nmake".
-
-  (run 'configure.py --help' to see how to change things)
-
-* 'makefile' will have ben created.  As instructed, execute 'nmake'.  
-  If the build succeeds, the last message printed should be:
-  > Zope built.  Next, do 'nmake install'.
-
-* As instructed, execute 'nmake install'.  A few warnings will be generated, 
-  but they can be ignored.  The last message in the build process should be:
-  > Zope binaries installed successfully.
-
-* Zope itself has now been installed.  We need to create an instance.  Run:
-  % python.exe {install_path}\bin\mkzopeinstance.py
-  
-  We will be prompted, via the console, for the instance directory and 
-  username/password for the admin user.
-
-* We are now ready to start zope.  Run:
-  % {zope_instance}\bin\runzope.bat
-  Zope should start with nice log messages being printed to
-  stdout.  When Zope is ready, you should see:
-  > ------
-  > 2004-10-13T12:27:58 INFO(0) Zope Ready to handle requests
-  
-  Press Ctrl+C to stop this instance of the server.
-
-* Optionally, install as a Windows service.  Execute:
-  % python {zope_instance}\bin\zopeservice.py
-  to see the valid options.  You may want something like:
-  % python {zope_instance}\bin\zopeservice.py --startup=auto install
-
-  Once installed, it can be started any number of ways:
-  - % {zope_instance}\bin\zopectl.bat start
-  - % python {zope_instance}\bin\zopeservice.py start
-  - Control Panel
-  - % net start service_short_name (eg, `net start Zope_-1227678699`)

Modified: Zope/trunk/doc/index.rst
===================================================================
--- Zope/trunk/doc/index.rst	2009-10-06 00:12:56 UTC (rev 104818)
+++ Zope/trunk/doc/index.rst	2009-10-06 00:28:03 UTC (rev 104819)
@@ -8,12 +8,11 @@
    :maxdepth: 2
 
    WHATSNEW.rst
-   CHANGES.rst
    INSTALL.rst
+   USERS.rst
    SECURITY.rst
-   ZOPE3.rst
    SETUID.rst
    SIGNALS.rst
    DEBUGGING.rst
-   CREDITS.rst
+   CHANGES.rst
 

Copied: Zope/trunk/doc/make.bat (from rev 104695, Zope/branches/2.12/doc/make.bat)
===================================================================
--- Zope/trunk/doc/make.bat	                        (rev 0)
+++ Zope/trunk/doc/make.bat	2009-10-06 00:28:03 UTC (rev 104819)
@@ -0,0 +1,100 @@
+ at ECHO OFF
+
+REM Command file for Sphinx documentation
+
+set SPHINXBUILD=..\bin\sphinx-build
+set ALLSPHINXOPTS=-d .build/doctrees %SPHINXOPTS% .
+if NOT "%PAPER%" == "" (
+	set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+	:help
+	echo.Please use `make ^<target^>` where ^<target^> is one of
+	echo.  html      to make standalone HTML files
+	echo.  dirhtml   to make HTML files named index.html in directories
+	echo.  pickle    to make pickle files
+	echo.  json      to make JSON files
+	echo.  htmlhelp  to make HTML files and a HTML help project
+	echo.  latex     to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+	echo.  changes   to make an overview over all changed/added/deprecated items
+	echo.  linkcheck to check all external links for integrity
+	echo.  doctest   to run all doctests embedded in the documentation if enabled
+	goto end
+)
+
+if "%1" == "clean" (
+	for /d %%i in (.build\*) do rmdir /q /s %%i
+	del /q /s .build\*
+	goto end
+)
+
+if "%1" == "html" (
+	%SPHINXBUILD% -b html %ALLSPHINXOPTS% .build/html
+	echo.
+	echo.Build finished. The HTML pages are in .build/html.
+	goto end
+)
+
+if "%1" == "dirhtml" (
+	%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% .build/dirhtml
+	echo.
+	echo.Build finished. The HTML pages are in .build/dirhtml.
+	goto end
+)
+
+if "%1" == "pickle" (
+	%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% .build/pickle
+	echo.
+	echo.Build finished; now you can process the pickle files.
+	goto end
+)
+
+if "%1" == "json" (
+	%SPHINXBUILD% -b json %ALLSPHINXOPTS% .build/json
+	echo.
+	echo.Build finished; now you can process the JSON files.
+	goto end
+)
+
+if "%1" == "htmlhelp" (
+	%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% .build/htmlhelp
+	echo.
+	echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in .build/htmlhelp.
+	goto end
+)
+
+if "%1" == "latex" (
+	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% .build/latex
+	echo.
+	echo.Build finished; the LaTeX files are in .build/latex.
+	goto end
+)
+
+if "%1" == "changes" (
+	%SPHINXBUILD% -b changes %ALLSPHINXOPTS% .build/changes
+	echo.
+	echo.The overview file is in .build/changes.
+	goto end
+)
+
+if "%1" == "linkcheck" (
+	%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% .build/linkcheck
+	echo.
+	echo.Link check complete; look for any errors in the above output ^
+or in .build/linkcheck/output.txt.
+	goto end
+)
+
+if "%1" == "doctest" (
+	%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% .build/doctest
+	echo.
+	echo.Testing of doctests in the sources finished, look at the ^
+results in .build/doctest/output.txt.
+	goto end
+)
+
+:end


Property changes on: Zope/trunk/src/AccessControl
___________________________________________________________________
Modified: svn:ignore
   - *so
*.pyc
build

   + *so
*.pyc
build
*.pyd



Property changes on: Zope/trunk/src/DocumentTemplate
___________________________________________________________________
Modified: svn:ignore
   - *so
*.pyc
build

   + *so
*.pyc
build
*.pyd



Property changes on: Zope/trunk/src/Missing
___________________________________________________________________
Modified: svn:ignore
   - *so
*.pyc
build

   + *so
*.pyc
build
*.pyd



Property changes on: Zope/trunk/src/MultiMapping
___________________________________________________________________
Modified: svn:ignore
   - *so
*.pyc
build

   + *so
*.pyc
build
*.pyd



Property changes on: Zope/trunk/src/Products/ZCTextIndex
___________________________________________________________________
Modified: svn:ignore
   - *so
*.pyc
build

   + *so
*.pyc
build
*.pyd



Property changes on: Zope/trunk/src/Record
___________________________________________________________________
Modified: svn:ignore
   - *so
*.pyc
build

   + *so
*.pyc
build
*.pyd



Property changes on: Zope/trunk/src/ThreadLock
___________________________________________________________________
Modified: svn:ignore
   - *so
*.pyc
build

   + *so
*.pyc
build
*.pyd


Modified: Zope/trunk/src/Zope2/Startup/zopectl.py
===================================================================
--- Zope/trunk/src/Zope2/Startup/zopectl.py	2009-10-06 00:12:56 UTC (rev 104818)
+++ Zope/trunk/src/Zope2/Startup/zopectl.py	2009-10-06 00:28:03 UTC (rev 104819)
@@ -51,10 +51,56 @@
 WIN = False
 if sys.platform[:3].lower() == "win":
     WIN = True
+    import win32serviceutil
+    from nt_svcutils import service
+    
+    def do_windows(command):
+        def inner(self,arg):
 
+            INSTANCE_HOME = self.options.directory
+            name = 'Zope'+str(hash(INSTANCE_HOME.lower()))
+            display_name = 'Zope instance at '+INSTANCE_HOME
+
+            # This class exists only so we can take advantage of
+            # win32serviceutil.HandleCommandLine, it is never
+            # instantiated.
+            class InstanceService(service.Service):
+                _svc_name_ = name
+                _svc_display_name_ = display_name
+                _svc_description_ = "A Zope application instance running as a service"
+
+            # getopt sucks :-(
+            argv = [sys.argv[0]]
+            argv.extend(arg.split())
+            argv.append(command)
+
+            # we need to supply this manually as HandleCommandLine guesses wrong
+            serviceClassName = os.path.splitext(service.__file__)[0]+'.Service'
+            
+            err = win32serviceutil.HandleCommandLine(
+                InstanceService,
+                serviceClassName,
+                argv=argv,
+                )
+
+            return err,InstanceService
+            
+        return inner
+
 def string_list(arg):
     return arg.split()
 
+def quote_command(command):
+    print " ".join(command)
+    # Quote the program name, so it works even if it contains spaces
+    command = " ".join(['"%s"' % x for x in command])
+    if WIN:
+        # odd, but true: the windows cmd processor can't handle more than
+        # one quoted item per string unless you add quotes around the
+        # whole line.
+        command = '"%s"' % command
+    return command
+
 class ZopeCtlOptions(ZDOptions):
     # Zope controller options.
     # 
@@ -132,11 +178,6 @@
         self.python = os.environ.get('PYTHON', config.python) or sys.executable
         self.zdrun = os.path.join(os.path.dirname(zdaemon.__file__),
                                   "zdrun.py")
-        if WIN:
-            # Add the path to the zopeservice.py script, which is needed for
-            # some of the Windows specific commands
-            servicescript = os.path.join(self.directory, 'bin', 'zopeservice.py')
-            self.servicescript = '"%s" %s' % (self.python, servicescript)
 
         self.exitcodes = [0, 2]
         if self.logfile is None and config.eventlog is not None:
@@ -171,6 +212,13 @@
             args = [opt, svalue]
         return args
 
+    def do_start(self, arg):
+        # signal to Zope that it is being managed
+        # (to indicate it's web-restartable)
+        os.putenv('ZMANAGED', '1')
+
+    ## START OF WINDOWS ONLY STUFF
+    
     if WIN:
         def get_status(self):
             # get_status from zdaemon relies on *nix specific socket handling.
@@ -182,52 +230,48 @@
             self.zd_status = None
             return
 
-        def do_stop(self, arg):
-            # Stop the Windows service
-            program = "%s stop" % self.options.servicescript
-            print program
-            os.system(program)
+        do_start = do_windows('start')
+        do_stop = do_windows('stop')
+        do_restart = do_windows('restart')
 
-        def do_restart(self, arg):
-            # Restart the Windows service
-            program = "%s restart" % self.options.servicescript
-            print program
-            os.system(program)
-
         # Add extra commands to install and remove the Windows service
 
-        def do_install(self, arg):
-            program = "%s install" % self.options.servicescript
-            print program
-            os.system(program)
+        def do_install(self,arg):
+            err,InstanceClass = do_windows('install')(self,arg)
+            if not err:
+                # If we installed successfully, put info in registry for the
+                # real Service class to use:
+                command = '"%s" -C "%s"' % (
+                    # This gives us the instance script for buildout instances
+                    # and the install script for classic instances.
+                    os.path.join(os.path.split(sys.argv[0])[0],'runzope'),
+                    self.options.configfile
+                    )
+                InstanceClass.setReg('command',command)
+                
+                # This is unfortunately needed because runzope.exe is a setuptools
+                # generated .exe that spawns off a sub process, so pid would give us
+                # the wrong event name.
+                InstanceClass.setReg('pid_filename',self.options.configroot.pid_filename)
 
         def help_install(self):
             print "install -- Installs Zope as a Windows service."
 
-        def do_remove(self, arg):
-            program = "%s remove" % self.options.servicescript
-            print program
-            os.system(program)
+        do_remove = do_windows('remove')
 
         def help_remove(self):
             print "remove -- Removes the Zope Windows service."
 
-    def do_start(self, arg):
-        # signal to Zope that it is being managed
-        # (to indicate it's web-restartable)
-        os.putenv('ZMANAGED', '1')
-        if WIN:
-            # On Windows start the service, this fails with a reasonable
-            # error message as long as the service is not installed
-            program = "%s start" % self.options.servicescript
-            print program
-            os.system(program)
-        else:
-            ZDCmd.do_start(self, arg)
+        do_windebug = do_windows('debug')
+        
+        def help_windebug(self):
+            print "windebug -- Runs the Zope Windows service in the foreground, in debug mode."
 
+    ## END OF WINDOWS ONLY STUFF
+            
     def get_startup_cmd(self, python, more):
         cmdline = ( '%s -c "from Zope2 import configure;'
-                    'configure(\'%s\');' %
+                    'configure(%r);' %
                     (python, self.options.configfile)
                     )
         return cmdline + more + '\"'
@@ -240,18 +284,19 @@
         os.system(cmdline)
 
     def do_foreground(self, arg):
-        if WIN:
-            # Adding arguments to the program is not supported on Windows
-            # and the runzope script doesn't put you in debug-mode either
-            ZDCmd.do_foreground(self, arg)
-        else:
-            self.options.program[1:1] = ["-X", "debug-mode=on"]
-            try:
-                ZDCmd.do_foreground(self, arg)
-            finally:
-                self.options.program.remove("-X")
-                self.options.program.remove("debug-mode=on")
-
+        program = self.options.program
+        local_additions = []
+        if not program.count('-X'):
+            local_additions += ['-X']
+        if not program.count('debug-mode=on'):
+            local_additions += ['debug-mode=on']
+        program[1:1] = local_additions
+        command = quote_command(program)
+        try:
+            return os.system(command)
+        finally:
+            for addition in local_additions: program.remove(addition)
+            
     def help_debug(self):
         print "debug -- run the Zope debugger to inspect your database"
         print "         manually using a Python interactive shell"

Deleted: Zope/trunk/src/Zope2/utilities/skel/bin/zopeservice.py.in
===================================================================
--- Zope/trunk/src/Zope2/utilities/skel/bin/zopeservice.py.in	2009-10-06 00:12:56 UTC (rev 104818)
+++ Zope/trunk/src/Zope2/utilities/skel/bin/zopeservice.py.in	2009-10-06 00:28:03 UTC (rev 104819)
@@ -1,117 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2002 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
-#
-##############################################################################
-"""
-A Zope Windows NT service frontend.
-
-Usage:
-
-  Installation
-
-    The Zope service should be installed by the Zope Windows
-    installer. You can manually install, uninstall the service from
-    the commandline.
-
-      ntservice.py [options] install|update|remove|start [...]
-           |stop|restart [...]|debug [...]
-
-    Options for 'install' and 'update' commands only:
-
-     --username domain\username : The Username the service is to run
-                                  under
-
-     --password password : The password for the username
-
-     --startup [manual|auto|disabled] : How the service starts,
-                                        default = manual
-
-    Commands
-
-      install : Installs the service
-
-      update : Updates the service.  Use this if you change any
-               configuration settings and need the service to be
-               re-registered.
-
-      remove : Removes the service
-
-      start : Starts the service, this can also be done from the
-              services control panel
-
-      stop : Stops the service, this can also be done from the
-             services control panel
-
-      restart : Restarts the service
-
-      debug : Runs the service in debug mode
-
-    You can view the usage options by running this module without any
-    arguments.
-
-  Starting Zope
-
-    Start Zope by clicking the 'start' button in the services control
-    panel. You can set Zope to automatically start at boot time by
-    choosing 'Auto' startup by clicking the 'statup' button.
-
-  Stopping Zope
-
-    Stop Zope by clicking the 'stop' button in the services control
-    panel. You can also stop Zope through the web by going to the
-    Zope control panel and by clicking 'Shutdown'.
-
-  Event logging
-
-    Service related events (such as startup, shutdown, or errors executing
-    the Zope process) are logged to the NT application event log. Use the
-    event viewer to see these events.
-
-    Zope Events are still written to the Zope event logs.
-
-"""
-import sys, os
-
-# these are replacements from mkzopeinstance
-INSTANCE_HOME = r'<<INSTANCE_HOME>>'
-ZOPE_SCRIPTS = r'<<ZOPE_SCRIPTS>>'
-ZOPE2PATH = r'<<ZOPE2PATH>>'
-
-ZOPE_RUN = os.path.join(ZOPE_SCRIPTS, 'runzope')
-CONFIG_FILE = os.path.join(INSTANCE_HOME, 'etc', 'zope.conf')
-PYTHONSERVICE_EXE = os.path.join(ZOPE_SCRIPTS, 'PythonService.exe')
-
-os.environ["INSTANCE_HOME"] = INSTANCE_HOME
-
-# XXX: we need to find nt_svcutils.service
-sys.path[0:0] = [ZOPE2PATH]
-
-from nt_svcutils.service import Service
-
-servicename = 'Zope_%s' % str(hash(INSTANCE_HOME.lower()))
-
-class InstanceService(Service):
-    _svc_name_ = servicename
-    _svc_display_name_ = 'Zope instance at %s' % INSTANCE_HOME
-    # _svc_description_ can also be set (but what to say isn't clear!)
-    # If the exe we expect is not there, let the service framework search
-    # for it.  This will be true for people running from source builds and
-    # relying on pre-installed pythonservice.exe.
-    # Note this is only used at install time, not runtime.
-    if os.path.isfile(PYTHONSERVICE_EXE):
-        _exe_name_ = PYTHONSERVICE_EXE
-
-    process_runner = ZOPE_RUN
-    process_args = '-C "%s"' % CONFIG_FILE
-
-if __name__ == '__main__':
-    import win32serviceutil
-    win32serviceutil.HandleCommandLine(InstanceService)


Property changes on: Zope/trunk/src/initgroups
___________________________________________________________________
Modified: svn:ignore
   - _initgroups.so

   + _initgroups.so
*.pyd


Modified: Zope/trunk/src/nt_svcutils/service.py
===================================================================
--- Zope/trunk/src/nt_svcutils/service.py	2009-10-06 00:12:56 UTC (rev 104818)
+++ Zope/trunk/src/nt_svcutils/service.py	2009-10-06 00:28:03 UTC (rev 104819)
@@ -1,6 +1,6 @@
 ##############################################################################
 #
-# Copyright (c) 2003 Zope Corporation and Contributors.
+# Copyright (c) 2003-2009 Zope Corporation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,
@@ -50,104 +50,37 @@
     should be created in the instance home.
     """
 
-    # The PythonService model requires that an actual on-disk class declaration
-    # represent a single service.  Thus, the definitions below for the instance
-    # must be overridden in a subclass in a file within the instance home for
-    # each instance.
-    # The values below are just examples.
-    _svc_name_ = r'Zope-Instance'
-    _svc_display_name_ = r'Zope instance at C:\Zope-Instance'
-
-    process_runner = r'C:\Program Files\Zope-2.7.0-a1\bin\python.exe'
-    process_args = r'{path_to}\run.py -C {path_to}\zope.conf'
     evtlog_name = 'Zope'
 
     def __init__(self, args):
+
+        # We get passed in the service name
+        self._svc_name_ = args[0]
+
+        # ...and from that, we can look up the other needed bits
+        # from the registry:
+        self._svc_display_name_ = self.getReg('DisplayName')
+        self._svc_command_ = self.getReg('command',keyname='PythonClass')
+        
         win32serviceutil.ServiceFramework.__init__(self, args)
-        # Just say "Zope", instead of "Zope_-xxxxx"
-        try:
-            servicemanager.SetEventSourceName(self.evtlog_name)
-        except AttributeError:
-            # old pywin32 - that's ok.
-            pass
+
+        # Don't use the service name as the event source name:        
+        servicemanager.SetEventSourceName(self.evtlog_name)
+        
         # Create an event which we will use to wait on.
         # The "service stop" request will set this event.
         # We create it inheritable so we can pass it to the child process, so
         # it too can act on the stop event.
         sa = win32security.SECURITY_ATTRIBUTES()
         sa.bInheritHandle = True
-
         self.hWaitStop = win32event.CreateEvent(sa, 0, 0, None)
-        self.redirect_thread = None
 
-    def SvcStop(self):
-        # Before we do anything, tell the SCM we are starting the stop process.
-        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
-        self.onStop()
-        # Set the stop event - the main loop takes care of termination.
-        win32event.SetEvent(self.hWaitStop)
-
-    # SvcStop only gets triggered when the user explictly stops (or restarts)
-    # the service.  To shut the service down cleanly when Windows is shutting
-    # down, we also need to hook SvcShutdown.
-    SvcShutdown = SvcStop
-
-    def onStop(self):
-        # A hook for subclasses to override
-        pass
-
-    def createProcess(self, cmd):
-        self.start_time = time.time()
-        return self.createProcessCaptureIO(cmd)
-
-    def logmsg(self, event):
-        # log a service event using servicemanager.LogMsg
-        try:
-            servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
-                                  event,
-                                  (self._svc_name_,
-                                   " (%s)" % self._svc_display_name_))
-        except win32api.error, details:
-            # Failed to write a log entry - most likely problem is
-            # that the event log is full.  We don't want this to kill us
-            try:
-                print "FAILED to write INFO event", event, ":", details
-            except IOError:
-                pass
-
-    def _dolog(self, func, msg):
-        try:
-            fullmsg = "%s (%s): %s" % \
-                      (self._svc_name_, self._svc_display_name_, msg)
-            func(fullmsg)
-        except win32api.error, details:
-            # Failed to write a log entry - most likely problem is
-            # that the event log is full.  We don't want this to kill us
-            try:
-                print "FAILED to write event log entry:", details
-                print msg
-            except IOError:
-                # And if running as a service, its likely our sys.stdout
-                # is invalid
-                pass
-
-    def info(self, s):
-        self._dolog(servicemanager.LogInfoMsg, s)
-
-    def warning(self, s):
-        self._dolog(servicemanager.LogWarningMsg, s)
-
-    def error(self, s):
-        self._dolog(servicemanager.LogErrorMsg, s)
-
+    ### ServiceFramework methods
+        
     def SvcDoRun(self):
         # indicate to Zope that the process is daemon managed (restartable)
         os.environ['ZMANAGED'] = '1'
 
-        # XXX the restart behavior is different here than it is for
-        # zdaemon.zdrun.  we should probably do the same thing in both
-        # places.
-
         # daemon behavior:  we want to to restart the process if it
         # dies, but if it dies too many times, we need to give up.
 
@@ -165,78 +98,35 @@
         # the cumulative backoff seconds counter
         self.backoff_cumulative = 0
 
+        self.ReportServiceStatus(win32service.SERVICE_START_PENDING)
         self.logmsg(servicemanager.PYS_SERVICE_STARTED)
-
         while 1:
-            # We pass *this* file and the handle as the first 2 params, then
-            # the 'normal' startup args.
-            # See the bottom of this script for how that is handled.
-            cmd = '"%s" %s' % (self.process_runner, self.process_args)
-            info = self.createProcess(cmd)
-            # info is (hProcess, hThread, pid, tid)
-            self.hZope = info[0] # process handle
-            # XXX why the test before the log message?
-            if self.backoff_interval > BACKOFF_INITIAL_INTERVAL:
-                self.info("created process")
-            if not (self.run() and self.checkRestart()):
+            self.hZope, hThread, pid, tid = self.createProcess(self._svc_command_)
+            self.ReportServiceStatus(win32service.SERVICE_RUNNING)
+            keep_running = self.run()
+            if not keep_running:
+                # The daemon process has asked to stop
                 break
-
+            # should we attempt a restart?
+            if not self.checkRestart():
+                # No, we should not
+                break
         self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
-        # Stop the child process by opening the special named event.
-        # We give it 90 seconds to shutdown normally.  If that doesn't
-        # stop things, we give it 30 seconds to do a "fast" shutdown.
-        # After that, we just knock it on the head.
-        winver = sys.getwindowsversion()
-        for sig, timeout in ((signal.SIGINT, 30), (signal.SIGTERM, 10)):
-            event_name = "Zope-%d-%d" % (info[2], sig)
-            # sys.getwindowsversion() -> major, minor, build, platform_id, ver_string
-            # for platform_id, 2==VER_PLATFORM_WIN32_NT
-            if winver[0] >= 5 and winver[3] == 2:
-                event_name = "Global\\" + event_name
-            try:
-                he = win32event.OpenEvent(win32event.EVENT_MODIFY_STATE, 0,
-                                          event_name)
-            except win32event.error, details:
-                if details[0] == winerror.ERROR_FILE_NOT_FOUND:
-                    # process already dead!
-                    break
-                # no other expected error - report it.
-                self.warning("Failed to open child shutdown event %s"
-                             % (event_name,))
-                continue
+        self.stop(pid)
+        self.ReportServiceStatus(win32service.SERVICE_STOPPED)
+        self.logmsg(servicemanager.PYS_SERVICE_STOPPED)
 
-            win32event.SetEvent(he)
-            # It should be shutting down now - wait for termination, reporting
-            # progress as we go.
-            for i in range(timeout):
-                self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
-                rc = win32event.WaitForSingleObject(self.hZope, 3000)
-                if rc == win32event.WAIT_OBJECT_0:
-                    break
-            # Process terminated - no need to try harder.
-            if rc == win32event.WAIT_OBJECT_0:
-                break
+    def SvcStop(self):
+        # Set the stop event - the main loop takes care of termination.
+        win32event.SetEvent(self.hWaitStop)
 
-        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
-        # If necessary, kill it
-        if win32process.GetExitCodeProcess(self.hZope)==win32con.STILL_ACTIVE:
-            win32api.TerminateProcess(self.hZope, 3)
-        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
+    # SvcStop only gets triggered when the user explictly stops (or restarts)
+    # the service.  To shut the service down cleanly when Windows is shutting
+    # down, we also need to hook SvcShutdown.
+    SvcShutdown = SvcStop
 
-        # Wait for the redirect thread - it should have died as the remote
-        # process terminated.
-        # As we are shutting down, we do the join with a little more care,
-        # reporting progress as we wait (even though we never will <wink>)
-        if self.redirect_thread is not None:
-            for i in range(5):
-                self.redirect_thread.join(1)
-                self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
-                if not self.redirect_thread.isAlive():
-                    break
-            else:
-                self.warning("Redirect thread did not stop!")
-        self.logmsg(servicemanager.PYS_SERVICE_STOPPED)
-
+    ### Helper methods
+    
     def run(self):
         """Monitor the daemon process.
 
@@ -250,24 +140,19 @@
                                                0, # bWaitAll
                                                win32event.INFINITE)
         if rc == win32event.WAIT_OBJECT_0:
-            # user sent a stop service request
+            # a stop service request was recieved
             keep_running = False
         elif rc == win32event.WAIT_OBJECT_0 + 1:
-            # user did not send a service stop request, but
             # the process died; this may be an error condition
             status = win32process.GetExitCodeProcess(self.hZope)
-            # exit status 0 means the user caused a clean shutdown,
-            # presumably via the web interface.  Any other status
-            # is an error that gets written to the event log.
-            if status != 0:
-                # This should never block - the child process terminating
-                # has closed the redirection pipe, so our thread dies.
-                self.redirect_thread.join(5)
-                if self.redirect_thread.isAlive():
-                    self.warning("Redirect thread did not stop!")
-                self.warning("process terminated with exit code %d.\n%s" \
-                             % (status, "".join(self.captured_blocks)))
+            # exit status 0 means a clean shutdown,
+            # presumably via the web interface.
             keep_running = status != 0
+            if keep_running:
+                # Any other status is an error so we write it and
+                # any output to the event log
+                self.warning("Process terminated with exit code %d.\n%s" \
+                             % (status, self.getCapturedOutput()))
         else:
             # No other valid return codes.
             assert 0, rc
@@ -276,10 +161,13 @@
     def checkRestart(self):
         # this was an abormal shutdown.
         if self.backoff_cumulative > BACKOFF_MAX:
-            self.error("restarting too frequently; quit")
+            self.error("Attempted restarting more than %s times, aborting."
+                       % BACKOFF_MAX)
             return False
-        self.warning("sleep %s to avoid rapid restarts"
-                     % self.backoff_interval)
+        self.warning(
+            "Process died unexpectedly, will attempt restart after %s seconds."
+            % self.backoff_interval
+            )
         if time.time() - self.start_time > BACKOFF_CLEAR_TIME:
             self.backoff_interval = BACKOFF_INITIAL_INTERVAL
             self.backoff_cumulative = 0
@@ -292,7 +180,9 @@
         self.backoff_interval *= 2
         return True
 
-    def createProcessCaptureIO(self, cmd):
+    def createProcess(self, cmd):
+        self.start_time = time.time()
+        
         hInputRead, hInputWriteTemp = self.newPipe()
         hOutReadTemp, hOutWrite = self.newPipe()
         pid = win32api.GetCurrentProcess()
@@ -328,18 +218,19 @@
         hInputWrite.Close()
 
         # start a thread collecting output
-        t = threading.Thread(target=self.redirectCaptureThread,
-                             args = (hOutRead,))
+        t = threading.Thread(
+            target=self.outputCaptureThread,
+            args = (hOutRead,)
+            )
         t.start()
-        self.redirect_thread = t
+        self.output_thread = t
         return info
 
-    def redirectCaptureThread(self, handle):
+    def outputCaptureThread(self, handle):
         # Only one of these running at a time, and handling both stdout and
         # stderr on a single handle.  The read data is never referenced until
         # the thread dies - so no need for locks around self.captured_blocks.
         self.captured_blocks = []
-        #self.info("Redirect thread starting")
         while 1:
             try:
                 ec, data = win32file.ReadFile(handle, CHILDCAPTURE_BLOCK_SIZE)
@@ -352,8 +243,13 @@
             self.captured_blocks.append(data)
             del self.captured_blocks[CHILDCAPTURE_MAX_BLOCKS:]
         handle.Close()
-        #self.info("Redirect capture thread terminating")
 
+    def getCapturedOutput(self):
+        self.output_thread.join(5)
+        if self.output_thread.isAlive():
+            self.warning("Output capturing thread failed to terminate!")
+        return "".join(self.captured_blocks)
+
     def newPipe(self):
         sa = win32security.SECURITY_ATTRIBUTES()
         sa.bInheritHandle = True
@@ -369,8 +265,134 @@
         pipe.Close()
         return dup
 
-# Real __main__ bootstrap code is in the instance's service module.
-if __name__ == '__main__':
-    print "This is a framework module - you don't run it directly."
-    print "See your installation directory for the service script."
-    sys.exit(1)
+    def stop(self,pid):
+        # call the method that any subclasses out there may implement:
+        self.onStop()
+        
+        winver = sys.getwindowsversion()
+        # This is unfortunately needed because runzope.exe is a setuptools
+        # generated .exe that spawns off a sub process, so pid would give us
+        # the wrong event name.
+        child_pid = int(
+            open(self.getReg('pid_filename',keyname='PythonClass')).read()
+            )
+        
+        # Stop the child process by sending signals to the special named event.
+        for sig, timeout in (
+            (signal.SIGINT, 30), # We give it 90 seconds to shutdown normally.
+            (signal.SIGTERM, 10) # If that doesn't stop things, we give it 30
+                                 # seconds to do a "fast" shutdown.
+            ):
+            # See the Signals.WinSignalHandler module for
+            # the source of this event name
+            event_name = "Zope-%d-%d" % (child_pid,sig)
+            # sys.getwindowsversion() -> major, minor, build, platform_id, ver_string
+            # for platform_id, 2==VER_PLATFORM_WIN32_NT
+            if winver[0] >= 5 and winver[3] == 2:
+                event_name = "Global\\" + event_name
+            try:
+                he = win32event.OpenEvent(win32event.EVENT_MODIFY_STATE, 0,
+                                          event_name)
+            except win32event.error, details:
+                # no other expected error - report it.
+                self.warning("Failed to open child shutdown event %s"
+                             % (event_name,))
+                continue
+
+            win32event.SetEvent(he)
+            # It should be shutting down now - wait for termination, reporting
+            # progress as we go.
+            for i in range(timeout):
+                # wait for one second
+                rc = win32event.WaitForSingleObject(self.hZope, 1000)
+                if rc == win32event.WAIT_OBJECT_0:
+                    break
+            # Process terminated - no need to try harder.
+            if rc == win32event.WAIT_OBJECT_0:
+                break
+
+        if win32process.GetExitCodeProcess(self.hZope)==win32con.STILL_ACTIVE:
+            # None of the signals worked, so kill the process
+            self.warning(
+                "Terminating process as it could not be gracefully ended"
+                )
+            win32api.TerminateProcess(self.hZope, 3)
+
+        output = self.getCapturedOutput()
+        if output:
+            self.info("Process terminated with output:\n"+output)
+                
+    ### Overridable subclass methods
+
+    def onStop(self):
+        # A hook for subclasses to override.
+        # Called just before the service is stopped.
+        pass
+
+    ### Registry interaction methods
+    
+    @classmethod
+    def openKey(cls,serviceName,keyname=None):
+        keypath = "System\\CurrentControlSet\\Services\\"+serviceName
+        if keyname:
+            keypath += ('\\'+keyname)
+        return win32api.RegOpenKey(
+            win32con.HKEY_LOCAL_MACHINE,keypath,0,win32con.KEY_ALL_ACCESS
+            )
+    
+    @classmethod
+    def setReg(cls,name,value,serviceName=None,keyname='PythonClass'):
+        if not serviceName:
+            serviceName = cls._svc_name_
+        key = cls.openKey(serviceName,keyname)
+        try:
+            win32api.RegSetValueEx(key, name, 0, win32con.REG_SZ, value)
+        finally:
+            win32api.RegCloseKey(key)
+
+    def getReg(self,name,keyname=None):
+        key = self.openKey(self._svc_name_,keyname)
+        return win32api.RegQueryValueEx(key,name)[0]
+    
+    ### Logging methods
+        
+    def logmsg(self, event):
+        # log a service event using servicemanager.LogMsg
+        try:
+            servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
+                                  event,
+                                  (self._svc_name_,
+                                   " (%s)" % self._svc_display_name_))
+        except win32api.error, details:
+            # Failed to write a log entry - most likely problem is
+            # that the event log is full.  We don't want this to kill us
+            try:
+                print "FAILED to write INFO event", event, ":", details
+            except IOError:
+                pass
+
+    def _dolog(self, func, msg):
+        try:
+            fullmsg = "%s (%s): %s" % \
+                      (self._svc_name_, self._svc_display_name_, msg)
+            func(fullmsg)
+        except win32api.error, details:
+            # Failed to write a log entry - most likely problem is
+            # that the event log is full.  We don't want this to kill us
+            try:
+                print "FAILED to write event log entry:", details
+                print msg
+            except IOError:
+                # And if running as a service, its likely our sys.stdout
+                # is invalid
+                pass
+
+    def info(self, s):
+        self._dolog(servicemanager.LogInfoMsg, s)
+
+    def warning(self, s):
+        self._dolog(servicemanager.LogWarningMsg, s)
+
+    def error(self, s):
+        self._dolog(servicemanager.LogErrorMsg, s)
+



More information about the Zope-Checkins mailing list