[Zope-Checkins] SVN: Products.Five/branches/1.4/ Added preliminary
support for being able to register any python package as a
zope2 product (so it shows up in Control Panel).
Rocky Burt
rocky at serverzen.com
Sun Mar 12 14:42:15 EST 2006
Log message for revision 65927:
Added preliminary support for being able to register any python package as a zope2 product (so it shows up in Control Panel).
Changed:
_U Products.Five/branches/1.4/
U Products.Five/branches/1.4/__init__.py
U Products.Five/branches/1.4/fiveconfigure.py
A Products.Five/branches/1.4/pythonproducts.py
U Products.Five/branches/1.4/tests/test_registerpackage.py
A Products.Five/branches/1.4/tests/testing/pythonproduct2/
A Products.Five/branches/1.4/tests/testing/pythonproduct2/__init__.py
-=-
Property changes on: Products.Five/branches/1.4
___________________________________________________________________
Name: svk:merge
- 98c0701e-2f07-0410-a1a0-c1bc72243522:/local/Products.Five/branches/1.4:8741
+ 98c0701e-2f07-0410-a1a0-c1bc72243522:/local/Products.Five/branches/1.4:8742
Modified: Products.Five/branches/1.4/__init__.py
===================================================================
--- Products.Five/branches/1.4/__init__.py 2006-03-12 19:41:31 UTC (rev 65926)
+++ Products.Five/branches/1.4/__init__.py 2006-03-12 19:42:14 UTC (rev 65927)
@@ -19,6 +19,7 @@
from Globals import INSTANCE_HOME
import zcml
+import pythonproducts
# public API provided by Five
# usage: from Products.Five import <something>
@@ -26,4 +27,5 @@
from skin.standardmacros import StandardMacros
def initialize(context):
+ pythonproducts.setupPythonProducts(context)
zcml.load_site()
Modified: Products.Five/branches/1.4/fiveconfigure.py
===================================================================
--- Products.Five/branches/1.4/fiveconfigure.py 2006-03-12 19:41:31 UTC (rev 65926)
+++ Products.Five/branches/1.4/fiveconfigure.py 2006-03-12 19:42:14 UTC (rev 65927)
@@ -23,6 +23,8 @@
import warnings
import App.config
+from App.Product import initializeProduct
+from App.ProductContext import ProductContext
import Products
from zLOG import LOG, ERROR
@@ -41,6 +43,7 @@
from traversable import Traversable
from bridge import fromZ2Interface
from browser.metaconfigure import page
+import pythonproducts
debug_mode = App.config.getConfiguration().debug_mode
@@ -258,13 +261,26 @@
args = (class_, meta_type, permission, addview, icon, global_)
)
-# clean up code
-
def _registerPackage(module_, initFunc=None):
"""Registers the given python package as a Zope 2 style product
"""
+ if not hasattr(module_, '__path__'):
+ raise ValueError("Must be a package and the " \
+ "package must be filesystem based")
+
+ product = initializeProduct(module_,
+ module_.__name__,
+ module_.__path__[0],
+ pythonproducts._zope_app)
+ product.package_name = module_.__name__
+
+ if initFunc is not None:
+ newContext = ProductContext(product, pythonproducts._zope_app, module_)
+ initFunc(newContext)
+
+
def registerPackage(_context, package, initialize=None):
"""ZCML directive function for registering a python package product
"""
@@ -275,6 +291,7 @@
args = (package,initialize)
)
+# clean up code
def killMonkey(class_, name, fallback, attr=None):
"""Die monkey, die!"""
Added: Products.Five/branches/1.4/pythonproducts.py
===================================================================
--- Products.Five/branches/1.4/pythonproducts.py 2006-03-12 19:41:31 UTC (rev 65926)
+++ Products.Five/branches/1.4/pythonproducts.py 2006-03-12 19:42:14 UTC (rev 65927)
@@ -0,0 +1,147 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Setup necessary monkey patches and other related logic for using
+regular python packages for zope2 products
+
+$Id$
+"""
+__author__ = "Rocky Burt"
+
+import os
+import types
+
+import Products
+from App.Product import initializeProduct
+from App.ProductContext import ProductContext
+
+_zope_app = None
+
+def setupPythonProducts(appOrContext):
+ """Initialize the python-packages-as-products logic
+ """
+
+ from OFS.Application import Application
+
+ if isinstance(appOrContext, Application):
+ _zope_app = appOrContext
+ else:
+ _zope_app = appOrContext._ProductContext__app
+
+ global _zope_app
+ #applyPatches(_zope_app)
+
+
+def applyPatches(app):
+ """Apply necessary monkey patches to force Zope 2 to be capable of
+ handling "products" that are not necessarily located under the Products
+ package. Ultimately all functionality provided by these patches should
+ be folded into Zope 2 core.
+ """
+
+ patch_ProductDispatcher__bobo_traverse__(app)
+ patch_externalmethod(app)
+
+
+# BEGIN MONKEY PATCHES
+# Most of these monkey patches were repurposed from the code I
+# wrote for Basket - Rocky
+
+def product_packages(app):
+ """Returns all product packages including the regularly defined
+ zope2 packages and those without the Products namespace package.
+ """
+
+ old_product_packages = {}
+ for x in dir(Products):
+ m = getattr(Products, x)
+ if isinstance(m, types.ModuleType):
+ old_product_packages[x] = m
+
+ packages = {}
+ products = app.Control_Panel.Products
+ for product_id in products.objectIds():
+ product = products[product_id]
+ if hasattr(product, 'package_name'):
+ packages[product_id] = __import__(product.package_name)
+ elif old_product_packages.has_key(product_id):
+ packages[product_id] = old_product_packages[product_id]
+
+ return packages
+
+def patch_ProductDispatcher__bobo_traverse__(app):
+ """Currently, z2's App.FactoryDispatcher.ProductDispatcher only checks
+ the Products module for products to look up existing factory dispatchers
+ on. This needs to be fixed to look in all enabled product packages
+ as well.
+ """
+
+ from App.FactoryDispatcher import FactoryDispatcher, ProductDispatcher
+ _original__bobo_traverse__ = ProductDispatcher.__bobo_traverse__
+ global _original__bobo_traverse__
+
+ def __bobo_traverse__(self, REQUEST, name):
+ product=self.aq_acquire('_getProducts')()._product(name)
+
+ # Try to get a custom dispatcher from a Python product
+ productPkgs = product_packages(app)
+ dispatcher_class=getattr(
+ productPkgs.get(name, None),
+ '__FactoryDispatcher__',
+ FactoryDispatcher)
+
+ dispatcher=dispatcher_class(product, self.aq_parent, REQUEST)
+ return dispatcher.__of__(self)
+
+ ProductDispatcher.__bobo_traverse__ = __bobo_traverse__
+
+
+def patch_externalmethod(app):
+ """In an effort to make External Methods work with regular python
+ packages, this function replaces App.Extensions.getPath with a custom
+ getPath function. See the getPath doc string for extra details.
+ """
+
+ from App import Extensions, FactoryDispatcher
+ from Products.ExternalMethod import ExternalMethod
+
+ _originalGetPath = Extensions.getPath
+ global _originalGetPath
+
+ def getPath(prefix, name, checkProduct=1, suffixes=('',)):
+ """Make sure to check paths of all registered product packages.
+ """
+
+ result = _originalGetPath(prefix, name, checkProduct, suffixes)
+ if result is not None:
+ return result
+
+ try:
+ l = name.find('.')
+ if l > 0:
+ realName = name[l + 1:]
+ toplevel = name[:l]
+
+ m = __import__(toplevel)
+
+ d = os.path.join(m.__path__[0], prefix, realName)
+ for s in suffixes:
+ if s: s="%s.%s" % (d, s)
+ else: s=d
+ if os.path.exists(s):
+ return s
+ except:
+ pass
+
+ Extensions.getPath = getPath
+ ExternalMethod.getPath = getPath
Modified: Products.Five/branches/1.4/tests/test_registerpackage.py
===================================================================
--- Products.Five/branches/1.4/tests/test_registerpackage.py 2006-03-12 19:41:31 UTC (rev 65926)
+++ Products.Five/branches/1.4/tests/test_registerpackage.py 2006-03-12 19:42:14 UTC (rev 65927)
@@ -28,9 +28,13 @@
>>> import Products
>>> import Products.Five
>>> from Products.Five import zcml
+ >>> from Products.Five import pythonproducts
+ >>> zcml.load_config('meta.zcml', Products.Five)
+ >>> pythonproducts.setupPythonProducts(app)
- Use the five:registerPackage directive::
-
+ Make sure a python package with no initialize (even though one
+ is specified) will fail::
+
>>> configure_zcml = '''
... <configure
... xmlns="http://namespaces.zope.org/zope"
@@ -38,11 +42,38 @@
... i18n_domain="foo">
... <five:registerPackage
... package="Products.Five.tests.testing.pythonproduct1"
+ ... initialize="Products.Five.tests.testing.pythonproduct1.initialize"
... />
... </configure>'''
- >>> zcml.load_config('meta.zcml', Products.Five)
>>> zcml.load_string(configure_zcml)
+ Traceback (most recent call last):
+ ...
+ ZopeXMLConfigurationError: ...
+ ConfigurationError: ('...pythonproduct1 has no global initialize')
+ Make sure a python package with a valid initialize gets its
+ initialize function called::
+
+ >>> configure_zcml = '''
+ ... <configure
+ ... xmlns="http://namespaces.zope.org/zope"
+ ... xmlns:five="http://namespaces.zope.org/five"
+ ... i18n_domain="foo">
+ ... <five:registerPackage
+ ... package="Products.Five.tests.testing.pythonproduct2"
+ ... initialize="Products.Five.tests.testing.pythonproduct2.initialize"
+ ... />
+ ... </configure>'''
+ >>> zcml.load_string(configure_zcml)
+ pythonproduct2 initialized
+
+ Test to see if the pythonproduct2 python package actually gets setup
+ as a zope2 product in the Control Panel.
+
+ >>> productListing = app.Control_Panel.Products.objectIds()
+ >>> 'Products.Five.tests.testing.pythonproduct2' in productListing
+ True
+
Clean up:
>>> tearDown()
Added: Products.Five/branches/1.4/tests/testing/pythonproduct2/__init__.py
===================================================================
--- Products.Five/branches/1.4/tests/testing/pythonproduct2/__init__.py 2006-03-12 19:41:31 UTC (rev 65926)
+++ Products.Five/branches/1.4/tests/testing/pythonproduct2/__init__.py 2006-03-12 19:42:14 UTC (rev 65927)
@@ -0,0 +1,4 @@
+
+
+def initialize(context):
+ print "pythonproduct2 initialized"
More information about the Zope-Checkins
mailing list