"""
PySummary

A PySummary instance reads a Python file and creates a summary of the file
which you can access by using it as a (e.g., %s or str()).

The notion of a "category" is recognized. A category is simply a group of
methods with a given name. The default prefix for denoting a category is ##.

"""

from types import DictType
import os


class PySummary:


	## Init ##

	def __init__(self):
		self._filename = None
		self._lines = []
		self.invalidateCache()
		if os.path.exists('PySummary.config'):
			self.readConfig('PySummary.config')


	## Config ##

	def readConfig(self, filename):
		self._settings = eval(open(filename).read())
		assert type(self._settings) is DictType


	## Reading files ##

	def readFileNamed(self, filename):
		self._filename = filename
		file = open(filename)
		self.readFile(file)
		file.close()

	def readFile(self, file):
		self.invalidateCache()
		lines = file.readlines()
		while lines:
			line = lines.pop(0).rstrip()
			sline = line.lstrip()
			if not sline:
				continue
			try:
				if sline[:6] == 'class ' and sline[6] != '_' and (
					sline.find('(', 7) >= 0 or sline.find(':', 7) >= 0):
					self._lines.append(Line('class', line)) # a class
					line = lines.pop(0).lstrip()
					if line[:3] == '"""': # skip docstring
						while lines and line.rstrip()[-3:] != '"""':
							line = lines.pop(0)
					while lines and line.lstrip(): # skip body
						line = lines.pop(0)
				elif sline[:4] == 'def ' and (sline[4] != '_'
					or sline[5] == '_') and sline.find('(', 5) >= 0:
					self._lines.append(Line('def', line)) # a method
					line = lines.pop(0).lstrip()
					if line[:3] == '"""': # skip docstring
						while lines and line.rstrip()[-3:] != '"""':
							line = lines.pop(0)
					while lines and line.lstrip(): # skip body
						line = lines.pop(0)
				elif sline[:3] == '## ': # a category
					self._lines.append(Line('category', line))
			except IndexError:
				pass


	## Reports ##

	def text(self):
		return self.render('text')

	def html(self):
		return self.render('html')

	def render(self, format):
		filename = self._filename
		path, basename = os.path.split(filename)
		module = os.path.splitext(basename)[0]
		package = '%s.%s' % (path, module)
		span = format == 'html'
		settings = self._settings[format]
		res = []
		res.append(settings['file'][0] % locals())
		if self._lines:
			for line in self._lines:
				type = line.type()
				if line.text()[:1].lstrip() \
					and settings[type][0][:1] == '\n':
					res.append('\n')
				res.append(settings[type][0])
				if span:
					res.append('<span class="line_%s">' % type)
				res.append(getattr(line, format)()) # e.g., line.format()
				if span:
					res.append('</span>')
				res.append('\n')
				res.append(settings[type][1])
		else:
			res.append('# No classes or functions defined in this module.')
		res.append('\n')
		res.append(settings['file'][1] % locals())
		res = ''.join(res)
		res = res.replace('\t', settings['tabSubstitute'])
		return res


	## As strings ##

	def __repr__(self):
		return self.text()

	def __str__(self):
		return self.html()


	## Self utility ##

	def invalidateCache(self):
		self._text = None
		self._html = None


class Line:

	def __init__(self, type, contents):
		self._type = type
		self._text = contents
		self._html = None

	def type(self):
		return self._type

	def text(self):
		return self._text

	def html(self):
		if self._html is None:
			if self._type == 'class' or self._type == 'def':
				ht = self._text
				start = ht.find(self._type) + len(self._type) + 1
				end = ht.find('(', start)
				if end < 0:
					end = ht.find(':', start)
					if end < 0:
						end = start
				if self._type == 'def':
					end2 = ht.find(')', end + 1)
					if end2 >= 0:
						ht = ht[:end2+1]
				ht = '%s<span class="name_%s">%s</span>%s' % (
					ht[:start], self._type, ht[start:end], ht[end:])
			else:
				ht = self._text
			self._html = ht
		return self._html

	def __str__(self):
		return self.text()


## Auxiliary functions ##


def test():
	print 'Testing on self...'
	sum = PySummary()
	sum.readFileNamed('PySummary.py')
	open('PySummary.py.sum.text', 'w').write(sum.text())
	open('PySummary.py.sum.html', 'w').write(sum.html())
	print 'Wrote PySummary.py.sum.* files.'
	print 'Finished.'


if __name__ == '__main__':
	test()
