Chris McDonough
2010-07-04 4dc529e4bbc36b7e5e2bd3ea199a7929e407a267
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
:mod:`repoze.bfg` TODOs
=======================
 
- Supply ``X-Vhm-Host`` support.
 
- Basic WSGI documentation (pipeline / app / server).
 
- Provide a webob.Response class facade for forward compat.
 
- Fix message catalog extraction / compilation documentation.
 
- Change docs about creating a venusian decorator to not use ZCA.
 
- Add a ``susbcriber`` decorator and docs.
 
- ``decorator=`` parameter to bfg_view.
 
Renderer overhaul
------------------
 
Currently the division of responsibility between the BFG configurator
and a BFG renderer implementation is awkward and wrong.
 
- Renderer factories have no ability to convert a raw ``renderer=``
  path (e.g. ``templates/foo.pt```) into something internally
  meaningful.  Instead, BFG mangles the string into a package-relative
  spec before it is passed to the renderer factory.  This is wrong, as
  some renderers may not be interested in package-relative specs at
  all (for instance, loader-style renderers which have a hardcoded set
  of template locations).  The reason, however, that BFG currently
  does it this way is that the raw renderer path alone does not
  contain enough information itself to be useful; knowledge of the
  *package* is also required for package-based renderers to make sense
  of relative renderer strings (e.g. ``templates/foo.pt`` could mean
  the ``templates/foo.pt`` file within the ``mypackage`` package).
 
  To fix this, we need to provide some way to pass the package name to
  the renderer factory as well as the renderer path.  But the package
  name isn't the only thing an *arbitrary* renderer might need.
  Another renderer might need, for example, a deployment setting.  So
  we'll need to identify all the crap that *might* be useful to a
  renderer factory and we'll need to pass all of it into the renderer
  factory as a garbage barge dictionary; individual renderers will
  make use of whatever they can from that garbage barge dictionary.
  Garbage barge dict item candidates: ``package`` (the "current"
  package), ``config`` (the configurator), ``package_name`` (the
  current package's ``__name__``), ``settings`` (the deployment
  settings), ``registry`` (the component registry).
 
- A BFG renderer currently returns a *string*.  It would be more
  symmetric if a renderer always returned a Response object.  Then the
  calling machinery inside BFG could treat a view which happened to
  use a renderer exactly the same as a view which returns a response
  directly.  Maybe.  Problem: varying response attributes such as
  ``content-type``, etc only makes sense when the view callable uses a
  renderer; not when it doesn't, so there's some asymmetry there.
  Maybe we make renderers return Responses, but still keep the
  attribute-inspection stuff inside BFG, only used when we call a view
  which we know uses a renderer.  We *could* always call the attribute
  inspection stuff, but it would be a slowdown in cases where views
  really do always return a Response directly.
 
- The ``system`` value passed to a renderer is not extensible.  It
  should be extensible on a per-application basis.  For example, you
  might want to add a top-level variable ``c`` to the values passed to
  all renderers representing a template context to emulate Pylons.
 
- ``repoze.bfg.chameleon_zpt.render_template_to_response``, et. al. do
  not use the same machinery as view renderers.  It would be useful if
  templates rendered with ``render_template_to_response`` had the same
  ``system`` values available to it as templates renderered via a view
  renderer.
 
To at least partially ameliorate the above, renderer factories should
be changed to things that have a set of interfaces something like
this::
 
    class IRendererFactory(Interface):
        def __call__(path, info):
            "" Return an IRenderer."""
 
    class IRenderer(Interface):
        def __call__(value, system):
            """  Return a Response """
 
A semi-pseudocode example:
 
    from webob import Response
 
    class SampleRendererFactory(object):
        def __init__(self, **config):
            self.config = config
 
        def __call__(self, path, info):
            path = do_something_to_evaluate_path_using_info(path, info)
            search_path = self.config['search_path']
            debug = self.config['debug']
            def renderer(value, system):
                string = do_rendering(search_path, debug, path, value, system)
                return Response(string)
            return renderer
 
   if __name__ == '__main__':
 
        def view1(request):
            return {'a':1}
 
        def view2(request):
            return {'a':2}
 
        renderer_factory = SampleRendererFactory(search_path=['/a', '/b'])
 
        package_name = 'some.package'
 
        for view, path in [
                            (view1, 'templates/foo.pt'), 
                            (view2, 'templates/bar.pt'),
                           ]:
            renderer = renderer_factory(path, dict(package_name=package_name))
            register_renderer_for_view(renderer, view)
 
This is mostly an amelioration for the first and second bullet points
above.  The second two bullet points are not addressed by it.
 
Also: think about generalizing this a bit into something which wraps a
view callable like a decorator, which can influence input and output.