← Back to team overview

yade-dev team mailing list archive

[Branch ~yade-dev/yade/trunk] Rev 1899: 1. use stderr for standard startup messages

 

------------------------------------------------------------
revno: 1899
committer: Václav Šmilauer <eudoxos@xxxxxxxx>
branch nick: trunk
timestamp: Sun 2009-12-13 23:51:22 +0100
message:
  1. use stderr for standard startup messages
  2. add scripts/test/dispatch-torture.py for visualizing dispatch matrices in html. Add support HTML.py module from the web.
  3. call cleanupTemps if exiting; we should leave cruft in /tmp/yade-* only if crashing before trying to exit (unusual)
added:
  scripts/test/HTML.py
  scripts/test/dispatcher-torture.py
modified:
  core/main/main.py.in
  py/remote.py
  py/system.py
  py/utils.py
  py/yadeWrapper/yadeWrapper.cpp


--
lp:yade
https://code.launchpad.net/~yade-dev/yade/trunk

Your team Yade developers is subscribed to branch lp:yade.
To unsubscribe from this branch go to https://code.launchpad.net/~yade-dev/yade/trunk/+edit-subscription.
=== modified file 'core/main/main.py.in'
--- core/main/main.py.in	2009-12-08 12:08:48 +0000
+++ core/main/main.py.in	2009-12-13 22:51:22 +0000
@@ -24,7 +24,7 @@
 # c++ boot code checks for YADE_DEBUG at some places; debug verbosity is equivalent
 if opts.verbosity>1: os.environ['YADE_DEBUG']='1'
 
-print 'Welcome to Yade '+version
+sys.stderr.write('Welcome to Yade '+version+'\n')
 
 # initialization and c++ plugins import
 import yade
@@ -65,10 +65,10 @@
 if len(args)>0:
 	if args[0].endswith('.xml') or args[0].endswith('.xml.bz2'):
 		if len(args)>1: raise RuntimeError('Extra arguments to XML simulation to run: '+' '.join(args[1:]))
-		print "Running simulation "+args[0]
+		sys.stderr.write("Running simulation "+args[0]+'\n')
 		O=yade.wrapper.Omega(); O.load(args[0]); O.run()
 	if args[0].endswith('.py'):
-		print "Running script "+args[0]
+		sys.stderr.write("Running script "+args[0]+'\n')
 		yade.runtime.argv=args[1:]
 		try:
 			execfile(args[0])

=== modified file 'py/remote.py'
--- py/remote.py	2009-12-04 21:56:59 +0000
+++ py/remote.py	2009-12-13 22:51:22 +0000
@@ -87,7 +87,7 @@
 class GenericTCPServer:
 	"Base class for socket server, handling port allocation, initial logging and thead backgrounding."
 	def __init__(self,handler,title,cookie=True,minPort=9000,host='',maxPort=65536,background=True):
-		import socket, random
+		import socket, random, sys
 		self.port=-1
 		self.host=host
 		tryPort=minPort
@@ -99,9 +99,9 @@
 				if cookie:
 					self.server.cookie=''.join([i for i in random.sample('yadesucks',6)])
 					self.server.authenticated=[]
-					print title,"on %s:%d, auth cookie `%s'"%(host if host else 'localhost',self.port,self.server.cookie)
+					sys.stderr.write(title+" on %s:%d, auth cookie `%s'\n"%(host if host else 'localhost',self.port,self.server.cookie))
 				else:
-					print title,"on %s:%d"%(host if host else 'localhost',self.port)
+					sys.stderr.write(title+" on %s:%d\n"%(host if host else 'localhost',self.port))
 				if background:
 					import thread; thread.start_new_thread(self.server.serve_forever,())
 				else: self.server.serve_forever()

=== modified file 'py/system.py'
--- py/system.py	2009-12-13 20:46:46 +0000
+++ py/system.py	2009-12-13 22:51:22 +0000
@@ -11,9 +11,10 @@
 from yade import config
 O=wrapper.Omega()
 
-def childClasses(base):
-	"""Recursively enumerate classes deriving from given base (as string). Returns set."""
+def childClasses(base,recurse=True):
+	"""Enumerate classes deriving from given base (as string), recursively by default. Returns set."""
 	ret=set(O.childClassesNonrecursive(base)); ret2=set()
+	if not recurse: return ret
 	for bb in ret:
 		ret2|=childClasses(bb)
 	return ret | ret2

=== modified file 'py/utils.py'
--- py/utils.py	2009-12-13 20:30:13 +0000
+++ py/utils.py	2009-12-13 22:51:22 +0000
@@ -27,14 +27,13 @@
 
 	For example, variables a=5, b=66 and c=7.5e-4 are defined. To save those, use::
 
-		>>> from yade import utils
-		>>> utils.saveVars('mark',a=1,b=2,c=3,loadNow=True)
+		>>> saveVars('mark',a=1,b=2,c=3,loadNow=True)
 		>>> a,b,c
 		(1, 2, 3)
 
 	those variables will be save in the .xml file, when the simulation itself is saved. To recover those variables once the .xml is loaded again, use
 
-		>>> utils.loadVars('mark')
+		>>> loadVars('mark')
 
 	and they will be defined in the __builtin__ namespace (i.e. available from anywhere in the python code).
 
@@ -60,8 +59,12 @@
 
 
 def SpherePWaveTimeStep(radius,density,young):
-	"""Compute P-wave critical timestep for a single sphere.
-	If you want to compute minimum critical timestep for all spheres in the simulation, use utils.PWaveTimeStep() instead."""
+	r"""Compute P-wave critical timestep for a single (presumably representative) sphere, using formula for P-Wave propagation speed :math:`\Delta t_{c}=\frac{r}{E/\rho}`.
+	If you want to compute minimum critical timestep for all spheres in the simulation, use utils.PWaveTimeStep() instead.
+	
+	>>> SpherePWaveTimeStep(1e-3,2400,30e9)
+	2.8284271247461903e-07
+	"""
 	from math import sqrt
 	return radius/sqrt(young/density)
 
@@ -109,20 +112,19 @@
 	:return:
 		A Body instance with desired characteristics.
 
-	>>> O.reset()
-	>>> from yade import utils
 
 	Creating default shared material if none exists neither is given::
 
+		>>> O.reset()
 		>>> len(O.materials)
 		0
-		>>> s0=utils.sphere([2,0,0],1)
+		>>> s0=sphere([2,0,0],1)
 		>>> len(O.materials)
 		1
 
 	Instance of material can be given::
 
-		>>> s1=utils.sphere([0,0,0],1,wire=False,color=(0,1,0),material=ElasticMat(young=30e9,density=2e3))
+		>>> s1=sphere([0,0,0],1,wire=False,color=(0,1,0),material=ElasticMat(young=30e9,density=2e3))
 		>>> s1.shape['wire']
 		False
 		>>> s1.shape['diffuseColor']
@@ -134,7 +136,7 @@
 
 		>>> O.materials.append(GranularMat(young=10e9,poisson=.11,label='myMaterial'))
 		1
-		>>> s2=utils.sphere([0,0,2],1,material='myMaterial')
+		>>> s2=sphere([0,0,2],1,material='myMaterial')
 		>>> s2.mat['label']
 		'myMaterial'
 		>>> s2.mat['poisson']
@@ -382,11 +384,16 @@
 
 def encodeVideoFromFrames(frameSpec,out,renameNotOverwrite=True,fps=24):
 	"""Create .ogg video from external image files.
-	
-	@param frameSpec: If string, wildcard in format understood by GStreamer's multifilesrc plugin (e.g. '/tmp/frame-%04d.png'). If list or tuple, filenames to be encoded in given order. B{Warning:} GStreamer is picky about the wildcard; if you pass a wrong one, if will not complain, but silently stall.
-	@param out: file to save video into
-	@param renameNotOverwrite: if True, existing same-named video file will have ~[number] appended; will be overwritten otherwise.
-	@param fps: Frames per second.
+
+	:parameters:
+		`frameSpec`: wildcard | sequence of filenames
+			If string, wildcard in format understood by GStreamer's multifilesrc plugin (e.g. '/tmp/frame-%04d.png'). If list or tuple, filenames to be encoded in given order. B{Warning:} GStreamer is picky about the wildcard; if you pass a wrong one, if will not complain, but silently stall.
+		`out`: filename
+			file to save video into
+		`renameNotOverwrite`: bool
+			if True, existing same-named video file will have ~[number] appended; will be overwritten otherwise.
+		`fps`:
+			Frames per second.
 	"""
 	import pygst,sys,gobject,os,tempfile,shutil
 	pygst.require("0.10")
@@ -438,19 +445,22 @@
 def uniaxialTestFeatures(filename=None,areaSections=10,**kw):
 	"""Get some data about the current packing useful for uniaxial test:
 	
-		1. Find the dimensions that is the longest (uniaxial loading axis)
-
-		2. Find the minimum cross-section area of the speciment by examining several (areaSections)
-			sections perpendicular to axis, computing area of the convex hull for each one. This will
-			work also for non-prismatic specimen.
-
-		3. Find the bodies that are on the negative/positive boundary, to which the straining condition
-			should be applied.
-
-	@param filename: if given, spheres will be loaded from this file (ASCII format); if not, current simulation will be used.
-	@param areaSection: number of section that will be used to estimate cross-section
-
-	@return: dictionary with keys 'negIds', 'posIds', 'axis', 'area'.
+#. Find the dimensions that is the longest (uniaxial loading axis)
+
+#. Find the minimum cross-section area of the speciment by examining several (areaSections)
+	sections perpendicular to axis, computing area of the convex hull for each one. This will
+	work also for non-prismatic specimen.
+
+#. Find the bodies that are on the negative/positive boundary, to which the straining condition
+	should be applied.
+
+:parameters:
+	`filename`:
+		if given, spheres will be loaded from this file (ASCII format); if not, current simulation will be used.
+	`areaSection`:
+		number of section that will be used to estimate cross-section
+
+:return: dictionary with keys 'negIds', 'posIds', 'axis', 'area'.
 	"""
 	if filename: ids=spheresFromFile(filename,**kw)
 	else: ids=[b.id for b in O.bodies]
@@ -471,8 +481,8 @@
 
 def xMirror(half):
 	"""Mirror a sequence of 2d points around the x axis (changing sign on the y coord).
-	The sequence should start up and then it will wrap from y downwards (or vice versa).
-	If the last point's x coord is zero, it will not be duplicated."""
+The sequence should start up and then it will wrap from y downwards (or vice versa).
+If the last point's x coord is zero, it will not be duplicated."""
 	return list(half)+[(x,-y) for x,y in reversed(half[:-1] if half[-1][0]==0 else half)]
 
 #############################
@@ -510,8 +520,8 @@
 You may use special column named 'description' to describe this parameter set;
 if such colum is absent, description will be built by concatenating column names and corresponding values (``param1=34,param2=12.22,param4=foo``)
 
-  * from columns ending in ``!`` (the ``!`` is not included in the column name)
-  * from all columns, if no columns end in ``!``.
+* from columns ending in ``!`` (the ``!`` is not included in the column name)
+* from all columns, if no columns end in ``!``.
 
 Empty lines within the file are ignored (although counted); ``#`` starts comment till the end of line. Number of blank-separated columns must be the same for all non-empty lines.
 

=== modified file 'py/yadeWrapper/yadeWrapper.cpp'
--- py/yadeWrapper/yadeWrapper.cpp	2009-12-13 20:30:13 +0000
+++ py/yadeWrapper/yadeWrapper.cpp	2009-12-13 22:51:22 +0000
@@ -538,6 +538,8 @@
 	void exitNoBacktrace(int status=0){
 		if(status==0) signal(SIGSEGV,termHandlerNormal); /* unset the handler that runs gdb and prints backtrace */
 		else signal(SIGSEGV,termHandlerError);
+		// try to clean our mess
+		Omega::instance().cleanupTemps();
 		exit(status);
 	}
 	void runEngine(const shared_ptr<Engine>& e){ e->action(OMEGA.getScene().get()); }

=== added file 'scripts/test/HTML.py'
--- scripts/test/HTML.py	1970-01-01 00:00:00 +0000
+++ scripts/test/HTML.py	2009-12-13 22:51:22 +0000
@@ -0,0 +1,493 @@
+#!/usr/bin/python
+# -*- coding: iso-8859-1 -*-
+"""
+HTML.py - v0.04 2009-07-28 Philippe Lagadec
+
+This module provides a few classes to easily generate HTML code such as tables
+and lists.
+
+Project website: http://www.decalage.info/python/html
+
+License: CeCILL (open-source GPL compatible), see source code for details.
+         http://www.cecill.info
+"""
+
+__version__ = '0.04'
+__date__    = '2009-07-28'
+__author__  = 'Philippe Lagadec'
+
+#--- LICENSE ------------------------------------------------------------------
+
+# Copyright Philippe Lagadec - see http://www.decalage.info/contact for contact info
+#
+# This module provides a few classes to easily generate HTML tables and lists.
+#
+# This software is governed by the CeCILL license under French law and
+# abiding by the rules of distribution of free software.  You can  use,
+# modify and/or redistribute the software under the terms of the CeCILL
+# license as circulated by CEA, CNRS and INRIA at the following URL
+# "http://www.cecill.info";.
+#
+# A copy of the CeCILL license is also provided in these attached files:
+# Licence_CeCILL_V2-en.html and Licence_CeCILL_V2-fr.html
+#
+# As a counterpart to the access to the source code and  rights to copy,
+# modify and redistribute granted by the license, users are provided only
+# with a limited warranty  and the software's author, the holder of the
+# economic rights, and the successive licensors have only limited
+# liability.
+#
+# In this respect, the user's attention is drawn to the risks associated
+# with loading,  using,  modifying and/or developing or reproducing the
+# software by the user in light of its specific status of free software,
+# that may mean  that it is complicated to manipulate,  and  that  also
+# therefore means  that it is reserved for developers  and  experienced
+# professionals having in-depth computer knowledge. Users are therefore
+# encouraged to load and test the software's suitability as regards their
+# requirements in conditions enabling the security of their systems and/or
+# data to be ensured and,  more generally, to use and operate it in the
+# same conditions as regards security.
+#
+# The fact that you are presently reading this means that you have had
+# knowledge of the CeCILL license and that you accept its terms.
+
+
+#--- CHANGES ------------------------------------------------------------------
+
+# 2008-10-06 v0.01 PL: - First version
+# 2008-10-13 v0.02 PL: - added cellspacing and cellpadding to table
+#                      - added functions to ease one-step creation of tables
+#                        and lists
+# 2009-07-21 v0.03 PL: - added column attributes and styles (first attempt)
+#                        (thanks to an idea submitted by Michal Cernoevic)
+# 2009-07-28 v0.04 PL: - improved column styles, workaround for Mozilla
+
+
+#-------------------------------------------------------------------------------
+#TODO:
+# - method to return a generator (yield each row) instead of a single string
+# - unicode support (input and output)
+# - escape text in cells (optional)
+# - constants for standard colors
+# - use lxml to generate well-formed HTML ?
+# - add classes/functions to generate a HTML page, paragraphs, headings, etc...
+
+
+#--- THANKS --------------------------------------------------------------------
+
+# - Michal Cernoevic, for the idea of column styles.
+
+#--- REFERENCES ----------------------------------------------------------------
+
+# HTML 4.01 specs: http://www.w3.org/TR/html4/struct/tables.html
+
+# Colors: http://www.w3.org/TR/html4/types.html#type-color
+
+# Columns alignement and style, one of the oldest and trickiest bugs in Mozilla:
+# https://bugzilla.mozilla.org/show_bug.cgi?id=915
+
+
+#--- CONSTANTS -----------------------------------------------------------------
+
+# Table style to get thin black lines in Mozilla/Firefox instead of 3D borders
+TABLE_STYLE_THINBORDER = "border: 1px solid #000000; border-collapse: collapse;"
+#TABLE_STYLE_THINBORDER = "border: 1px solid #000000;"
+
+
+#=== CLASSES ===================================================================
+
+class TableCell (object):
+    """
+    a TableCell object is used to create a cell in a HTML table. (TD or TH)
+
+    Attributes:
+    - text: text in the cell (may contain HTML tags). May be any object which
+            can be converted to a string using str().
+    - header: bool, false for a normal data cell (TD), true for a header cell (TH)
+    - bgcolor: str, background color
+    - width: str, width
+    - align: str, horizontal alignement (left, center, right, justify or char)
+    - char: str, alignment character, decimal point if not specified
+    - charoff: str, see HTML specs
+    - valign: str, vertical alignment (top|middle|bottom|baseline)
+    - style: str, CSS style
+    - attribs: dict, additional attributes for the TD/TH tag
+
+    Reference: http://www.w3.org/TR/html4/struct/tables.html#h-11.2.6
+    """
+
+    def __init__(self, text="", bgcolor=None, header=False, width=None,
+                align=None, char=None, charoff=None, valign=None, style=None,
+                attribs=None):
+        """TableCell constructor"""
+        self.text    = text
+        self.bgcolor = bgcolor
+        self.header  = header
+        self.width   = width
+        self.align   = align
+        self.char    = char
+        self.charoff = charoff
+        self.valign  = valign
+        self.style   = style
+        self.attribs = attribs
+        if attribs==None:
+            self.attribs = {}
+
+    def __str__(self):
+        """return the HTML code for the table cell as a string"""
+        attribs_str = ""
+        if self.bgcolor: self.attribs['bgcolor'] = self.bgcolor
+        if self.width:   self.attribs['width']   = self.width
+        if self.align:   self.attribs['align']   = self.align
+        if self.char:    self.attribs['char']    = self.char
+        if self.charoff: self.attribs['charoff'] = self.charoff
+        if self.valign:  self.attribs['valign']  = self.valign
+        if self.style:   self.attribs['style']   = self.style
+        for attr in self.attribs:
+            attribs_str += ' %s="%s"' % (attr, self.attribs[attr])
+        if self.text:
+            text = str(self.text)
+        else:
+            # An empty cell should at least contain a non-breaking space
+            text = '&nbsp;'
+        if self.header:
+            return '  <TH%s>%s</TH>\n' % (attribs_str, text)
+        else:
+            return '  <TD%s>%s</TD>\n' % (attribs_str, text)
+
+#-------------------------------------------------------------------------------
+
+class TableRow (object):
+    """
+    a TableRow object is used to create a row in a HTML table. (TR tag)
+
+    Attributes:
+    - cells: list, tuple or any iterable, containing one string or TableCell
+             object for each cell
+    - header: bool, true for a header row (TH), false for a normal data row (TD)
+    - bgcolor: str, background color
+    - col_align, col_valign, col_char, col_charoff, col_styles: see Table class
+    - attribs: dict, additional attributes for the TR tag
+
+    Reference: http://www.w3.org/TR/html4/struct/tables.html#h-11.2.5
+    """
+
+    def __init__(self, cells=None, bgcolor=None, header=False, attribs=None,
+                col_align=None, col_valign=None, col_char=None,
+                col_charoff=None, col_styles=None):
+        """TableCell constructor"""
+        self.bgcolor     = bgcolor
+        self.cells       = cells
+        self.header      = header
+        self.col_align   = col_align
+        self.col_valign  = col_valign
+        self.col_char    = col_char
+        self.col_charoff = col_charoff
+        self.col_styles  = col_styles
+        self.attribs     = attribs
+        if attribs==None:
+            self.attribs = {}
+
+    def __str__(self):
+        """return the HTML code for the table row as a string"""
+        attribs_str = ""
+        if self.bgcolor: self.attribs['bgcolor'] = self.bgcolor
+        for attr in self.attribs:
+            attribs_str += ' %s="%s"' % (attr, self.attribs[attr])
+        result = ' <TR%s>\n' % attribs_str
+        for cell in self.cells:
+            col = self.cells.index(cell)    # cell column index
+            if not isinstance(cell, TableCell):
+                cell = TableCell(cell, header=self.header)
+            # apply column alignment if specified:
+            if self.col_align and cell.align==None:
+                cell.align = self.col_align[col]
+            if self.col_char and cell.char==None:
+                cell.char = self.col_char[col]
+            if self.col_charoff and cell.charoff==None:
+                cell.charoff = self.col_charoff[col]
+            if self.col_valign and cell.valign==None:
+                cell.valign = self.col_valign[col]
+            # apply column style if specified:
+            if self.col_styles and cell.style==None:
+                cell.style = self.col_styles[col]
+            result += str(cell)
+        result += ' </TR>\n'
+        return result
+
+#-------------------------------------------------------------------------------
+
+class Table (object):
+    """
+    a Table object is used to create a HTML table. (TABLE tag)
+
+    Attributes:
+    - rows: list, tuple or any iterable, containing one iterable or TableRow
+            object for each row
+    - header_row: list, tuple or any iterable, containing the header row (optional)
+    - border: str or int, border width
+    - style: str, table style in CSS syntax (thin black borders by default)
+    - width: str, width of the table on the page
+    - attribs: dict, additional attributes for the TABLE tag
+    - col_width: list or tuple defining width for each column
+    - col_align: list or tuple defining horizontal alignment for each column
+    - col_char: list or tuple defining alignment character for each column
+    - col_charoff: list or tuple defining charoff attribute for each column
+    - col_valign: list or tuple defining vertical alignment for each column
+    - col_styles: list or tuple of HTML styles for each column
+
+    Reference: http://www.w3.org/TR/html4/struct/tables.html#h-11.2.1
+    """
+
+    def __init__(self, rows=None, border='1', style=None, width=None,
+                cellspacing=None, cellpadding=4, attribs=None, header_row=None,
+                col_width=None, col_align=None, col_valign=None,
+                col_char=None, col_charoff=None, col_styles=None):
+        """TableCell constructor"""
+        self.border = border
+        self.style = style
+        # style for thin borders by default
+        if style == None: self.style = TABLE_STYLE_THINBORDER
+        self.width       = width
+        self.cellspacing = cellspacing
+        self.cellpadding = cellpadding
+        self.header_row  = header_row
+        self.rows        = rows
+        if not rows: self.rows = []
+        self.attribs     = attribs
+        if not attribs: self.attribs = {}
+        self.col_width   = col_width
+        self.col_align   = col_align
+        self.col_char    = col_char
+        self.col_charoff = col_charoff
+        self.col_valign  = col_valign
+        self.col_styles  = col_styles
+
+    def __str__(self):
+        """return the HTML code for the table as a string"""
+        attribs_str = ""
+        if self.border: self.attribs['border'] = self.border
+        if self.style:  self.attribs['style'] = self.style
+        if self.width:  self.attribs['width'] = self.width
+        if self.cellspacing:  self.attribs['cellspacing'] = self.cellspacing
+        if self.cellpadding:  self.attribs['cellpadding'] = self.cellpadding
+        for attr in self.attribs:
+            attribs_str += ' %s="%s"' % (attr, self.attribs[attr])
+        result = '<TABLE%s>\n' % attribs_str
+        # insert column tags and attributes if specified:
+        if self.col_width:
+            for width in self.col_width:
+                result += '  <COL width="%s">\n' % width
+        # The following code would also generate column attributes for style
+        # and alignement according to HTML4 specs,
+        # BUT it is not supported completely (only width) on Mozilla Firefox:
+        # see https://bugzilla.mozilla.org/show_bug.cgi?id=915
+##        n_cols = max(len(self.col_styles), len(self.col_width),
+##                     len(self.col_align), len(self.col_valign))
+##        for i in range(n_cols):
+##            col = ''
+##            try:
+##                if self.col_styles[i]:
+##                    col += ' style="%s"' % self.col_styles[i]
+##            except: pass
+##            try:
+##                if self.col_width[i]:
+##                    col += ' width="%s"' % self.col_width[i]
+##            except: pass
+##            try:
+##                if self.col_align[i]:
+##                    col += ' align="%s"' % self.col_align[i]
+##            except: pass
+##            try:
+##                if self.col_valign[i]:
+##                    col += ' valign="%s"' % self.col_valign[i]
+##            except: pass
+##            result += '<COL%s>\n' % col
+        # First insert a header row if specified:
+        if self.header_row:
+            if not isinstance(self.header_row, TableRow):
+                result += str(TableRow(self.header_row, header=True))
+            else:
+                result += str(self.header_row)
+        # Then all data rows:
+        for row in self.rows:
+            if not isinstance(row, TableRow):
+                row = TableRow(row)
+            # apply column alignments  and styles to each row if specified:
+            # (Mozilla bug workaround)
+            if self.col_align and not row.col_align:
+                row.col_align = self.col_align
+            if self.col_char and not row.col_char:
+                row.col_char = self.col_char
+            if self.col_charoff and not row.col_charoff:
+                row.col_charoff = self.col_charoff
+            if self.col_valign and not row.col_valign:
+                row.col_valign = self.col_valign
+            if self.col_styles and not row.col_styles:
+                row.col_styles = self.col_styles
+            result += str(row)
+        result += '</TABLE>'
+        return result
+
+
+#-------------------------------------------------------------------------------
+
+class List (object):
+    """
+    a List object is used to create an ordered or unordered list in HTML.
+    (UL/OL tag)
+
+    Attributes:
+    - lines: list, tuple or any iterable, containing one string for each line
+    - ordered: bool, choice between an ordered (OL) or unordered list (UL)
+    - attribs: dict, additional attributes for the OL/UL tag
+
+    Reference: http://www.w3.org/TR/html4/struct/lists.html
+    """
+
+    def __init__(self, lines=None, ordered=False, start=None, attribs=None):
+        """List constructor"""
+        if lines:
+            self.lines = lines
+        else:
+            self.lines = []
+        self.ordered = ordered
+        self.start = start
+        if attribs:
+            self.attribs = attribs
+        else:
+            self.attribs = {}
+
+    def __str__(self):
+        """return the HTML code for the list as a string"""
+        attribs_str = ""
+        if self.start:  self.attribs['start'] = self.start
+        for attr in self.attribs:
+            attribs_str += ' %s="%s"' % (attr, self.attribs[attr])
+        if self.ordered: tag = 'OL'
+        else:            tag = 'UL'
+        result = '<%s%s>\n' % (tag, attribs_str)
+        for line in self.lines:
+            result += ' <LI>%s\n' % str(line)
+        result += '</%s>\n' % tag
+        return result
+
+
+##class Link (object):
+##    """
+##    a Link object is used to create link in HTML. (<a> tag)
+##
+##    Attributes:
+##    - text: str, text of the link
+##    - url: str, URL of the link
+##    - attribs: dict, additional attributes for the A tag
+##
+##    Reference: http://www.w3.org/TR/html4
+##    """
+##
+##    def __init__(self, text, url=None, attribs=None):
+##        """Link constructor"""
+##        self.text = text
+##        self.url = url
+##        if attribs:
+##            self.attribs = attribs
+##        else:
+##            self.attribs = {}
+##
+##    def __str__(self):
+##        """return the HTML code for the link as a string"""
+##        attribs_str = ""
+##        if self.url:  self.attribs['href'] = self.url
+##        for attr in self.attribs:
+##            attribs_str += ' %s="%s"' % (attr, self.attribs[attr])
+##        return '<a%s>%s</a>' % (attribs_str, text)
+
+
+#=== FUNCTIONS ================================================================
+
+# much simpler definition of a link as a function:
+def Link(text, url):
+    return '<a href="%s">%s</a>' % (url, text)
+
+def link(text, url):
+    return '<a href="%s">%s</a>' % (url, text)
+
+def table(*args, **kwargs):
+    'return HTML code for a table as a string. See Table class for parameters.'
+    return str(Table(*args, **kwargs))
+
+def list(*args, **kwargs):
+    'return HTML code for a list as a string. See List class for parameters.'
+    return str(List(*args, **kwargs))
+
+
+#=== MAIN =====================================================================
+
+# Show sample usage when this file is launched as a script.
+
+if __name__ == '__main__':
+
+    # open an HTML file to show output in a browser
+    f = open('test.html', 'w')
+
+    t = Table()
+    t.rows.append(TableRow(['A', 'B', 'C'], header=True))
+    t.rows.append(TableRow(['D', 'E', 'F']))
+    t.rows.append(('i', 'j', 'k'))
+    f.write(str(t) + '<p>\n')
+    print str(t)
+    print '-'*79
+
+    t2 = Table([
+            ('1', '2'),
+            ['3', '4']
+        ], width='100%', header_row=('col1', 'col2'),
+        col_width=('', '75%'))
+    f.write(str(t2) + '<p>\n')
+    print t2
+    print '-'*79
+
+    t2.rows.append(['5', '6'])
+    t2.rows[1][1] = TableCell('new', bgcolor='red')
+    t2.rows.append(TableRow(['7', '8'], attribs={'align': 'center'}))
+    f.write(str(t2) + '<p>\n')
+    print t2
+    print '-'*79
+
+    # sample table with column attributes and styles:
+    table_data = [
+            ['Smith',       'John',         30,    4.5],
+            ['Carpenter',   'Jack',         47,    7],
+            ['Johnson',     'Paul',         62,    10.55],
+        ]
+    htmlcode = HTML.table(table_data,
+        header_row = ['Last name',   'First name',   'Age', 'Score'],
+        col_width=['', '20%', '10%', '10%'],
+        col_align=['left', 'center', 'right', 'char'],
+        col_styles=['font-size: large', '', 'font-size: small', 'background-color:yellow'])
+    f.write(htmlcode + '<p>\n')
+    print htmlcode
+    print '-'*79
+
+    def gen_table_squares(n):
+        """
+        Generator to create table rows for integers from 1 to n
+        """
+##        # First, header row:
+##        yield TableRow(('x', 'square(x)'), header=True, bgcolor='blue')
+##        # Then all rows:
+        for x in range(1, n+1):
+            yield (x, x*x)
+
+    t = Table(rows=gen_table_squares(10), header_row=('x', 'square(x)'))
+    f.write(str(t) + '<p>\n')
+
+    print '-'*79
+    l = List(['aaa', 'bbb', 'ccc'])
+    f.write(str(l) + '<p>\n')
+    l.ordered = True
+    f.write(str(l) + '<p>\n')
+    l.start=10
+    f.write(str(l) + '<p>\n')
+
+    f.close()

=== added file 'scripts/test/dispatcher-torture.py'
--- scripts/test/dispatcher-torture.py	1970-01-01 00:00:00 +0000
+++ scripts/test/dispatcher-torture.py	2009-12-13 22:51:22 +0000
@@ -0,0 +1,41 @@
+"""
+Script that shows dispatch matrices when all available functors are loaded.
+Later ones will overwrite earlier ones, this is not what you will get in
+reality.
+
+Pipe the output to file and open it in browser:
+
+$ yade-trunk dispatcher-torture.py > /tmp/aa.html
+$ firefox /tmp/aa.html
+
+"""
+
+dispatches={'Law':('InteractionGeometry','InteractionPhysics'),
+'InteractionGeometry':('Shape','Shape'),
+'InteractionPhysics':('Material','Material'),
+'Bound':('Shape','Bound')
+}
+
+sys.path.append('.')
+import HTML
+outStr=''
+for D in dispatches.keys():
+	functors=yade.system.childClasses(D+'Functor')
+	# create dispatcher with all available functors
+	dispatcher=eval(D+'Dispatcher([%s])'%(','.join(['%s()'%f for f in functors])))
+	if len(dispatches[D])!=2: raise NotImplementedError("Only 2d dispatchers implemented now.")
+	# lists of types the dispatcher accepts
+	allDim0=list(yade.system.childClasses(dispatches[D][0]))
+	allDim1=list(yade.system.childClasses(dispatches[D][1]))
+	table=HTML.Table(header_row=['']+allDim1)
+	for d0 in allDim0:
+		row=[d0]
+		for d1 in allDim1:
+			dd0,dd1=eval(d0+'()'),eval(d1+'()')
+			f=dispatcher.dispFunctor(dd0,dd1)
+			row.append(f.name if f else '-')
+		table.rows.append(row)
+	outStr+='\n<h1>%sDispatcher</h1>'%D
+	outStr+=str(table)
+print outStr
+quit()