Commit 6ff963e8 authored by Tres Seaver's avatar Tres Seaver

LP #143232: Added option to 'zope.conf' to specify an extensions directory.

o This directory is searched for 'App.Extensions' lookups.  Thanks to Rodrigo
  Senra for the patch.
parent 6dbe74b5
...@@ -100,6 +100,10 @@ Restructuring ...@@ -100,6 +100,10 @@ Restructuring
Features Added Features Added
++++++++++++++ ++++++++++++++
- LP #143232: Added option to 'zope.conf' to specify an additional directory
to be searched for 'App.Extensions' lookups. Thanks to Rodrigo Senra for
the patch.
- Integrated the Products.signalstack / z3c.deadlockdebugger packages. You can - Integrated the Products.signalstack / z3c.deadlockdebugger packages. You can
now send a SIGUSR1 signal to a Zope process and get a stack trace of all now send a SIGUSR1 signal to a Zope process and get a stack trace of all
threads printed out on the console. This works even if all threads are stuck. threads printed out on the console. This works even if all threads are stuck.
......
...@@ -106,6 +106,11 @@ def getPath(prefix, name, checkProduct=1, suffixes=('',), cfg=None): ...@@ -106,6 +106,11 @@ def getPath(prefix, name, checkProduct=1, suffixes=('',), cfg=None):
import App.config import App.config
cfg = App.config.getConfiguration() cfg = App.config.getConfiguration()
if prefix == "Extensions" and getattr(cfg, 'extensions', None) is not None:
found = _getPath(cfg.extensions, '', name, suffixes)
if found is not None:
return found
locations = [cfg.instancehome] locations = [cfg.instancehome]
softwarehome = getattr(cfg, 'softwarehome', None) softwarehome = getattr(cfg, 'softwarehome', None)
......
...@@ -195,12 +195,29 @@ class Test_getPath(_TempdirBase, unittest.TestCase): ...@@ -195,12 +195,29 @@ class Test_getPath(_TempdirBase, unittest.TestCase):
swext = self._makeTempExtension(name=None, dir=swdir) swext = self._makeTempExtension(name=None, dir=swdir)
swfqn = self._makeFile(swext, 'extension.py') swfqn = self._makeFile(swext, 'extension.py')
cfg = self._makeConfig(instancehome=instdir, cfg = self._makeConfig(instancehome=instdir,
softwarehome=swdir, softwarehome=swdir,
) )
path = self._callFUT('Extensions', 'extension', checkProduct=0, path = self._callFUT('Extensions', 'extension', checkProduct=0,
suffixes=('py',), cfg=cfg) suffixes=('py',), cfg=cfg)
self.assertEqual(path, instfqn) self.assertEqual(path, instfqn)
def test_w_cfg_extensions(self):
cfgdir = self._makeTempdir()
cfgfqn = self._makeFile(cfgdir, 'extension.py')
instdir = self._makeTempdir()
instext = self._makeTempExtension(name=None, dir=instdir)
instfqn = self._makeFile(instext, 'extension.py')
swdir = self._makeTempdir()
swext = self._makeTempExtension(name=None, dir=swdir)
swfqn = self._makeFile(swext, 'extension.py')
cfg = self._makeConfig(extensions=cfgdir,
instancehome=instdir,
softwarehome=swdir,
)
path = self._callFUT('Extensions', 'extension', checkProduct=0,
suffixes=('py',), cfg=cfg)
self.assertEqual(path, cfgfqn)
def test_not_found_in_instancehome(self): def test_not_found_in_instancehome(self):
import os import os
instdir = self._makeTempdir() instdir = self._makeTempdir()
...@@ -258,6 +275,27 @@ class Test_getPath(_TempdirBase, unittest.TestCase): ...@@ -258,6 +275,27 @@ class Test_getPath(_TempdirBase, unittest.TestCase):
suffixes=('py',), cfg=cfg) suffixes=('py',), cfg=cfg)
self.assertEqual(path, subpkgfqn) self.assertEqual(path, subpkgfqn)
"""
Index: lib/python/App/Extensions.py
===================================================================
--- lib/python/App/Extensions.py (revision 28473)
+++ lib/python/App/Extensions.py (working copy)
@@ -87,8 +87,14 @@
r = _getPath(product_dir, os.path.join(p, prefix), n, suffixes)
if r is not None: return r
+
import App.config
cfg = App.config.getConfiguration()
+
+ if (prefix=="Extensions") and (cfg.extensions is not None):
+ r=_getPath(cfg.extensions, '', name, suffixes)
+ if r is not None: return r
+
sw=os.path.dirname(os.path.dirname(cfg.softwarehome))
for home in (cfg.instancehome, sw):
r=_getPath(home, prefix, name, suffixes)
"""
class Test_getObject(_TempdirBase, unittest.TestCase): class Test_getObject(_TempdirBase, unittest.TestCase):
......
...@@ -47,7 +47,7 @@ manage_addExternalMethodForm=DTMLFile('dtml/methodAdd', globals()) ...@@ -47,7 +47,7 @@ manage_addExternalMethodForm=DTMLFile('dtml/methodAdd', globals())
def manage_addExternalMethod(self, id, title, module, function, REQUEST=None): def manage_addExternalMethod(self, id, title, module, function, REQUEST=None):
"""Add an external method to a folder """Add an external method to a folder
Un addition to the standard object-creation arguments, In addition to the standard object-creation arguments,
'id' and title, the following arguments are defined: 'id' and title, the following arguments are defined:
function -- The name of the python function. This can be a function -- The name of the python function. This can be a
...@@ -56,11 +56,15 @@ def manage_addExternalMethod(self, id, title, module, function, REQUEST=None): ...@@ -56,11 +56,15 @@ def manage_addExternalMethod(self, id, title, module, function, REQUEST=None):
module -- The name of the file containing the function module -- The name of the file containing the function
definition. definition.
The module normally resides in the 'Extensions' The module normally resides in the 'Extensions' directory.
directory, however, the file name may have a prefix of
If the zope.conf directive 'extensions' was overriden, then
it will specify where modules should reside.
However, the file name may have a prefix of
'product.', indicating that it should be found in a product 'product.', indicating that it should be found in a product
directory. directory.
For example, if the module is: 'ACMEWidgets.foo', then an For example, if the module is: 'ACMEWidgets.foo', then an
attempt will first be made to use the file attempt will first be made to use the file
'lib/python/Products/ACMEWidgets/Extensions/foo.py'. If this 'lib/python/Products/ACMEWidgets/Extensions/foo.py'. If this
...@@ -84,7 +88,8 @@ class ExternalMethod(Item, Persistent, Explicit, ...@@ -84,7 +88,8 @@ class ExternalMethod(Item, Persistent, Explicit,
The function is defined in an external file. This file is treated The function is defined in an external file. This file is treated
like a module, but is not a module. It is not imported directly, like a module, but is not a module. It is not imported directly,
but is rather read and evaluated. The file must reside in the but is rather read and evaluated. The file must reside in the
'Extensions' subdirectory of the Zope installation, or in an 'Extensions' subdirectory of the Zope installation, or in the directory
specified by the 'extensions' directive in zope.conf, or in an
'Extensions' subdirectory of a product directory. 'Extensions' subdirectory of a product directory.
Due to the way ExternalMethods are loaded, it is not *currently* Due to the way ExternalMethods are loaded, it is not *currently*
...@@ -131,7 +136,7 @@ class ExternalMethod(Item, Persistent, Explicit, ...@@ -131,7 +136,7 @@ class ExternalMethod(Item, Persistent, Explicit,
"""Change the external method """Change the external method
See the description of manage_addExternalMethod for a See the description of manage_addExternalMethod for a
descriotion of the arguments 'module' and 'function'. description of the arguments 'module' and 'function'.
Note that calling 'manage_edit' causes the "module" to be Note that calling 'manage_edit' causes the "module" to be
effectively reloaded. This is useful during debugging to see effectively reloaded. This is useful during debugging to see
......
...@@ -48,7 +48,8 @@ class ExternalMethod: ...@@ -48,7 +48,8 @@ class ExternalMethod:
The function is defined in an external file. This file is treated The function is defined in an external file. This file is treated
like a module, but is not a module. It is not imported directly, like a module, but is not a module. It is not imported directly,
but is rather read and evaluated. The file must reside in the but is rather read and evaluated. The file must reside in the
'Extensions' subdirectory of the Zope installation, or in an 'Extensions' subdirectory of the Zope installation, or reside in
the path specified by 'extensions' directive in zope.conf, or in an
'Extensions' subdirectory of a product directory. 'Extensions' subdirectory of a product directory.
Due to the way ExternalMethods are loaded, it is not *currently* Due to the way ExternalMethods are loaded, it is not *currently*
......
...@@ -343,6 +343,14 @@ ...@@ -343,6 +343,14 @@
<metadefault>$instancehome/Products</metadefault> <metadefault>$instancehome/Products</metadefault>
</multikey> </multikey>
<key name="extensions" datatype="existing-directory">
<description>
This overrides the path to the Extensions directory.
</description>
<metadefault>$instancehome/Extensions</metadefault>
</key>
<multikey name="path" datatype="string"> <multikey name="path" datatype="string">
<description> <description>
This specifies additional paths directories which are inserted into This specifies additional paths directories which are inserted into
......
...@@ -91,6 +91,21 @@ instancehome $INSTANCE ...@@ -91,6 +91,21 @@ instancehome $INSTANCE
# products /home/chrism/projects/myproducts # products /home/chrism/projects/myproducts
# Directive: extensions
#
# Description:
# Name of a directory that contains additional "extensions"
# (implementations of ExternalMethods or catalog brains). This
# directive The directory identified will be searched before the
# 'Extensions' directory of the 'instancehome'.
#
# Default: unset
#
# Example:
#
# extensions /home/chrism/extensions
# Directive: environment # Directive: environment
# #
# Description: # Description:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment