...
Here's an example similar to the one that started all this. Let's say
that the PEP HTML Writer overrides the visit_problematic method, but
leaves depart_problematic alone.
In _html_base.HTMLTranslator, these methods
use the context stack. A diff patch is below. Attached is
context_check.py, which I propose adding to _html_base.py.
Index: pep_html/__init__.py
===================================================================
--- pep_html/__init__.py (revision 8028)
+++ pep_html/__init__.py (working copy)
@@ -16,6 +16,7 @@
import docutils
from docutils import frontend, nodes, utils, writers
from docutils.writers import html4css1
+from docutils.writers._html_base import context_check
@@ -102,3 +103,15 @@
html4css1.HTMLTranslator.depart_field_list(self, node)
self.body.append('<hr />\n')
+
+ self.body.append('<a href="#%s">' % node['refid'])
+ self.context.append('</a>')
+ self.context.append('')
+ self.body.append(self.starttag(node, 'span', '', CLASS='problematic'))
+
+ html4css1.HTMLTranslator.depart_problematic(self, node)
In the diff, you'll see that there's a depart_problematic, guarded
with a context_check(-1) decorator. The -1 means that the context
stack size is expected to decrease by 1 after this code is run. The
visit_problematic method doesn't call the superclass's method; it's a
pure override. The context stack usage is balanced though; one item is
pushed at the visit, and one item is popped at the depart.
Defensive programming would define the depart_problematic method in PEP as
+ def depart_problematic(self, node):
+ self.body.append('</span>')
+ self.body.append(self.context.pop())
Now let's say that in the superclass (_html_base.HTMLTranslator), the
visit/depart_problematic methods have been modified such that they no
longer use the context stack. The PEP pair of methods are no longer
balanced wrt their use of the context stack. The guard code in the
sample above will definitely generate an exception, pinpointing the
issue.
... while the safe subclassing (either call both upstream visit_* and
depart_* or overwrite both) would ensure balanced stack use independent
of the superclass change.
Look at the example code I provide above, and critique that please.
The example is slightly better in that is does not trigger a spurious
exception.
But I still do not see a convincing use case:
What is the use of "an exception, pinpointing the issue" whe we can easily
avoid the issue?
The "guard code" does not help the users of 3rd-party applications. The
Docutils change will still break the application! OTOH, with safe
subclassing, the change in Docutils will not break the 3rd-party
application.
Had code such as the above been in place, the issue with Sphinx would
have been easily and immediately identified.
...
There are several points here:
1. We did not think about context stack use (now it is part of the
documentation and mindset).
2. We did not give a pre-release to Sphinx for testing (now added to
our release procedure steps).
So, yes,
* if the Sphinx developers had put in place such code and
* if at least one Sphinx user had tested Sphinx with a Docutils
pre-release (or the devel version)
the issue with Sphinx could have been easier identified.
However, with the right problem awareness, Sphinx developers could have
put in place the safe subclassing of visit/depart method pairs and the
problem would not even have occured!
With
c) Overwrite both, call the parent functions under the same
conditions::
def visit_example(self, node):
if foo:
<my special code>
else: # call the parent method
_html_base.HTMLTranslator.visit_example(self, node)
def depart_example(self, node):
if foo:
<my special code>
else: # call the parent method
_html_base.HTMLTranslator.depart_example(self, node)
Sphinx can be used with Docutils 0.12 and 0.13. No need for code that
generates an exception.
Summary:
I don't see a convincing use case for code to "easily and immediately
identify a problem" when the same problem can just as easily be
prevented.
=========================================================================
...
Post by Guenter MildePost by David GoodgerI think it would be useful to add this guard code to show by example.
The guard code is already in my local version but you pointed out it
violates DRY.
So it is OK to commit?
Please show it first so I know exactly what you're proposing.
It seems there was a misunderstanding regarding what can be regarded
"guard code example":
I propose to give a good example by using "safe subclassing" as safeguard
against context stack problems --- even if this implies a bit of code
duplication (see below).
I agree that we should avoid code duplication, but not at any cost.
Günter
Exec: svn 'diff' '__init__.py' 2>&1
Dir: docutils/docutils/writers/html5_polyglot/
Index: __init__.py
===================================================================
--- __init__.py (Revision 8036)
+++ __init__.py (Arbeitskopie)
@@ -148,11 +148,13 @@
class HTMLTranslator(writers._html_base.HTMLTranslator):
"""
This writer generates `polyglot markup`: HTML5 that is also valid XML.
+
+ See the docstring of docutils.writers._html_base.HTMLTranslator for
+ safe subclassing.
"""
# def __init__(self, document):
# writers._html_base.HTMLTranslator.__init__(self, document)
-
# <acronym> tag not supported in HTML5. Use the <abbr> tag instead.
def visit_acronym(self, node):
# @@@ implementation incomplete ("title" attribute)
@@ -165,13 +167,23 @@
def visit_authors(self, node):
self.visit_docinfo_item(node, 'authors', meta=False)
+ def depart_authors(self, node):
+ self.depart_docinfo_item()
+
+ # no meta tag in HTML5
def visit_copyright(self, node):
self.visit_docinfo_item(node, 'copyright', meta=False)
+ def depart_copyright(self, node):
+ self.depart_docinfo_item()
+
# no meta tag in HTML5
def visit_date(self, node):
self.visit_docinfo_item(node, 'date', meta=False)
+ def depart_date(self, node):
+ self.depart_docinfo_item()
+
# TODO: use HTML5 <footer> element?
# def visit_footer(self, node):
# def depart_footer(self, node):
@@ -178,6 +190,7 @@
# TODO: use the new HTML5 element <aside>? (Also for footnote text)
# def visit_footnote(self, node):
+ # def depart_footnote(self, node):
# Meta tags: 'lang' attribute replaced by 'xml:lang' in XHTML 1.1
# HTML5/polyglot recommends using both
@@ -188,12 +201,20 @@
meta = self.emptytag(node, 'meta', **node.non_default_attributes())
self.add_meta(meta)
+ def depart_meta(self, node):
+ pass
+
# no meta tag in HTML5
def visit_organization(self, node):
self.visit_docinfo_item(node, 'organization', meta=False)
+ def depart_organization(self, node):
+ self.depart_docinfo_item()
+
# TODO: use the new HTML5 element <section>?
# def visit_section(self, node):
+ # def depart_section(self, node):
# TODO: use the new HTML5 element <aside>?
# def visit_topic(self, node):
+ # def depart_topic(self, node):
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, SlashDot.org! http://sdm.link/slashdot
_______________________________________________
Docutils-develop mailing list
Docutils-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/docutils-develop
Please use "Reply A