Attachment 'bibtex.py'

Download

   1 # -*- coding: iso-8859-1 -*-
   2 """MoinMoin - Bibtex Parser
   3 
   4 Configuration Options:
   5 
   6     bibtex_bib2html_cmd -- path and command name of bib2xhtml, needs to
   7                            be set if bib2xhtml is not in PATH
   8     bibtex_bst_path     -- path to bib2xhtml bst-style files, needs to
   9                            be set if these are not found by bibtex
  10                            automatically
  11 
  12 Notes:
  13 This parser depends on bibtex and bib2xhtml, see
  14 http://en.wikipedia.org/wiki/Bibtex and
  15 http://www.spinellis.gr/sw/textproc/bib2xhtml/ for more information.
  16 
  17 TODO:
  18  - attachement links
  19 
  20 $Id: bibtex.py,v 1.1.1.1 2007-06-07 20:36:15 gber Exp $
  21 Copyright 2007 by Guido Berhoerster <guido+moinmoin@berhoerster.name>
  22 Licensed under the GNU GPL, see COPYING for details.
  23 
  24 $Log: bibtex.py,v $
  25 Revision 1.1.1.1  2007-06-07 20:36:15  gber
  26 initial import into CVS
  27 
  28 
  29 """
  30 
  31 import os
  32 import tempfile
  33 import subprocess
  34 import codecs
  35 import re
  36 from MoinMoin import wikiutil
  37 
  38 Dependencies = []
  39 
  40 # subprocess.check_call and CalledProcessError are only available in
  41 # Python 2.5, so recreate them here
  42 class CalledProcessError(Exception):
  43     """ This exception is raised when a process run by check_call()
  44     returns a non-zero exit status.  The exit status will be stored in
  45     the returncode attribute. """
  46     def __init__(self, returncode, cmd):
  47         self.returncode = returncode
  48         self.cmd = cmd
  49     def __str__(self):
  50         return "Command '%s' returned non-zero exit status %d" \
  51                 % (self.cmd, self.returncode)
  52 
  53 
  54 class BibtexRenderer:
  55     """Abstracted bibtex renderer
  56 
  57     Arguments:
  58         bibtex  -- string containing bibtex markup to be rendered
  59         request -- request object
  60 
  61     Keyword arguments:
  62         citations     -- list with keys of bibtex entries to be
  63                          rendered (default: empty)
  64         abstracts     -- boolean, show abstracts or not
  65                          (default: False)
  66         chronological -- boolean or the string "reversed",
  67                          chronological or reversed chronological
  68                          sorting (default: False)
  69         style         -- string "empty", "plain", "alpha", "named",
  70                          "unsort", "unsortlist", determining the style
  71                          to use (default: None)
  72 
  73     Notes:
  74     Raises OSError if bib2xhtml is not found or CalledProcessError if
  75     bib2xhtml returns wit a non-zero exit status.
  76 
  77     """
  78 
  79     def __init__(self, bibtex, request, citations=[], abstracts=False,\
  80             label=False, chronological=False, style=None):
  81         cfg = request.cfg
  82         self.bib2html_cmd = "bib2xhtml"
  83         self.bst_path = None
  84 
  85         # retrieve configuration
  86         try:
  87              self.bib2html_cmd = cfg.bibtex_bib2html
  88         except AttributeError:
  89              pass
  90         try:
  91              self.bst_path = cfg.bibtex_bst_path
  92         except AttributeError:
  93              pass
  94 
  95         # the original bibtex implementation is not 8-bit clean, replace
  96         # non-ASCII characters with "?"
  97         self.bibtex = bibtex.encode("ascii", "replace")
  98 
  99         self.args = [self.bib2html_cmd, "-u", "-dMoinMoin"]
 100 
 101         if citations:
 102             cit_list = []
 103             cit_list.append(u"<!-- BEGIN CITATIONS MoinMoin -->")
 104             cit_list.append(u"<!--")
 105             cit_list.extend([ur"\citation{%s}" % c for c in citations])
 106             cit_list.append(u"-->")
 107             cit_list.append(u"<!-- END CITATIONS MoinMoin -->")
 108             self.citations = u"\n".join(cit_list)
 109             # also encode as ASCII
 110             self.citations = self.citations.encode("ascii", "replace")
 111             self.args.append("-i")
 112         else:
 113             self.citations = None
 114 
 115         if abstracts:
 116             self.args.append("-a")
 117         if label:
 118             self.args.append("-k")
 119         if chronological and chronological == "reversed":
 120             self.args.extend(["-c", "-r"])
 121         elif chronological:
 122             self.args.append("-c")
 123         if style in ("empty", "plain", "alpha", "named", "unsort", "unsortlist"):
 124             self.args.extend(["-s", style])
 125 
 126     def render(self):
 127         """Render the bibtex markup (if requested, only cited entries)
 128         and return HTML output in a string.
 129         """
 130         output = []
 131         # create temporary files for Bibtex input, HTML output, and logging
 132         bibfd, bibfile = tempfile.mkstemp(".bib")
 133         xhtmlfd, xhtmlfile = tempfile.mkstemp(".xhtml")
 134         #logfd, logfile = tempfile.mkstemp(".log")
 135         self.args.extend([bibfile, xhtmlfile])
 136 
 137         # write Bibtex input to temporary file
 138         f = open(bibfile, "w")
 139         f.write(self.bibtex)
 140         f.close()
 141 
 142         if self.citations:
 143             # write citations to temporary output file
 144             f = open(xhtmlfile, "w")
 145             f.write(self.citations)
 146             f.close()
 147 
 148         # execute bib2xhtml converter subprocess on forementionened
 149         # temporary files, bib2xhtml creates its temporary files in the
 150         # current working directory, so set it to a reasonable location, set
 151         # the set the BSTINPUTS environment variable if required in order to
 152         # help Bibtex finds the needed .bst files
 153         if self.bst_path:
 154             bstinputs = {"BSTINPUTS": self.bst_path}
 155         else:
 156             bstinputs = None
 157         try:
 158             retcode = subprocess.call(self.args,
 159                 env=bstinputs, cwd=tempfile.gettempdir(),
 160                 stdout=open(os.devnull, "w"), stderr=open(os.devnull, "w"))
 161             #retcode = subprocess.call(self.args,
 162             #    env=bstinputs, cwd=tempfile.gettempdir(),
 163             #    stdout=open(os.devnull, "w"), stderr=open(logfile, "w"))
 164             if retcode:
 165                 raise CalledProcessError(retcode, "".join(self.args))
 166         except OSError, error:
 167             # bib2xhtml not found or not executable
 168             os.remove(bibfile)
 169             os.remove(xhtmlfile)
 170             raise
 171         except CalledProcessError, error:
 172             # non-zero exit status
 173             os.remove(bibfile)
 174             os.remove(xhtmlfile)
 175             #os.remove(logfile)
 176             raise
 177 
 178         os.remove(bibfile)
 179         #os.remove(logfile)
 180 
 181         name_pattern = re.compile('<a name="(?P<anchor>[^"]*)">', re.UNICODE)
 182         href_pattern = re.compile('<a href="#(?P<anchor>[^"]*)">', re.UNICODE)
 183         inside_dl = False
 184 
 185         # read the output (encoded as utf-8) back in
 186         f = codecs.open(xhtmlfile, "r", encoding="utf-8")
 187 
 188         for line in f.readlines():
 189             if line.startswith(u'<!-- Generated by: '):
 190                 # throw away comments at the beginning...
 191                 inside_dl = True
 192                 continue
 193             elif line == u'<!-- END BIBLIOGRAPHY MoinMoin -->\n':
 194                 # ...and the end
 195                 break
 196             if inside_dl:
 197                 # use a ref:-prefix for anchor links in order to avoid
 198                 # interference with other anchors and replace the name- with
 199                 # the id-attribute (it would be more appropriate to fix this in
 200                 # the bst-file)
 201                 line = name_pattern.sub(ur'<a id="ref:\g<anchor>">', line)
 202                 line = href_pattern.sub(ur'<a href="#ref:\g<anchor>">', line)
 203                 output.append(line)
 204 
 205         f.close()
 206         os.remove(xhtmlfile)
 207 
 208         output = "".join(output)
 209         return output
 210 
 211 
 212 class Parser:
 213     """Parse Bibtex markup."""
 214 
 215     extensions = ['.bib']
 216     Dependencies = []
 217 
 218     def __init__(self, raw, request, **kw):
 219         self.request = request
 220         self.show = True
 221         self.abstracts = False
 222         self.label = True
 223         self.chronological = False
 224         self.style = "named"
 225         self.raw = raw
 226         self.request = request
 227         self._ = request.getText
 228 
 229         # parse format arguments
 230         attrs, msg = wikiutil.parseAttributes(self.request, kw.get(
 231                                                              'format_args',''))
 232         if not msg:
 233             if attrs.get('show','"on"')[1:-1].lower() not in ('on', 'true',
 234                                                                         'yes'):
 235                 self.show = False
 236             if attrs.get('abstracts','"off"')[1:-1].lower() in ('on', 'true',
 237                                                                         'yes'):
 238                 self.abstracts = True
 239             if attrs.get('label','"on"')[1:-1].lower() not in ('on', 'true',
 240                                                                         'yes'):
 241                 self.label = False
 242             if attrs.get('chronological','"off"')[1:-1].lower() in ('on',
 243                                                                 'true', 'yes'):
 244                 self.chronological = True
 245             elif attrs.get('chronological','"off"')[1:-1].lower() in (
 246                                                         'reverse', 'reversed'):
 247                 self.chronological = "reversed"
 248             if attrs.get('style','"empty"')[1:-1].lower() in ('empty', 'plain',
 249                                      'alpha', 'named', 'unsort', 'unsortlist'):
 250                 self.style = attrs.get('style','')[1:-1].lower()
 251 
 252     def format(self, formatter):
 253         """Format Bibtex markup as HTML and write output."""
 254 
 255         if not self.show: return
 256 
 257         _ = self._
 258 
 259         bib_renderer = BibtexRenderer(self.raw, self.request,
 260                 abstracts=self.abstracts, label=self.label,
 261                 chronological=self.chronological, style=self.style)
 262         try:
 263             output = bib_renderer.render()
 264         except OSError, error:
 265             self.request.write(formatter.sysmsg(1))
 266             self.request.write(formatter.escapedText(
 267                        _("Error: %(bib2html_cmd)s could not be found.") % 
 268                        {"bib2html_cmd": self.bib2html_cmd}))
 269             self.request.write(formatter.sysmsg(0))
 270             return
 271         except CalledProcessError, error:
 272             msg = (_("An error occured while executing %(bib2html_cmd)s:" %
 273                                           {"bib2html_cmd": self.bib2html_cmd}))
 274             self.request.write(formatter.sysmsg(1))
 275             self.request.write(formatter.escapedText(msg))
 276             self.request.write(formatter.sysmsg(0))
 277             return
 278 
 279         # write through the rawHTML formatter if possible, otherwise write
 280         # just the Bibtex markup as preformatted text
 281         self.request.write(formatter.div(1, css_class="bibliography"))
 282         self.request.write(formatter.heading(1, 1))
 283         self.request.write(formatter.escapedText(_("Bibliography")))
 284         self.request.write(formatter.heading(0, 1))
 285         try:
 286             self.request.write(formatter.rawHTML(output))
 287         except:
 288             self.request.write(formatter.preformatted(1))
 289             self.request.write(formatter.text(self.raw.expandtabs()))
 290             self.request.write(formatter.preformatted(0))

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.
  • [get | view] (2007-06-07 21:34:41, 10.8 KB) [[attachment:bibtex.py]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.