[Erp5-report] r25895 - in /experimental/erp5.buildout: ./ bootstrap/ patches/ profiles/ rec...
nobody at svn.erp5.org
nobody at svn.erp5.org
Thu Mar 5 16:49:31 CET 2009
Author: nicolas
Date: Thu Mar 5 16:49:30 2009
New Revision: 25895
URL: http://svn.erp5.org?rev=25895&view=rev
Log:
Initial import of experimental buildout configuration files for ERP5.
- Multiple profiles are available
- Some recipes needs to be eggified
Added:
experimental/erp5.buildout/
experimental/erp5.buildout/EXTERNALS.TXT (with props)
experimental/erp5.buildout/README.txt (with props)
experimental/erp5.buildout/bootstrap/
experimental/erp5.buildout/bootstrap/bootstrap.py
experimental/erp5.buildout/buildout.cfg
experimental/erp5.buildout/patches/
experimental/erp5.buildout/patches/Zope-2.8.0-final-aq_dynamic.patch
experimental/erp5.buildout/profiles/
experimental/erp5.buildout/profiles/base.cfg
experimental/erp5.buildout/profiles/cluster.cfg
experimental/erp5.buildout/profiles/create-site.cfg
experimental/erp5.buildout/profiles/development.cfg
experimental/erp5.buildout/profiles/versions.cfg
experimental/erp5.buildout/recipes/
experimental/erp5.buildout/recipes/create_erp5_instance.py
experimental/erp5.buildout/recipes/create_erp5_site.py
experimental/erp5.buildout/recipes/run_unit_test.py
experimental/erp5.buildout/recipes/setup.py
experimental/erp5.buildout/recipes/zope2install.py
experimental/erp5.buildout/src/
experimental/erp5.buildout/src/EXTERNALS.TXT (with props)
experimental/erp5.buildout/src/erp5diff/
experimental/erp5.buildout/src/erp5diff/ERP5Diff.py (with props)
experimental/erp5.buildout/src/erp5diff/MAINTAINERS.txt (with props)
experimental/erp5.buildout/src/erp5diff/README (with props)
experimental/erp5.buildout/src/erp5diff/erp5diff (with props)
experimental/erp5.buildout/src/erp5diff/erp5diff.1 (with props)
experimental/erp5.buildout/src/erp5diff/setup.py (with props)
experimental/erp5.buildout/src/timerserver/
experimental/erp5.buildout/src/timerserver/setup.py
experimental/erp5.buildout/src/timerserver/timerserver/
experimental/erp5.buildout/src/timerserver/timerserver/TimerServer.py
experimental/erp5.buildout/src/timerserver/timerserver/__init__.py
experimental/erp5.buildout/src/timerserver/timerserver/component.xml
experimental/erp5.buildout/src/timerserver/version.txt (with props)
experimental/erp5.buildout/templates/
experimental/erp5.buildout/templates/haproxy.cfg.default
Added: experimental/erp5.buildout/EXTERNALS.TXT
URL: http://svn.erp5.org/experimental/erp5.buildout/EXTERNALS.TXT?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/EXTERNALS.TXT (added)
+++ experimental/erp5.buildout/EXTERNALS.TXT [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,10 @@
+#
+# Used for maintenance of external resources in this svn bundle. Edit
+# this file as appropriate and then run the following command from within
+# the checkout directory where this file lives on your local machine:
+#
+# svn propset svn:externals -F ./EXTERNALS.TXT .
+#
+
+bootstrap svn://svn.zope.org/repos/main/zc.buildout/trunk/bootstrap
+
Propchange: experimental/erp5.buildout/EXTERNALS.TXT
------------------------------------------------------------------------------
svn:eol-style = native
Added: experimental/erp5.buildout/README.txt
URL: http://svn.erp5.org/experimental/erp5.buildout/README.txt?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/README.txt (added)
+++ experimental/erp5.buildout/README.txt [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,68 @@
+ERP5 buildout
+=============
+
+This is a zc.buildout environment for ERP5 instance. The original use case is
+to help developpers in rapidly creating new instances, or to try out other
+versions of dependancy products.
+
+Requirements (Mandriva package names in paranthesis):
+
+ * python2.4 development packages (python2.4-devel)
+ * Development packages for mysql (mysql-devel)
+ * convert (From ImageMagick) (imagemagick)
+ * patch (patch)
+ * OpenLDAP v2 development packages (openldap2-devel)
+ * SSL development packages (openssl-devel)
+ * XML and XSLT development packages (libxslt-devel libxml2-devel)
+
+Note: Use buildout for python2.4
+
+
+Basic Usage
+-----------
+
+The default use create a development site and install some business templates.
+
+Example use::
+
+ # in this example, we use -S to prevent importing site packages, you could
+ # also use virtualenv, or you could also want to use your site packages, for
+ # example not to build pysvn again.
+ python2.4 -S bootstrap/bootstrap.py
+ python2.4 -S bin/buildout
+
+
+Customization
+-------------
+
+Write your own buildout.cfg, extending one of the available profiles
+(cluster.cfg, development.cfg etc).
+You can also change some variables, such as mysql connection string, or install
+more products or business templates, you can do this by extending existing
+parts (adding urls in [products-deps] for example).
+
+
+Notes
+=====
+
+The Acquisition patch
+---------------------
+
+ERP5 uses a patched version of zope for dynamic acquisition. This buildout
+extends plone.recipe.zope2install to patch zope before installing it. The patch
+is in patches subdirectory.
+
+
+TODO:
+ convert
+ Numpy
+ GLPK
+ runUnitTest --save/--load does not work
+ itools 0.20.6 does not compile on 64bits, using itools 0.20.2 workarounds
+
+
+Notes:
+ CMFReportTool is not installed, because reportlab cannot be installed via
+ easy_install.
+
+
Propchange: experimental/erp5.buildout/README.txt
------------------------------------------------------------------------------
svn:eol-style = native
Added: experimental/erp5.buildout/bootstrap/bootstrap.py
URL: http://svn.erp5.org/experimental/erp5.buildout/bootstrap/bootstrap.py?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/bootstrap/bootstrap.py (added)
+++ experimental/erp5.buildout/bootstrap/bootstrap.py [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,77 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id: bootstrap.py 90478 2008-08-27 22:44:46Z georgyberdyshev $
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+is_jython = sys.platform.startswith('java')
+
+try:
+ import pkg_resources
+except ImportError:
+ ez = {}
+ exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+ ).read() in ez
+ ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+ import pkg_resources
+
+if sys.platform == 'win32':
+ def quote(c):
+ if ' ' in c:
+ return '"%s"' % c # work around spawn lamosity on windows
+ else:
+ return c
+else:
+ def quote (c):
+ return c
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+ws = pkg_resources.working_set
+
+if is_jython:
+ import subprocess
+
+ assert subprocess.Popen([sys.executable] + ['-c', quote(cmd), '-mqNxd',
+ quote(tmpeggs), 'zc.buildout'],
+ env=dict(os.environ,
+ PYTHONPATH=
+ ws.find(pkg_resources.Requirement.parse('setuptools')).location
+ ),
+ ).wait() == 0
+
+else:
+ assert os.spawnle(
+ os.P_WAIT, sys.executable, quote (sys.executable),
+ '-c', quote (cmd), '-mqNxd', quote (tmpeggs), 'zc.buildout',
+ dict(os.environ,
+ PYTHONPATH=
+ ws.find(pkg_resources.Requirement.parse('setuptools')).location
+ ),
+ ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)
Added: experimental/erp5.buildout/buildout.cfg
URL: http://svn.erp5.org/experimental/erp5.buildout/buildout.cfg?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/buildout.cfg (added)
+++ experimental/erp5.buildout/buildout.cfg [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,3 @@
+[buildout]
+extends = profiles/create-site.cfg
+
Added: experimental/erp5.buildout/patches/Zope-2.8.0-final-aq_dynamic.patch
URL: http://svn.erp5.org/experimental/erp5.buildout/patches/Zope-2.8.0-final-aq_dynamic.patch?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/patches/Zope-2.8.0-final-aq_dynamic.patch (added)
+++ experimental/erp5.buildout/patches/Zope-2.8.0-final-aq_dynamic.patch [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,104 @@
+diff -urN Zope-2.8.0-final.orig/lib/python/Acquisition/_Acquisition.c Zope-2.8.0-final/lib/python/Acquisition/_Acquisition.c
+--- Zope-2.8.0-final.orig/lib/python/Acquisition/_Acquisition.c 2005-06-11 08:24:02.000000000 +0200
++++ Zope-2.8.0-final/lib/python/Acquisition/_Acquisition.c 2005-06-17 16:26:00.742556205 +0200
+@@ -410,6 +410,64 @@
+ int explicit, int containment);
+
+ static PyObject *
++Wrapper_GetAttr(PyObject *self, PyObject *attr_name, PyObject *orig)
++{
++ /* This function retrieves an attribute from an object by PyObject_GetAttr.
++
++ The main difference between Wrapper_GetAttr and PyObject_GetAttr is that
++ Wrapper_GetAttr calls _aq_dynamic to generate an attribute dynamically, if
++ the attribute is not found.
++ */
++ PyObject *r, *v, *tb;
++ PyObject *d, *m;
++ PyObject *o;
++
++ if (isWrapper (self))
++ o = WRAPPER(self)->obj;
++ else
++ o = self;
++
++ /* Try to get an attribute in the normal way first. */
++ r = PyObject_GetAttr(o, attr_name);
++ if (r)
++ return r;
++
++ /* If an unexpected error happens, return immediately. */
++ PyErr_Fetch(&r,&v,&tb);
++ if (r != PyExc_AttributeError)
++ {
++ PyErr_Restore(r,v,tb);
++ return NULL;
++ }
++
++ /* Try to get _aq_dynamic. */
++ m = PyObject_GetAttrString(o, "_aq_dynamic");
++ if (! m) {
++ PyErr_Restore(r,v,tb);
++ return NULL;
++ }
++
++ /* Call _aq_dynamic in the context of the original acquisition wrapper. */
++ if (PyECMethod_Check(m) && PyECMethod_Self(m)==o)
++ ASSIGN(m,PyECMethod_New(m,OBJECT(self)));
++ else if (has__of__(m)) ASSIGN(m,__of__(m,OBJECT(self)));
++ d = PyObject_CallFunction(m, "O", attr_name);
++ Py_DECREF(m);
++
++ /* In the case of None, assume that the attribute is not found. */
++ if (d == Py_None) {
++ Py_DECREF(d);
++ PyErr_Restore(r,v,tb);
++ return NULL;
++ }
++
++ Py_XDECREF(r);
++ Py_XDECREF(v);
++ Py_XDECREF(tb);
++ return d;
++}
++
++static PyObject *
+ Wrapper_findattr(Wrapper *self, PyObject *oname,
+ PyObject *filter, PyObject *extra, PyObject *orig,
+ int sob, int sco, int explicit, int containment)
+@@ -476,7 +534,7 @@
+ Py_XDECREF(r); Py_XDECREF(v); Py_XDECREF(tb);
+ r=NULL;
+ }
+- else if ((r=PyObject_GetAttr(self->obj,oname)))
++ else if ((r=Wrapper_GetAttr(OBJECT(self),oname,orig)))
+ {
+ if (r==Acquired)
+ {
+@@ -550,7 +608,7 @@
+ }
+ else
+ {
+- if ((r=PyObject_GetAttr(self->container,oname))) {
++ if ((r=Wrapper_GetAttr(self->container,oname,orig))) {
+ if (r == Acquired) {
+ Py_DECREF(r);
+ }
+@@ -587,7 +645,7 @@
+ Wrapper_getattro(Wrapper *self, PyObject *oname)
+ {
+ if (self->obj || self->container)
+- return Wrapper_findattr(self, oname, NULL, NULL, NULL, 1, 1, 0, 0);
++ return Wrapper_findattr(self, oname, NULL, NULL, OBJECT(self), 1, 1, 0, 0);
+
+ /* Maybe we are getting initialized? */
+ return Py_FindAttr(OBJECT(self),oname);
+@@ -604,7 +662,7 @@
+ return Py_FindAttr(OBJECT(self),oname);
+
+ if (self->obj || self->container)
+- return Wrapper_findattr(self, oname, NULL, NULL, NULL, 1, 0, 0, 0);
++ return Wrapper_findattr(self, oname, NULL, NULL, OBJECT(self), 1, 0, 0, 0);
+
+ /* Maybe we are getting initialized? */
+ return Py_FindAttr(OBJECT(self),oname);
Added: experimental/erp5.buildout/profiles/base.cfg
URL: http://svn.erp5.org/experimental/erp5.buildout/profiles/base.cfg?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/profiles/base.cfg (added)
+++ experimental/erp5.buildout/profiles/base.cfg [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,142 @@
+[buildout]
+extends = versions.cfg
+versions = versions
+develop =
+ recipes
+ src/timerserver
+ src/erp5diff
+offline = false
+; index = http://pypi.python.org/simple
+; http://pypi.netsight.co.uk/
+; http://pypi.zopyx.com/
+; http://pypi.inqbus.de
+find-links =
+ http://download.zope.org/distribution/
+ https://svn.erp5.org/repos/public/erp5/trunk/utils/erp5diff
+ http://download.hforge.org/itools/0.20/
+ http://download.hforge.org/itools/
+eggs =
+ MySQL-python
+ python-memcached
+ elementtree
+ PyXML
+ 4Suite-XML
+ ply
+ itools
+ python-ldap
+ lxml
+parts =
+ eggs-deps
+ zope2
+ cmf15
+ products-deps
+ products-other
+ mkdir-instances
+
+[eggs-deps]
+recipe = zc.recipe.egg:scripts
+interpreter = python2.4
+eggs =
+ mechanize
+ ClientForm
+ timerserver
+
+[cmf15]
+recipe = plone.recipe.distros
+urls =
+ http://www.zope.org/Products/CMF/CMF-1.5.4/CMF-1.5.4.tar.gz
+version-suffix-packages =
+ CMF-1.5.4.tar.gz
+
+[products-deps]
+recipe = plone.recipe.distros
+urls =
+ http://www.zope.org/Members/shh/ExtFile/1.4.4/ExtFile-1.4.4.tar.gz
+ http://www.zope.org/Members/NIP/ZMailIn/1.0.1/ZMailIn-1-0-1.tgz
+ http://www.zope.org/Members/NIP/ZMailIn/1.0.0/CMFMailIn-1.0.0
+ http://www.zope.org/Products/PluggableAuthService/PluggableAuthService-1.1b2/PluggableAuthService-1.1b2.tar.gz
+ http://www.infrae.com/download/Formulator/1.6.2/Formulator-1.6.2.tgz
+ http://kent.dl.sourceforge.net/sourceforge/fckeditor/FCKeditor.Plone_2.2.tar.gz
+ http://download.ikaaro.org/localizer/Localizer-1.2.3.tar.gz
+version-suffix-packages =
+ Localizer-1.2.3.tar.gz
+
+[products-erp5]
+recipe = infrae.subversion
+# You can Control Revision by adding @{revision number} in uri
+# eg: https://svn.erp5.org/repos/public/erp5/trunk/products/ERP5@24320 ERP5
+# or use a tag as base eg. base =
+# https://svn.erp5.org/repos/public/erp5/tags/version-5.4/products/
+base = https://svn.erp5.org/repos/public/erp5/trunk/products/
+urls =
+ ${products-erp5:base}/CMFActivity CMFActivity
+ ${products-erp5:base}/CMFCategory CMFCategory
+ ${products-erp5:base}/ERP5 ERP5
+ ${products-erp5:base}/ERP5Banking ERP5Banking
+ ${products-erp5:base}/ERP5Catalog ERP5Catalog
+ ${products-erp5:base}/ERP5Form ERP5Form
+ ${products-erp5:base}/ERP5OOo ERP5OOo
+ ${products-erp5:base}/ERP5SyncML ERP5SyncML
+ ${products-erp5:base}/ERP5Type ERP5Type
+ ${products-erp5:base}/ERP5Security ERP5Security
+ ${products-erp5:base}/MailTemplates MailTemplates
+ ${products-erp5:base}/PortalTransforms PortalTransforms
+ ${products-erp5:base}/TIDStorage TIDStorage
+ ${products-erp5:base}/TimerService TimerService
+ ${products-erp5:base}/ZMySQLDA ZMySQLDA
+ ${products-erp5:base}/ZMySQLDDA ZMySQLDDA
+ ${products-erp5:base}/ZSQLCatalog ZSQLCatalog
+ ${products-erp5:base}/ERP5Subversion ERP5Subversion
+
+[products-other]
+recipe = infrae.subversion
+#Archetypes should be contained in Products Folder
+location = parts/products-other/Products
+urls =
+ svn://svn.zope.org/repos/main/Zelenium/trunk/ Zelenium
+ svn://svn.zope.org/repos/main/PluginRegistry/tags/1.0 PluginRegistry
+ http://svn.plone.org/svn/archetypes/MimetypesRegistry/tags/Archetypes-1.4.0-final MimetypesRegistry
+ https://svn.plone.org/svn/collective/CMFPhoto/trunk/ CMFPhoto
+
+[zope2]
+recipe = recipes:zope2install
+url = http://www.zope.org/Products/Zope/2.8.10/Zope-2.8.10-final.tgz
+patch = patches/Zope-2.8.0-final-aq_dynamic.patch
+#http://www.zope.org/Products/Zope/2.9.8/Zope-2.9.8-final.tgz
+
+
+[erp5_instance]
+recipe = plone.recipe.zope2instance
+zope2-location = ${zope2:location}
+user = zope:zope
+http-address = 18080
+debug-mode = off
+control-script = zopectl
+#Only for zope2.8
+default-zpublisher-encoding =
+eggs =
+ ${buildout:eggs}
+ timerserver
+ erp5diff
+
+products =
+ ${cmf15:location}/CMF/
+ ${products-deps:location}
+ ${products-erp5:location}
+ ${products-other:location}
+zope-conf-additional=
+ %import timerserver
+ <timer-server>
+ interval 5
+ </timer-server>
+
+#XXX bug in 4Suite
+[mkdir-instances]
+recipe = ore.recipe.fs:mkdir
+createpath = true
+path = ${erp5_instance:location}/lib/python
+
+# [ldap]
+# recipe = z3c.recipe.ldap
+# todo
+
Added: experimental/erp5.buildout/profiles/cluster.cfg
URL: http://svn.erp5.org/experimental/erp5.buildout/profiles/cluster.cfg?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/profiles/cluster.cfg (added)
+++ experimental/erp5.buildout/profiles/cluster.cfg [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,127 @@
+[buildout]
+extends = base.cfg
+parts +=
+ apacheconfig
+ haproxy
+ zeo_server
+ zeo_client_1
+ zeo_client_2
+ cluster
+parts -= erp5_instance
+
+[apachebuild]
+recipe = plone.recipe.apache:build
+url = http://apache.multidist.com/httpd/httpd-2.2.11.tar.gz
+modules = mod_cache
+ mod_mpm
+ mod_proxy
+ mod_deflate
+ mod_conf
+ mod_base
+ mod_modules
+ mod_disk_cache
+ mod_ssl
+ mod_htcacheclean
+
+[apacheconfig]
+recipe = plone.recipe.apache:config
+mainconfig = ${apachebuild:location}/conf/httpd.conf
+bind = 443
+zope2_vhm_map = localhost:/erp5
+backends = localhost:127.0.0.1:2002
+
+
+[haproxy]
+recipe = plone.recipe.haproxy
+url = http://haproxy.1wt.eu/download/1.3/src/haproxy-1.3.15.7.tar.gz
+target = linux26
+
+
+[haproxy_etc_folder]
+recipe = ore.recipe.fs:mkdir
+path = ${haproxy:location}/etc
+
+
+[haproxyconfig]
+recipe = buildout_script:template
+template = haproxy.cfg.default
+output_dir = ${haproxy:location}/etc
+maxconn = 2000
+retries = 3
+port = 2002
+balance = roundrobin
+rules =
+ server zeo_client_1 127.0.0.1:${zeo_client_1:http-address} cookie zeo_client_1 check inter 3s rise 2 fall 4 maxconn 1
+ server zeo_client_2 127.0.0.1:${zeo_client_2:http-address} cookie zeo_client_2 check inter 3s rise 2 fall 4 maxconn 1
+httpchk = /erp5/getId
+
+
+[zeo_server]
+recipe = plone.recipe.zope2zeoserver
+zope2-location = ${zope2:location}
+user = admin:admin
+zeo-address = 8100
+eggs =
+ ${buildout:eggs}
+ timerserver
+ erp5diff
+
+
+[zeo_client_1]
+recipe = plone.recipe.zope2instance
+zope2-location = ${zope2:location}
+user = ${zeo_server:user}
+http-address = 18180
+debug-mode = off
+zeo-client = on
+zeo-address = 8100
+control-script = zopectl-zeo_client_1
+eggs = ${zeo_server:eggs}
+products =
+ ${cmf15:location}/CMF/
+ ${products-deps:location}
+ ${products-erp5:location}
+ ${products-other:location}
+zope-conf-additional=
+ %import timerserver
+ <timer-server>
+ interval 5
+ </timer-server>
+
+
+[zeo_client_2]
+recipe = plone.recipe.zope2instance
+zope2-location = ${zope2:location}
+user = ${zeo_server:user}
+http-address = 18280
+debug-mode = off
+zeo-client = on
+zeo-address = 8100
+control-script = zopectl-zeo_client_2
+eggs = ${zeo_server:eggs}
+products =
+ ${cmf15:location}/CMF/
+ ${products-deps:location}
+ ${products-erp5:location}
+ ${products-other:location}
+zope-conf-additional=
+ %import timerserver
+ <timer-server>
+ interval 5
+ </timer-server>
+
+[cluster]
+recipe = plone.recipe.cluster
+start =
+ ${buildout:bin-directory}/zeo_server start
+ ${buildout:bin-directory}/zopectl-zeo_client_1 start
+ ${buildout:bin-directory}/zopectl-zeo_client_2 start
+stop =
+ ${buildout:bin-directory}/zeo_server stop
+ ${buildout:bin-directory}/zopectl-zeo_client_1 stop
+ ${buildout:bin-directory}/zopectl-zeo_client_2 stop
+restart =
+ ${buildout:bin-directory}/zeo_server restart
+ ${buildout:bin-directory}/zopectl-zeo_client_1 restart
+ ${buildout:bin-directory}/zopectl-zeo_client_2 restart
+
Added: experimental/erp5.buildout/profiles/create-site.cfg
URL: http://svn.erp5.org/experimental/erp5.buildout/profiles/create-site.cfg?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/profiles/create-site.cfg (added)
+++ experimental/erp5.buildout/profiles/create-site.cfg [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,70 @@
+; This configuration creates a site and install some business templates inside
+[buildout]
+extends = base.cfg
+parts += create_erp5_site
+
+
+[download_bt5]
+recipe = infrae.subversion
+base = https://svn.erp5.org/repos/public/erp5/trunk/bt5
+location = ${erp5_instance:location}/bt5
+urls =
+ ${download_bt5:base}/erp5_base/ erp5_base/
+ ${download_bt5:base}/erp5_pdm/ erp5_pdm/
+ ${download_bt5:base}/erp5_trade/ erp5_trade/
+ ${download_bt5:base}/erp5_accounting/ erp5_accounting/
+ ${download_bt5:base}/erp5_invoicing/ erp5_invoicing/
+ ${download_bt5:base}/erp5_ods_style/ erp5_ods_style/
+ ${download_bt5:base}/erp5_odt_style/ erp5_odt_style/
+ ${download_bt5:base}/erp5_ingestion_mysql_innodb_catalog/ erp5_ingestion_mysql_innodb_catalog/
+ ${download_bt5:base}/erp5_ingestion/ erp5_ingestion/
+ ${download_bt5:base}/erp5_crm/ erp5_crm/
+ ${download_bt5:base}/erp5_web/ erp5_web/
+ ${download_bt5:base}/erp5_forge/ erp5_forge/
+ ${download_bt5:base}/erp5_dms_mysql_innodb_catalog/ erp5_dms_mysql_innodb_catalog/
+ ${download_bt5:base}/erp5_dms/ erp5_dms/
+ ${download_bt5:base}/erp5_ui_test_core/ erp5_ui_test_core/
+ ${download_bt5:base}/erp5_ui_test/ erp5_ui_test/
+ ${download_bt5:base}/erp5_pdm_ui_test/ erp5_pdm_ui_test/
+ ${download_bt5:base}/erp5_accounting_ui_test/ erp5_accounting_ui_test/
+ ${download_bt5:base}/erp5_web_ui_test/ erp5_web_ui_test/
+ ${download_bt5:base}/erp5_dms_ui_test/ erp5_dms_ui_test/
+ ${download_bt5:base}/erp5_l10n_fr/ erp5_l10n_fr/
+ ${download_bt5:base}/erp5_l10n_ja/ erp5_l10n_ja/
+ ${download_bt5:base}/erp5_l10n_pl_PL/ erp5_l10n_pl_PL/
+ ${download_bt5:base}/erp5_l10n_pt-BR/ erp5_l10n_pt-BR/
+
+
+[create_erp5_site]
+recipe = recipes:create_erp5_site
+portal_id = erp5
+control-script = ${erp5_instance:control-script}
+user = ${erp5_instance:user}
+erp5_sql_connection_string = erp5%20root
+bt5-path = ${download_bt5:location}
+bt5 =
+ erp5_base
+ erp5_pdm
+ erp5_trade
+ erp5_accounting
+ erp5_invoicing
+ erp5_ods_style
+ erp5_odt_style
+ erp5_ingestion_mysql_innodb_catalog
+ erp5_ingestion
+ erp5_crm
+ erp5_forge
+ erp5_web
+ erp5_dms_mysql_innodb_catalog
+ erp5_dms
+ erp5_ui_test_core
+ erp5_ui_test
+ erp5_pdm_ui_test
+ erp5_accounting_ui_test
+ erp5_web_ui_test
+ erp5_dms_ui_test
+ erp5_l10n_fr
+ erp5_l10n_ja
+ erp5_l10n_pl_PL
+ erp5_l10n_pt-BR
+
Added: experimental/erp5.buildout/profiles/development.cfg
URL: http://svn.erp5.org/experimental/erp5.buildout/profiles/development.cfg?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/profiles/development.cfg (added)
+++ experimental/erp5.buildout/profiles/development.cfg [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,59 @@
+[buildout]
+extends = create-site.cfg
+parts +=
+ erp5flakes
+ runUnitTest
+ zopepy
+ pysvn
+
+# We active debug on the instance and add some products.
+[erp5_instance]
+debug-mode = on
+;eggs +=
+; Products.PDBDebugMode
+; Products.PTProfiler
+
+[apachebuild]
+recipe = plone.recipe.apache:build
+url = http://apache.multidist.com/httpd/httpd-2.2.11.tar.gz
+
+[pysvn]
+recipe=ore.recipe.svnlib
+url=http://subversion.tigris.org/downloads/subversion-1.5.0-rc4.tar.bz2
+download_cache = cache
+extra_options=--with-apr=${apachebuild:location}/bin/ --with-apr-util=${apachebuild:location}/bin/ PYTHON=/usr/bin/python2.4
+
+[eggs-deps]
+extra-paths += ${pysvn:location}/lib/svn-python
+eggs += ipdb
+
+[products-deps]
+urls += http://www.zope.org/Members/panjunyong/DCWorkflowGraph/dcworkflowgraph-0_3/dcworkflowgraph-0_3.tgz
+
+[erp5flakes]
+recipe = zc.recipe.egg
+scripts =
+ pyflakes
+eggs =
+ pyflakes
+ setuptools
+entry-points = erp5flakes=pkg_resources:run_script
+arguments = 'erp5flakes', 'erp5flakes'
+
+[zopepy]
+recipe = zc.recipe.egg
+eggs =
+ ${buildout:eggs}
+ timerserver
+ erp5diff
+interpreter = zopepy
+extra-paths = ${zope2:location}/lib/python
+scripts = zopepy
+
+[runUnitTest]
+recipe = recipes:run_unit_test
+zope2-location = ${zope2:location}
+products = ${erp5_instance:products}
+eggs = ${erp5_instance:eggs}
+bt5_path = ${download_bt5:location}
+
Added: experimental/erp5.buildout/profiles/versions.cfg
URL: http://svn.erp5.org/experimental/erp5.buildout/profiles/versions.cfg?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/profiles/versions.cfg (added)
+++ experimental/erp5.buildout/profiles/versions.cfg [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,12 @@
+[versions]
+MySQL-python = 1.2.2
+python-memcached = 1.43
+elementtree = 1.2.7_20070827_preview
+PyXML = 0.8.4
+4Suite-XML = 1.0.2
+lxml = 2.1.5
+ply = 3.0
+ipdb = 0.1dev_r1716
+itools = 0.20.6
+python-ldap = 2.3.5
+
Added: experimental/erp5.buildout/recipes/create_erp5_instance.py
URL: http://svn.erp5.org/experimental/erp5.buildout/recipes/create_erp5_instance.py?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/recipes/create_erp5_instance.py (added)
+++ experimental/erp5.buildout/recipes/create_erp5_instance.py [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,102 @@
+# Create an ERP5 instance
+# usage: zopectl run create_erp5_instance [options] [business templates]
+
+import os
+from optparse import OptionParser
+from urllib import unquote
+
+from Testing.makerequest import makerequest
+from AccessControl.SecurityManagement import newSecurityManager
+
+parser = OptionParser()
+parser.add_option("-p", "--portal_id", dest="portal_id",
+ help="The ID of the Portal", default="erp5")
+parser.add_option("--erp5_sql_connection_string",
+ dest="erp5_sql_connection_string",
+ help="Connection String used for ZSQLCatalog "
+ "(use %20 for space)",
+ default="test test")
+parser.add_option("--cmf_activity_sql_connection_string",
+ dest="cmf_activity_sql_connection_string",
+ help="Connection String used for CMFActivity")
+parser.add_option("--erp5_catalog_storage",
+ dest="erp5_catalog_storage",
+ help="Business Template for Catalog Storage")
+parser.add_option("-u", "--initial-user",
+ dest="user_and_pass",
+ help="User and Password, separated by :",
+ default="zope:zope")
+parser.add_option("--bt5-path",
+ dest="bt5_path",
+ help="Path to folder containing business templates",
+ default="bt5")
+
+(options, args) = parser.parse_args()
+
+# cmf activity connection string defaults to zsqlcatalog's one
+if not options.cmf_activity_sql_connection_string:
+ options.cmf_activity_sql_connection_string = \
+ options.erp5_sql_connection_string
+
+# connection strings have to contain a space, for conveniance, this space can
+# be replaced by %20 character. It's actually the only way due to (probably) a bug in Cmd
+options.erp5_sql_connection_string =\
+ unquote(options.erp5_sql_connection_string)
+options.cmf_activity_sql_connection_string =\
+ unquote(options.cmf_activity_sql_connection_string)
+
+username, password = options.user_and_pass.split(':')
+
+try:
+ import transaction
+except ImportError:
+ class Transaction:
+ def commit(self):
+ return get_transaction().commit()
+ transaction = Transaction()
+
+app = makerequest(app)
+
+user = app.acl_users.getUserById(username)
+if user is None:
+ uf = app.acl_users
+ uf._doAddUser(username, password, ['Manager', 'Member', 'Assignee',
+ 'Assignor', 'Author'], [])
+ user = uf.getUserById(username)
+
+newSecurityManager(None, user.__of__(app.acl_users))
+
+print 'Adding ERP5 site %s' % options.portal_id
+portal = getattr(app, options.portal_id, None)
+if portal is None:
+ app.manage_addProduct['ERP5'].manage_addERP5Site(
+ id=options.portal_id,
+ erp5_sql_connection_string=options.erp5_sql_connection_string,
+ erp5_sql_deferred_connection_string=\
+ options.erp5_sql_connection_string,
+ cmf_activity_sql_connection_string=\
+ options.cmf_activity_sql_connection_string,
+ erp5_catalog_storage='erp5_mysql_innodb_catalog')
+
+ transaction.commit()
+ portal = app._getOb(options.portal_id)
+
+# set preference for erp5_subversion
+from App.config import getConfiguration
+default_site_preference = portal.portal_preferences.default_site_preference
+instance_home = getConfiguration().instancehome
+default_site_preference.edit(
+ preferred_subversion_working_copy_list=['%s/bt5/' % instance_home,
+ '%s/Products/ERP5/bootstrap/' % instance_home])
+default_site_preference.enable()
+
+# install our business templates
+bt5_list = []
+for arg in args:
+ bt_path = os.path.join(options.bt5_path, arg)
+ print 'Installing bt %s' % bt_path
+ bt = portal.portal_templates.download(bt_path)
+ bt.install(force=True)
+ transaction.commit()
+
+transaction.commit()
Added: experimental/erp5.buildout/recipes/create_erp5_site.py
URL: http://svn.erp5.org/experimental/erp5.buildout/recipes/create_erp5_site.py?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/recipes/create_erp5_site.py (added)
+++ experimental/erp5.buildout/recipes/create_erp5_site.py [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,62 @@
+import os, re, shutil, tempfile, urllib2, urlparse
+import setuptools.archive_util
+
+class Recipe:
+ '''Recipe to create an ERP5 site using zopectl run
+ '''
+
+ def __init__(self, buildout, name, options):
+ self.buildout, self.name, self.options = buildout, name, options
+ # Guess the name of the install script by looking at the section using
+ # zope2instance recipe
+ for k, v in self.buildout.items():
+ if v.get('recipe') == 'plone.recipe.zope2instance':
+ instance_script = v.get('control-script')
+ options['control-script'] = instance_script or k
+ options['instance-location'] = v['location']
+ assert 'control-script' in options, 'Unable to find a zope2 instance'
+
+ options['bin-directory'] = buildout['buildout']['bin-directory']
+
+ def install(self):
+ options = self.options
+ zeo_ctl = options.get('zeo-control-script', None)
+ if zeo_ctl:
+ zeo_ctl_path = os.path.join(options['bin-directory'], zeo_ctl)
+ assert os.spawnl(
+ os.P_WAIT, zeo_ctl_path, *[zeo_ctl_path, 'start']) == 0
+ zopectl_path = os.path.join(options['bin-directory'],
+ options['control-script'])
+ script_name = os.path.join(os.path.dirname(__file__),
+ 'create_erp5_instance.py')
+ argv = [zopectl_path, 'run', script_name]
+
+ if options.get('portal_id'):
+ argv.extend(['--portal_id', options['portal_id']])
+ if options.get('erp5_sql_connection_string'):
+ argv.extend(['--erp5_sql_connection_string',
+ options['erp5_sql_connection_string']])
+
+ if options.get('cmf_activity_sql_connection_string'):
+ argv.extend(['--cmf_activity_sql_connection_string',
+ options['cmf_activity_sql_connection_string']])
+ if options.get('erp5_catalog_storage'):
+ argv.extend(['--erp5_catalog_storage',
+ options['erp5_catalog_storage']])
+ if options.get('user'):
+ # XXX read rom zope2instance section ?
+ argv.extend(['--initial-user',
+ options['user']])
+
+ argv.extend(['--bt5-path',
+ os.path.join(options['bt5-path'])])
+ argv.extend([bt for bt in options.get('bt5', '').split('\n') if bt])
+
+ assert os.spawnl(
+ os.P_WAIT, zopectl_path, *argv ) == 0
+
+ return []
+
+ def update(self):
+ pass
+
Added: experimental/erp5.buildout/recipes/run_unit_test.py
URL: http://svn.erp5.org/experimental/erp5.buildout/recipes/run_unit_test.py?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/recipes/run_unit_test.py (added)
+++ experimental/erp5.buildout/recipes/run_unit_test.py [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,94 @@
+##############################################################################
+#
+# Copyright (c) 2006-2008 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, glob
+import zc.buildout
+import zc.recipe.egg
+
+class Recipe:
+ '''Creates a runUnitTest aware of eggs and custom paths used in this
+ buildbot.
+ '''
+ name = 'runUnitTest'
+
+ def __init__(self, buildout, name, options):
+ self.egg = zc.recipe.egg.Egg(buildout, 'recipes', options)
+ self.buildout, self.options, self.name = buildout, options, name
+
+ options['location'] = os.path.join(
+ buildout['buildout']['parts-directory'],
+ self.name,
+ )
+ options['bin-directory'] = buildout['buildout']['bin-directory']
+ options['scripts'] = '' # suppress script generation. ??? XXX
+
+ def install(self):
+ options = self.options
+ location = options['location']
+
+ extra_paths = []
+ if 'zope2-location' in options:
+ extra_paths.append(
+ os.path.join(options['zope2-location'], 'lib', 'python'))
+ extra_paths.extend(options.get('extra-paths', []))
+
+ init = init_template
+ for product in options.get('products', '').splitlines():
+ if product:
+ init += add_product_path_template % dict(product_path=product)
+ for product_test_path in glob.glob(
+ os.path.join(product, '*', 'tests')):
+ init += add_product_tests_path_template % dict(
+ product_test_path=product_test_path)
+
+ for bt5_path in options.get('bt5_path', '').splitlines():
+ if bt5_path:
+ init += add_bt5_path_template % dict(bt5_path=bt5_path)
+
+ requirements, ws = self.egg.working_set()
+
+ zc.buildout.easy_install.scripts(
+ [(self.options.get('control-script', self.name),
+ 'Products.ERP5Type.tests.runUnitTest', 'main')],
+ ws, options['executable'], options['bin-directory'],
+ extra_paths = extra_paths,
+ initialization = init,
+ )
+
+ return []
+
+init_template = '''
+import Products
+import sys
+
+import Ft # XXX workaround strange bug:
+# File "4Suite_XML-1.0.2-py2.4-linux-i686.egg/Ft/Lib/ImportUtil.py"
+# line 296, in get_loader
+# return find_loader(fullname)
+# File "4Suite_XML-1.0.2-py2.4-linux-i686.egg/Ft/Lib/ImportUtil.py",
+# line 306, in find_loader
+# loader = importer.find_module(fullname)
+# AttributeError: 'NoneType' object has no attribute 'find_module'
+'''
+
+add_product_path_template = '''
+Products.__path__.append('%(product_path)s')'''
+
+add_product_tests_path_template = '''
+sys.path.append('%(product_test_path)s')'''
+
+add_bt5_path_template = '''
+sys.argv.insert(1, '%(bt5_path)s')
+sys.argv.insert(1, '--bt5_path')'''
+
Added: experimental/erp5.buildout/recipes/setup.py
URL: http://svn.erp5.org/experimental/erp5.buildout/recipes/setup.py?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/recipes/setup.py (added)
+++ experimental/erp5.buildout/recipes/setup.py [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,9 @@
+from setuptools import setup
+
+setup(name="recipes",
+ version="0.0.1",
+ entry_points={'zc.buildout': ['zope2install = zope2install:Recipe',
+ 'create_erp5_site = create_erp5_site:Recipe',
+ 'run_unit_test = run_unit_test:Recipe']},
+ install_requires=['plone.recipe.zope2install',], )
+
Added: experimental/erp5.buildout/recipes/zope2install.py
URL: http://svn.erp5.org/experimental/erp5.buildout/recipes/zope2install.py?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/recipes/zope2install.py (added)
+++ experimental/erp5.buildout/recipes/zope2install.py [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,85 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# Contains code from plone.recipe.zope2install
+#
+# 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, tempfile, urllib2, urlparse
+import setuptools.archive_util
+import plone.recipe.zope2install
+
+class Recipe(plone.recipe.zope2install.Recipe):
+
+ def __init__(self, buildout, name, options):
+ plone.recipe.zope2install.Recipe.__init__(self, buildout, name, options)
+ self.patch = os.path.join(self.buildout['buildout']['directory'],
+ options['patch'])
+
+ def install(self):
+ options = self.options
+ location = options['location']
+ download_dir = self.buildout['buildout']['download-cache']
+
+ if os.path.exists(location):
+ # if the zope installation exists and is shared, then we are done
+ # and don't return a path, so the shared installation doesn't get
+ # deleted on uninstall
+ if options.get('shared-zope') == 'true':
+ return []
+ else:
+ shutil.rmtree(location)
+
+ if self.svn:
+ assert os.system('svn co %s %s' % (options['svn'], location)
+ ) == 0
+ else:
+ if not os.path.isdir(download_dir):
+ os.mkdir(download_dir)
+
+ _, _, urlpath, _, _, _ = urlparse.urlparse(self.url)
+ tmp = tempfile.mkdtemp('buildout-'+self.name)
+ try:
+ fname = os.path.join(download_dir, urlpath.split('/')[-1])
+ # Have we already downloaded the file
+ if not os.path.exists(fname):
+ f = open(fname, 'wb')
+ try:
+ f.write(urllib2.urlopen(self.url).read())
+ except:
+ os.remove(fname)
+ raise
+ f.close()
+
+ setuptools.archive_util.unpack_archive(fname, tmp)
+ # The Zope tarballs have a Zope-<version> folder at the root
+ # level, so we need to move that one into the right place.
+ files = os.listdir(tmp)
+ shutil.move(os.path.join(tmp, files[0]), location)
+ finally:
+ shutil.rmtree(tmp)
+
+ os.chdir(location)
+
+ # erp5: apply a patch to zope before build
+ assert os.system('patch -p1 < %s' % self.patch) == 0
+
+ assert os.spawnl(
+ os.P_WAIT, options['executable'], options['executable'],
+ 'setup.py',
+ 'build_ext', '-i',
+ ) == 0
+
+ if self.url and options.get('shared-zope') == 'true':
+ # don't return path if the installation is shared
+ return []
+ return location
+
Added: experimental/erp5.buildout/src/EXTERNALS.TXT
URL: http://svn.erp5.org/experimental/erp5.buildout/src/EXTERNALS.TXT?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/src/EXTERNALS.TXT (added)
+++ experimental/erp5.buildout/src/EXTERNALS.TXT [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,10 @@
+#
+# Used for maintenance of external resources in this svn bundle. Edit
+# this file as appropriate and then run the following command from within
+# the checkout directory where this file lives on your local machine:
+#
+# svn propset svn:externals -F ./EXTERNALS.TXT .
+#
+
+timerserver https://svn.erp5.org/repos/public/erp5/trunk/products/TimerService/timerserver/
+erp5diff https://svn.erp5.org/repos/public/erp5/trunk/utils/erp5diff/
Propchange: experimental/erp5.buildout/src/EXTERNALS.TXT
------------------------------------------------------------------------------
svn:eol-style = native
Added: experimental/erp5.buildout/src/erp5diff/ERP5Diff.py
URL: http://svn.erp5.org/experimental/erp5.buildout/src/erp5diff/ERP5Diff.py?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/src/erp5diff/ERP5Diff.py (added)
+++ experimental/erp5.buildout/src/erp5diff/ERP5Diff.py [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,544 @@
+##############################################################################
+#
+# Yoshinori OKUJI <yo at nexedi.com>
+#
+# Copyright (C) 2003 Nexedi SARL
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ?See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA ?02111-1307, USA.
+#
+##############################################################################
+
+try:
+ from Ft.Xml import Parse as parse
+ from Ft.Xml.Domlette import NonvalidatingReader, PrettyPrint
+ parseString = NonvalidatingReader.parseString
+ from Ft.Xml.Domlette import implementation
+ from Ft.Xml import EMPTY_NAMESPACE
+ def getDOMImplementation():
+ return implementation
+except ImportError:
+ from xml.dom.minidom import parse, parseString
+ from xml.dom.minidom import getDOMImplementation
+ from xml.dom.ext import PrettyPrint
+ EMPTY_NAMESPACE = None
+
+import sys
+import getopt
+import os
+from StringIO import StringIO
+import re
+import codecs
+
+class ERP5Diff:
+ """
+ Make a difference between two XML documents using XUpdate.
+ Use some assumptions in ERP5's data representation.
+
+ The strategy is:
+ 1. Find a matching element among elements of the other XML document at the same depth.
+ 2. Use the first matching element, even if there can be other better elements.
+ 3. Assume that two elements are matching, if the tag names are identical. If either of
+ them has an attribute 'id', the values of the attributes 'id' also must be identical.
+ 4. Don't use xupdate:rename for elements. It should be quite rare to rename tag names
+ in ERP5, and it is too complicated to support this renaming.
+ 5. Ignore some types of nodes, such as EntityReference and Comment, because they are not
+ used in ERP5 XML documents.
+ """
+ def __init__(self):
+ """
+ Initialize itself.
+ """
+ self._verbose = 0
+ self._result = None
+ self._ns = 'http://www.xmldb.org/xupdate'
+
+ def setVerbosity(self, verbose):
+ """
+ Set the verbosity.
+ """
+ self._verbose = verbose
+
+ def _p(self, msg):
+ """
+ Print a message only if being verbose.
+ """
+ if self._verbose:
+ sys.stderr.write(str(msg) + os.linesep)
+
+ def _makeDocList(self, *args):
+ """
+ Make a list of Document objects.
+ """
+ doc_list = []
+ for a in args:
+ if type(a) == type(''):
+ doc_list.append(parseString(a))
+ else:
+ doc_list.append(parse(a))
+ return doc_list
+
+ def _concatPath(self, p1, p2, separator='/'):
+ """
+ Concatenate 'p1' and 'p2'. Add a separator between them,
+ only if 'p1' does not end with a separator.
+ """
+ if p1.endswith(separator):
+ return p1 + p2
+ return p1 + separator + p2
+
+ def _getResultRoot(self):
+ """
+ Return the root element of the result document.
+ """
+ return self._result.documentElement
+
+ def _xupdateAppendAttributes(self, dict, path):
+ """
+ Append attributes to the element at 'path'.
+ """
+ root = self._getResultRoot()
+ createElement = self._result.createElementNS
+ createTextNode = self._result.createTextNode
+ append_element = createElement(self._ns, 'xupdate:append')
+ append_element.setAttributeNS(EMPTY_NAMESPACE, 'select', path)
+ for name, val in dict.iteritems():
+ attr_element = createElement(self._ns, 'xupdate:attribute')
+ attr_element.setAttributeNS(EMPTY_NAMESPACE, 'name', name)
+ text_node = createTextNode(val)
+ attr_element.appendChild(text_node)
+ append_element.appendChild(attr_element)
+ root.appendChild(append_element)
+
+ def _xupdateRemoveAttribute(self, name, path):
+ """
+ Remove an attribute from the element at 'path'.
+ """
+ root = self._getResultRoot()
+ createElement = self._result.createElementNS
+ remove_element = createElement(self._ns, 'xupdate:remove')
+ remove_element.setAttributeNS(EMPTY_NAMESPACE, 'select', self._concatPath(path, 'attribute::' + name))
+ root.appendChild(remove_element)
+
+ def _xupdateUpdateAttribute(self, name, val, path):
+ """
+ Update the value of an attribute of the element at 'path'.
+ """
+ root = self._getResultRoot()
+ createElement = self._result.createElementNS
+ createTextNode = self._result.createTextNode
+ update_element = createElement(self._ns, 'xupdate:update')
+ update_element.setAttributeNS(EMPTY_NAMESPACE, 'select', self._concatPath(path, 'attribute::' + name))
+ text_node = createTextNode(val)
+ update_element.appendChild(text_node)
+ root.appendChild(update_element)
+
+ def _xupdateRenameElement(self, name, path):
+ """
+ Rename an existing element at 'path'.
+ """
+ root = self._getResultRoot()
+ createElement = self._result.createElementNS
+ createTextNode = self._result.createTextNode
+ rename_element = createElement(self._ns, 'xupdate:rename')
+ rename_element.setAttributeNS(EMPTY_NAMESPACE, 'select', path)
+ text_node = createTextNode(name)
+ rename_element.appendChild(text_node)
+ root.appendChild(rename_element)
+
+ def _xupdateUpdateElement(self, element, path):
+ """
+ Update the contents of an element at 'path' to that of 'element'.
+ """
+ root = self._getResultRoot()
+ createElement = self._result.createElementNS
+ update_element = createElement(self._ns, 'xupdate:update')
+ update_element.setAttributeNS(EMPTY_NAMESPACE, 'select', path)
+ for node in element.childNodes:
+ #self._p("node is %s" % repr(node))
+ clone_node = node.cloneNode(1)
+ update_element.appendChild(clone_node)
+ root.appendChild(update_element)
+
+ def _xupdateRemoveElement(self, path):
+ """
+ Remove an element at 'path'.
+ """
+ root = self._getResultRoot()
+ createElement = self._result.createElementNS
+ remove_element = createElement(self._ns, 'xupdate:remove')
+ remove_element.setAttributeNS(EMPTY_NAMESPACE, 'select', path)
+ root.appendChild(remove_element)
+
+ def _xupdateInsertBefore(self, element_list, path):
+ """
+ Insert elements before the element at 'path'.
+ """
+ root = self._getResultRoot()
+ createElement = self._result.createElementNS
+ createTextNode = self._result.createTextNode
+ insert_element = createElement(self._ns, 'xupdate:insert-before')
+ insert_element.setAttributeNS(EMPTY_NAMESPACE, 'select', path)
+ for element in element_list:
+ child_element = createElement(self._ns, 'xupdate:element')
+ child_element.setAttributeNS(EMPTY_NAMESPACE, 'name', element.tagName)
+ attr_map = element.attributes
+ for attr in attr_map.values():
+ attr_element = createElement(self._ns, 'xupdate:attribute')
+ attr_element.setAttributeNS(EMPTY_NAMESPACE, 'name', attr.name)
+ text_node = createTextNode(attr.nodeValue)
+ attr_element.appendChild(text_node)
+ child_element.appendChild(attr_element)
+ for child in element.childNodes:
+ clone_node = child.cloneNode(1)
+ child_element.appendChild(clone_node)
+ insert_element.appendChild(child_element)
+ root.appendChild(insert_element)
+
+ def _xupdateAppendElements(self, element_list, path):
+ """
+ Append elements to the element at 'path'.
+ """
+ root = self._getResultRoot()
+ createElement = self._result.createElementNS
+ createTextNode = self._result.createTextNode
+ append_element = createElement(self._ns, 'xupdate:append')
+ append_element.setAttributeNS(EMPTY_NAMESPACE, 'select', path)
+ for element in element_list:
+ child_element = createElement(self._ns, 'xupdate:element')
+ child_element.setAttributeNS(EMPTY_NAMESPACE, 'name', element.tagName)
+ attr_map = element.attributes
+ for attr in attr_map.values():
+ attr_element = createElement(self._ns, 'xupdate:attribute')
+ attr_element.setAttributeNS(EMPTY_NAMESPACE, 'name', attr.name)
+ text_node = createTextNode(attr.nodeValue)
+ attr_element.appendChild(text_node)
+ child_element.appendChild(attr_element)
+ for child in element.childNodes:
+ clone_node = child.cloneNode(1)
+ child_element.appendChild(clone_node)
+ append_element.appendChild(child_element)
+ root.appendChild(append_element)
+
+ def _testElements(self, element1, element2):
+ """
+ Test if two given elements are matching. Matching does not mean that they are identical.
+ """
+ # Make sure that they are elements.
+ if element1.nodeType != element2.nodeType or element1.nodeType != element1.ELEMENT_NODE:
+ return 0
+
+ if element1.tagName != element2.tagName:
+ return 0
+
+ id_list = []
+ for attr_map in (element1.attributes, element2.attributes):
+ for attr in attr_map.values():
+ if attr.name == 'id':
+ id_list.append(attr.nodeValue)
+ break
+
+ if len(id_list) == 0:
+ return 1
+ if len(id_list) == 1:
+ return 0
+ return (id_list[0] == id_list[1])
+
+ def _testAttributes(self, element1, element2, path):
+ """
+ Test attributes of two given elements. Add differences, if any.
+ """
+ # Make a list of dictionaries of the attributes.
+ dict_list = []
+ for attr_map in (element1.attributes, element2.attributes):
+ dict = {}
+ for attr in attr_map.values():
+ dict[attr.name] = attr.nodeValue
+ dict_list.append(dict)
+ dict1, dict2 = dict_list
+
+ # Find all added or removed or changed attributes.
+ for name1, val1 in dict1.iteritems():
+ if name1 in dict2:
+ if val1 != dict2[name1]:
+ # The value is different.
+ self._xupdateUpdateAttribute(name1, dict2[name1], path)
+ # Mark this attribute.
+ dict2[name1] = None
+ else:
+ # This attribute is removed.
+ self._xupdateRemoveAttribute(name1, path)
+ dict = {}
+ for name2, val2 in dict2.iteritems():
+ if val2 is not None:
+ # This attribute is added.
+ dict[name2] = val2
+ if dict != {}:
+ self._xupdateAppendAttributes(dict, path)
+
+ def _checkEmptiness(self, element):
+ """
+ Check if an element has child values.
+ """
+ for child in element.childNodes:
+ if child.nodeType == child.ELEMENT_NODE or child.nodeType == child.TEXT_NODE:
+ return 0
+ return 1
+
+ def _checkIgnoreText(self, element):
+ """
+ Determine if text should be ignored by heuristics,
+ because ERP5 does not define any schema at the moment.
+ """
+ for child in element.childNodes:
+ if child.nodeType == child.ELEMENT_NODE:
+ return 1
+ return 0
+
+ def _makeRelativePathList(self, element_list):
+ """
+ Make a list of relative paths from a list of elements.
+ """
+ num_map = {}
+ count_map = {}
+ for element in element_list:
+ if element.tagName in num_map:
+ num_map[element.tagName] += 1
+ else:
+ num_map[element.tagName] = 1
+ count_map[element.tagName] = 0
+
+ path_list = []
+ for element in element_list:
+ # Check if this element has an attribute 'id'.
+ id_val = None
+ attr_map = element.attributes
+ for attr in attr_map.values():
+ if attr.name == 'id':
+ id_val = attr.nodeValue
+ break
+
+ if id_val is not None:
+ # If an attribute 'id' is present, uses the attribute for convenience.
+ path_list.append("%s[@id='%s']" % (element.tagName, id_val))
+ # Increase the count, for a case where other elements with the same tag name do not have
+ # 'id' attributes.
+ count_map[element.tagName] += 1
+ elif num_map[element.tagName] > 1:
+ path_list.append('%s[%d]' % (element.tagName, count_map[element.tagName]))
+ count_map[element.tagName] += 1
+ else:
+ path_list.append(element.tagName)
+
+ return path_list
+
+ def _aggregateElements(self, element):
+ """
+ Aggregate child elements of an element into a list.
+ """
+ element_list = []
+ for child in element.childNodes:
+ if child.nodeType == child.ELEMENT_NODE:
+ element_list.append(child)
+ return element_list
+
+ def _aggregateText(self, element):
+ """
+ Aggregate child text nodes of an element into a single string.
+ """
+ text = ''
+ for child in element.childNodes:
+ if child.nodeType == child.TEXT_NODE:
+ text += child.nodeValue
+ return text
+
+ def _compareChildNodes(self, old_element, new_element, path):
+ """
+ Compare children of two elements, and add differences into the result, if any.
+ Call itself recursively, if these elements have grandchilden.
+ """
+ self._p("Comparing %s with %s at %s..." % (repr(old_element), repr(new_element), path))
+
+ # First, determine if they are empty.
+ old_is_empty = self._checkEmptiness(old_element)
+ new_is_empty = self._checkEmptiness(new_element)
+
+ if old_is_empty and new_is_empty:
+ # Nothing to do.
+ self._p("Both are empty.")
+ pass
+ elif old_is_empty or new_is_empty:
+ # Perhaps they are very different.
+ self._p("One of them is empty, so just update all the contents.")
+ self._xupdateUpdateElement(new_element, path)
+ else:
+ # Second, determine if text should be ignored.
+ old_ignore_text = self._checkIgnoreText(old_element)
+ new_ignore_text = self._checkIgnoreText(new_element)
+
+ if old_ignore_text != new_ignore_text:
+ # This means that the semantics of this element is quite different.
+ self._p("One of them has only text and the other does not, so just update all the contents.")
+ self._xupdateUpdateElement(new_element, path)
+ elif not old_ignore_text:
+ # The contents are only text.
+ self._p("Both have only text.")
+ old_text = self._aggregateText(old_element)
+ new_text = self._aggregateText(new_element)
+ if old_text != new_text:
+ self._p("They differ, so update the elements.")
+ self._xupdateUpdateElement(new_element, path)
+ else:
+ # The contents are elements.
+ self._p("Both have elements.")
+ old_list = self._aggregateElements(old_element)
+ path_list = self._makeRelativePathList(old_list)
+ new_list = self._aggregateElements(new_element)
+ new_start = 0
+ new_len = len(new_list)
+ for old_node, node_path in zip(old_list, path_list):
+ child_path = self._concatPath(path, node_path)
+ for new_current in range(new_start, new_len):
+ new_node = new_list[new_current]
+ if self._testElements(old_node, new_node):
+ self._testAttributes(old_node, new_node, child_path)
+ self._compareChildNodes(old_node, new_node, child_path)
+ if new_current > new_start:
+ # There are skipped nodes in the new children.
+ self._xupdateInsertBefore(new_list[new_start:new_current], child_path)
+ new_start = new_current + 1
+ break
+ else:
+ # There is no matching node. So this element must be removed.
+ self._xupdateRemoveElement(child_path)
+ if new_len > new_start:
+ # There are remaining nodes in the new children.
+ self._xupdateAppendElements(new_list[new_start:new_len], path)
+
+ def compare(self, old_xml, new_xml):
+ """
+ Compare two given XML documents.
+ If an argument is a string, it is assumed to be a XML document itself.
+ Otherwise, it is assumed to be a file object which contains a XML document.
+ """
+ old_doc, new_doc = self._makeDocList(old_xml, new_xml)
+ old_root_element = old_doc.documentElement
+ new_root_element = new_doc.documentElement
+ try:
+ impl = getDOMImplementation()
+ # XXX this namespace argument won't be handled correctly in minidom.
+ # XXX So work around that problem when outputting the result.
+ if self._result is not None:
+ self._result.close()
+ self._result = impl.createDocument(self._ns, 'xupdate:modifications', None)
+ attr_version = self._result.createAttributeNS(EMPTY_NAMESPACE, 'version')
+ attr_version.value = '1.0'
+ self._result.documentElement.setAttributeNodeNS(attr_version)
+ if self._testElements(old_root_element, new_root_element):
+ self._testAttributes(old_root_element, new_root_element, '/')
+ self._compareChildNodes(old_root_element, new_root_element, '/')
+ else:
+ # These XML documents seem to be completely different...
+ if old_root_element.tagName != new_root_element.tagName:
+ self._xupdateRenameElement(new_root_element.tagName, '/')
+ self._testAttributes(old_root_element, new_root_element, '/')
+ self._xupdateUpdateElement(new_root_element, '/')
+ finally:
+ del old_doc
+ del new_doc
+
+ def output(self, file=None):
+ """
+ Output the result of parsing XML documents to 'file'.
+ If it is not specified, stdout is assumed.
+ """
+ if file is None:
+ file = sys.stdout
+
+ PrettyPrint(self._result.documentElement, stream=file, encoding='UTF-8')
+
+ def outputString(self):
+ """
+ Return the result as a string object.
+ """
+ io = StringIO()
+ self.output(io)
+ ret = io.getvalue()
+ io.close()
+ return ret
+
+def main():
+ """
+ The main routine of ERP5Diff.
+ """
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "ho:v", ["help", "output=", "verbose"])
+ except getopt.GetoptError, msg:
+ print msg
+ print "Try ``erp5diff --help'' for more information."
+ sys.exit(2)
+ output = None
+ verbose = 0
+ for o, a in opts:
+ if o == "-v":
+ verbose = 1
+ elif o in ("-h", "--help"):
+ print '''Usage: erp5diff [OPTION]... OLD_XML NEW_XML
+Make a difference between two XML documents in XUpdate format.
+
+ -h, --help display this message and exit
+ -o, --output=FILE output the result to the file FILE
+ -v, --verbose print verbose messages
+
+Report bugs to <yo at nexedi.com>.'''
+ sys.exit()
+ elif o in ("-o", "--output"):
+ output = a
+
+ if len(args) != 2:
+ if len(args) > 2:
+ print "Too many arguments."
+ else:
+ print "Too few arguments."
+ print "Try ``erp5diff --help'' for more information."
+ sys.exit(2)
+
+ d = ERP5Diff()
+ d.setVerbosity(verbose)
+
+ old_xml = open(args[0])
+ new_xml = open(args[1])
+ d.compare(old_xml, new_xml)
+ old_xml.close()
+ new_xml.close()
+
+ try:
+ if output is not None:
+ file = open(output, 'w')
+ else:
+ file = None
+ d.output(file)
+ except:
+ if output is not None:
+ file.close()
+ os.remove(output)
+ raise
+ else:
+ if file is not None:
+ file.close()
+
+ sys.exit()
+
+if __name__ == '__main__':
+ main()
Propchange: experimental/erp5.buildout/src/erp5diff/ERP5Diff.py
------------------------------------------------------------------------------
svn:executable =
Added: experimental/erp5.buildout/src/erp5diff/MAINTAINERS.txt
URL: http://svn.erp5.org/experimental/erp5.buildout/src/erp5diff/MAINTAINERS.txt?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/src/erp5diff/MAINTAINERS.txt (added)
+++ experimental/erp5.buildout/src/erp5diff/MAINTAINERS.txt [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,2 @@
+yo
+seb
Propchange: experimental/erp5.buildout/src/erp5diff/MAINTAINERS.txt
------------------------------------------------------------------------------
svn:eol-style = native
Added: experimental/erp5.buildout/src/erp5diff/README
URL: http://svn.erp5.org/experimental/erp5.buildout/src/erp5diff/README?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/src/erp5diff/README (added)
+++ experimental/erp5.buildout/src/erp5diff/README [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,17 @@
+This is a XUpdate Generator for ERP5.
+
+See <http://www.xmldb.org/xupdate/index.html> for information on
+XUpdate.
+
+See <http://erp5.org/> for information on ERP5.
+
+For the installation, do "python setup.py install".
+
+Once you have installed erp5diff, you can use "erp5diff" in a shell:
+$ erp5diff old.xml new.xml
+See the manpage erp5diff(1) or "erp5diff --help" for more information.
+
+Also, you can use the module ERP5Diff from your Python script.
+Do "pydoc ERP5Diff" for more information.
+
+- 2003-12-04, Yoshinori OKUJI <yo at nexedi.com>
Propchange: experimental/erp5.buildout/src/erp5diff/README
------------------------------------------------------------------------------
svn:executable =
Added: experimental/erp5.buildout/src/erp5diff/erp5diff
URL: http://svn.erp5.org/experimental/erp5.buildout/src/erp5diff/erp5diff?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/src/erp5diff/erp5diff (added)
+++ experimental/erp5.buildout/src/erp5diff/erp5diff [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,27 @@
+#! /usr/bin/python
+
+##############################################################################
+#
+# Yoshinori OKUJI <yo at nexedi.com>
+#
+# Copyright (C) 2003 Nexedi SARL
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ?See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA ?02111-1307, USA.
+#
+##############################################################################
+
+from ERP5Diff import main
+
+main()
Propchange: experimental/erp5.buildout/src/erp5diff/erp5diff
------------------------------------------------------------------------------
svn:executable =
Added: experimental/erp5.buildout/src/erp5diff/erp5diff.1
URL: http://svn.erp5.org/experimental/erp5.buildout/src/erp5diff/erp5diff.1?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/src/erp5diff/erp5diff.1 (added)
+++ experimental/erp5.buildout/src/erp5diff/erp5diff.1 [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,32 @@
+.TH ERP5DIFF 1 "4 Dec 2003" "ERP5DIFF version 0.1" Nexedi
+.SH NAME
+erp5diff \- find differences between two XML documents for ERP5
+.SH SYNOPSIS
+.B erp5diff
+[\fIoptions\fR]...
+.LP
+.SH DESCRIPTION
+ERP5Diff is a XUpdate Generator for ERP5. It takes two XML files
+as input data, and generates differences between these two XML
+documents in XUpdate language.
+.LP
+ERP5Diff depends on more or less ERP5's XML data format. So this tool
+cannot be used for general purpose, but might work if your XML files
+are similar to ERP5's.
+.SH OPTIONS
+.TP
+\fB\-o\fR, \fB\-\-output\fR=\fIFILE\fR
+Specify the output file. The standard output is used by default.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Display the usage and exit.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+Print verbose messages. Only useful for debugging.
+.SH AUTHOR
+Yoshinori OKUJI <yo at nexedi.com>
+.SH "SEE ALSO"
+\fIhttp://www.xmldb.org/xupdate/index.html\fR,
+\fIhttp://www.w3.org/TR/xpath\fR,
+\fIhttp://www.w3.org/TR/REC-xml\fR,
+\fIhttp://erp5.org\fR
Propchange: experimental/erp5.buildout/src/erp5diff/erp5diff.1
------------------------------------------------------------------------------
svn:executable =
Added: experimental/erp5.buildout/src/erp5diff/setup.py
URL: http://svn.erp5.org/experimental/erp5.buildout/src/erp5diff/setup.py?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/src/erp5diff/setup.py (added)
+++ experimental/erp5.buildout/src/erp5diff/setup.py [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,15 @@
+#! /usr/bin/env python
+
+from distutils.core import setup
+
+setup(name="erp5diff",
+ version="0.1",
+ description="XUpdate Generator for ERP5",
+ author="Yoshinori OKUJI",
+ author_email="yo at nexedi.com",
+ url="http://nexedi.com",
+ license="GPL",
+ py_modules=["ERP5Diff"],
+ scripts=["erp5diff"],
+ data_files=[('share/man/man1', ['erp5diff.1'])]
+ )
Propchange: experimental/erp5.buildout/src/erp5diff/setup.py
------------------------------------------------------------------------------
svn:executable =
Added: experimental/erp5.buildout/src/timerserver/setup.py
URL: http://svn.erp5.org/experimental/erp5.buildout/src/timerserver/setup.py?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/src/timerserver/setup.py (added)
+++ experimental/erp5.buildout/src/timerserver/setup.py [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,35 @@
+
+modname = 'timerserver'
+version = open('version.txt').read().strip()
+numversion = version.split('.')
+
+license = 'GPL'
+copyright = '''Nikolay Kim (c) 2004'''
+
+author = "Nikolay Kim"
+author_email = "fafhrd at legco.biz"
+
+short_desc = "Timer Server for Zope"
+long_desc = short_desc
+
+web = ""
+ftp = ""
+mailing_list = ""
+#!/usr/bin/env python
+import sys
+try:
+ from setuptools import setup
+except ImportError:
+ from distutils.core import setup
+
+setup(name='timerserver',
+ version='2.0',
+ license='GPL',
+ description='Timer Server for Zope',
+ long_description='',
+ author='Nikolay Kim',
+ author_email='fafhrd at legco.biz',
+ packages=['timerserver'],
+ zip_safe=False,
+ package_data={'timerserver': ['component.xml']},
+ )
Added: experimental/erp5.buildout/src/timerserver/timerserver/TimerServer.py
URL: http://svn.erp5.org/experimental/erp5.buildout/src/timerserver/timerserver/TimerServer.py?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/src/timerserver/timerserver/TimerServer.py (added)
+++ experimental/erp5.buildout/src/timerserver/timerserver/TimerServer.py [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,147 @@
+# -*- coding: UTF-8 -*-
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# Authors: Nik Kim <fafhrd at legco.biz>
+__version__ = 'TimerServer for Zope 0.1'
+
+import traceback
+
+import thread
+import re
+import sys, os, errno, time, socket
+from StringIO import StringIO
+from zLOG import LOG, INFO
+
+from ZServer.PubCore import handle
+from ZPublisher.BaseRequest import BaseRequest
+from ZPublisher.BaseResponse import BaseResponse
+from ZPublisher.HTTPRequest import HTTPRequest
+import ZPublisher.HTTPRequest
+
+class TimerServer:
+ def __init__(self, module, interval=600):
+ self.module = module
+
+ self.interval = interval
+
+ sync = thread.allocate_lock()
+
+ self._a = sync.acquire
+ self._r = sync.release
+
+ self._a()
+ thread.start_new_thread(self.run, ())
+ self._r()
+
+ LOG('ZServer', INFO,
+ 'Timer server started at %s\n'
+ '\tInterval: %s seconds.\n'%(time.ctime(time.time()), interval))
+
+ def run(self):
+ # wait until the zhttp_server exist in socket_map
+ # because TimerService has to be started after the Zope HTTPServer
+ from asyncore import socket_map
+ while 1:
+ time.sleep(5)
+ for k, v in socket_map.items():
+ if hasattr(v, 'port'):
+ # see Zope/lib/python/App/ApplicationManager.py: def getServers(self)
+ type = str(getattr(v, '__class__', 'unknown'))
+ if type == 'ZServer.HTTPServer.zhttp_server':
+ port = v.port
+ break
+ if port:
+ break
+
+ ip = socket.gethostbyname(socket.gethostname())
+
+ # To be very sure, try to connect to the HTTPServer
+ # and only start after we are able to connect and got a response
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.settimeout(None)
+ while 1:
+ try:
+ s.connect((ip, port))
+ except socket.error:
+ time.sleep(5)
+ continue
+ s.send('GET / HTTP/1.1\r\n\r\n')
+ s.recv(4096) # blocks until a response is received
+ break
+ s.close()
+
+ module = self.module
+ interval = self.interval
+
+ # minutes = time.gmtime(time.time()[4], seconds = time.gmtime(time.time()[5]
+ # max interval is therefore 59*60 + 59 = 208919 seconds
+
+ wait = ((time.gmtime(time.time())[4] * 60) + time.gmtime(time.time())[5]) % interval
+ sleep = interval - wait
+
+ if sleep > 0:
+ time.sleep(sleep)
+
+ LOG('ZServer', INFO, 'Timerserver ready, starting timer services.')
+
+ while 1:
+ time.sleep(interval)
+ # send message to zope
+ try:
+ out = StringIO()
+ err = StringIO()
+ response = TimerResponse(out, err)
+ handle(module, TimerRequest(response, interval), response)
+ except:
+ pass
+
+
+class TimerResponse(BaseResponse):
+ def _finish(self):
+ pass
+
+ def unauthorized(self):
+ pass
+
+ # This is taken from ZPublisher.HTTPResponse
+ # I don't think it's safe to make TimerResponse a subclass of HTTPResponse,
+ # so I inline here the method . This is required it you want unicode page
+ # templates to be usable by timer service.
+ # This is used by an iHotFix patch on PageTemplate.StringIO method
+ def _encode_unicode(self, body,
+ charset_re=re.compile(r'(?:application|text)/[-+0-9a-z]+\s*;\s*' +
+ r'charset=([-_0-9a-z]+' +
+ r')(?:(?:\s*;)|\Z)',
+ re.IGNORECASE)):
+ # Encode the Unicode data as requested
+ if self.headers.has_key('content-type'):
+ match = charset_re.match(self.headers['content-type'])
+ if match:
+ encoding = match.group(1)
+ return body.encode(encoding)
+ # Use the default character encoding
+ return body.encode(ZPublisher.HTTPResponse.default_encoding,'replace')
+
+
+class TimerRequest(HTTPRequest):
+
+ retry_max_count = 0
+
+ def __init__(self, response, interval):
+ stdin=StringIO()
+ environ=self._get_env(stdin)
+ HTTPRequest.__init__(self, stdin, environ, response, clean=1)
+
+ self.other['interval'] = interval
+
+ def _get_env(self, stdin):
+ "Returns a CGI style environment"
+ env={}
+ env['REQUEST_METHOD']='GET'
+ env['SERVER_SOFTWARE']= 'TimerServer for Zope'
+ env['SERVER_NAME'] = ''
+ env['SERVER_PORT'] = ''
+ env['REMOTE_ADDR'] = ''
+ env['GATEWAY_INTERFACE'] = 'CGI/1.1'
+
+ env['PATH_INFO']= '/Control_Panel/timer_service/process_timer'
+ return env
Added: experimental/erp5.buildout/src/timerserver/timerserver/__init__.py
URL: http://svn.erp5.org/experimental/erp5.buildout/src/timerserver/timerserver/__init__.py?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/src/timerserver/timerserver/__init__.py (added)
+++ experimental/erp5.buildout/src/timerserver/timerserver/__init__.py [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,10 @@
+from ZServer.datatypes import ServerFactory
+
+class TimerServerFactory(ServerFactory):
+ def __init__(self, section):
+ ServerFactory.__init__(self)
+ self.interval = section.interval
+
+ def create(self):
+ from timerserver.TimerServer import TimerServer
+ return TimerServer(self.module, self.interval)
Added: experimental/erp5.buildout/src/timerserver/timerserver/component.xml
URL: http://svn.erp5.org/experimental/erp5.buildout/src/timerserver/timerserver/component.xml?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/src/timerserver/timerserver/component.xml (added)
+++ experimental/erp5.buildout/src/timerserver/timerserver/component.xml [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,14 @@
+<component>
+ <import package="ZServer" />
+
+ <sectiontype name="timer-server"
+ datatype="timerserver.TimerServerFactory"
+ implements="ZServer.server">
+ <key name="interval" datatype="integer" default="600">
+ <description>
+ Interval in seconds.
+ </description>
+ </key>
+ </sectiontype>
+
+</component>
Added: experimental/erp5.buildout/src/timerserver/version.txt
URL: http://svn.erp5.org/experimental/erp5.buildout/src/timerserver/version.txt?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/src/timerserver/version.txt (added)
+++ experimental/erp5.buildout/src/timerserver/version.txt [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,1 @@
+0.2
Propchange: experimental/erp5.buildout/src/timerserver/version.txt
------------------------------------------------------------------------------
svn:eol-style = native
Added: experimental/erp5.buildout/templates/haproxy.cfg.default
URL: http://svn.erp5.org/experimental/erp5.buildout/templates/haproxy.cfg.default?rev=25895&view=auto
==============================================================================
--- experimental/erp5.buildout/templates/haproxy.cfg.default (added)
+++ experimental/erp5.buildout/templates/haproxy.cfg.default [utf8] Thu Mar 5 16:49:30 2009
@@ -1,0 +1,36 @@
+# this config needs haproxy-1.1.28 or haproxy-1.2.1
+
+global
+ log 127.0.0.1 local0
+ log 127.0.0.1 local1 notice
+ #log loghost local0 info
+ maxconn %(maxconn)s
+ uid 99
+ gid 99
+ daemon
+ #debug
+ #quiet
+
+defaults
+ log global
+ mode http
+ option httplog
+ option dontlognull
+ option redispatch
+ retries %(retries)s
+ maxconn %(maxconn)s
+ contimeout 5000
+ clitimeout 50000
+ srvtimeout 50000
+
+
+listen internal_access localhost:%(port)s
+ cookie SERVERID insert
+ balance %(balance)s
+
+ %(rules)s
+
+ option httpchk %(httpchk)s
+
+ stats uri /haproxy
+ stats realm Global\ statistics
More information about the Erp5-report
mailing list