Merge lp:~corydodt/txgenshi/506147 into lp:txgenshi

Proposed by Cory Dodt
Status: Merged
Merged at revision: not available
Proposed branch: lp:~corydodt/txgenshi/506147
Merge into: lp:txgenshi
Diff against target: 235 lines (+117/-29)
5 files modified
txgenshi/loader.py (+75/-27)
txgenshi/test/example.py (+11/-2)
txgenshi/test/test_example.py (+25/-0)
txgenshi/util.py (+1/-0)
txgenshidemo/templates/element.xhtml (+5/-0)
To merge this branch: bzr merge lp:~corydodt/txgenshi/506147
Reviewer Review Type Date Requested Status
Duncan McGreggor Approve
Review via email: mp+17203@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Cory Dodt (corydodt) wrote :

Hide the loader behind a descriptor. Descriptors have a magical ability to access the instance they are attached to, which makes it a fine replacement for using the context (which is deprecated in Nevow). And it works with Elements.

Revision history for this message
Duncan McGreggor (oubiwann) wrote :

I remember having a passing thought about Elements (mostly dread and avoidance) a couple years ago. Fascinating approach; I love what you've done. +1 for merge with the following minor points addressed (or discussed):

[1] Let's not use the private "_" prefix for classes. Instead, let's augment the docstring with something like the following at the end:
  """
  ...
  This class is intended for private use and is liable to change.
  """

[2] If a class has no docstring, let's put a new line after the classes and before the first method. But I guess a docstring would really be better ;-)

[3] Can you fix the indentation in this old code:

34 + nevowRun = super(_GenshiCommonLoader, self).load(ctx=ctx,
35 preprocessors=preprocessors)

If it won't all fit on one line, let's change it to this:

nevowRun = super(GenshiCommonLoader, self).load(
    ctx=ctx, preprocessors=preprocessors)

Note to self: I should probably open a clean-up branch and take care of the old warts...

Again, nice work. You're doing a great job of breaking the tasks up into discrete branches, and not being tempted into tackling stuff that's out of scope. This is rare, and it makes successive branches a delight to review :-)

review: Approve
Revision history for this message
Cory Dodt (corydodt) wrote :

On Mon, Jan 11, 2010 at 10:48 PM, Duncan McGreggor <email address hidden>wrote:

> Review: Approve
> I remember having a passing thought about Elements (mostly dread and
> avoidance) a couple years ago. Fascinating approach; I love what you've
> done. +1 for merge with the following minor points addressed (or discussed):
>
> [1] Let's not use the private "_" prefix for classes. Instead, let's
> augment the docstring with something like the following at the end:
> """
> ...
> This class is intended for private use and is liable to change.
> """
>
> [2] If a class has no docstring, let's put a new line after the classes and
> before the first method. But I guess a docstring would really be better ;-)
>

[1,2] resolved, r37 ([2] resolved by adding docstrings).

Any feelings, one way or the other, about using __all__ to document
public-ness?

>
> [3] Can you fix the indentation in this old code:
>
> 34 + nevowRun = super(_GenshiCommonLoader, self).load(ctx=ctx,
> 35 preprocessors=preprocessors)
>
> If it won't all fit on one line, let's change it to this:
>
> nevowRun = super(GenshiCommonLoader, self).load(
> ctx=ctx, preprocessors=preprocessors)
>

* Resolved, r35.

>
> Note to self: I should probably open a clean-up branch and take care of the
> old warts...
>
> Again, nice work. You're doing a great job of breaking the tasks up into
> discrete branches, and not being tempted into tackling stuff that's out of
> scope. This is rare, and it makes successive branches a delight to review
> :-)
> --
> https://code.edge.launchpad.net/~corydodt/txgenshi/506147/+merge/17203
> You are the owner of lp:~corydodt/txgenshi/506147.
>

--
_____________________

lp:~corydodt/txgenshi/506147 updated
35. By Cory Dodt

#506147: whitespace

36. By Cory Dodt

#506147: pyflakes cleanup

37. By Cory Dodt

#506147: style--no leading _ in class names

Revision history for this message
Cory Dodt (corydodt) wrote :

Based on prior approval with these changes, I am merging.

Revision history for this message
Duncan McGreggor (oubiwann) wrote :

I'm +1 for the use of __all__ for public objects.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'txgenshi/loader.py'
2--- txgenshi/loader.py 2010-01-11 19:03:21 +0000
3+++ txgenshi/loader.py 2010-01-12 07:47:11 +0000
4@@ -17,7 +17,6 @@
5 from inspect import getmembers
6 from collections import deque
7
8-from nevow import inevow
9 from nevow.loaders import xmlstr
10
11 from genshi.template import MarkupTemplate, Context
12@@ -90,12 +89,23 @@
13
14
15 def getGenshiString(template, data):
16+ """
17+ Execute a Genshi template, rendering Genshi markup into a markup string.
18+ """
19 tmpl = MarkupTemplate(template)
20 ctx = TxGenshiContext(data)
21 return tmpl.generate(ctx).render('xhtml')
22
23
24-class genshistr(xmlstr):
25+class GenshiCommonLoader(xmlstr):
26+ """
27+ Load a Genshi template and use it to render a resource or fragment
28+
29+ This class is intended for private use and is liable to change.
30+ """
31+ def __init__(self, container, template):
32+ self.dataContainer = container
33+ xmlstr.__init__(self, template)
34
35 def load(self, ctx=None, preprocessors=()):
36 """
37@@ -111,14 +121,15 @@
38 syntax in the template(s). If you will only be using Genshi, you can
39 enable your own caching mechanism and not up-call to the parent class.
40 """
41- renderer = inevow.IRenderer(ctx)
42- data = DataProxy(renderer)
43+ if not hasattr(self, 'orig'):
44+ self.orig = self._getTemplate()
45+ data = DataProxy(self.dataContainer)
46 # save a copy of the original, unmolested template, complete with
47 # Genshi markup
48 orig = self.template
49- self.template = getGenshiString(self.template, data)
50- nevowRun = super(genshistr, self).load(ctx=ctx,
51- preprocessors=preprocessors)
52+ self.template = getGenshiString(self.orig, data)
53+ nevowRun = super(GenshiCommonLoader, self).load(
54+ ctx=ctx, preprocessors=preprocessors)
55 # null out Nevow's caching of the template
56 self._cache = None
57 # revert to the unparsed Genshi template so that genshi attrs/methods
58@@ -127,23 +138,60 @@
59 return nevowRun
60
61
62-class genshifile(xmlstr):
63-
64- def __init__(self, filename):
65- self._filename = filename
66-
67- def load(self, ctx=None, preprocessors=()):
68- if not hasattr(self, 'orig'):
69- self.orig = open(self._filename).read()
70- #self._reallyLoad(self._filename, ctx, preprocessors)
71- renderer = inevow.IRenderer(ctx)
72- data = DataProxy(renderer)
73- self.template = getGenshiString(self.orig, data)
74- nevowRun = super(genshifile, self).load(ctx=ctx,
75- preprocessors=preprocessors)
76- # null out Nevow's caching of the template
77- self._cache = None
78- # revert to the unparsed Genshi template so that genshi attrs/methods
79- # have the chance to generate fresh data
80- self.template = self.orig
81- return nevowRun
82+class GenshiStr(GenshiCommonLoader):
83+ """
84+ A version of the "true" loader that accepts a string.
85+
86+ This class is intended for private use and is liable to change.
87+ """
88+ def _getTemplate(self):
89+ return self.template
90+
91+
92+class GenshiFile(GenshiCommonLoader):
93+ """
94+ A version of the "true" loader that accepts a filename.
95+
96+ This class is intended for private use and is liable to change.
97+ """
98+ def _getTemplate(self):
99+ return open(self.template).read()
100+
101+
102+class DescriptorLoader(object):
103+ """
104+ A descriptor that can be used as docFactory where you would normally find
105+ a loader, at the class level. If you wish to instantiate a loader at the
106+ instance level, see GenshiFile and GenshiString.
107+
108+ This class is intended for private use and is liable to change.
109+ """
110+ loaderClass = None
111+
112+ def __init__(self, template):
113+ self.template = template
114+ self._cache = LAZY
115+
116+ def __get__(self, instance, cls):
117+ if self._cache is not LAZY:
118+ return self._cache
119+
120+ loader = self.loaderClass(instance, self.template)
121+ self._cache = loader
122+ return loader
123+
124+
125+class genshistr(DescriptorLoader):
126+ """
127+ Loader to read a string containing a genshi template and output a
128+ populated string.
129+ """
130+ loaderClass = GenshiStr
131+
132+
133+class genshifile(DescriptorLoader):
134+ """
135+ Loader to read a file containing a genshi template and output a
136+ populated string.
137+ """
138+ loaderClass = GenshiFile
139
140=== modified file 'txgenshi/test/example.py'
141--- txgenshi/test/example.py 2010-01-11 19:03:21 +0000
142+++ txgenshi/test/example.py 2010-01-12 07:47:11 +0000
143@@ -5,7 +5,7 @@
144
145 from twisted.python.util import sibpath
146
147-from nevow import rend
148+from nevow import rend, page
149
150 from txgenshi.loader import genshifile, genshistr
151
152@@ -15,7 +15,8 @@
153 color = "red"
154
155
156-TEMPLATEFILE = sibpath(__file__, '../../demo/templates/genshimix.xhtml')
157+TEMPLATEFILE = sibpath(__file__, '../../txgenshidemo/templates/genshimix.xhtml')
158+ELEMENTFILE = sibpath(__file__, '../../txgenshidemo/templates/element.xhtml')
159
160
161 class GenshiMixPage(rend.Page):
162@@ -58,3 +59,11 @@
163
164 class GenshiMixPageStr(GenshiMixPage):
165 docFactory = genshistr(open(TEMPLATEFILE).read())
166+
167+
168+class GenshiMixElement(page.Element):
169+ docFactory = genshifile(ELEMENTFILE)
170+
171+ @page.renderer
172+ def hello(self, request, tag):
173+ return tag["Hello"]
174
175=== modified file 'txgenshi/test/test_example.py'
176--- txgenshi/test/test_example.py 2010-01-10 09:01:59 +0000
177+++ txgenshi/test/test_example.py 2010-01-12 07:47:11 +0000
178@@ -5,6 +5,8 @@
179
180 from twisted.trial import unittest
181
182+from nevow import flat
183+
184 from txgenshi.test import example
185
186
187@@ -63,3 +65,26 @@
188 self.fail("no implementation of xinclude yet") # pragma: nocover
189
190 test_xinclude.skip = "need to implement xinclude"
191+
192+
193+class ElementTest(unittest.TestCase):
194+ """
195+ Templates work just as well using Elements as they do using Pages
196+ """
197+ def setUp(self):
198+ self.el = example.GenshiMixElement()
199+ self.rendered = str(flat.flatten(self.el))
200+
201+ def test_nevow(self):
202+ """
203+ Nevow rendering works on Elements using genshifile
204+ """
205+ # verify that genshi is not adding any html boilerplate
206+ self.assertEqual(self.rendered[:5], '<div>')
207+ self.assertSubstring('<b>Hello</b>', self.rendered)
208+
209+ def test_genshi(self):
210+ """
211+ Genshi rendering works on Elements using genshifile
212+ """
213+ self.assertSubstring('<b>Earth</b>', self.rendered)
214
215=== modified file 'txgenshi/util.py'
216--- txgenshi/util.py 2010-01-11 01:42:19 +0000
217+++ txgenshi/util.py 2010-01-12 07:47:11 +0000
218@@ -200,6 +200,7 @@
219 'classifiers': txgenshi.classifiers,
220 'package_data': {
221 'txgenshidemo': ['demo.tac',
222+ 'templates/element.xhtml',
223 'templates/genshimix.xhtml',
224 'templates/genshiinclude.xhtml',
225 'templates/home.xhtml',
226
227=== added file 'txgenshidemo/templates/element.xhtml'
228--- txgenshidemo/templates/element.xhtml 1970-01-01 00:00:00 +0000
229+++ txgenshidemo/templates/element.xhtml 2010-01-12 07:47:11 +0000
230@@ -0,0 +1,5 @@
231+<div xmlns:n=
232+ "http://nevow.com/ns/nevow/0.1" xmlns:genshi=
233+ "http://genshi.edgewall.org/"><b n:render=
234+ "hello"/><b genshi:with=
235+ "world='Earth'">$world</b></div>

Subscribers

People subscribed via source and target branches

to all changes: