diff options
| -rw-r--r-- | CHANGES.txt | 25 | ||||
| -rw-r--r-- | docs/narr/myproject/myproject.ini | 3 | ||||
| -rw-r--r-- | docs/narr/myproject/myproject/configure.zcml | 4 | ||||
| -rw-r--r-- | docs/narr/myproject/myproject/run.py | 12 | ||||
| -rw-r--r-- | docs/narr/myproject/setup.py | 4 | ||||
| -rw-r--r-- | docs/narr/project.rst | 95 | ||||
| -rw-r--r-- | repoze/bfg/__init__.py | 1 | ||||
| -rw-r--r-- | repoze/bfg/meta.zcml | 6 | ||||
| -rw-r--r-- | repoze/bfg/paster_template/+package+.ini_tmpl | 5 | ||||
| -rw-r--r-- | repoze/bfg/paster_template/+package+/run.py_tmpl | 12 | ||||
| -rw-r--r-- | repoze/bfg/paster_template/setup.py_tmpl | 4 | ||||
| -rw-r--r-- | repoze/bfg/registry.py | 24 | ||||
| -rw-r--r-- | repoze/bfg/router.py | 19 | ||||
| -rw-r--r-- | repoze/bfg/sampleapp/configure.zcml | 3 | ||||
| -rw-r--r-- | repoze/bfg/sampleapp/run.py | 2 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_registry.py | 22 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_zcml.py | 31 | ||||
| -rw-r--r-- | repoze/bfg/zcml.py | 27 |
18 files changed, 147 insertions, 152 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 4717cfce2..6325e6c32 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,13 +1,22 @@ Next release - - Add ``<bfg:settings>`` directive. This directive currently allows - only one attribute: ``reload_templates``. If e.g.:: - - <bfg:settings reload_templates="true"/> - - is in your application's ZCML, you will not need to restart the - appserver in order for ``z3c.pt`` or XSLT template changes to be - detected and displayed. + - Generated application differences: ``make_app`` entry point + renamed to ``app`` in order to have a different name than the bfg + function of the same name, to prevent confusion. + + - Add "options" processing to bfg's ``make_app`` to support runtime + options. A new API function named ``get_options`` was added to + the registry module. This function is typically used in an + application's ``app`` entry point. The Paste config file section + for the app can now supply the ``reload_templates`` option, which, + if true, will prevent the need to restart the appserver in order + for ``z3c.pt`` or XSLT template changes to be detected. + + - Use only the module name in generated project's "test_suite" (run + all tests found in the package). + + - Default port for generated apps changed from 5432 to 6543 + (Postgres default port is 6543). 0.3.0 diff --git a/docs/narr/myproject/myproject.ini b/docs/narr/myproject/myproject.ini index 21292d14c..318c2214a 100644 --- a/docs/narr/myproject/myproject.ini +++ b/docs/narr/myproject/myproject.ini @@ -2,7 +2,8 @@ debug = true [app:main] -use = egg:myproject#make_app +use = egg:myproject#app +reload_templates = true [server:main] use = egg:PasteScript#cherrypy diff --git a/docs/narr/myproject/myproject/configure.zcml b/docs/narr/myproject/myproject/configure.zcml index 6cd692784..fdae69cb3 100644 --- a/docs/narr/myproject/myproject/configure.zcml +++ b/docs/narr/myproject/myproject/configure.zcml @@ -5,10 +5,6 @@ <!-- this directive must be included for the view declarations to work --> <include package="repoze.bfg" /> - <!-- this directive indicates that changes to templates should show up - immediately --> - <bfg:settings reload_templates="true"/> - <bfg:view for=".models.IMyModel" view=".views.my_view" diff --git a/docs/narr/myproject/myproject/run.py b/docs/narr/myproject/myproject/run.py index 9592ce441..f60a4023f 100644 --- a/docs/narr/myproject/myproject/run.py +++ b/docs/narr/myproject/myproject/run.py @@ -1,13 +1,13 @@ -def make_app(global_config, **kw): +from repoze.bfg import make_app +from repoze.bfg import get_options + +def app(global_config, **kw): # paster app config callback - from repoze.bfg import make_app from myproject.models import get_root import myproject - app = make_app(get_root, myproject) + app = make_app(get_root, myproject, options=get_options(kw)) return app if __name__ == '__main__': from paste import httpserver - app = make_app(None) - httpserver.serve(app, host='0.0.0.0', port='5432') - + httpserver.serve(app(None), host='0.0.0.0', port='6543') diff --git a/docs/narr/myproject/setup.py b/docs/narr/myproject/setup.py index 7797776c7..2d9ccda1d 100644 --- a/docs/narr/myproject/setup.py +++ b/docs/narr/myproject/setup.py @@ -35,10 +35,10 @@ setup(name='myproject', tests_require=[ 'repoze.bfg', ], - test_suite="myproject.tests", + test_suite="myproject", entry_points = """\ [paste.app_factory] - make_app = myproject.run:make_app + app = myproject.run:app """ ) diff --git a/docs/narr/project.rst b/docs/narr/project.rst index 6995f5e20..a050f8824 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -125,10 +125,10 @@ Here's sample output from a run:: $ paster serve myproject/myproject.ini Starting server in PID 16601. - serving on 0.0.0.0:5432 view at http://127.0.0.1:5432 + serving on 0.0.0.0:6543 view at http://127.0.0.1:6543 By default, generated :mod:`repoze.bfg` applications will listen on -port 5432. +port 6543. .. note:: During development, it's often useful to run ``paster serve`` using its ``--reload`` option. When any Python module your @@ -139,7 +139,7 @@ port 5432. Viewing the Application ----------------------- -Visit *http://localhost:5432/* in your browser. You will see:: +Visit ``http://localhost:6542/`` in your browser. You will see:: Welcome to myproject @@ -238,9 +238,8 @@ convention signifying that it the default application. The ``use`` setting is required in the ``[app:main]`` section. The ``use`` setting points at a :term:`setuptools` "entry point" named -``myproject#make_app`` (the ``egg:`` prefix in -``egg:myproject#make_app`` indicates that this is an entry point -specifier). +``myproject#app`` (the ``egg:`` prefix in ``egg:myproject#app`` +indicates that this is an entry point specifier). .. note:: @@ -250,34 +249,42 @@ specifier). ``setup.py`` points at a string which looks a lot like an ``.ini`` file. This string representation of an ``.ini`` file has a section named ``[paste.app_factory]``. Within this section, there is a key - named ``make_app`` (the entry point name) which has a value - ``myproject.run:make_app``. The *key* ``make_app`` is what our - ``egg:myproject#make_app`` value of the ``use`` section in our - config file is pointing at. The value represents a Python - "dotted-name" path, which refers to a callable in our ``myproject`` - package's ``run.py`` module. + named ``app`` (the entry point name) which has a value + ``myproject.run:app``. The *key* ``app`` is what our + ``egg:myproject#app`` value of the ``use`` section in our config + file is pointing at. The value represents a Python "dotted-name" + path, which refers to a callable in our ``myproject`` package's + ``run.py`` module. In English, this entry point can thus be referred to as a "Paste application factory in the ``myproject`` package which has the - entry point named ``make_app`` where the entry point refers to a - ``make_app`` function in the ``mypackage.run`` module". If indeed - if you open up the ``run.py`` module generated within the - ``myproject`` package, you'll see a ``make_app`` function. This is - the function called :term:`PasteDeploy` when the ``paster serve`` - command is invoked against our application. It accepts a global - configuration object and *returns* an instance of our application. + entry point named ``app`` where the entry point refers to a ``app`` + function in the ``mypackage.run`` module". If indeed if you open + up the ``run.py`` module generated within the ``myproject`` + package, you'll see a ``app`` function. This is the function + called :term:`PasteDeploy` when the ``paster serve`` command is + invoked against our application. It accepts a global configuration + object and *returns* an instance of our application. The ``use`` setting is the only setting required in the ``[app:main]`` section unless you've changed the callable referred to by the -``myproject#make_app`` entry point to accept more arguments: other -settings you add to this section are passed as keywords arguments to -the callable represented by this entry point (``make_app`` in our -``run.py`` module). You can provide startup-time configuration -parameters to your application by requiring more settings in this -section. +``myproject#app`` entry point to accept more arguments: other settings +you add to this section are passed as keywords arguments to the +callable represented by this entry point (``app`` in our ``run.py`` +module). You can provide startup-time configuration parameters to +your application by requiring more settings in this section. + +The ``reload_templates`` setting in the ``[app:main]`` section is a +:mod:`repoze.bfg`-specific setting which is passed into the framework. +If it exists, and is ``true``, :term:`z3c.pt` and XSLT template +changes will not require an application restart to be detected. + +.. warning:: The ``reload_templates`` option should be turned off for + production applications, as template rendering is slowed when it is + turned on. The ``[server:main]`` section of the configuration file configures a -WSGI server which listens on port 5432. It is configured to listen on +WSGI server which listens on port 6543. It is configured to listen on all interfaces (``0.0.0.0``), and is configured to use four threads for our application. @@ -340,9 +347,11 @@ those files are checked into version control). ``zip_safe` indicates that this package is not safe to ship as a zipped egg (it will unpack as a directory, which is more convenient). ``install_requires`` and ``tests_require`` indicate that this package depends on the -``repoze.bfg`` package. ``test_suite`` points at the unittest module -for our application. We examined ``entry_points`` in our discussion -of the ``myproject.ini`` file. +``repoze.bfg`` package. ``test_suite`` points at the package for our +application, which means all tests found in the package will be +installed. We examined ``entry_points`` in our discussion of the +``myproject.ini`` file; this file defines the ``app`` entry point that +represent's our project's application. Usually you only need to think about the contents of the ``setup.py`` file when distributing your application to other people, or when @@ -416,10 +425,7 @@ registry`. It looks like so: #. Line 6 initializes :mod:`repoze.bfg`-specific configuration directives by including it as a package. -#. Line 10 tells :mod:`repoze.bfg` to detect changes made to - ``z3c.pt`` and XSLT templates immediately. - -#. Lines 12-15 register a single view. It is ``for`` model objects +#. Lines 8-11 register a single view. It is ``for`` model objects that support the IMyModel interface. The ``view`` attribute points at a Python function that does all the work for this view. Note that the values of both the ``for`` attribute and the ``view`` @@ -454,16 +460,15 @@ in the model, and the HTML given back to the browser. ``Request`` class representing the browser's request to our server. #. The view renders a :term:`template` and returns the result as the - :term:`response`. Note that because our ``configure.zcml`` has a - ``bfg:settings`` directive indicating that templates should be - reloaded when they change, you won't need to restart the + :term:`response`. Note that because our ``myproject.ini`` has a + ``reload_templates = true`` directive indicating that templates + should be reloaded when they change, you won't need to restart the application server to see changes you make to templates. During development, this is handy. If this directive had been ``false`` (or if the directive did not exist), you would need to restart the application server for each template change. For production - applications, you should set your ``bfg:settings`` - ``reload_templates`` to ``false`` to increase the speed at which - templates may be rendered. + applications, you should set your project's ``reload_templates`` to + ``false`` to increase the speed at which templates may be rendered. .. note:: @@ -526,14 +531,16 @@ without the PasteDeploy configuration file: .. literalinclude:: myproject/myproject/run.py :linenos: -#. Lines 1 - 7 define a function that returns a :mod:`repoze.bfg` - Router application from :ref:`router_module` . This is meant to be - called by the :term:`PasteDeploy` framework as a result of running +#. Lines 1 - 2 import functions from :mod:`repoze.bfg` that we use later. + +#. Lines 4-9 define a function that returns a :mod:`repoze.bfg` Router + application from :ref:`router_module` . This is meant to be called + by the :term:`PasteDeploy` framework as a result of running ``paster serve``. -#. Lines 9 - 12 allow this file to serve optionally as a shortcut for +#. Lines 11 - 13 allow this file to serve optionally as a shortcut for executing our program if the ``run.py`` file is executed directly. - It starts our application under a web server on port 5432. + It starts our application under a web server on port 6543. ``templates/mytemplate.pt`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/repoze/bfg/__init__.py b/repoze/bfg/__init__.py index e677d851b..e603a8fac 100644 --- a/repoze/bfg/__init__.py +++ b/repoze/bfg/__init__.py @@ -1,5 +1,6 @@ try: from repoze.bfg.router import make_app # for import elsewhere + from repoze.bfg.registry import get_options # for import elsewhere except ImportError: # don't try so hard that we cause setup.py test to fail when the # right modules aren't installed. diff --git a/repoze/bfg/meta.zcml b/repoze/bfg/meta.zcml index 91b4f40e8..72e963ae6 100644 --- a/repoze/bfg/meta.zcml +++ b/repoze/bfg/meta.zcml @@ -10,12 +10,6 @@ handler=".zcml.view" /> - <meta:directive - name="settings" - schema=".zcml.ISettingsDirective" - handler=".zcml.settings" - /> - </meta:directives> </configure> diff --git a/repoze/bfg/paster_template/+package+.ini_tmpl b/repoze/bfg/paster_template/+package+.ini_tmpl index 9bb03718c..2d6c4d0a2 100644 --- a/repoze/bfg/paster_template/+package+.ini_tmpl +++ b/repoze/bfg/paster_template/+package+.ini_tmpl @@ -2,10 +2,11 @@ debug = true [app:main] -use = egg:{{project}}#make_app +use = egg:{{project}}#app +reload_templates = true [server:main] use = egg:PasteScript#cherrypy host = 0.0.0.0 -port = 5432 +port = 6543 numthreads = 4 diff --git a/repoze/bfg/paster_template/+package+/run.py_tmpl b/repoze/bfg/paster_template/+package+/run.py_tmpl index f8228d30a..b3c3d1ed7 100644 --- a/repoze/bfg/paster_template/+package+/run.py_tmpl +++ b/repoze/bfg/paster_template/+package+/run.py_tmpl @@ -1,13 +1,13 @@ -def make_app(global_config, **kw): +from repoze.bfg import make_app +from repoze.bfg import get_options + +def app(global_config, **kw): # paster app config callback - from repoze.bfg import make_app from {{project}}.models import get_root import {{ project}} - app = make_app(get_root, {{project}}) - return app + return make_app(get_root, {{project}}, options=get_options(kw)) if __name__ == '__main__': from paste import httpserver - app = make_app(None) - httpserver.serve(app, host='0.0.0.0', port='5432') + httpserver.serve(app(None), host='0.0.0.0', port='6543') diff --git a/repoze/bfg/paster_template/setup.py_tmpl b/repoze/bfg/paster_template/setup.py_tmpl index 278153de8..d2b4d575b 100644 --- a/repoze/bfg/paster_template/setup.py_tmpl +++ b/repoze/bfg/paster_template/setup.py_tmpl @@ -35,10 +35,10 @@ setup(name='{{project}}', tests_require=[ 'repoze.bfg', ], - test_suite="{{project}}.tests", + test_suite="{{project}}", entry_points = """\ [paste.app_factory] - make_app = {{project}}.run:make_app + app = {{project}}.run:app """ ) diff --git a/repoze/bfg/registry.py b/repoze/bfg/registry.py index e1887bbdd..e02e5124e 100644 --- a/repoze/bfg/registry.py +++ b/repoze/bfg/registry.py @@ -6,9 +6,12 @@ from zope.component.interfaces import ComponentLookupError from zope.component.interfaces import IComponentLookup from zope.component.registry import Components from zope.component import getSiteManager as original_getSiteManager - from zope.configuration import xmlconfig +from zope.interface import implements + +from repoze.bfg.interfaces import ISettings + class ThreadLocalRegistryManager(threading.local): registry = getGlobalSiteManager() def set(self, registry): @@ -28,7 +31,7 @@ def setRegistryManager(manager): # for unit tests registry_manager = manager return old_registry_manager -def makeRegistry(filename, package, lock=threading.Lock()): +def makeRegistry(filename, package, options=None, lock=threading.Lock()): # This is absurd and probably not worth it. We want to try to # push our ZCML-defined configuration into an app-local component # registry in order to allow more than one bfg app to live in the @@ -50,12 +53,21 @@ def makeRegistry(filename, package, lock=threading.Lock()): original_getSiteManager.sethook(getSiteManager) zope.component.getGlobalSiteManager = registry_manager.get xmlconfig.file(filename, package=package) + if options is None: + options = {} + settings = Settings(options) + registry.registerUtility(settings, ISettings) return registry finally: zope.component.getGlobalSiteManager = getGlobalSiteManager lock.release() registry_manager.clear() +class Settings(object): + implements(ISettings) + def __init__(self, options): + self.reload_templates = options.get('reload_templates', False) + def getSiteManager(context=None): if context is None: return registry_manager.get() @@ -65,6 +77,14 @@ def getSiteManager(context=None): except TypeError, error: raise ComponentLookupError(*error.args) +def asbool(s): + s = str(s).strip() + return s.lower() in ('t', 'true', 'y', 'yes', 'on', '1') + +def get_options(kw): + reload_templates = asbool(kw.get('reload_templates')) + return {'reload_templates':reload_templates} + from zope.testing.cleanup import addCleanUp try: addCleanUp(original_getSiteManager.reset) diff --git a/repoze/bfg/router.py b/repoze/bfg/router.py index d6b60f5ec..49ccd1ad6 100644 --- a/repoze/bfg/router.py +++ b/repoze/bfg/router.py @@ -74,16 +74,21 @@ def isResponse(ob): isinstance(ob.status, basestring) ) : return True -def make_app(root_policy, package=None, filename='configure.zcml'): +def make_app(root_policy, package=None, filename='configure.zcml', + options=None): """ Create a view registry based on the application's ZCML. and return a Router object, representing a ``repoze.bfg`` WSGI - application. 'root_policy' must be a callable that accepts a WSGI - environment and returns a graph root object. 'package' is the - dotted-Python-path packagename of the application, 'filename' is - the filesystem path to a ZCML file (optionally relative to the - package path) that should be parsed to create the view registry.""" + application. ``root_policy`` must be a callable that accepts a + WSGI environment and returns a graph root object. ``package`` is + a Python module representing the application's package, + ``filename`` is the filesystem path to a ZCML file (optionally + relative to the package path) that should be parsed to create the + view registry. ``options``, if used, should be a dictionary + containing bfg-specific runtime options, with each key + representing the option and the key's value representing the + specific option value, e.g. ``{'reload_templates':True}``""" from repoze.bfg.registry import makeRegistry - registry = makeRegistry(filename, package) + registry = makeRegistry(filename, package, options) return Router(root_policy, registry) diff --git a/repoze/bfg/sampleapp/configure.zcml b/repoze/bfg/sampleapp/configure.zcml index d20d7d8f9..a5f27595e 100644 --- a/repoze/bfg/sampleapp/configure.zcml +++ b/repoze/bfg/sampleapp/configure.zcml @@ -4,9 +4,6 @@ <include package="repoze.bfg" /> - <bfg:settings - reload_templates="true"/> - <utility provides="repoze.bfg.interfaces.ISecurityPolicy" factory="repoze.bfg.security.RemoteUserACLSecurityPolicy" diff --git a/repoze/bfg/sampleapp/run.py b/repoze/bfg/sampleapp/run.py index 6c36c430e..df2ceca39 100644 --- a/repoze/bfg/sampleapp/run.py +++ b/repoze/bfg/sampleapp/run.py @@ -3,7 +3,7 @@ from repoze.bfg import sampleapp from repoze.bfg.sampleapp.models import get_root def main(): - app = make_app(get_root, sampleapp) + app = make_app(get_root, sampleapp, options={'reload_templates':True}) from paste import httpserver httpserver.serve(app, host='0.0.0.0', port='5432') diff --git a/repoze/bfg/tests/test_registry.py b/repoze/bfg/tests/test_registry.py index 0d051c131..de8481fd8 100644 --- a/repoze/bfg/tests/test_registry.py +++ b/repoze/bfg/tests/test_registry.py @@ -23,15 +23,37 @@ class TestMakeRegistry(unittest.TestCase, PlacelessSetup): old = repoze.bfg.registry.setRegistryManager(dummyregmgr) registry = makeRegistry('configure.zcml', fixtureapp, + options={'reload_templates':True}, lock=dummylock) from zope.component.registry import Components self.failUnless(isinstance(registry, Components)) self.assertEqual(dummylock.acquired, True) self.assertEqual(dummylock.released, True) self.assertEqual(dummyregmgr.registry, registry) + from zope.component import getUtility + from repoze.bfg.interfaces import ISettings + settings = getUtility(ISettings) + self.assertEqual(settings.reload_templates, True) finally: repoze.bfg.registry.setRegistryManager(old) +class TestGetOptions(unittest.TestCase): + def _getFUT(self): + from repoze.bfg.registry import get_options + return get_options + + def test_it(self): + get_options = self._getFUT() + self.assertEqual(get_options({}), + {'reload_templates':False}) + self.assertEqual(get_options({'reload_templates':'false'}), + {'reload_templates':False}) + self.assertEqual(get_options({'reload_templates':'t'}), + {'reload_templates':True}) + self.assertEqual(get_options({'reload_templates':'1'}), + {'reload_templates':True}) + + class TestThreadLocalRegistryManager(unittest.TestCase, PlacelessSetup): def setUp(self): PlacelessSetup.setUp(self) diff --git a/repoze/bfg/tests/test_zcml.py b/repoze/bfg/tests/test_zcml.py index 407eaf24e..663022796 100644 --- a/repoze/bfg/tests/test_zcml.py +++ b/repoze/bfg/tests/test_zcml.py @@ -114,37 +114,6 @@ class TestViewDirective(unittest.TestCase, PlacelessSetup): self.assertEqual(regadapt['args'][4], '') self.assertEqual(regadapt['args'][5], None) -class TestSettingsDirective(unittest.TestCase, PlacelessSetup): - def setUp(self): - PlacelessSetup.setUp(self) - - def tearDown(self): - PlacelessSetup.tearDown(self) - - def _getFUT(self): - from repoze.bfg.zcml import settings - return settings - - def test_defaults(self): - context = DummyContext() - settings = self._getFUT() - settings(context) - actions = context.actions - from repoze.bfg.interfaces import ISettings - from zope.component.zcml import handler - self.assertEqual(len(actions), 1) - action = actions[0] - self.assertEqual(action['discriminator'], ('settings', ISettings)) - self.assertEqual(action['callable'], handler) - self.assertEqual(len(action['args']), 5) - self.assertEqual(action['args'][0], 'registerUtility') - settings = action['args'][1] - self.assertEqual(settings.reload_templates, False) - self.failUnless(ISettings.providedBy(settings), settings) - self.assertEqual(action['args'][2], ISettings) - self.assertEqual(action['args'][3], '') - self.assertEqual(action['args'][4], context.info) - class TestSampleApp(unittest.TestCase, PlacelessSetup): def setUp(self): PlacelessSetup.setUp(self) diff --git a/repoze/bfg/zcml.py b/repoze/bfg/zcml.py index bb37c6581..343abc1d7 100644 --- a/repoze/bfg/zcml.py +++ b/repoze/bfg/zcml.py @@ -2,44 +2,17 @@ from zope.component.zcml import handler from zope.component.interface import provideInterface from zope.configuration.exceptions import ConfigurationError from zope.configuration.fields import GlobalObject -from zope.configuration.fields import Bool from zope.interface import Interface -from zope.interface import implements from zope.schema import TextLine from repoze.bfg.interfaces import IRequest from repoze.bfg.interfaces import IViewPermission from repoze.bfg.interfaces import IView -from repoze.bfg.interfaces import ISettings from repoze.bfg.security import ViewPermissionFactory -def _handler(*arg, **kw): - import pdb; pdb.set_trace() - return handler(*arg, **kw) - -class Settings(object): - implements(ISettings) - def __init__(self, reload_templates=False): - self.reload_templates = reload_templates - -def settings(_context, reload_templates=False): - settings = Settings(reload_templates=reload_templates) - _context.action( - discriminator = ('settings', ISettings), - callable = handler, - args = ('registerUtility', settings, ISettings, '', _context.info), - ) - -class ISettingsDirective(Interface): - reload_templates = Bool( - title=u"Reload templates when they change", - description=(u"Specifies whether templates should be reloaded when" - "a change is made"), - default=False) - def view(_context, permission=None, for_=None, |
