]> granicus.if.org Git - python/commitdiff
Removed faqmain.py -- it was an earlier implementation and no
authorGuido van Rossum <guido@python.org>
Wed, 27 Aug 1997 22:31:18 +0000 (22:31 +0000)
committerGuido van Rossum <guido@python.org>
Wed, 27 Aug 1997 22:31:18 +0000 (22:31 +0000)
longer relevant.

Tools/faqwiz/faqmain.py [deleted file]

diff --git a/Tools/faqwiz/faqmain.py b/Tools/faqwiz/faqmain.py
deleted file mode 100644 (file)
index 7550bd0..0000000
+++ /dev/null
@@ -1,858 +0,0 @@
-"""Interactive FAQ project.
-
-Note that this is not an executable script; it's an importable module.
-The actual CGI script can be kept minimal; it's appended at the end of
-this file as a string constant.
-
-XXX TO DO
-
-XXX User Features TO DO
-
-- next/prev/index links in do_show???
-- explanation of editing somewhere
-- embellishments, GIFs, hints, etc.
-- support adding annotations, too
-- restrict recent changes to last week (or make it an option)
-- extended search capabilities
-
-XXX Management Features TO DO
-
-- username/password for authors
-- create new sections
-- rearrange entries
-- delete entries
-- freeze entries
-- send email on changes?
-- send email on ERRORS!
-- optional staging of entries until reviewed?
-  (could be done using rcs branches!)
-- prevent race conditions on nearly simultaneous commits
-
-XXX Performance
-
-- could cache generated HTML
-- could speed up searches with a separate index file
-
-XXX Code organization TO DO
-
-- read section titles from a file (could be a Python file: import faqcustom)
-- customize rcs command pathnames (and everything else)
-- make it more generic (so you can create your own FAQ)
-- more OO structure, e.g. add a class representing one FAQ entry
-
-"""
-
-# NB for timing purposes, the imports are at the end of this file
-
-PASSWORD = "Spam"
-
-NAMEPAT = "faq??.???.htp"
-NAMEREG = "^faq\([0-9][0-9]\)\.\([0-9][0-9][0-9]\)\.htp$"
-
-SECTIONS = {
-    "1": "General information and availability",
-    "2": "Python in the real world",
-    "3": "Building Python and Other Known Bugs",
-    "4": "Programming in Python",
-    "5": "Extending Python",
-    "6": "Python's design",
-    "7": "Using Python on non-UNIX platforms",
-}
-
-class FAQServer:
-
-    def __init__(self):
-       pass
-
-    def main(self):
-       self.form = cgi.FieldStorage()
-       req = self.req or 'frontpage'
-       try:
-           method = getattr(self, 'do_%s' % req)
-       except AttributeError:
-           print "Unrecognized request type", req
-       else:
-           method()
-           self.epilogue()
-
-    KEYS = ['req', 'query', 'name', 'text', 'commit', 'title',
-           'author', 'email', 'log', 'section', 'number', 'add',
-           'version', 'edit', 'password']
-
-    def __getattr__(self, key):
-       if key not in self.KEYS:
-           raise AttributeError
-       try:
-           form = self.form
-           try:
-               item = form[key]
-           except TypeError, msg:
-               raise KeyError, msg, sys.exc_traceback
-       except KeyError:
-           return ''
-       value = self.form[key].value
-       value = string.strip(value)
-       setattr(self, key, value)
-       return value
-
-    def do_frontpage(self):
-       self.prologue("Python FAQ Wizard (beta test)")
-       print """
-       <UL>
-       <LI><A HREF="faq.py?req=index">FAQ index</A>
-       <LI><A HREF="faq.py?req=all">The whole FAQ</A>
-       <LI><A HREF="faq.py?req=roulette">FAQ roulette</A>
-       <LI><A HREF="faq.py?req=recent">Recently changed FAQ entries</A>
-       <LI><A HREF="faq.py?req=add">Add a new FAQ entry</A>
-       <LI><A HREF="faq.py?req=delete">Delete a FAQ entry</A>
-       </UL>
-
-       <HR>
-
-       <H2>Search the FAQ</H2>
-
-       <FORM ACTION="faq.py">
-       <INPUT TYPE=text NAME=query>
-       <INPUT TYPE=submit VALUE="Search"><BR>
-       (Case insensitive regular expressions.)
-       <INPUT TYPE=hidden NAME=req VALUE=query>
-       </FORM>
-       <HR>
-       <P>
-       Disclaimer: these pages are intended to be edited by anyone.
-       Please exercise discretion when editing, don't be rude, etc.
-       """
-
-    def do_index(self):
-       self.prologue("Python FAQ Index")
-       names = os.listdir(os.curdir)
-       names.sort()
-       section = None
-       for name in names:
-           headers, text = self.read(name)
-           if headers:
-               title = headers['title']
-               i = string.find(title, '.')
-               nsec = title[:i]
-               if nsec != section:
-                   if section:
-                       print """
-                       <P>
-                       <LI><A HREF="faq.py?req=add&amp;section=%s"
-                       >Add new entry</A> (at this point)
-                       </UL>
-                       """ % section
-                   section = nsec
-                   if SECTIONS.has_key(section):
-                       stitle = SECTIONS[section]
-                   else:
-                       stitle = ""
-                   print "<H2>Section %s. %s</H2>" % (section, stitle)
-                   print "<UL>"
-               print '<LI><A HREF="faq.py?req=show&name=%s">%s</A>' % (
-                   name, cgi.escape(title))
-       if section:
-           print """
-           <P>
-           <LI><A HREF="faq.py?req=add&amp;section=%s">Add new entry</A>
-           (at this point)
-           </UL>
-           """ % section
-       else:
-           print "No FAQ entries?!?!"
-
-    def do_show(self):
-       self.prologue("Python FAQ Entry")
-       print "<HR>"
-       name = self.name
-       headers, text = self.read(name)
-       if not headers:
-           self.error("Invalid file name", name)
-           return
-       self.show(name, headers['title'], text)
-
-    def do_all(self):
-       import fnmatch, stat
-       self.prologue("The Whole Python FAQ")
-       names = os.listdir(os.curdir)
-       lastmtime = 0
-       for name in names:
-           if not fnmatch.fnmatch(name, NAMEPAT):
-               continue
-           try:
-               st = os.stat(name)
-           except os.error:
-               continue
-           lastmtime = max(lastmtime, st[stat.ST_MTIME])
-       if lastmtime:
-           print time.strftime("Last changed on %c %Z",
-                               time.localtime(lastmtime))
-       names.sort()
-       section = None
-       print "<HR>"
-       for name in names:
-           headers, text = self.read(name)
-           if headers:
-               title = headers['title']
-               i = string.find(title, '.')
-               nsec = title[:i]
-               if nsec != section:
-                   section = nsec
-                   if SECTIONS.has_key(section):
-                       stitle = SECTIONS[section]
-                   else:
-                       stitle = ""
-                   print "<H1>Section %s. %s</H1>" % (section, stitle)
-                   print "<HR>"
-               self.show(name, title, text, edit=(self.edit != 'no'))
-       if not section:
-           print "No FAQ entries?!?!"
-
-    def do_roulette(self):
-       import whrandom
-       self.prologue("Python FAQ Roulette")
-       print """
-       Please check the correctness of the entry below.
-       If you find any problems, please edit the entry.
-       <P>
-       <HR>
-       """
-       names = os.listdir(os.curdir)
-       while names:
-           name = whrandom.choice(names)
-           headers, text = self.read(name)
-           if headers:
-               self.show(name, headers['title'], text)
-               print "<P>Use `Reload' to show another one."
-               break
-           else:
-               names.remove(name)
-       else:
-           print "No FAQ entries?!?!"
-
-    def do_recent(self):
-       import fnmatch, stat
-       names = os.listdir(os.curdir)
-       now = time.time()
-       list = []
-       for name in names:
-           if not fnmatch.fnmatch(name, NAMEPAT):
-               continue
-           try:
-               st = os.stat(name)
-           except os.error:
-               continue
-           tuple = (st[stat.ST_MTIME], name)
-           list.append(tuple)
-       list.sort()
-       list.reverse()
-       self.prologue("Python FAQ, Most Recently Modified First")
-       print "<HR>"
-       n = 0
-       for (mtime, name) in list:
-           headers, text = self.read(name)
-           if headers and headers.has_key('last-changed-date'):
-               self.show(name, headers['title'], text)
-               n = n+1
-       if not n:
-           print "No FAQ entries?!?!"
-
-    def do_query(self):
-       query = self.query
-       if not query:
-           self.error("No query string")
-           return
-       import regex
-       self.prologue("Python FAQ Query Results")
-       p = regex.compile(query, regex.casefold)
-       names = os.listdir(os.curdir)
-       names.sort()
-       print "<HR>"
-       n = 0
-       for name in names:
-           headers, text = self.read(name)
-           if headers:
-               title = headers['title']
-               if p.search(title) >= 0 or p.search(text) >= 0:
-                   self.show(name, title, text)
-                   n = n+1
-       if not n:
-           print "No hits."
-
-    def do_add(self):
-       section = self.section
-       if not section:
-           self.prologue("How to add a new FAQ entry")
-           print """
-           Go to the <A HREF="faq.py?req=index">FAQ index</A>
-           and click on the "Add new entry" link at the end
-           of the section to which you want to add the entry.
-           """
-           return
-       try:
-           nsec = string.atoi(section)
-       except ValueError:
-           print "Bad section number", nsec
-       names = os.listdir(os.curdir)
-       max = 0
-       import regex
-       prog = regex.compile(NAMEREG)
-       for name in names:
-           if prog.match(name) >= 0:
-               s1, s2 = prog.group(1, 2)
-               n1, n2 = string.atoi(s1), string.atoi(s2)
-               if n1 == nsec:
-                   if n2 > max:
-                       max = n2
-       if not max:
-           self.error("Can't add new sections yet.")
-           return
-       num = max+1
-       name = "faq%02d.%03d.htp" % (nsec, num)
-       self.name = name
-       self.add = "yes"
-       self.number = str(num)
-       self.do_edit()
-
-    def do_delete(self):
-       self.prologue("How to delete a FAQ entry")
-       print """
-       At the moment, there's no direct way to delete entries.
-       This is because the entry numbers are also their
-       unique identifiers -- it's a bad idea to renumber entries.
-       <P>
-       If you really think an entry needs to be deleted,
-       change the title to "(deleted)" and make the body
-       empty (keep the entry number in the title though).
-       """
-
-    def do_edit(self):
-       name = self.name
-       headers, text = self.read(name)
-       if not headers:
-           self.error("Invalid file name", name)
-           return
-       self.prologue("Python FAQ Edit Wizard - Edit Form")
-       print '<A HREF="/python/faqhelp.html">Click for Help</A>'
-       title = headers['title']
-       version = self.getversion(name)
-       print "<FORM METHOD=POST ACTION=faq.py>"
-       self.showedit(name, title, text)
-       if self.add:
-           print """
-           <INPUT TYPE=hidden NAME=add VALUE=%s>
-           <INPUT TYPE=hidden NAME=section VALUE=%s>
-           <INPUT TYPE=hidden NAME=number VALUE=%s>
-           """ % (self.add, self.section, self.number)
-       print """
-       <INPUT TYPE=submit VALUE="Review Edit">
-       <INPUT TYPE=hidden NAME=req VALUE=review>
-       <INPUT TYPE=hidden NAME=name VALUE=%s>
-       <INPUT TYPE=hidden NAME=version VALUE=%s>
-       </FORM>
-       <HR>
-       """ % (name, version)
-       self.show(name, title, text, edit=0)
-
-    def do_review(self):
-       if self.commit:
-           self.checkin()
-           return
-       name = self.name
-       text = self.text
-       title = self.title
-       headers, oldtext = self.read(name)
-       if not headers:
-           self.error("Invalid file name", name)
-           return
-       if self.author or '@' in self.email or self.password:
-           self.set_cookie(self.author, self.email, self.password)
-       self.prologue("Python FAQ Edit Wizard - Review Form")
-       print '<A HREF="/python/faqhelp.html">Click for Help</A>'
-       print "<HR>"
-       self.show(name, title, text, edit=0)
-       print "<FORM METHOD=POST ACTION=faq.py>"
-       if self.password == PASSWORD \
-          and self.log and self.author and '@' in self.email:
-           print """
-           <INPUT TYPE=submit NAME=commit VALUE="Commit">
-           Click this button to commit the change.
-           <P>
-           <HR>
-           <P>
-           """
-       else:
-           print """
-           To commit this change, please enter a log message,
-           your name, your email address,
-           and the correct password in the form below.
-           <P>
-           <HR>
-           <P>
-           """
-       self.showedit(name, title, text)
-       if self.add:
-           print """
-           <INPUT TYPE=hidden NAME=add VALUE=%s>
-           <INPUT TYPE=hidden NAME=section VALUE=%s>
-           <INPUT TYPE=hidden NAME=number VALUE=%s>
-           """ % (self.add, self.section, self.number)
-       print """
-       <BR>
-       <INPUT TYPE=submit VALUE="Review Edit">
-       <INPUT TYPE=hidden NAME=req VALUE=review>
-       <INPUT TYPE=hidden NAME=name VALUE=%s>
-       <INPUT TYPE=hidden NAME=version VALUE=%s>
-       </FORM>
-       <HR>
-       """ % (name, self.version)
-
-    def do_info(self):
-       name = self.name
-       headers, text = self.read(name)
-       if not headers:
-           self.error("Invalid file name", name)
-           return
-       self.prologue("Info for %s" % name)
-       print '<PRE>'
-       p = os.popen("/depot/gnu/plat/bin/rlog -r %s </dev/null 2>&1" %
-                    self.name)
-       output = p.read()
-       p.close()
-       print cgi.escape(output)
-       print '</PRE>'
-       print '<A HREF="faq.py?req=rlog&name=%s">View full rcs log</A>' % name
-
-    def do_rlog(self):
-       name = self.name
-       headers, text = self.read(name)
-       if not headers:
-           self.error("Invalid file name", name)
-           return
-       self.prologue("RCS log for %s" % name)
-       print '<PRE>'
-       p = os.popen("/depot/gnu/plat/bin/rlog %s </dev/null 2>&1" % self.name)
-       output = p.read()
-       p.close()
-       print cgi.escape(output)
-       print '</PRE>'
-
-    def checkin(self):
-       import regsub, time, tempfile
-       name = self.name
-       password = self.password
-       if password != PASSWORD:
-           self.error("Invalid password.")
-           return
-       if not (self.log and self.author and '@' in self.email):
-           self.error("No log message, no author, or invalid email.")
-           return
-       headers, oldtext = self.read(name)
-       if not headers:
-           self.error("Invalid file name", name)
-           return
-       version = self.version
-       curversion = self.getversion(name)
-       if version != curversion:
-           self.error(
-               "Version conflict.",
-               "You edited version %s but current version is %s." % (
-                   version, curversion),
-               """
-               <P>
-               The two most common causes of this problem are:
-               <UL>
-               <LI>After committing a change, you went back in your browser,
-                   edited the entry some more, and clicked Commit again.
-               <LI>Someone else started editing the same entry and committed
-                   before you did.
-               </UL>
-               <P>
-               """,
-               '<A HREF="faq.py?req=show&name=%s"' % name,
-               '>Click here to reload the entry and try again.</A>')
-           return
-       text = self.text
-       title = self.title
-       author = self.author
-       email = self.email
-       log = self.log
-       text = regsub.gsub("\r\n", "\n", text)
-       log = regsub.gsub("\r\n", "\n", log)
-       author = string.join(string.split(author))
-       email = string.join(string.split(email))
-       title = string.join(string.split(title))
-       oldtitle = headers['title']
-       oldtitle = string.join(string.split(oldtitle))
-       text = string.strip(text)
-       oldtext = string.strip(oldtext)
-       if text == oldtext and title == oldtitle:
-           self.error("No changes.")
-           return
-       # Check that the FAQ entry number didn't change
-       if string.split(title)[:1] != string.split(oldtitle)[:1]:
-           self.error("Don't change the FAQ entry number please.")
-           return
-       remhost = os.environ["REMOTE_HOST"]
-       remaddr = os.environ["REMOTE_ADDR"]
-       try:
-           os.unlink(name + "~")
-       except os.error:
-           pass
-       try:
-           os.rename(name, name + "~")
-       except os.error:
-           pass
-       try:
-           os.unlink(name)
-       except os.error:
-           pass
-       try:
-           f = open(name, "w")
-       except IOError, msg:
-           self.error("Can't open", name, "for writing:", cgi.escape(str(msg)))
-           return
-       now = time.ctime(time.time())
-       f.write("Title: %s\n" % title)
-       f.write("Last-Changed-Date: %s\n" % now)
-       f.write("Last-Changed-Author: %s\n" % author)
-       f.write("Last-Changed-Email: %s\n" % email)
-       f.write("Last-Changed-Remote-Host: %s\n" % remhost)
-       f.write("Last-Changed-Remote-Address: %s\n" % remaddr)
-       keys = headers.keys()
-       keys.sort()
-       keys.remove('title')
-       for key in keys:
-           if key[:13] != 'last-changed-':
-               f.write("%s: %s\n" % (string.capwords(key, '-'),
-                                     headers[key]))
-       f.write("\n")
-       f.write(text)
-       f.write("\n")
-       f.close()
-
-       tfn = tempfile.mktemp()
-       f = open(tfn, "w")
-       f.write("Last-Changed-Date: %s\n" % now)
-       f.write("Last-Changed-Author: %s\n" % author)
-       f.write("Last-Changed-Email: %s\n" % email)
-       f.write("Last-Changed-Remote-Host: %s\n" % remhost)
-       f.write("Last-Changed-Remote-Address: %s\n" % remaddr)
-       f.write("\n")
-       f.write(log)
-       f.write("\n")
-       f.close()
-
-       # Do this for show() below
-       self.headers = {
-           'title': title,
-           'last-changed-date': now,
-           'last-changed-author': author,
-           'last-changed-email': email,
-           'last-changed-remote-host': remhost,
-           'last-changed-remote-address': remaddr,
-           }
-       
-       p = os.popen("""
-       /depot/gnu/plat/bin/rcs -l %s </dev/null 2>&1
-       /depot/gnu/plat/bin/ci -u %s <%s 2>&1
-       rm -f %s
-       """ % (name, name, tfn, tfn))
-       output = p.read()
-       sts = p.close()
-       if not sts:
-           self.set_cookie(author, email, password)
-           self.prologue("Python FAQ Entry Edited")
-           print "<HR>"
-           self.show(name, title, text)
-           if output:
-               print "<PRE>%s</PRE>" % cgi.escape(output)
-       else:
-           self.error("Python FAQ Entry Commit Failed",
-                      "Exit status 0x%04x" % sts)
-           if output:
-               print "<PRE>%s</PRE>" % cgi.escape(output)
-
-    def set_cookie(self, author, email, password):
-       name = "Python-FAQ-Wizard"
-       value = "%s/%s/%s" % (author, email, password)
-       import urllib
-       value = urllib.quote(value)
-       print "Set-Cookie: %s=%s; path=/cgi-bin/;" % (name, value),
-       import time
-       now = time.time()
-       then = now + 28 * 24 * 3600
-       gmt = time.gmtime(then)
-       print time.strftime("expires=%a, %d-%b-%x %X GMT", gmt)
-
-    def get_cookie(self):
-       if not os.environ.has_key('HTTP_COOKIE'):
-           return "", "", ""
-       raw = os.environ['HTTP_COOKIE']
-       words = map(string.strip, string.split(raw, ';'))
-       cookies = {}
-       for word in words:
-           i = string.find(word, '=')
-           if i >= 0:
-               key, value = word[:i], word[i+1:]
-               cookies[key] = value
-       if not cookies.has_key('Python-FAQ-Wizard'):
-           return "", "", ""
-       value = cookies['Python-FAQ-Wizard']
-       import urllib
-       value = urllib.unquote(value)
-       words = string.split(value, '/')
-       while len(words) < 3:
-           words.append('')
-       author = string.join(words[:-2], '/')
-       email = words[-2]
-       password = words[-1]
-       return author, email, password
-
-    def showedit(self, name, title, text):
-       author = self.author
-       email = self.email
-       password = self.password
-       if not author or not email or not password:
-           a, e, p = self.get_cookie()
-           author = author or a
-           email = email or e
-           password = password or p
-       print """
-       Title: <INPUT TYPE=text SIZE=70 NAME=title VALUE="%s"><BR>
-       <TEXTAREA COLS=80 ROWS=20 NAME=text>%s\n</TEXTAREA>""" % (
-           self.escape(title), cgi.escape(string.strip(text)))
-       print """<BR>
-       Log message (reason for the change):<BR>
-       <TEXTAREA COLS=80 ROWS=5 NAME=log>%s\n</TEXTAREA><BR>
-       Please provide the following information for logging purposes:
-       <TABLE FRAME=none COLS=2>
-         <TR>
-           <TD>Name:
-           <TD><INPUT TYPE=text SIZE=40 NAME=author VALUE="%s">
-         <TR>
-           <TD>Email:
-           <TD><INPUT TYPE=text SIZE=40 NAME=email VALUE="%s">
-         <TR>
-           <TD>Password:
-           <TD><INPUT TYPE=password SIZE=40 NAME=password VALUE="%s">
-       </TABLE>
-       """ % (self.escape(self.log), self.escape(author),
-              self.escape(email), self.escape(password))
-
-    def escape(self, s):
-       import regsub
-       if '&' in s:
-           s = regsub.gsub("&", "&amp;", s)    # Must be done first!
-       if '<' in s:
-           s = regsub.gsub("<", "&lt;", s)
-       if '>' in s:
-           s = regsub.gsub(">", "&gt;", s)
-       if '"' in s:
-           s = regsub.gsub('"', "&quot;", s)
-       return s
-
-    def showheaders(self, headers):
-       print "<UL>"
-       keys = map(string.lower, headers.keys())
-       keys.sort()
-       for key in keys:
-           print "<LI><B>%s:</B> %s" % (string.capwords(key, '-'),
-                                        headers[key] or '')
-       print "</UL>"
-
-    headers = None
-
-    def read(self, name):
-       self.headers = None
-       import fnmatch, rfc822
-       if not fnmatch.fnmatch(name, NAMEPAT):
-           return None, None
-       if self.add:
-           try:
-               fname = "faq%02d.%03d.htp" % (string.atoi(self.section),
-                                             string.atoi(self.number))
-           except ValueError:
-               return None, None
-           if fname != name:
-               return None, None
-           headers = {'title': "%s.%s. " % (self.section, self.number)}
-           text = ""
-       else:
-           f = open(name)
-           headers = rfc822.Message(f)
-           text = f.read()
-           f.close()
-       self.headers = headers
-       return headers, text
-
-    def show(self, name, title, text, edit=1):
-       print "<H2>%s</H2>" % cgi.escape(title)
-       pre = 0
-       for line in string.split(text, '\n'):
-           if not string.strip(line):
-               if pre:
-                   print '</PRE>'
-                   pre = 0
-               else:
-                   print '<P>'
-           else:
-               if line[0] not in string.whitespace:
-                   if pre:
-                       print '</PRE>'
-                       pre = 0
-               else:
-                   if not pre:
-                       print '<PRE>'
-                       pre = 1
-               if '/' in line or '@' in line:
-                   line = self.translate(line)
-               elif '<' in line or '&' in line:
-                   line = cgi.escape(line)
-               if not pre and '*' in line:
-                   line = self.emphasize(line)
-               print line
-       if pre:
-           print '</PRE>'
-           pre = 0
-       print '<P>'
-       if edit:
-           print """
-           <A HREF="faq.py?req=edit&name=%s">Edit this entry</A> /
-           <A HREF="faq.py?req=info&name=%s" TARGET=rlog>Log info</A>
-           """ % (name, name)
-           if self.headers:
-               try:
-                   date = self.headers['last-changed-date']
-                   author = self.headers['last-changed-author']
-                   email = self.headers['last-changed-email']
-               except KeyError:
-                   pass
-               else:
-                   s = '/ Last changed on %s by <A HREF="mailto:%s">%s</A>'
-                   print s % (date, email, author)
-           print '<P>'
-       print "<HR>"
-
-    def getversion(self, name):
-       p = os.popen("/depot/gnu/plat/bin/rlog -h %s </dev/null 2>&1" % name)
-       head = "*new*"
-       while 1:
-           line = p.readline()
-           if not line:
-               break
-           if line[:5] == 'head:':
-               head = string.strip(line[5:])
-       p.close()
-       return head
-
-    def prologue(self, title):
-       title = cgi.escape(title)
-       print '''
-       <HTML>
-       <HEAD>
-       <TITLE>%s</TITLE>
-       </HEAD>
-       <BODY BACKGROUND="http://www.python.org/pics/RedShort.gif"
-             BGCOLOR="#FFFFFF"
-             TEXT="#000000"
-             LINK="#AA0000"
-             VLINK="#906A6A">
-       <H1>%s</H1>
-       ''' % (title, title)
-
-    def error(self, *messages):
-       self.prologue("Python FAQ error")
-       print "Sorry, an error occurred:<BR>"
-       for message in messages:
-           print message,
-       print
-
-    def epilogue(self):
-       if self.edit == 'no':
-           global wanttime
-           wanttime = 0
-       else:
-           print '''
-           <P>
-           <HR>
-           <A HREF="http://www.python.org">Python home</A> /
-           <A HREF="faq.py?req=frontpage">FAQ Wizard home</A> /
-           Feedback to <A HREF="mailto:guido@python.org">GvR</A>
-           '''
-       print '''
-       </BODY>
-       </HTML>
-       '''
-
-    translate_prog = None
-
-    def translate(self, text):
-       if not self.translate_prog:
-           import regex
-           url = '\(http\|ftp\)://[^ \t\r\n]*'
-           email = '\<[-a-zA-Z0-9._]+@[-a-zA-Z0-9._]+'
-           self.translate_prog = prog = regex.compile(url + "\|" + email)
-       else:
-           prog = self.translate_prog
-       i = 0
-       list = []
-       while 1:
-           j = prog.search(text, i)
-           if j < 0:
-               break
-           list.append(cgi.escape(text[i:j]))
-           i = j
-           url = prog.group(0)
-           while url[-1] in ");:,.?'\"":
-               url = url[:-1]
-           url = self.escape(url)
-           if ':' in url:
-               repl = '<A HREF="%s">%s</A>' % (url, url)
-           else:
-               repl = '<A HREF="mailto:%s">&lt;%s&gt;</A>' % (url, url)
-           list.append(repl)
-           i = i + len(url)
-       j = len(text)
-       list.append(cgi.escape(text[i:j]))
-       return string.join(list, '')
-
-    emphasize_prog = None
-
-    def emphasize(self, line):
-       import regsub
-       if not self.emphasize_prog:
-           import regex
-           pat = "\*\([a-zA-Z]+\)\*"
-           self.emphasize_prog = prog = regex.compile(pat)
-       else:
-           prog = self.emphasize_prog
-       return regsub.gsub(prog, "<I>\\1</I>", line)
-
-print "Content-type: text/html"
-dt = 0
-wanttime = 0
-try:
-    import time
-    t1 = time.time()
-    import cgi, string, os, sys
-    x = FAQServer()
-    x.main()
-    t2 = time.time()
-    dt = t2-t1
-    wanttime = 1
-except:
-    print "\n<HR>Sorry, an error occurred"
-    cgi.print_exception()
-if wanttime:
-    print "<BR>(running time = %s seconds)" % str(round(dt, 3))
-
-# The following bootstrap script must be placed in cgi-bin/faq.py:
-BOOTSTRAP = """
-#! /usr/local/bin/python
-FAQDIR = "/usr/people/guido/python/FAQ"
-import os, sys
-os.chdir(FAQDIR)
-sys.path.insert(0, os.curdir)
-import faqmain
-"""