[DOC-SIG] simple but flexible text document classes
Scott
scott@chronis.icgroup.com
Fri, 7 Nov 1997 23:08:28 -0500
periodically I end up writing scripts or programs wchich have the need to
automate the production of fairly simple text documents. After some time
pondering how to make an easy to use interface to these documents while
separating the documents themselves from the control flow of the program, I
started to use the following library.
It's simple, flexible, and about as efficient as I can imagine it being
without being coded in C.
Just thought I'd post it here in case anyone would want to use it one day.
# -*-Python-*-
"""
this module defines two classes and one function. The first class, ddoc,
defines a document which can contain other documents. each ddoc may be
treated like a dictionary to some degree. When you assign a key that belongs
to any subdocuments, it will place the value where you intend it. This
recursive structure allows for creating text documents with many different
parts put together in a structured manner. The topmost document still allows
access and assignment to the variables in the inner documents. The whole
thing is implemented using the format operator so it's pretty fast.
The listdoc class acts much the same way, but allows for repetition of a
single document with different values for the variables each time. With a
listdoc, you simply give the variables in the document (accessed as keys)
value of a list, and it does the rest of the work for you.
the load function facilitates storing the whole document in a separate file.
each document has a method 'save' which stores the document together with the
small amount of information needed to reload it. the load function will load
docs that have been saved with save methods. This doesn't use pickle or
Cpickle because part of the idea of saving the document in a separate file is
to be able to edit it without interfereing with the program(s) that use it.
"""
import string
class ddoc:
error = "error"
def __init__(self, text="", dict=None):
self.text = text
if not dict:
self.dict = {}
else:
self.dict = dict
def __repr__(self):
return self.text % self
def __setitem__(self, key, doc):
#
# if key is in top level values, set that value and return
#
for k in self.dict.keys():
if key == k:
self.dict[key] = doc
return
#
# if key is in top level text, set it at top level
#
if string.find(self.text, '%(' + key + ')') >= 0:
self.dict[key] = doc
#
# if key in keys of each sub doc, set that sub-doc's key to val and return
#
for c in self.children():
if key in c.dict.keys():
c[key] = doc
return
if string.find(c.text, '%(' + key + ')') >= 0:
c[key] = doc
return
#
# key isn't anywhere in doc, so create it at top level
#
self.dict[key] = doc
#
# return any subdoc or subsubdoc, etc or just text as value
#
def __getitem__(self, key):
try:
return self.dict[key]
except KeyError:
if string.find(self.text, "%(" + key + ")") >= 0:
return ''
for c in self.children():
try:
return c[key]
except KeyError:
continue
raise KeyError, key
def __delitem__(self, item):
del self.dict[item]
#
# totally makes object as if it were a newly created document
#
def clear(self):
self.text = ""
self.dict.clear()
#
# return all keys of self + all keys of subdocs, etc
# because of setitem constraints, this should always be a list of unqique elements
#
def keys(self):
return self.leaves() + self.namechildren()
#
# return all subdocs, even if subsubdocs, etc
#
def children(self):
d = self.dict
c = []
for k in d.keys():
if type(d[k]) is type(self):
c.insert(0, d[k])
c = c + d[k].children()
return c
def namechildren(self):
l = []
for k in self.dict.keys():
if type(self.dict[k]) is type(self):
l.insert(0, k)
l = l + self.dict[k].namechildren()
return l
#
# return only keys that are not documents
#
def leaves(self):
d = self.dict
l = []
for k in d.keys():
if type(d[k]) is type(''):
l.insert(0,k)
else:
l = l + d[k].leaves()
return l
def insert(self, index, text):
start = self.text[0:index]
finish = self.text[index:]
self.text = "%s%s%s" % (start, text, finish)
def set_default(self, default, *keys):
for k in keys:
self[k] = default
def has_children(self):
return len(self.namechildren()) == 0
def raw_texts(self):
tlist = [("text", '', self.text)]
for cn in self.namechildren():
tlist.append((cn, string.split(`self[cn].__class__`)[1], self[cn].text))
if self[cn].has_children():
tlist = tlist + self[cn].raw_texts()[1:]
return tlist
def save(self, filename):
fp = open(filename, "w")
rtl = self.raw_texts()
fp.write("\"\"\"" + rtl[0][2] + "\"\"\"\n\n")
d = [("thisdoc", string.split(`self.__class__`)[1])]
for n,c, rt in rtl[1:]:
fp.write("%s=\"\"\"%s\"\"\"\n\n\n" % (n,rt))
d.append((n,c))
fp.write("\n#\n# document types -- do not edit if you don't know how\n#\n_dtypes=\\\n\t%s" % (`d`))
fp.close()
class listdoc(ddoc):
def __init__(self, text="", dict=None):
ddoc.__init__(self, text, dict)
self.dict['__iterator'] = 1
self.__reg_vals = {}
def __setitem__(self, item, val):
if type(val) is type([]):
self.__reg_vals[item] = val
return
ddoc.__setitem__(self, item, val)
def __getitem__(self, item):
if type(item) is type(0):
for k in self.__reg_vals.keys():
self.dict[k] = self.__reg_vals[k][item]
return ddoc.__repr__(self)
return ddoc.__getitem__(self, item)
def __len__(self):
max = 1
for l in self.__reg_vals.values():
if len(l) > max:
max = len(l)
return max
def __repr__(self):
res = ""
for x in range(len(self)):
for k in self.__reg_vals.keys():
self.dict[k] = self.__reg_vals[k][x]
res = "%s%s" % (res, ddoc.__repr__(self))
return res
def clear(self):
ddoc.clear(self)
self.dict['__iterator'] = 1
self.__reg_vals.clear()
def load(filename):
import sys
pl = string.split(filename, "/")
if len(pl) > 1:
dir = string.join(pl[:-1], "/")
sys.path.insert(0, dir)
f = pl[-1][:-3]
else:
if filename[-3:] == ".py":
f = filename[:-3]
else:
f = filename
exec("import %s" % (f))
exec("dtl = %s._dtypes" % (f))
exec("thisdoc = %s()" % (dtl[0][1]))
exec("thisdoc.text = %s.__doc__" % (f))
for dn, dt in dtl[1:]:
exec("%s = %s(); %s.text = %s.%s; thisdoc['%s'] = %s" % (dn,dt,dn,f,dn,dn,dn))
return thisdoc
_______________
DOC-SIG - SIG for the Python Documentation Project
send messages to: doc-sig@python.org
administrivia to: doc-sig-request@python.org
_______________