|
From: Karl T. K. <ka...@us...> - 2001-03-06 00:12:19
|
Update of /cvsroot/linuxdc/docs/tools/ht2html
In directory usw-pr-cvs1:/tmp/cvs-serv31423
Added Files:
Banner.py HTParser.py LinkFixer.py LinuxDCGenerator.py
Sidebar.py Skeleton.py
Log Message:
Added a proper toolset
--- NEW FILE ---
"""Generate a site links table for use in a banner.
"""
import sys
try:
from cStringIO import StringIO
except IOError:
from StringIO import StringIO
class Banner:
def __init__(self, links, cols=4):
"""Initialize the Banner instance.
This class is intended to be a mixin-class with Skeleton.
links must be a list of 2-tuples of the form: (URL, text). If URL is
None, then the text is not hyperlinked. These are arranged in a table
in order, evenly in the specified number of columns.
"""
self.__links = links
self.__cols = cols
rows, leftover = divmod(len(links), self.__cols)
if leftover:
rows = rows + 1
self.__rows = rows
def get_banner(self):
stdout = sys.stdout
html = StringIO()
try:
sys.stdout = html
self.__start_table()
self.__do_table()
self.__end_table()
finally:
sys.stdout = stdout
return html.getvalue()
def __start_table(self):
print '<!-- start of site links table -->'
print '<TABLE WIDTH="100%" BORDER=0'
print self.get_banner_attributes()
print ' COLS=%s ROWS=%s BGCOLOR="%s">' % (
self.__cols, self.__rows, self.get_bgcolor())
print '<TR>'
def __end_table(self):
print '</TR>'
print '</TABLE><!-- end of site links table -->'
def __do_table(self):
col = 0
for item in self.__links:
if len(item) == 3:
url, text, extra = item
else:
url, text = item
extra = ''
if not url:
s = text + extra
else:
s = '<a href="%s">%s</a>%s' % (url, text, extra)
if col >= self.__cols:
# break the row
print '</TR><TR>'
col = 0
print ' <TD BGCOLOR="%s">' % self.get_lightshade()
print s
print ' </TD>'
col = col + 1
# fill rest of row with non-breaking spaces.
while col and col < self.__cols:
print ' <TD BGCOLOR="%s">' % self.get_lightshade()
print ' </TD>'
col = col + 1
from Skeleton import _Skeleton
class _Banner(_Skeleton, Banner):
def __init__(self, links):
Banner.__init__(self, links)
def get_banner(self):
return Banner.get_banner(self)
if __name__ == '__main__':
t = _Banner([('page1.html', 'First Page'),
('page2.html', 'Second Page'),
('page3.html', 'Third Page'),
('page4.html', 'Fourth Page'),
(None, '<b>Fifth Page</b>'),
('page6.html', 'Sixth Page'),
])
print t.makepage()
--- NEW FILE ---
"""HTParser -- parse a www.python.org .ht file.
"""
import os
import string
import re
import rfc822
from socket import gethostbyaddr, gethostbyname, gethostname
from types import StringType
class HTParser(rfc822.Message):
def __init__(self, filename, default_author=None):
self.__filename = filename
self.__fp = fp = open(filename)
self.__extraheaders = {}
rfc822.Message.__init__(self, fp)
#
# massage some standard headers we require
#
# title
if not self.has_key('title'):
parts = string.split(self.__filename, os.sep)
self.__extraheaders['title'] = parts[-1]
# author
if not self.has_key('author'):
if default_author is not None:
author = default_author
else:
domainname = None
h, a, ip = gethostbyaddr(gethostbyname(gethostname()))
for host in [h] + a:
i = string.find(host, '.')
if i > 0:
domainname = host[i+1:]
break
if domainname:
author = 'webmaster@' + domainname
else:
author = 'MUST SUPPLY AN AUTHOR'
self.__extraheaders['author'] = author
# public ivar
self.sidebar = []
# override so we can access our own internal dictionary if the message
# doesn't have it
def __getitem__(self, item):
try:
return rfc822.Message.__getitem__(self, item)
except KeyError:
return self.__extraheaders[item]
# might be using an older rfc822
def get(self, name, default=None):
if self.has_key(name):
return self.getheader(name)
elif self.__extraheaders.has_key(name):
return self.__extraheaders[name]
else:
return default
def process_sidebar(self):
# first process all link files. Either we hard code the use of
# ./links.h or we look for a Links: header. If the header exists, it
# must explicitly enumerate links.h
linkfiles = self.get('links', 'links.h')
for file in string.split(linkfiles):
try:
fp = open(string.strip(file))
except IOError:
continue
data = fp.read()
fp.close()
self.__parse(data)
# Other-links header specifies more links in-lined
otherlinks = self.get('other-links')
if otherlinks:
self.__parse(otherlinks)
# always have an email address
self.sidebar.append('Email Us')
author = self.get('author') # guaranteed
email = self.get('author-email', author)
self.sidebar.append(('mailto:' + email, author))
# regular expressions used in massage()
cre = re.compile(
r'(<h3>(?P<h3>.*?)</h3>)|'
r'(<li>(<a href="?(?P<link>[^<>"]*)"?>(?P<contents>[^<>]*)</a>)?)'
r'(?P<extra>[^\n]*)',
re.DOTALL | re.IGNORECASE)
def __parse(self, text):
"""Apply various bits of magic to the links in this list.
"""
start = 0
end = len(text)
while 1:
mo = self.cre.search(text, start)
if not mo:
## if start <> end:
## s = string.strip(text[start:])
## tags.append(s)
break
mstart = mo.start(0)
## s = string.strip(text[start:mstart])
## if s:
## tags.append(s)
h3, link, contents, extra = \
mo.group('h3', 'link', 'contents', 'extra')
if link is None:
link = ''
if contents is None:
contents = ''
if h3:
self.sidebar.append(string.strip(h3))
elif extra:
l = map(string.strip, (link, contents))
l.append(extra)
self.sidebar.append(tuple(l))
else:
link = tuple(map(string.strip, (link, contents)))
self.sidebar.append(link)
start = mo.end(0)
--- NEW FILE ---
"""LinkFixer class.
Does two things. Takes as input entry lists as expected by the Sidebar and
Banner classes, and returns a massaged list where any current links are
boldified and turned into non-links. Also supports global string
interpolation over the links using a dictionary.
"""
import sys
import string
from types import StringType
class LinkFixer:
def __init__(self, myurl, rootdir='.', relthis='.', verbose=0):
self.__rootdir = rootdir
self.__relthis = relthis
self.__verbose = verbose
self.msg('rootdir=%s, relthis=%s', rootdir, relthis)
self.__myurl = self.normalize(myurl)
def msg(self, fmt, *args):
if self.__verbose:
msg = fmt % args
if msg[-1] <> '\n':
msg = msg + '\n'
sys.stdout.write(msg)
def normalize(self, url):
self.msg('url= %s', url)
if url is None:
return
if url == './':
url = 'index.html'
elif url[-1] == '/':
url = url + 'index.html'
absurl = string.join([self.__rootdir, self.__relthis, url], '/')
# normalize the path, kind of the way os.path.normpath() does.
# urlparse ought to have something like this...
parts = []
for p in string.split(absurl, '/'):
if p == '.':
continue
if p == '..' and len(parts) > 0:
del parts[-1]
parts.append(p)
absurl = string.join(parts, '/')
self.msg('absurl= %s', absurl)
return absurl
def massage(self, links, dict=None, aboves=0):
"""Substitute in situ before massaging.
With dict, do a dictionary substitution on only the URLs first. With
aboves=1, boldify links if they are above myurl.
"""
for i in range(len(links)):
item = links[i]
if type(item) == StringType:
continue
if len(item) == 3:
url, text, extra = item
else:
url, text = item
extra = ''
try:
url = url % dict
except TypeError:
pass
if url:
absurl = self.normalize(url)
else:
absurl = None
self.msg('%s is %s ==? %s', url, absurl, self.__myurl)
if url is None:
links[i] = (url, text, extra)
elif absurl is None:
links[i] = (absurl, text, extra)
elif absurl == self.__myurl:
links[i] = (None, '<b>' + text + '</b>', extra)
elif aboves and self.above(absurl, self.__myurl):
links[i] = (url, '<b>' + text + '</b>', extra)
else:
links[i] = (url, text, extra)
def above(self, absurl, myurl):
"""Return true if absurl is above myurl."""
# Only do one level of checking, and don't match on the root, since
# that's always going to be above everything else.
myurl = self.normalize(myurl)
i = string.rfind(myurl, '/')
j = string.rfind(absurl, '/')
if i > 0 and j > 0 and \
absurl[:j] == myurl[:i] and \
myurl[i+1:] <> 'index.html':
return 1
return 0
## while myurl:
## print ' ', self.normalize(myurl)
## if absurl == self.normalize(myurl + '/'):
## return 1
## i = string.rfind(myurl, '/')
## if i < 0:
## return 0
## myurl = myurl[:i]
## return 0
def rootdir(self):
return self.__rootdir
def relthis(self):
return self.__relthis
def myurl(self):
return self.__myurl
--- NEW FILE ---
"""Generates LinuxDC's style.
"""
import os
from Skeleton import Skeleton
from Sidebar import Sidebar, BLANKCELL
from Banner import Banner
from HTParser import HTParser
from LinkFixer import LinkFixer
author = "ka...@us...";
sitelinks = [
('%(rootdir)s/index.html', 'Home'),
('%(rootdir)s/index.html', 'FAQ'),
('%(rootdir)s/index.html', 'Kernel HOWTO'),
('%(rootdir)s/index.html', 'Tools HOWTO')
]
BLANKCELL = (None, " ")
SEPARATOR = (None, "<hr size=10 noshade>")
leftlinks = [
# ('http://linuxdc.sourceforge.net/', '''<center><img border=0 src="%(rootdir)s/images/LinuxPoweredSmall.gif"></center>'''),
BLANKCELL,
('None', '<b>%(section)s</b>'),
SEPARATOR,
('%(rootdir)s/index.html', 'Kernel installation HOWTO'),
('%(rootdir)s/index.html', 'Toolchain installation HOWTO'),
('%(rootdir)s/index.html', 'Unofficial DC Technical Manual'),
('%(rootdir)s/index.html', 'TODO list'),
SEPARATOR
]
class LinuxDCGenerator(Skeleton, Sidebar, Banner):
def __init__(self, file, rootdir, relthis):
self.__body = None
root, ext = os.path.splitext(file)
html = root + '.html'
p = self.__parser = HTParser(file, author)
f = self.__linkfixer = LinkFixer(html, rootdir, relthis)
p.process_sidebar()
# massage our links
self.__d = {'rootdir': rootdir,
'section': self.get_section() }
self.__linkfixer.massage(p.sidebar, self.__d)
# tweak
for i in leftlinks:
if i[0]:
p.sidebar.append((i[0] % self.__d, i[1] % self.__d))
else:
p.sidebar.append(i)
copyright = self.__parser.get('copyright', '2000-2001')
p.sidebar.append((None, '© ' + copyright))
p.sidebar.append((None, 'The LinuxDC Team'))
Sidebar.__init__(self, p.sidebar)
#
# fix up our site links, no relthis because the site links are
# relative to the root of my web pages
#
sitelink_fixer = LinkFixer(f.myurl(), rootdir)
sitelink_fixer.massage(sitelinks, self.__d, aboves=1)
Banner.__init__(self, sitelinks, cols=2)
def get_corner(self):
rootdir = self.__linkfixer.rootdir()
return '''
<center>
<a href="%(rootdir)s/index.html">
<img border=0 src="%(rootdir)s/images/linuxdc.jpg"></a></center>''' \
% self.__d
def get_corner_bgcolor(self):
return 'black'
def get_banner(self):
return Banner.get_banner(self)
def get_title(self):
return self.__parser.get('title')
def get_section(self):
return self.__parser.get('section')
def get_sidebar(self):
return Sidebar.get_sidebar(self)
def get_banner_attributes(self):
return 'CELLSPACING=0 CELLPADDING=0'
def get_body(self):
if self.__body is None:
self.__body = self.__parser.fp.read()
return self.__body
# python.org color scheme overrides
def get_lightshade(self):
return '#99ccff'
def get_mediumshade(self):
return '#3399ff'
def get_darkshade(self):
return '#003366'
--- NEW FILE ---
"""Sidebar generator.
"""
import sys
from types import StringType
try:
from cStringIO import StringIO
except IOError:
from StringIO import StringIO
# a useful constant
BLANKCELL = (None, ' ')
class Sidebar:
def __init__(self, links):
"""Initialize the Sidebar instance.
This class is indented to be a mixin-class with Skeleton.
links must be a list of elements for the sidebar. Each entry in the
list can either be a string, indicating that the entry is a category
header, or a 2-tuple of the form: (URL, text) indicating it is taken
as a link to include under the category.
If the entry is a two tuple, the URL can be None to indicate that
there is no link to that text.
"""
self.links = links
def get_sidebar(self):
stdout = sys.stdout
html = StringIO()
try:
sys.stdout = html
self.__start_table()
self.__do_link()
self.__finish()
finally:
sys.stdout = stdout
return html.getvalue()
def __start_table(self):
print '<!-- start of sidebar table -->'
print '<TABLE WIDTH="100%" BORDER=0 CELLSPACING=0 CELLPADDING=3'
print ' BGCOLOR="%s">' % self.get_bgcolor()
def __finish(self):
print '</TABLE><!-- end of sidebar table -->'
def __do_link(self):
done_one = 0
for item in self.links:
if type(item) == StringType:
# category header
if done_one:
# get some separation between header and last item
print '<TR><TD BGCOLOR="%s"> ' % (
self.get_lightshade())
else:
done_one = 1
print '<TR><TD BGCOLOR="%s"><B><FONT COLOR="%s">' % (
self.get_darkshade(), self.get_bgcolor())
print item
print '</FONT></B></TD></TR>'
else:
if len(item) == 3:
url, text, extra = item
else:
url, text = item
extra = ''
if url is None:
s = text
else:
s = '<A HREF="%s">%s</A>' % (url, text)
print '<TR><TD BGCOLOR="%s">' % self.get_lightshade()
print '%s%s' % (s, extra)
print '</TD></TR>'
from Skeleton import _Skeleton
from Banner import _Banner
class _Sidebar(_Skeleton, _Banner, Sidebar):
def __init__(self, sitelinks, toclinks):
_Banner.__init__(self, sitelinks)
Sidebar.__init__(self, toclinks)
def get_sidebar(self):
return Sidebar.get_sidebar(self)
def get_banner(self):
return _Banner.get_banner(self)
if __name__ == '__main__':
t = _Sidebar(
# banner links
[('page1.html', 'First Page'),
('page2.html', 'Second Page'),
('page3.html', 'Third Page'),
('page4.html', 'Fourth Page'),
(None, '<b>Fifth Page</b>'),
('page6.html', 'Sixth Page'),
],
# sidebar links
['Special Topics',
('topics.html', 'Topic Guides'),
('download.html', 'Downloads'),
('windows.html', 'Windows'),
('jpython.html', 'JPython'),
('tkinter.html', 'Tkinter'),
('emacs.html', 'Emacs'),
'See also',
('conferences.html', 'Python Conferences'),
('sitemap.html', 'Site map'),
('mirrors.html', 'Mirror sites'),
(None, '<b>What is Python?</b>'),
'Exits',
('starship.html', '(New) Starship'),
('starship.html', 'Old Starship'),
('cnri.html', 'CNRI'),
'Email us',
(None, 'For help with Python:'),
('help.html', 'pyt...@py...'),
(None, 'For help with Website:'),
('web.html', 'web...@py...'),
(None, '<br>'),
('pp.html', '[Python Powered]'),
])
print t.makepage()
--- NEW FILE ---
"""Skeleton class.
Should be sub-classed to provide basic generation of able-contained HTML
document.
"""
from ht2html import __version__
import sys
import time
try:
from cStringIO import StringIO
except IOError:
from StringIO import StringIO
class Skeleton:
#
# for sub-classes to override
#
def get_banner(self):
"""Returns HTML for the top banner, or None if no banner.
"""
return None
def get_sidebar(self):
"""Returns HTML for the left sidebar or None.
"""
return None
def get_banner_width(self):
"""HTML `width' of banner column as a percentage.
Should be a string that does not include the percent sign (e.g. "90").
This affects the column containing both the banner and body text (if
they exist).
"""
return "90"
def get_corner(self):
"""Returns HTML for the upper-left corner or None.
Note that if get_banner() and get_sidebar() both return None, then
get_corner() is ignored. Also if both get_banner() and get_sidebar()
return a string, but get_corner() returns None, the smallest blank
corner possible is emitted.
"""
return None
def get_body(self):
"""Returns HTML for the internal document body.
Note that using this method causes the get_sidebar() -- if there is
one -- to run the full height of the page. If you don't want this,
then make sure get_cont() returns a string.
"""
return '<b>Intentionally left blank</b>'
def get_cont(self):
"""Returns HTML for the continuation of the body.
When this method returns a string, and there is a get_sidebar(), the
continuation text appears below the get_sidebar() and get_body() at
the full width of the screen. If there is no get_sidebar(), then
having a get_cont() is pointless.
"""
return None
def get_title(self):
"""Return the title of the page. Required."""
return 'Intentionally left blank'
def get_meta(self):
"""Return extra meta-data. Must be a string."""
return ''
def get_headers(self):
"""Return extra header information. Must be a string."""
return ''
def get_bgcolor(self):
"""Return the background color"""
return '#ffffff'
def get_fgcolor(self):
"""Return foreground color"""
return '#000000'
def get_linkcolor(self):
"""Return link color"""
return '#0000bb'
def get_vlinkcolor(self):
"""Return vlink color"""
return '#551a8b'
def get_alinkcolor(self):
"""Return alink color"""
return '#ff0000'
def get_corner_bgcolor(self):
"""Return the background color for the corner"""
return self.get_lightshade()
# Barry's prefs
def get_lightshade(self):
"""Return lightest of 3 color scheme shade."""
return '#cdba96'
def get_mediumshade(self):
"""Return middle of 3 color scheme shade."""
return '#cc9966'
def get_darkshade(self):
"""Return darkest of 3 color scheme shade."""
return '#b78900'
def get_body_attributes(self):
"""Return extra attributes for the body start tag."""
return 'TOPMARGIN="0" LEFTMARGIN="0" MARGINWIDTH="0" MARGINHEIGHT="0"'
def get_banner_attributes(self):
"""Return extra attributes for the TABLE in the banner."""
return 'CELLSPACING=0 CELLPADDING=2'
# Call this method
def makepage(self):
banner = self.get_banner()
sidebar = self.get_sidebar()
corner = self.get_corner()
body = self.get_body()
cont = self.get_cont()
html = StringIO()
stdout = sys.stdout
closed = 0
try:
sys.stdout = html
self.__do_head()
self.__start_body()
print '<!-- start of page table -->'
print '<TABLE WIDTH="100%" BORDER=0 CELLSPACING=0 CELLPADDING=0>'
if banner is not None:
print '<!-- start of banner row -->'
print '<TR>'
if corner is not None:
self.__do_corner(corner)
print '<!-- start of banner -->'
print '<TD WIDTH="%s%%" BGCOLOR="%s">' % (
self.get_banner_width(), self.get_lightshade())
print banner
print '</TD><!-- end of banner -->'
print '</TR><!-- end of banner row -->'
# if there is a body but no sidebar, then we'll just close the
# table right here and put the body (and any cont) in the full
# page. if there is a sidebar but no body, then we still create
# the new row and just populate the body cell with a non-breaking
# space. Watch out though because we don't want to close the
# table twice
if sidebar is None:
print '</TABLE><!-- end of page table -->'
closed = 1
else:
print '<TR><!-- start of sidebar/body row -->'
self.__do_sidebar(sidebar)
if body is not None:
if closed:
print body
else:
self.__do_body(body)
if not closed:
print '</TR><!-- end of sidebar/body row -->'
print '</TABLE><!-- end of page table -->'
if cont is not None:
self.__do_cont(cont)
self.__finish_all()
finally:
sys.stdout = stdout
return html.getvalue()
def __do_corner(self, corner):
print '<!-- start of corner cells -->'
print '<TD WIDTH=150 VALIGN=CENTER BGCOLOR="%s">' % (
self.get_corner_bgcolor())
# it is important not to have a newline between the corner text and
# the table close tag, otherwise layout is messed up
if corner is None:
print ' ',
else:
print corner,
print '</TD>'
print '<TD WIDTH=15 BGCOLOR="%s"> </TD><!--spacer-->' % (
self.get_lightshade())
print '<!-- end of corner cells -->'
def __do_sidebar(self, sidebar):
print '<!-- start of sidebar cells -->'
print '<TD WIDTH=150 VALIGN=TOP BGCOLOR="%s">' % (
self.get_lightshade())
print sidebar
print '</TD>'
print '<TD WIDTH=15> </TD><!--spacer-->'
print '<!-- end of sidebar cell -->'
def __do_body(self, body):
print '<!-- start of body cell -->'
print '<TD VALIGN=TOP WIDTH="%s%%"><BR>' % (
self.get_banner_width())
print body
print '</TD><!-- end of body cell -->'
def __do_cont(self, cont):
print '<!-- start of continued wide-body text -->'
print cont
print '<!-- end of continued wide-body text -->'
def __do_head(self):
"""Return the HTML <head> stuff."""
print '''\
<HTML>
<!-- THIS PAGE IS AUTOMATICALLY GENERATED. DO NOT EDIT. -->
<!-- %(time)s -->
<!-- USING HT2HTML %(version)s -->
<!-- SEE http://www.python.org/~bwarsaw/software/pyware.html -->
<!-- User-specified headers:
Title: %(title)s
%(headers)s
-->
<HEAD>
<TITLE>%(title)s</TITLE>
%(meta)s
</HEAD>''' % {'title' : self.get_title(),
'headers': self.get_headers(),
'meta' : self.get_meta(),
'time' : time.ctime(time.time()),
'version': __version__,
}
def __start_body(self):
print '''\
<BODY BGCOLOR="%(bgcolor)s" TEXT="%(fgcolor)s"
%(extraattrs)s
LINK="%(linkcolor)s" VLINK="%(vlinkcolor)s"
ALINK="%(alinkcolor)s">''' % {
'bgcolor' : self.get_bgcolor(),
'fgcolor' : self.get_fgcolor(),
'linkcolor' : self.get_linkcolor(),
'vlinkcolor': self.get_vlinkcolor(),
'alinkcolor': self.get_alinkcolor(),
'extraattrs': self.get_body_attributes(),
}
def __finish_all(self):
print '</BODY></HTML>'
# test script
class _Skeleton(Skeleton):
def get_banner(self):
return '<b>The Banner</b>'
def get_sidebar(self):
return '''<ul><li>Sidebar line 1
<li>Sidebar line 2
<li>Sidebar line 3
</ul>'''
def get_corner(self):
return '<center><em>CORNER</em></center>'
def get_body(self):
return 'intentionally left blank ' * 110
def get_cont(self):
return 'wide stuff ' * 100
def get_corner_bgcolor(self):
return 'yellow'
def get_banner_width(self):
return "80"
if __name__ == '__main__':
t = _Skeleton()
print t.makepage()
|