summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/api/config.rst2
-rw-r--r--pyramid/config.py60
-rw-r--r--pyramid/tests/test_config.py81
3 files changed, 73 insertions, 70 deletions
diff --git a/docs/api/config.rst b/docs/api/config.rst
index 3f37e739c..51666f53f 100644
--- a/docs/api/config.rst
+++ b/docs/api/config.rst
@@ -76,6 +76,8 @@
.. automethod:: set_renderer_globals_factory
+ .. automethod:: add_directive
+
.. automethod:: testing_securitypolicy
.. automethod:: testing_resources
diff --git a/pyramid/config.py b/pyramid/config.py
index 468618c92..b144b8fe3 100644
--- a/pyramid/config.py
+++ b/pyramid/config.py
@@ -2,6 +2,7 @@ import inspect
import os
import re
import sys
+import types
import threading
import traceback
@@ -275,7 +276,6 @@ class Configurator(object):
default_permission=None,
session_factory=None,
default_view_mapper=None,
- extends = None,
autocommit=False,
):
if package is None:
@@ -303,8 +303,9 @@ class Configurator(object):
session_factory=session_factory,
default_view_mapper=default_view_mapper,
)
- if extends:
- self.extend(*extends)
+ if hasattr(registry, '_directives'):
+ for name, directive in registry._directives.items():
+ self.add_directive(name, directive)
def _set_settings(self, mapping):
settings = Settings(mapping or {})
@@ -428,7 +429,6 @@ class Configurator(object):
context = PyramidConfigurationMachine()
registerCommonDirectives(context)
context.registry = self.registry
- context.extends = []
context.autocommit = autocommit
return context
@@ -562,33 +562,29 @@ class Configurator(object):
config = self.__class__.with_context(context)
c(config)
- def extend(self, *callables):
- _context = self._ctx
- if _context is None:
- _context = self._ctx = self._make_context(self.autocommit)
-
- for c in callables:
- c = self.maybe_dotted(c)
- name = c.__name__
- sourcefile = inspect.getsourcefile(c)
- module = inspect.getmodule(c)
- spec = module.__name__ + ':' + name
- if _context.processSpec(spec):
- if hasattr(self, name):
- raise ConfigurationError(
- "Configurator already have a method named %s" % name)
- context = GroupingContextDecorator(_context)
- context.basepath = os.path.dirname(sourcefile)
- context.includepath = _context.includepath + (spec,)
- context.package = package_of(module)
- config = self.__class__.with_context(context)
- wrapped = action_method(c)
- def wrapper(*args, **kwargs):
- return wrapped(config, *args, **kwargs)
- wrapper.__name__ = name
- wrapper.__doc__ = c.__doc__
- self.__dict__[name] = wrapper
- context.extends.append(c)
+ def add_directive(self, name, directive):
+ """
+ Add a directive method to the configurator.
+
+ Framework extenders can add directive methods to a configurator by
+ instructing their users to call ``config.add_directive('somename',
+ 'some.callable')``. This will make ``some.callable`` accessible as
+ ``config.somename``. ``some.callable`` should be a function which
+ accepts ``config`` as a first argument, and arbitrary positional and
+ keyword arguments following. It should use config.action as
+ necessary to perform actions. Directive methods can then be invoked
+ like 'built-in' directives such as ``add_view``, ``add_route``, etc.
+
+ ``add_directive`` does not participate in conflict detection, and
+ later calls to ``add_directive`` will override earlier calls.
+ """
+ c = self.maybe_dotted(directive)
+ if not hasattr(self.registry, '_directives'):
+ self.registry._directives = {}
+ self.registry._directives[name] = c
+ c = action_method(c)
+ m = types.MethodType(c, self, self.__class__)
+ setattr(self, name, m)
@classmethod
def with_context(cls, context):
@@ -597,7 +593,7 @@ class Configurator(object):
:meth:`pyramid.config.Configurator.include` to obtain a configurator
with 'the right' context. Returns a new Configurator instance."""
configurator = cls(registry=context.registry, package=context.package,
- extends=context.extends, autocommit=context.autocommit)
+ autocommit=context.autocommit)
configurator._ctx = context
return configurator
diff --git a/pyramid/tests/test_config.py b/pyramid/tests/test_config.py
index 3fa9b954c..61a665f5a 100644
--- a/pyramid/tests/test_config.py
+++ b/pyramid/tests/test_config.py
@@ -3216,7 +3216,7 @@ class ConfiguratorTests(unittest.TestCase):
for confinst in conflict:
yield confinst[2]
-class TestConfiguratorExtender(unittest.TestCase):
+class TestConfigurator_add_directive(unittest.TestCase):
def setUp(self):
from pyramid.config import Configurator
@@ -3225,9 +3225,8 @@ class TestConfiguratorExtender(unittest.TestCase):
def test_extend_with_dotted_name(self):
from pyramid import tests
config = self.config
- context_before = config._make_context()
- config._ctx = context_before
- config.extend('pyramid.tests.test_config.dummy_extend')
+ config.add_directive(
+ 'dummy_extend', 'pyramid.tests.test_config.dummy_extend')
self.assert_(hasattr(config, 'dummy_extend'))
config.dummy_extend('discrim')
context_after = config._ctx
@@ -3237,18 +3236,12 @@ class TestConfiguratorExtender(unittest.TestCase):
context_after.actions[0][:3],
('discrim', None, tests),
)
- self.assertEqual(context_after.basepath, None)
- self.assertEqual(context_after.includepath, ())
- self.failUnless(context_after is context_before)
- self.assertEqual(len(context_before.extends), 1)
- self.assertEqual(context_before.extends, context_after.extends)
def test_extend_with_python_callable(self):
from pyramid import tests
config = self.config
- context_before = config._make_context()
- config._ctx = context_before
- config.extend(dummy_extend)
+ config.add_directive(
+ 'dummy_extend', dummy_extend)
self.assert_(hasattr(config, 'dummy_extend'))
config.dummy_extend('discrim')
context_after = config._ctx
@@ -3258,38 +3251,47 @@ class TestConfiguratorExtender(unittest.TestCase):
context_after.actions[0][:3],
('discrim', None, tests),
)
- self.assertEqual(context_after.basepath, None)
- self.assertEqual(context_after.includepath, ())
- self.failUnless(context_after is context_before)
- self.assertEqual(len(context_before.extends), 1)
- self.assertEqual(context_before.extends, context_after.extends)
- def test_extend_conflict(self):
- from pyramid.exceptions import ConfigurationError
+ def test_extend_same_name_doesnt_conflict(self):
config = self.config
- context_before = config._make_context()
- config._ctx = context_before
- config.extend(dummy_extend)
- self.assertRaises(ConfigurationError, config.extend, 'pyramid.tests.dummy_extend')
+ config.add_directive(
+ 'dummy_extend', dummy_extend)
+ config.add_directive(
+ 'dummy_extend', dummy_extend2)
+ self.assert_(hasattr(config, 'dummy_extend'))
+ config.dummy_extend('discrim')
+ context_after = config._ctx
+ actions = context_after.actions
+ self.assertEqual(len(actions), 1)
+ self.assertEqual(
+ context_after.actions[0][:3],
+ ('discrim', None, config.registry),
+ )
- def test_extend_no_conflict_with_two_instance(self):
- from pyramid.config import Configurator
+ def test_extend_action_method_successful(self):
+ from zope.configuration.config import ConfigurationConflictError
config = self.config
- config.extend(dummy_extend)
- config2 = Configurator()
- config2.extend(dummy_extend)
- self.assertEqual(config._ctx.extends, config2._ctx.extends)
+ config.add_directive(
+ 'dummy_extend', dummy_extend)
+ config.dummy_extend('discrim')
+ config.dummy_extend('discrim')
+ self.assertRaises(ConfigurationConflictError, config.commit)
- def test_extend_after_with_package(self):
- from pyramid import tests
+ def test_directive_persists_across_configurator_creations(self):
+ from zope.configuration.config import GroupingContextDecorator
config = self.config
- context_before = config._make_context()
- config._ctx = context_before
- config.extend(dummy_extend)
- config2 = config.with_package('pyramid.tests')
- self.assert_(hasattr(config2, 'dummy_extend'))
- self.assertEqual(len(config._ctx.extends), 1)
- self.assertEqual(config._ctx.extends, config2._ctx.extends)
+ config.add_directive('dummy_extend', dummy_extend)
+ context = config._make_context(autocommit=False)
+ context = GroupingContextDecorator(context)
+ config2 = config.with_context(context)
+ config2.dummy_extend('discrim')
+ context_after = config2._ctx
+ actions = context_after.actions
+ self.assertEqual(len(actions), 1)
+ self.assertEqual(
+ context_after.actions[0][:3],
+ ('discrim', None, config2.package),
+ )
class TestViewDeriver(unittest.TestCase):
def setUp(self):
@@ -5022,3 +5024,6 @@ def dummy_include(config):
def dummy_extend(config, discrim):
config.action(discrim, None, config.package)
+def dummy_extend2(config, discrim):
+ config.action(discrim, None, config.registry)
+