You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@gump.apache.org by le...@apache.org on 2005/04/07 18:53:17 UTC
svn commit: r160420 - in gump/branches/Gump3: ./ webgump/ webgump/bin/
webgump/conf/ webgump/htdocs/ webgump/htdocs/app/ webgump/lib/
webgump/lib/python/ webgump/lib/python/webgump/
webgump/lib/python/webgump/controllers/ webgump/lib/python/webgump/util/
webgump/logs/ webgump/sql/ webgump/templates/
Author: leosimons
Date: Thu Apr 7 09:53:11 2005
New Revision: 160420
URL: http://svn.apache.org/viewcvs?view=rev&rev=160420
Log:
Commit a demo version of a new python-based web application setup primarily to show it can actually be pretty easy to do nicely. Whether to keep it around is a decision to make later :-D
Added:
gump/branches/Gump3/webgump/
gump/branches/Gump3/webgump/README.txt
gump/branches/Gump3/webgump/bin/
gump/branches/Gump3/webgump/bin/cleanup (with props)
gump/branches/Gump3/webgump/bin/debug (with props)
gump/branches/Gump3/webgump/bin/get-apache (with props)
gump/branches/Gump3/webgump/bin/get-mod-python (with props)
gump/branches/Gump3/webgump/bin/restart (with props)
gump/branches/Gump3/webgump/bin/settings
gump/branches/Gump3/webgump/bin/start (with props)
gump/branches/Gump3/webgump/bin/stop (with props)
gump/branches/Gump3/webgump/conf/
gump/branches/Gump3/webgump/conf/giraffe.conf
gump/branches/Gump3/webgump/conf/httpd.conf
gump/branches/Gump3/webgump/htdocs/
gump/branches/Gump3/webgump/htdocs/app/ (with props)
gump/branches/Gump3/webgump/htdocs/app/__init__.py
gump/branches/Gump3/webgump/htdocs/app/handler.py
gump/branches/Gump3/webgump/lib/ (with props)
gump/branches/Gump3/webgump/lib/python/ (with props)
gump/branches/Gump3/webgump/lib/python/ezt.py
gump/branches/Gump3/webgump/lib/python/webgump/ (with props)
gump/branches/Gump3/webgump/lib/python/webgump/__init__.py
gump/branches/Gump3/webgump/lib/python/webgump/controllers/ (with props)
gump/branches/Gump3/webgump/lib/python/webgump/controllers/__init__.py
gump/branches/Gump3/webgump/lib/python/webgump/controllers/home.py
gump/branches/Gump3/webgump/lib/python/webgump/util/ (with props)
gump/branches/Gump3/webgump/lib/python/webgump/util/__init__.py
gump/branches/Gump3/webgump/lib/python/webgump/util/log.py
gump/branches/Gump3/webgump/lib/python/webgump/util/template.py
gump/branches/Gump3/webgump/logs/ (with props)
gump/branches/Gump3/webgump/sql/
gump/branches/Gump3/webgump/sql/webgump.sql
gump/branches/Gump3/webgump/templates/
gump/branches/Gump3/webgump/templates/view.ezt
Modified:
gump/branches/Gump3/gump
Modified: gump/branches/Gump3/gump
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/gump?view=diff&r1=160419&r2=160420
==============================================================================
--- gump/branches/Gump3/gump (original)
+++ gump/branches/Gump3/gump Thu Apr 7 09:53:11 2005
@@ -129,6 +129,20 @@
If no action is specified, gump passes run as the action to
execute."
;;
+ webgump)
+ echo "
+ Run Webgump.
+
+ Usage:
+ $0 webgump webgump-action [webgump-args ...]
+
+ The available actions are:
+"
+ ls -1 webgump/bin
+ echo "
+ If no action is specified, gump passes debug as the action to
+ execute."
+ ;;
update-host)
echo "
Updates the gump installation on the specified host.
@@ -149,7 +163,7 @@
;;
create-database)
echo "
- Create a new MySQL database with the gump database schema.
+ Create a new MySQL database with the dynagump database schema.
Usage:
$0 create-database [database-name] [mysql-args ...]
@@ -188,6 +202,7 @@
debug -- run pygump in debug mode
test -- run the pygump unit tests
dynagump -- run the dynagump web application server
+ webgump -- run the webgump application server
update-host -- update the configuration of a gump host
create-database -- create a new gump MySQL database
pycompile -- compile all pyump source files
@@ -707,6 +722,22 @@
cd "$current"
}
+# Run webgump
+function webgump
+{
+ # "debug" is the default command
+ local command="$1"
+ shift
+ if [[ -z "$command" ]]; then
+ command="debug"
+ fi
+
+ local current=`pwd`
+ cd "$GUMP_HOME/webgump"
+ ./bin/$command $@
+ cd "$current"
+}
+
# Create the gump database
function create_database
{
@@ -793,6 +824,9 @@
;;
dynagump)
dynagump $@
+ ;;
+ webgump)
+ webgump $@
;;
create-database)
create_database $@
Added: gump/branches/Gump3/webgump/README.txt
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/webgump/README.txt?view=auto&rev=160420
==============================================================================
--- gump/branches/Gump3/webgump/README.txt (added)
+++ gump/branches/Gump3/webgump/README.txt Thu Apr 7 09:53:11 2005
@@ -0,0 +1,11 @@
+Welcome to WebGump!
+
+Webgump is a small experiment in building a python-based web application. It's mainly meant to contrast against dynagump (dynagump big, webgump
+small), while both at the moment don't really do anything. It should serve as a discussion starter on the web side of things. Real options include
+
+ * dynagump-style (dynamic)
+ * webgump-style (dynamic)
+ * gump2-style (generated)
+ * gump2-old-style (generated by forrest)
+ * using some other ready made python framework
+ * using some other ready made java framework
Added: gump/branches/Gump3/webgump/bin/cleanup
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/webgump/bin/cleanup?view=auto&rev=160420
==============================================================================
--- gump/branches/Gump3/webgump/bin/cleanup (added)
+++ gump/branches/Gump3/webgump/bin/cleanup Thu Apr 7 09:53:11 2005
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+# Simple script to get rid of python object files in this webapp
+
+find $GUMP_HOME/webgump -name '*.pyc' -or -name '*.pyo' | xargs rm -f
+
Propchange: gump/branches/Gump3/webgump/bin/cleanup
------------------------------------------------------------------------------
svn:executable = *
Added: gump/branches/Gump3/webgump/bin/debug
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/webgump/bin/debug?view=auto&rev=160420
==============================================================================
--- gump/branches/Gump3/webgump/bin/debug (added)
+++ gump/branches/Gump3/webgump/bin/debug Thu Apr 7 09:53:11 2005
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+# Simple script to start a single process apache
+
+# look for this file
+current=`pwd`
+thisscript="`pwd`/start"
+if [[ -f "$thisscript" ]]; then
+ # guess...
+ scriptdir="$current"
+else
+ # look for the path to this file
+ homedir=`dirname $0`
+ thisscript="$homedir/start"
+ if [[ -f "$thisscript" ]]; then
+ # guess...
+ scriptdir="$homedir"
+ fi
+fi
+
+# source settings
+. $scriptdir/settings
+
+$APACHECMD -f $APACHECONF -X
Propchange: gump/branches/Gump3/webgump/bin/debug
------------------------------------------------------------------------------
svn:executable = *
Added: gump/branches/Gump3/webgump/bin/get-apache
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/webgump/bin/get-apache?view=auto&rev=160420
==============================================================================
--- gump/branches/Gump3/webgump/bin/get-apache (added)
+++ gump/branches/Gump3/webgump/bin/get-apache Thu Apr 7 09:53:11 2005
@@ -0,0 +1,121 @@
+#!/bin/bash
+# simple script to setup apache
+
+# Where are we?
+if [[ -z "$GUMP_HOME" ]]; then
+ echo "ERROR: \$GUMP_HOME not set!"
+ exit 1
+fi
+
+###
+### Settings
+###
+APACHE_VERSION=2.0.53
+APACHE_INSTALL=$GUMP_HOME/webgump/lib/apache2-install
+
+###
+### Install apache
+###
+if [[ ! -d "$APACHE_INSTALL/current" ]]; then
+ mkdir $APACHE_INSTALL
+ cd $APACHE_INSTALL
+ if [[ ! -d "$APACHE_INSTALL/httpd-$APACHE_VERSION" ]]; then
+ wget http://apache.surfnet.nl/httpd/httpd-$APACHE_VERSION.tar.gz
+ tar zxf httpd-$APACHE_VERSION.tar.gz
+ fi
+ cd $APACHE_INSTALL/httpd-$APACHE_VERSION
+ ./configure --prefix=$APACHE_INSTALL/current --enable-mods-shared=most
+ make
+ make install
+else
+ echo "WARNING appears apache is already installed at"
+ echo "WARNING $APACHE_INSTALL/current"
+ echo "WARNING We won't attempt re-install"
+fi
+
+###
+### Install mod_python
+###
+$GUMP_HOME/webgump/bin/get-mod-python
+
+###
+### Use our own config
+###
+if [[ -d "$APACHE_INSTALL/current/conf.dist" ]]; then
+ echo "WARNING appears our custom apache config is already installed at"
+ echo "WARNING $APACHE_INSTALL/current/conf"
+ echo "WARNING We won't attempt re-install"
+else
+ echo "Installing config..."
+ cd $APACHE_INSTALL/current
+ mv conf conf.dist
+ ln -s $GUMP_HOME/webgump/conf
+ cd conf
+fi
+
+# Write the basic path-dependent config settings into a seperate file
+host=`hostname -s`
+if [[ -f "$GUMP_HOME/webgump/conf/$host.conf" ]]; then
+ echo "WARNING appears machine specific apache config is already installed at"
+ echo "WARNING $GUMP_HOME/webgump/conf/$host.conf"
+ echo "WARNING We won't attempt re-install"
+else
+ echo "Installing machine-specific config"
+ cat > $GUMP_HOME/webgump/conf/$host.conf <<ENDECHO
+ServerName $host
+ServerRoot "$APACHE_INSTALL/current"
+# get most settings from the common config
+Include "conf/httpd.conf"
+
+ErrorLog "$GUMP_HOME/webgump/logs/error_log"
+
+DocumentRoot "$GUMP_HOME/webgump/htdocs"
+<Directory />
+ Options FollowSymLinks
+ AllowOverride None
+</Directory>
+
+<Directory "$GUMP_HOME/webgump/htdocs">
+ Options Indexes FollowSymLinks
+ AllowOverride None
+ Order allow,deny
+ Allow from all
+</Directory>
+
+<Directory "$GUMP_HOME/webgump/htdocs/app">
+ AddHandler python-program .py
+ PythonHandler handler::Handler.handle
+ PythonPath "sys.path+['$GUMP_HOME/webgump/conf', '$GUMP_HOME/webgump/htdocs/app', '$GUMP_HOME/webgump/lib/python', '$GUMP_HOME/pygump/python']"
+ PythonDebug On
+
+ Order allow,deny
+ Allow from all
+
+ Options +Includes
+ SetOutputFilter INCLUDES
+ AcceptPathInfo On
+</Directory>
+ENDECHO
+
+fi
+
+###
+### Done. Report!
+###
+cat <<ENDMSG
+
+if All went well, apache2 is now installed inside
+
+ $APACHE_INSTALL/current
+
+ You may wish to customize the file
+
+ "$GUMP_HOME/webgump/conf/$host.conf"
+
+to further customize webgump's behaviour. After that,
+you should be able to run apache using
+
+ $GUMP_HOME/webgump/bin/start
+
+good luck!
+ENDMSG
Propchange: gump/branches/Gump3/webgump/bin/get-apache
------------------------------------------------------------------------------
svn:executable = *
Added: gump/branches/Gump3/webgump/bin/get-mod-python
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/webgump/bin/get-mod-python?view=auto&rev=160420
==============================================================================
--- gump/branches/Gump3/webgump/bin/get-mod-python (added)
+++ gump/branches/Gump3/webgump/bin/get-mod-python Thu Apr 7 09:53:11 2005
@@ -0,0 +1,35 @@
+#!/bin/bash
+#
+# Simple script to install mod_python
+
+# Where are we?
+if [[ -z "$GUMP_HOME" ]]; then
+ echo "ERROR: \$GUMP_HOME not set!"
+ exit 1
+fi
+
+###
+### Settings
+###
+MOD_PYTHON_VERSION=3.1.4
+APACHE_INSTALL=$GUMP_HOME/webgump/lib/apache2-install
+WEBGUMP=$GUMP_HOME/webgump
+
+###
+### Install mod_python
+###
+if [[ ! -d "$WEBGUMP/lib/python/mod_python" ]]; then
+ cd $APACHE_INSTALL
+ wget http://dist.apache.easynet.nl/httpd/modpython/mod_python-$MOD_PYTHON_VERSION.tgz
+ tar zxf mod_python-$MOD_PYTHON_VERSION.tgz
+ cd mod_python-$MOD_PYTHON_VERSION
+ ./configure --with-apxs=$APACHE_INSTALL/current/bin/apxs
+ make
+ make install_dso
+ cd dist
+ python setup.py install --install-lib=$WEBGUMP/lib/python
+else
+ echo "WARNING appears mod_python is already installed at"
+ echo "WARNING $WEBGUMP/lib/python"
+ echo "WARNING We won't attempt re-install"
+fi
Propchange: gump/branches/Gump3/webgump/bin/get-mod-python
------------------------------------------------------------------------------
svn:executable = *
Added: gump/branches/Gump3/webgump/bin/restart
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/webgump/bin/restart?view=auto&rev=160420
==============================================================================
--- gump/branches/Gump3/webgump/bin/restart (added)
+++ gump/branches/Gump3/webgump/bin/restart Thu Apr 7 09:53:11 2005
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+# Simple script to restart apache
+
+# look for this file
+current=`pwd`
+thisscript="`pwd`/start"
+if [[ -f "$thisscript" ]]; then
+ # guess...
+ scriptdir="$current"
+else
+ # look for the path to this file
+ homedir=`dirname $0`
+ thisscript="$homedir/start"
+ if [[ -f "$thisscript" ]]; then
+ # guess...
+ scriptdir="$homedir"
+ fi
+fi
+
+# source settings
+. $scriptdir/settings
+
+$APACHECTL -f $APACHECONF -k restart
Propchange: gump/branches/Gump3/webgump/bin/restart
------------------------------------------------------------------------------
svn:executable = *
Added: gump/branches/Gump3/webgump/bin/settings
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/webgump/bin/settings?view=auto&rev=160420
==============================================================================
--- gump/branches/Gump3/webgump/bin/settings (added)
+++ gump/branches/Gump3/webgump/bin/settings Thu Apr 7 09:53:11 2005
@@ -0,0 +1,7 @@
+if [[ -z "$GUMP_HOME" ]]; then
+ echo "ERROR: \$GUMP_HOME is not set!"
+ exit 1
+fi
+APACHECTL=$GUMP_HOME/webgump/lib/apache2-install/current/bin/apachectl
+APACHECMD=$GUMP_HOME/webgump/lib/apache2-install/current/bin/httpd
+APACHECONF=$GUMP_HOME/webgump/conf/`hostname -s`.conf
Added: gump/branches/Gump3/webgump/bin/start
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/webgump/bin/start?view=auto&rev=160420
==============================================================================
--- gump/branches/Gump3/webgump/bin/start (added)
+++ gump/branches/Gump3/webgump/bin/start Thu Apr 7 09:53:11 2005
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+# Simple script to start apache
+
+# look for this file
+current=`pwd`
+thisscript="`pwd`/start"
+if [[ -f "$thisscript" ]]; then
+ # guess...
+ scriptdir="$current"
+else
+ # look for the path to this file
+ homedir=`dirname $0`
+ thisscript="$homedir/start"
+ if [[ -f "$thisscript" ]]; then
+ # guess...
+ scriptdir="$homedir"
+ fi
+fi
+
+# source settings
+. $scriptdir/settings
+
+$APACHECTL -f $APACHECONF -k start
Propchange: gump/branches/Gump3/webgump/bin/start
------------------------------------------------------------------------------
svn:executable = *
Added: gump/branches/Gump3/webgump/bin/stop
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/webgump/bin/stop?view=auto&rev=160420
==============================================================================
--- gump/branches/Gump3/webgump/bin/stop (added)
+++ gump/branches/Gump3/webgump/bin/stop Thu Apr 7 09:53:11 2005
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+# Simple script to stop apache
+
+# look for this file
+current=`pwd`
+thisscript="`pwd`/start"
+if [[ -f "$thisscript" ]]; then
+ # guess...
+ scriptdir="$current"
+else
+ # look for the path to this file
+ homedir=`dirname $0`
+ thisscript="$homedir/start"
+ if [[ -f "$thisscript" ]]; then
+ # guess...
+ scriptdir="$homedir"
+ fi
+fi
+
+# source settings
+. $scriptdir/settings
+
+$APACHECTL -f $APACHECONF -k stop
Propchange: gump/branches/Gump3/webgump/bin/stop
------------------------------------------------------------------------------
svn:executable = *
Added: gump/branches/Gump3/webgump/conf/giraffe.conf
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/webgump/conf/giraffe.conf?view=auto&rev=160420
==============================================================================
--- gump/branches/Gump3/webgump/conf/giraffe.conf (added)
+++ gump/branches/Gump3/webgump/conf/giraffe.conf Thu Apr 7 09:53:11 2005
@@ -0,0 +1,33 @@
+ServerName giraffe
+ServerRoot "/home/lsimons/svn/gump/branches/Gump3/webgump/lib/apache2-install/current"
+# get most settings from the common config
+Include "conf/httpd.conf"
+
+ErrorLog "/home/lsimons/svn/gump/branches/Gump3/webgump/logs/error_log"
+
+DocumentRoot "/home/lsimons/svn/gump/branches/Gump3/webgump/htdocs"
+<Directory />
+ Options FollowSymLinks
+ AllowOverride None
+</Directory>
+
+<Directory "/home/lsimons/svn/gump/branches/Gump3/webgump/htdocs">
+ Options Indexes FollowSymLinks
+ AllowOverride None
+ Order allow,deny
+ Allow from all
+</Directory>
+
+<Directory "/home/lsimons/svn/gump/branches/Gump3/webgump/htdocs/app">
+ AddHandler python-program .py
+ PythonHandler handler::Handler.handle
+ PythonPath "sys.path+['/home/lsimons/svn/gump/branches/Gump3/webgump/conf', '/home/lsimons/svn/gump/branches/Gump3/webgump/htdocs/app', '/home/lsimons/svn/gump/branches/Gump3/webgump/lib/python', '/home/lsimons/svn/gump/branches/Gump3/pygump/python']"
+ PythonDebug On
+
+ Order allow,deny
+ Allow from all
+
+ Options +Includes
+ SetOutputFilter INCLUDES
+ AcceptPathInfo On
+</Directory>
Added: gump/branches/Gump3/webgump/conf/httpd.conf
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/webgump/conf/httpd.conf?view=auto&rev=160420
==============================================================================
--- gump/branches/Gump3/webgump/conf/httpd.conf (added)
+++ gump/branches/Gump3/webgump/conf/httpd.conf Thu Apr 7 09:53:11 2005
@@ -0,0 +1,346 @@
+LoadModule access_module modules/mod_access.so
+LoadModule auth_module modules/mod_auth.so
+LoadModule ext_filter_module modules/mod_ext_filter.so
+LoadModule include_module modules/mod_include.so
+LoadModule log_config_module modules/mod_log_config.so
+LoadModule env_module modules/mod_env.so
+LoadModule expires_module modules/mod_expires.so
+LoadModule headers_module modules/mod_headers.so
+LoadModule setenvif_module modules/mod_setenvif.so
+LoadModule mime_module modules/mod_mime.so
+LoadModule status_module modules/mod_status.so
+LoadModule autoindex_module modules/mod_autoindex.so
+LoadModule asis_module modules/mod_asis.so
+LoadModule info_module modules/mod_info.so
+LoadModule cgi_module modules/mod_cgi.so
+LoadModule vhost_alias_module modules/mod_vhost_alias.so
+LoadModule negotiation_module modules/mod_negotiation.so
+LoadModule dir_module modules/mod_dir.so
+LoadModule actions_module modules/mod_actions.so
+LoadModule speling_module modules/mod_speling.so
+LoadModule userdir_module modules/mod_userdir.so
+LoadModule alias_module modules/mod_alias.so
+LoadModule rewrite_module modules/mod_rewrite.so
+LoadModule python_module modules/mod_python.so
+
+Listen 8080
+ServerAdmin general@gump.apache.org
+Timeout 300
+KeepAlive On
+MaxKeepAliveRequests 100
+KeepAliveTimeout 15
+UseCanonicalName Off
+LogLevel debug
+
+<IfModule !mpm_netware.c>
+PidFile logs/httpd.pid
+</IfModule>
+
+<Directory />
+ Options FollowSymLinks
+ AllowOverride None
+</Directory>
+
+DirectoryIndex index.html index.html.var index.py
+AccessFileName .htaccess
+
+<Files ~ "^\.ht">
+ Order allow,deny
+ Deny from all
+</Files>
+
+TypesConfig conf.dist/mime.types
+
+DefaultType text/plain
+
+<IfModule mod_mime_magic.c>
+ MIMEMagicFile conf.dist/magic
+</IfModule>
+
+HostnameLookups Off
+#EnableMMAP off
+#EnableSendfile off
+
+ServerTokens Full
+ServerSignature On
+
+IndexOptions VersionSort
+ReadmeName README.html
+HeaderName HEADER.html
+IndexIgnore .??* *~ *# HEADER* README* RCS CVS *,v *,t
+
+AddLanguage ca .ca
+AddLanguage cs .cz .cs
+AddLanguage da .dk
+AddLanguage de .de
+AddLanguage el .el
+AddLanguage en .en
+AddLanguage eo .eo
+AddLanguage es .es
+AddLanguage et .et
+AddLanguage fr .fr
+AddLanguage he .he
+AddLanguage hr .hr
+AddLanguage it .it
+AddLanguage ja .ja
+AddLanguage ko .ko
+AddLanguage ltz .ltz
+AddLanguage nl .nl
+AddLanguage nn .nn
+AddLanguage no .no
+AddLanguage pl .po
+AddLanguage pt .pt
+AddLanguage pt-BR .pt-br
+AddLanguage ru .ru
+AddLanguage sv .sv
+AddLanguage zh-CN .zh-cn
+AddLanguage zh-TW .zh-tw
+
+LanguagePriority en ca cs da de el eo es et fr he hr it ja ko ltz nl nn no pl pt pt-BR ru sv zh-CN zh-TW
+ForceLanguagePriority Prefer Fallback
+
+AddDefaultCharset ISO-8859-1
+AddCharset ISO-8859-1 .iso8859-1 .latin1
+AddCharset ISO-8859-2 .iso8859-2 .latin2 .cen
+AddCharset ISO-8859-3 .iso8859-3 .latin3
+AddCharset ISO-8859-4 .iso8859-4 .latin4
+AddCharset ISO-8859-5 .iso8859-5 .latin5 .cyr .iso-ru
+AddCharset ISO-8859-6 .iso8859-6 .latin6 .arb
+AddCharset ISO-8859-7 .iso8859-7 .latin7 .grk
+AddCharset ISO-8859-8 .iso8859-8 .latin8 .heb
+AddCharset ISO-8859-9 .iso8859-9 .latin9 .trk
+AddCharset ISO-2022-JP .iso2022-jp .jis
+AddCharset ISO-2022-KR .iso2022-kr .kis
+AddCharset ISO-2022-CN .iso2022-cn .cis
+AddCharset Big5 .Big5 .big5
+# For russian, more than one charset is used (depends on client, mostly):
+AddCharset WINDOWS-1251 .cp-1251 .win-1251
+AddCharset CP866 .cp866
+AddCharset KOI8-r .koi8-r .koi8-ru
+AddCharset KOI8-ru .koi8-uk .ua
+AddCharset ISO-10646-UCS-2 .ucs2
+AddCharset ISO-10646-UCS-4 .ucs4
+AddCharset UTF-8 .utf8
+AddCharset GB2312 .gb2312 .gb
+AddCharset utf-7 .utf7
+AddCharset utf-8 .utf8
+AddCharset big5 .big5 .b5
+AddCharset EUC-TW .euc-tw
+AddCharset EUC-JP .euc-jp
+AddCharset EUC-KR .euc-kr
+AddCharset shift_jis .sjis
+
+BrowserMatch "Mozilla/2" nokeepalive
+BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0
+BrowserMatch "RealPlayer 4\.0" force-response-1.0
+BrowserMatch "Java/1\.0" force-response-1.0
+BrowserMatch "JDK/1\.0" force-response-1.0
+BrowserMatch "Microsoft Data Access Internet Publishing Provider" redirect-carefully
+BrowserMatch "^WebDrive" redirect-carefully
+BrowserMatch "^WebDAVFS/1.[012]" redirect-carefully
+BrowserMatch "^gnome-vfs" redirect-carefully
+
+### .-------------------------------------------------------------.
+### | FURSWA: Fancy URL Rewriting Scheme for Web Applications |
+### .-------------------------------------------------------------.
+# The below combination of mod_rewrite directives allows any web application
+# that is built around a concept of controller+action pairs to have pretty
+# URLs on the outside, and easily parseable ones on the inside. All of the
+# rewrite rules combine to transform URLs that are requested like this:
+#
+# /controller/action/param1/value1/paramN/valueN/unparsed.stuff/index.html?otherparm=otherval#fragment
+#
+# into URLs like this as seen by your application:
+#
+# /myhandler?controller=controller&action=action¶m1=value1¶mN=valueN&path=unparsed.stuff/index.html&otherparm=otherval#fragment
+#
+# which can then be processed further by your web application in any way you
+# choose.
+#
+# The reverse process is also applied. This means that URLs that are requested
+# like this:
+#
+# /myhandler?controller=controller&action=action¶m1=value1¶mN=valueN&path=unparsed.stuff/index.html&otherparm=otherval#fragment
+#
+# are transformed into URLs like this:
+#
+# /controller/action/param1/value1/paramN/valueN/unparsed.stuff/index.html?otherparm=otherval#fragment
+#
+# which are sent back to the client with a 304 Moved Permanently redirect, so
+# that the client sees only the pretty url. This allows your web application to
+# output the ugly URLs and not worry about prettying those up.
+#
+# Note that none of the variables (controller, action, parameters, values)
+# that are parsed out can have a "/" or a "." in them. Also note that the order
+# of the controller, action and path parameters actually matters, not just when
+# transforming from ugly urls to pretty ones, but also when going back the other
+# way.
+#
+# Configuration
+# -------------
+#
+# Of course, you still need to configure the /myhandler location that will be
+# used to actually process the requests. Simply replace "/app/handler.py" with
+# the path to your webapplication in all the rules below.
+#
+# In addition, you will need to configure exactly what URLs are considered as
+# being part of the web application, and which are pointers to (for example)
+# regular files. You can do that by customizing RULE 02.
+#
+# Combining with other rules
+# --------------------------
+#
+# If you wish to use mod_rewrite rules in addition to the ones our "fancy
+# rewriting scheme" sets up, add them *after* all the rules below, i.e.,
+# define them as RULE 22 and later.
+
+###
+### Section I: Setup
+###
+RewriteEngine "On"
+# Enable the lines below for debugging
+#RewriteLog "logs/rewrite_log"
+#RewriteLogLevel 5
+
+###
+### Section II: Mode handling
+###
+### Step 01: Determine which mode we're in
+# Here, we configure a set of rules that puts us in one of three matching
+# modes:
+#
+# 1) prettyfication. We have an ugly query-string based url referencing part
+# of our web application, which is rewritten to something fancy, then
+# provided to the user with a 304 Moved Permanently redirect.
+# 2) redirection. We have a pretty webapp url which internally is rewritten
+# into a query-string based url for easy processing by the CGI application
+# 3) other stuff. Neither of the above is the case, so we jump to another bit
+# that does other kinds of redirection.
+# RULE 01
+RewriteCond "RULE1%{ENV:REWRITE_MODE}" =RULE1
+RewriteRule "^/app/handler.py$" - [env=REWRITE_MODE:prettyfy]
+
+# RULE 02
+RewriteCond "RULE2%{ENV:REWRITE_MODE}" =RULE2
+RewriteCond "%{REQUEST_FILENAME}" !-f
+# Specify what bits are webapplications here
+RewriteCond "%{REQUEST_URI}" "^/home"
+#RewriteCond "%{REQUEST_URI}" "^/webapp2" [OR]
+#RewriteCond "%{REQUEST_URI}" "^/webapp3" [OR]
+RewriteRule "^/([^/\.]+/[^/\.]+)(/.*)?$" - [env=REWRITE_MODE:redirection]
+
+# RULE 03
+RewriteCond "RULE3%{ENV:REWRITE_MODE}" =RULE3
+RewriteRule "." - [env=REWRITE_MODE:other]
+
+### Step 02: Jump to relevant section
+# Here, we configure a set of rules that skip forward a number of rules to the
+# section matching our REWRITE_MODE. This means it is important to keep track
+# of the number of rewrite rules in each section except the last one!
+
+# RULE 05
+# JUMP from RULE 05 to RULE 14: skip=(14-5-1=8)
+RewriteCond "RULE5%{ENV:REWRITE_MODE}" =RULE5redirection
+RewriteRule "." - [skip=8]
+
+# RULE 06
+# JUMP from RULE 06 to RULE 21: skip=(21-6-1=14)
+RewriteCond "RULE6%{ENV:REWRITE_MODE}" =RULE6other
+RewriteRule "." - [skip=14]
+
+###
+### Section III: Prettyfication
+###
+# Note that we never need to do qsappend here as we get "the rest" of
+# the query string all the time.
+
+### Step 01: /app/handler.py -> RULE 07
+### prettyfy:/
+# basic paranoia :-D
+RewriteCond "RULE7%{ENV:REWRITE_MODE}" =RULE7prettyfy
+RewriteRule "^/app/handler.py$" "prettyfy:/"
+
+### Step 02: /?controller=controller&... -> RULE 08
+### /controller/...?...
+RewriteCond "RULE8%{QUERY_STRING}" "RULE8controller=([^/\.&]+)&?(.*)"
+RewriteRule "^prettyfy:/$" "prettyfy:/%1?%2"
+
+### Step 03: /controller?action=action&... -> RULE 09
+### /controller/action/...?...
+RewriteCond "RULE9%{QUERY_STRING}" "RULE9action=([^/\.&]+)&?(.*)"
+RewriteRule "^prettyfy:/([^/\.]+)$" "prettyfy:/$1/%1?%2"
+
+### Step 04: /...?path=unparsed.stuff/index.html&... -> RULE 10
+### /...?... && $PRETTYFY_PATH=unparsed.stuff/index.html
+RewriteCond "RULE10%{QUERY_STRING}" "RULE10path=([^&]+)&?(.*)"
+RewriteRule "^prettyfy:/(.*)$" "prettyfy:/$1?%2" [env=PRETTYFY_PATH=%1]
+
+### Step 05: /controller/action?param=value&... -> RULE 11
+### /controller/action/param/value/...?...
+# JUMP from RULE 11 to RULE 01
+RewriteCond "RULE11%{QUERY_STRING}" "RULE11([^/\.&]+)=([^/\.&]+)&?(.*)"
+RewriteRule "^prettyfy:/([^/\.]+)/([^/\.]+)$" "prettyfy:/$1/$2/%1/%2?%3" [next]
+
+### Step 06: /controller/action/param=value/paramN=valueN?... -> RULE 12
+### /controller/action/param=value/paramN=valueN/unparsed.stuff/index.html?...
+RewriteCond "RULE12%{PRETTYFY_PATH}" !=RULE12
+RewriteCond "%{QUERY_STRING}" "(.*)"
+RewriteRule "^prettyfy:/(.*)$" "prettyfy:/$1/%{ENV:PRETTYFY_PATH}?%1"
+
+### Step 07: get rid of prefix RULE 13
+# STOP processing
+RewriteRule "^prettyfy:/(.*)$" "/$1" [last,redirect=permanent]
+
+
+###
+### Section IV: Redirection
+###
+
+### Step 01: /... -> webapp:/... RULE 14
+# basic paranoia :-D
+RewriteCond "RULE14%{ENV:REWRITE_MODE}" =RULE14redirection
+RewriteRule "^/(.*)$" "webapp:/$1"
+
+### Step 02: /$controller/... -> /..., saving $controller in ENV
+# this processing is run only once (deal with looping) RULE 15
+RewriteCond "RULE15%{ENV:WEBAPP_CONTROLLER}" =RULE15
+RewriteRule "^webapp:/([^/\.]+)/(.*)$" "webapp:/$2" [env=WEBAPP_CONTROLLER:$1,chain]
+# make sure we have a default if no controller is specified
+# (for example, someone might request /myhandler directly) RULE 16
+RewriteCond "RULE16%{ENV:WEBAPP_CONTROLLER}" =RULE16
+RewriteRule "." - [env=WEBAPP_CONTROLLER:home]
+
+### Step 03: /$action/... -> /..., saving $action in ENV
+# this processing is run only once (deal with looping) RULE 17
+RewriteCond "RULE17%{ENV:WEBAPP_ACTION}" =RULE17
+RewriteRule "^webapp:/([^/\.]+)/?(.*)$" "webapp:/$2" [env=WEBAPP_ACTION:$1,chain]
+# make sure we have a default if no controller is specified
+# (for example, someone might request /myhandler directly) RULE 18
+RewriteCond "RULE18%{ENV:WEBAPP_ACTION}" =RULE18
+RewriteRule "." - [env=WEBAPP_ACTION:view]
+
+### Step 04: /param/value/... -> /...?param=value
+# JUMP from RULE 19 to RULE 01 RULE 19
+RewriteRule "^webapp:/([^/\.]+)/([^/\.]+)/?(.*)$" "webapp:/$3?$1=$2" [next,qsappend]
+
+### Step 05: /unparsed.stuff/index.html ->
+### /app/handler.py?path=unparsed.stuff/index.html
+# save everything we did not process as "path", get rid of the
+# webapp: prefix, then call on the handler.
+# STOP processing RULE 20
+RewriteRule "^webapp:(.*)$" "/app/handler.py?controller=%{ENV:WEBAPP_CONTROLLER}&action=%{ENV:WEBAPP_ACTION}&path=$1" [last,qsappend]
+
+###
+### Section V: Other
+###
+
+### Step 01: Basic paranoia :-D RULE 21
+RewriteCond "RULE21%{ENV:REWRITE_MODE}" !=RULE21other
+RewriteRule "." - [L]
+
+### Step 02: Whatever you want after this :-D
+# This is the place where you should do your own redirect rules, normally you will
+# want to make these external redirects, otherwise the above logic will not be
+# applied!
+RewriteRule "^/$" "/home/view" [redirect,last]
+RewriteRule "^/index.html?$" "/home/view?path=bla" [redirect,last]
Propchange: gump/branches/Gump3/webgump/htdocs/app/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Thu Apr 7 09:53:11 2005
@@ -0,0 +1,2 @@
+*.pyc
+*.pyo
Added: gump/branches/Gump3/webgump/htdocs/app/__init__.py
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/webgump/htdocs/app/__init__.py?view=auto&rev=160420
==============================================================================
(empty)
Added: gump/branches/Gump3/webgump/htdocs/app/handler.py
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/webgump/htdocs/app/handler.py?view=auto&rev=160420
==============================================================================
--- gump/branches/Gump3/webgump/htdocs/app/handler.py (added)
+++ gump/branches/Gump3/webgump/htdocs/app/handler.py Thu Apr 7 09:53:11 2005
@@ -0,0 +1,145 @@
+#!/usr/bin/env python
+import os
+import sys
+import imp
+
+from mod_python import apache
+from mod_python import util
+
+from webgump.util import log
+from webgump.util.log import Logger
+
+#DEBUG = True
+DEBUG = False
+
+class Handler:
+ def __init__(self, req):
+ """A new handler is created by mod_python on every request."""
+ self.status = None
+ self.req = req
+
+ if DEBUG:
+ self.log = Logger(req, level=log.DEBUG, name="webgump.handler")
+ else:
+ self.log = Logger(req, level=log.INFO, name="webgump.handler")
+
+ def handle(self, req):
+ """This is the main delegation method that reads in config and then
+ fires up a controller."""
+ req.content_type = "text/html"
+ try:
+ self._settings()
+ action = self._get_action()
+ self.status = action(self.req, self.settings)
+ except IOError:
+ self.req = None
+ self.log.error("Client is gone.")
+ return apache.OK
+ except:
+ try:
+ # sufficiently advanced controllers should ideallly do their own
+ # exception handling and recovery. This is a minimal failsafe.
+ self.req.write("<p><strong>An unexpected problem has occurred!</strong></p>")
+ self.log.exception("Uncaught exception!")
+ except:
+ pass
+ #try:
+ # if DEBUG:
+ # self._debug_info()
+ #except:
+ # pass
+
+ if self.status:
+ return self.status
+ else:
+ return apache.OK
+
+ def _settings(self):
+ """Set defaults, then load config file, then set some more settings based
+ on the mod_python provided environment."""
+ self.req.add_common_vars()
+ self.settings = {
+ 'DEBUG': DEBUG,
+ 'PAGE_TITLE': "Front Page",
+ 'db_host': "localhost",
+ 'db_user': "webgump",
+ 'db_password': "webgump",
+ 'db_name': "webgump"
+ }
+ for key,val in self.req.subprocess_env.items():
+ self.settings[key] = val
+
+ query_vars = util.parse_qs(self.req.subprocess_env["QUERY_STRING"])
+ self.settings['HTTP_GET_VARS'] = query_vars
+ for key,val in query_vars.items():
+ val.reverse()
+ self.settings[key] = val[0]
+ val.reverse()
+
+ self.settings['TEMPLATE_ROOT'] = \
+ os.path.abspath(os.path.join(self.settings['DOCUMENT_ROOT'], "../templates"))
+
+
+ def _get_action(self):
+ """Determine the controller to fire up."""
+ controller = self.settings['controller'] or "home"
+ action = self.settings['action'] or "view"
+ self.log.debug("Executing %s::%s()" % (controller,action))
+ try:
+ module = _find_controller_module(controller)
+ except:
+ e = sys.exc_info()
+ raise e[0], "Unable to find controller '%s': %s" % (controller, e[1]), e[2]
+
+ if module and hasattr(module, action):
+ method = getattr(module, action)
+ if callable(method):
+ return method
+ else:
+ raise "Action '%s' of controller '%s' not callable" % (action, controller)
+ else:
+ raise "No action '%s' available in controller '%s'" % (action, controller)
+
+ #def _debug_info(self):
+ # """Write basic environment information to the web page."""
+ # self.req.write("<hr/><p><strong>DEBUG INFO</strong></p>\n")
+ # self.req.write("<p><em>Webapp Settings</em></p>\n<pre>")
+ # for (key,val) in self.settings.items():
+ # self.req.write("%s: %s\n" % (key,val))
+ # self.req.write("</pre>")
+
+
+def _find_controller_module(controller):
+ """Finds and loads a module within lsdblog.controllers based on the provided
+ controller name."""
+ import webgump.controllers
+
+ modulename = "webgump.controllers.%s" % controller
+ module = None
+ try:
+ module = sys.modules[modulename]
+ except KeyError:
+ n = "webgump"
+ (f1,p,d) = imp.find_module(n)
+ try:
+ m = imp.load_module(n,f1,p,d)
+ p = m.__path__
+ n = "controllers"
+ (f2,p,d) = imp.find_module(n,p)
+ try:
+ m = imp.load_module(n,f2,p,d)
+ p = m.__path__
+
+ (f3,p,d) = imp.find_module(controller, p)
+ try:
+ module = imp.load_module(controller, f3, p, d)
+ finally:
+ if f3:
+ f3.close()
+ finally:
+ if f2:
+ f2.close()
+ finally:
+ if f1:
+ f1.close()
+ return module
Propchange: gump/branches/Gump3/webgump/lib/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Thu Apr 7 09:53:11 2005
@@ -0,0 +1,2 @@
+apache2-install
+
Propchange: gump/branches/Gump3/webgump/lib/python/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Thu Apr 7 09:53:11 2005
@@ -0,0 +1,4 @@
+*.pyc
+*.pyo
+mod_python
+
Added: gump/branches/Gump3/webgump/lib/python/ezt.py
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/webgump/lib/python/ezt.py?view=auto&rev=160420
==============================================================================
--- gump/branches/Gump3/webgump/lib/python/ezt.py (added)
+++ gump/branches/Gump3/webgump/lib/python/ezt.py Thu Apr 7 09:53:11 2005
@@ -0,0 +1,585 @@
+#!/usr/bin/env python
+"""ezt.py -- easy templating
+
+ezt templates are very similar to standard HTML files. But additionaly
+they contain directives sprinkled in between. With these directives
+it possible to generate the dynamic content from the ezt templates.
+
+These directives are enclosed in square brackets. If you are a
+C-programmer, you might be familar with the #ifdef directives of the
+C preprocessor 'cpp'. ezt provides a similar concept for HTML. Additionally
+EZT has a 'for' directive, which allows to iterate (repeat) certain
+subsections of the template according to sequence of data items
+provided by the application.
+
+The HTML rendering is performed by the method generate() of the Template
+class. Building template instances can either be done using external
+EZT files (convention: use the suffix .ezt for such files):
+
+ >>> template = Template("../templates/log.ezt")
+
+or by calling the parse() method of a template instance directly with
+a EZT template string:
+
+ >>> template = Template()
+ >>> template.parse('''<html><head>
+ ... <title>[title_string]</title></head>
+ ... <body><h1>[title_string]</h1>
+ ... [for a_sequence] <p>[a_sequence]</p>
+ ... [end] <hr>
+ ... The [person] is [if-any state]in[else]out[end].
+ ... </body>
+ ... </html>
+ ... ''')
+
+The application should build a dictionary 'data' and pass it together
+with the output fileobject to the templates generate method:
+
+ >>> data = {'title_string' : "A Dummy Page",
+ ... 'a_sequence' : ['list item 1', 'list item 2', 'another element'],
+ ... 'person': "doctor",
+ ... 'state' : None }
+ >>> import sys
+ >>> template.generate(sys.stdout, data)
+ <html><head>
+ <title>A Dummy Page</title></head>
+ <body><h1>A Dummy Page</h1>
+ <p>list item 1</p>
+ <p>list item 2</p>
+ <p>another element</p>
+ <hr>
+ The doctor is out.
+ </body>
+ </html>
+
+Template syntax error reporting should be improved. Currently it is
+very sparse (template line numbers would be nice):
+
+ >>> Template().parse("[if-any where] foo [else] bar [end unexpected args]")
+ Traceback (innermost last):
+ File "<stdin>", line 1, in ?
+ File "ezt.py", line 220, in parse
+ self.program = self._parse(text)
+ File "ezt.py", line 275, in _parse
+ raise ArgCountSyntaxError(str(args[1:]))
+ ArgCountSyntaxError: ['unexpected', 'args']
+ >>> Template().parse("[if unmatched_end]foo[end]")
+ Traceback (innermost last):
+ File "<stdin>", line 1, in ?
+ File "ezt.py", line 206, in parse
+ self.program = self._parse(text)
+ File "ezt.py", line 266, in _parse
+ raise UnmatchedEndError()
+ UnmatchedEndError
+
+
+Directives
+==========
+
+ Several directives allow the use of dotted qualified names refering to objects
+ or attributes of objects contained in the data dictionary given to the
+ .generate() method.
+
+ Simple directives
+ -----------------
+
+ [QUAL_NAME]
+
+ This directive is simply replaced by the value of identifier from the data
+ dictionary. QUAL_NAME might be a dotted qualified name refering to some
+ instance attribute of objects contained in the dats dictionary.
+ Numbers are converted to string though.
+
+ [include "filename"] or [include QUAL_NAME]
+
+ This directive is replaced by content of the named include file.
+
+ Block directives
+ ----------------
+
+ [for QUAL_NAME] ... [end]
+
+ The text within the [for ...] directive and the corresponding [end]
+ is repeated for each element in the sequence referred to by the qualified
+ name in the for directive. Within the for block this identifiers now
+ refers to the actual item indexed by this loop iteration.
+
+ [if-any QUAL_NAME [QUAL_NAME2 ...]] ... [else] ... [end]
+
+ Test if any QUAL_NAME value is not None or an empty string or list.
+ The [else] clause is optional. CAUTION: Numeric values are converted to
+ string, so if QUAL_NAME refers to a numeric value 0, the then-clause is
+ substituted!
+
+ [if-index INDEX_FROM_FOR odd] ... [else] ... [end]
+ [if-index INDEX_FROM_FOR even] ... [else] ... [end]
+ [if-index INDEX_FROM_FOR first] ... [else] ... [end]
+ [if-index INDEX_FROM_FOR last] ... [else] ... [end]
+ [if-index INDEX_FROM_FOR NUMBER] ... [else] ... [end]
+
+ These five directives work similar to [if-any], but are only useful
+ within a [for ...]-block (see above). The odd/even directives are
+ for example useful to choose different background colors for adjacent rows
+ in a table. Similar the first/last directives might be used to
+ remove certain parts (for example "Diff to previous" doesn't make sense,
+ if there is no previous).
+
+ [is QUAL_NAME STRING] ... [else] ... [end]
+ [is QUAL_NAME QUAL_NAME] ... [else] ... [end]
+
+ The [is ...] directive is similar to the other conditional directives
+ above. But it allows to compare two value references or a value reference
+ with some constant string.
+
+"""
+#
+# Copyright (C) 2001-2002 Greg Stein. All Rights Reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+#
+# This software is maintained by Greg and is available at:
+# http://viewcvs.sourceforge.net/
+# it is also used by the following projects:
+# http://edna.sourceforge.net/
+#
+
+import string
+import re
+from types import StringType, IntType, FloatType
+import os
+
+#
+# This regular expression matches three alternatives:
+# expr: DIRECTIVE | BRACKET | COMMENT
+# DIRECTIVE: '[' ITEM (whitespace ITEM)* ']
+# ITEM: STRING | NAME
+# STRING: '"' (not-slash-or-dquote | '\' anychar)* '"'
+# NAME: (alphanum | '_' | '-' | '.')+
+# BRACKET: '[[]'
+# COMMENT: '[#' not-rbracket* ']'
+#
+# When used with the split() method, the return value will be composed of
+# non-matching text and the two paren groups (DIRECTIVE and BRACKET). Since
+# the COMMENT matches are not placed into a group, they are considered a
+# "splitting" value and simply dropped.
+#
+_item = r'(?:"(?:[^\\"]|\\.)*"|[-\w.]+)'
+_re_parse = re.compile(r'\[(%s(?: +%s)*)\]|(\[\[\])|\[#[^\]]*\]' % (_item, _item))
+
+_re_args = re.compile(r'"(?:[^\\"]|\\.)*"|[-\w.]+')
+
+# block commands and their argument counts
+_block_cmd_specs = { 'if-index':2, 'for':1, 'is':2 }
+_block_cmds = _block_cmd_specs.keys()
+
+# two regular expresssions for compressing whitespace. the first is used to
+# compress any whitespace including a newline into a single newline. the
+# second regex is used to compress runs of whitespace into a single space.
+_re_newline = re.compile('[ \t\r\f\v]*\n\\s*')
+_re_whitespace = re.compile(r'\s\s+')
+
+# this regex is used to substitute arguments into a value. we split the value,
+# replace the relevant pieces, and then put it all back together. splitting
+# will produce a list of: TEXT ( splitter TEXT )*. splitter will be '%' or
+# an integer.
+_re_subst = re.compile('%(%|[0-9]+)')
+
+class Template:
+
+ def __init__(self, fname=None, compress_whitespace=1):
+ self.compress_whitespace = compress_whitespace
+ if fname:
+ self.parse_file(fname)
+
+ def parse_file(self, fname):
+ "fname -> a string object with pathname of file containg an EZT template."
+
+ self.program = self._parse(_FileReader(fname))
+
+ def parse(self, text_or_reader):
+ """Parse the template specified by text_or_reader.
+
+ The argument should be a string containing the template, or it should
+ specify a subclass of ezt.Reader which can read templates.
+ """
+ if not isinstance(text_or_reader, Reader):
+ # assume the argument is a plain text string
+ text_or_reader = _TextReader(text_or_reader)
+ self.program = self._parse(text_or_reader)
+
+ def generate(self, fp, data):
+ ctx = _context()
+ ctx.data = data
+ ctx.for_index = { }
+ self._execute(self.program, fp, ctx)
+
+ def _parse(self, reader, for_names=None, file_args=()):
+ """text -> string object containing the HTML template.
+
+ This is a private helper function doing the real work for method parse.
+ It returns the parsed template as a 'program'. This program is a sequence
+ made out of strings or (function, argument) 2-tuples.
+
+ Note: comment directives [# ...] are automatically dropped by _re_parse.
+ """
+
+ # parse the template program into: (TEXT DIRECTIVE BRACKET)* TEXT
+ parts = _re_parse.split(reader.text)
+
+ program = [ ]
+ stack = [ ]
+ if not for_names:
+ for_names = [ ]
+
+ for i in range(len(parts)):
+ piece = parts[i]
+ which = i % 3 # discriminate between: TEXT DIRECTIVE BRACKET
+ if which == 0:
+ # TEXT. append if non-empty.
+ if piece:
+ if self.compress_whitespace:
+ piece = _re_whitespace.sub(' ', _re_newline.sub('\n', piece))
+ program.append(piece)
+ elif which == 2:
+ # BRACKET directive. append '[' if present.
+ if piece:
+ program.append('[')
+ elif piece:
+ # DIRECTIVE is present.
+ args = _re_args.findall(piece)
+ cmd = args[0]
+ if cmd == 'else':
+ if len(args) > 1:
+ raise ArgCountSyntaxError(str(args[1:]))
+ ### check: don't allow for 'for' cmd
+ idx = stack[-1][1]
+ true_section = program[idx:]
+ del program[idx:]
+ stack[-1][3] = true_section
+ elif cmd == 'end':
+ if len(args) > 1:
+ raise ArgCountSyntaxError(str(args[1:]))
+ # note: true-section may be None
+ try:
+ cmd, idx, args, true_section = stack.pop()
+ except IndexError:
+ raise UnmatchedEndError()
+ else_section = program[idx:]
+ func = getattr(self, '_cmd_' + re.sub('-', '_', cmd))
+ program[idx:] = [ (func, (args, true_section, else_section)) ]
+ if cmd == 'for':
+ for_names.pop()
+ elif cmd in _block_cmds:
+ if len(args) > _block_cmd_specs[cmd] + 1:
+ raise ArgCountSyntaxError(str(args[1:]))
+ ### this assumes arg1 is always a ref
+ args[1] = _prepare_ref(args[1], for_names, file_args)
+
+ # handle arg2 for the 'is' command
+ if cmd == 'is':
+ args[2] = _prepare_ref(args[2], for_names, file_args)
+ elif cmd == 'for':
+ for_names.append(args[1][0])
+
+ # remember the cmd, current pos, args, and a section placeholder
+ stack.append([cmd, len(program), args[1:], None])
+ elif cmd == 'include':
+ if args[1][0] == '"':
+ include_filename = args[1][1:-1]
+ f_args = [ ]
+ for arg in args[2:]:
+ f_args.append(_prepare_ref(arg, for_names, file_args))
+ program.extend(self._parse(reader.read_other(include_filename),
+ for_names,
+ f_args))
+ else:
+ if len(args) != 2:
+ raise ArgCountSyntaxError(str(args))
+ program.append((self._cmd_include,
+ (_prepare_ref(args[1], for_names, file_args),
+ reader)))
+ elif cmd == 'if-any':
+ f_args = [ ]
+ for arg in args[1:]:
+ f_args.append(_prepare_ref(arg, for_names, file_args))
+ stack.append(['if-any', len(program), f_args, None])
+ else:
+ # implied PRINT command
+ if len(args) > 1:
+ f_args = [ ]
+ for arg in args:
+ f_args.append(_prepare_ref(arg, for_names, file_args))
+ program.append((self._cmd_format, (f_args[0], f_args[1:])))
+ else:
+ program.append((self._cmd_print,
+ _prepare_ref(args[0], for_names, file_args)))
+
+ if stack:
+ ### would be nice to say which blocks...
+ raise UnclosedBlocksError()
+ return program
+
+ def _execute(self, program, fp, ctx):
+ """This private helper function takes a 'program' sequence as created
+ by the method '_parse' and executes it step by step. strings are written
+ to the file object 'fp' and functions are called.
+ """
+ for step in program:
+ if isinstance(step, StringType):
+ fp.write(step)
+ else:
+ step[0](step[1], fp, ctx)
+
+ def _cmd_print(self, valref, fp, ctx):
+ value = _get_value(valref, ctx)
+
+ # if the value has a 'read' attribute, then it is a stream: copy it
+ if hasattr(value, 'read'):
+ while 1:
+ chunk = value.read(16384)
+ if not chunk:
+ break
+ fp.write(chunk)
+ else:
+ fp.write(value)
+
+ def _cmd_format(self, (valref, args), fp, ctx):
+ fmt = _get_value(valref, ctx)
+ parts = _re_subst.split(fmt)
+ for i in range(len(parts)):
+ piece = parts[i]
+ if i%2 == 1 and piece != '%':
+ idx = int(piece)
+ if idx < len(args):
+ piece = _get_value(args[idx], ctx)
+ else:
+ piece = '<undef>'
+ fp.write(piece)
+
+ def _cmd_include(self, (valref, reader), fp, ctx):
+ fname = _get_value(valref, ctx)
+ ### note: we don't have the set of for_names to pass into this parse.
+ ### I don't think there is anything to do but document it.
+ self._execute(self._parse(reader.read_other(fname)), fp, ctx)
+
+ def _cmd_if_any(self, args, fp, ctx):
+ "If any value is a non-empty string or non-empty list, then T else F."
+ (valrefs, t_section, f_section) = args
+ value = 0
+ for valref in valrefs:
+ if _get_value(valref, ctx):
+ value = 1
+ break
+ self._do_if(value, t_section, f_section, fp, ctx)
+
+ def _cmd_if_index(self, args, fp, ctx):
+ ((valref, value), t_section, f_section) = args
+ list, idx = ctx.for_index[valref[0]]
+ if value == 'even':
+ value = idx % 2 == 0
+ elif value == 'odd':
+ value = idx % 2 == 1
+ elif value == 'first':
+ value = idx == 0
+ elif value == 'last':
+ value = idx == len(list)-1
+ else:
+ value = idx == int(value)
+ self._do_if(value, t_section, f_section, fp, ctx)
+
+ def _cmd_is(self, args, fp, ctx):
+ ((left_ref, right_ref), t_section, f_section) = args
+ value = _get_value(right_ref, ctx)
+ value = string.lower(_get_value(left_ref, ctx)) == string.lower(value)
+ self._do_if(value, t_section, f_section, fp, ctx)
+
+ def _do_if(self, value, t_section, f_section, fp, ctx):
+ if t_section is None:
+ t_section = f_section
+ f_section = None
+ if value:
+ section = t_section
+ else:
+ section = f_section
+ if section is not None:
+ self._execute(section, fp, ctx)
+
+ def _cmd_for(self, args, fp, ctx):
+ ((valref,), unused, section) = args
+ list = _get_value(valref, ctx)
+ if isinstance(list, StringType):
+ raise NeedSequenceError()
+ refname = valref[0]
+ ctx.for_index[refname] = idx = [ list, 0 ]
+ for item in list:
+ self._execute(section, fp, ctx)
+ idx[1] = idx[1] + 1
+ del ctx.for_index[refname]
+
+def boolean(value):
+ "Return a value suitable for [if-any bool_var] usage in a template."
+ if value:
+ return 'yes'
+ return None
+
+
+def _prepare_ref(refname, for_names, file_args):
+ """refname -> a string containing a dotted identifier. example:"foo.bar.bang"
+ for_names -> a list of active for sequences.
+
+ Returns a `value reference', a 3-Tupel made out of (refname, start, rest),
+ for fast access later.
+ """
+ # is the reference a string constant?
+ if refname[0] == '"':
+ return None, refname[1:-1], None
+
+ # if this is an include-argument, then just return the prepared ref
+ if refname[:3] == 'arg':
+ try:
+ idx = int(refname[3:])
+ except ValueError:
+ pass
+ else:
+ if idx < len(file_args):
+ return file_args[idx]
+
+ parts = string.split(refname, '.')
+ start = parts[0]
+ rest = parts[1:]
+ while rest and (start in for_names):
+ # check if the next part is also a "for name"
+ name = start + '.' + rest[0]
+ if name in for_names:
+ start = name
+ del rest[0]
+ else:
+ break
+ return refname, start, rest
+
+def _get_value((refname, start, rest), ctx):
+ """(refname, start, rest) -> a prepared `value reference' (see above).
+ ctx -> an execution context instance.
+
+ Does a name space lookup within the template name space. Active
+ for blocks take precedence over data dictionary members with the
+ same name.
+ """
+ if rest is None:
+ # it was a string constant
+ return start
+ if ctx.for_index.has_key(start):
+ list, idx = ctx.for_index[start]
+ ob = list[idx]
+ elif ctx.data.has_key(start):
+ ob = ctx.data[start]
+ else:
+ raise UnknownReference(refname)
+
+ # walk the rest of the dotted reference
+ for attr in rest:
+ try:
+ ob = getattr(ob, attr)
+ except AttributeError:
+ raise UnknownReference(refname)
+
+ # make sure we return a string instead of some various Python types
+ if isinstance(ob, IntType) or isinstance(ob, FloatType):
+ return str(ob)
+ if ob is None:
+ return ''
+
+ # string or a sequence
+ return ob
+
+
+class _context:
+ """A container for the execution context"""
+
+
+class Reader:
+ "Abstract class which allows EZT to detect Reader objects."
+
+class _FileReader(Reader):
+ """Reads templates from the filesystem."""
+ def __init__(self, fname):
+ self.text = open(fname, 'rb').read()
+ self._dir = os.path.dirname(fname)
+ def read_other(self, relative):
+ return _FileReader(os.path.join(self._dir, relative))
+
+class _TextReader(Reader):
+ """'Reads' a template from provided text."""
+ def __init__(self, text):
+ self.text = text
+ def read_other(self, relative):
+ raise BaseUnavailableError()
+
+
+class EZTException(Exception):
+ """Parent class of all EZT exceptions."""
+
+class ArgCountSyntaxError(EZTException):
+ """A bracket directive got the wrong number of arguments."""
+
+class UnknownReference(EZTException):
+ """The template references an object not contained in the data dictionary."""
+
+class NeedSequenceError(EZTException):
+ """The object dereferenced by the template is no sequence (tuple or list)."""
+
+class UnclosedBlocksError(EZTException):
+ """This error may be simply a missing [end]."""
+
+class UnmatchedEndError(EZTException):
+ """This error may be caused by a misspelled if directive."""
+
+class BaseUnavailableError(EZTException):
+ """Base location is unavailable, which disables includes."""
+
+
+# --- standard test environment ---
+def test_parse():
+ assert _re_parse.split('[a]') == ['', '[a]', None, '']
+ assert _re_parse.split('[a] [b]') == \
+ ['', '[a]', None, ' ', '[b]', None, '']
+ assert _re_parse.split('[a c] [b]') == \
+ ['', '[a c]', None, ' ', '[b]', None, '']
+ assert _re_parse.split('x [a] y [b] z') == \
+ ['x ', '[a]', None, ' y ', '[b]', None, ' z']
+ assert _re_parse.split('[a "b" c "d"]') == \
+ ['', '[a "b" c "d"]', None, '']
+ assert _re_parse.split(r'["a \"b[foo]" c.d f]') == \
+ ['', '["a \\"b[foo]" c.d f]', None, '']
+
+def _test(argv):
+ import doctest, ezt
+ verbose = "-v" in argv
+ return doctest.testmod(ezt, verbose=verbose)
+
+if __name__ == "__main__":
+ # invoke unit test for this module:
+ import sys
+ sys.exit(_test(sys.argv)[0])
Propchange: gump/branches/Gump3/webgump/lib/python/webgump/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Thu Apr 7 09:53:11 2005
@@ -0,0 +1,2 @@
+*.pyc
+*.pyo
Added: gump/branches/Gump3/webgump/lib/python/webgump/__init__.py
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/webgump/lib/python/webgump/__init__.py?view=auto&rev=160420
==============================================================================
(empty)
Propchange: gump/branches/Gump3/webgump/lib/python/webgump/controllers/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Thu Apr 7 09:53:11 2005
@@ -0,0 +1,2 @@
+*.pyc
+*.pyo
Added: gump/branches/Gump3/webgump/lib/python/webgump/controllers/__init__.py
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/webgump/lib/python/webgump/controllers/__init__.py?view=auto&rev=160420
==============================================================================
(empty)
Added: gump/branches/Gump3/webgump/lib/python/webgump/controllers/home.py
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/webgump/lib/python/webgump/controllers/home.py?view=auto&rev=160420
==============================================================================
--- gump/branches/Gump3/webgump/lib/python/webgump/controllers/home.py (added)
+++ gump/branches/Gump3/webgump/lib/python/webgump/controllers/home.py Thu Apr 7 09:53:11 2005
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+from webgump.util.template import TemplateEngine
+from gump.util.mysql import Database
+from webgump.util.log import Logger
+
+def view(req, settings):
+ req.content_type = "text/html"
+
+ dblog = Logger(req, name="webgump.db")
+ db = Database(dblog,settings["db_host"], settings["db_user"],
+ settings["db_password"],settings["db_name"])
+
+ (affected, rows) = db.execute('SELECT * FROM test;')
+ content = []
+ for row in rows:
+ content.append(row["content"])
+
+ settings["content"] = content
+ settings["log"] = Logger(req, name="webgump.ezt")
+ template = "view"
+ template_engine = TemplateEngine(settings['TEMPLATE_ROOT'])
+ try:
+ template_engine.render_template(template, req, settings)
+ except:
+ req.write("<p><strong>Error occurred rendering template '%s':</strong></p>\n" % template)
+ raise
Propchange: gump/branches/Gump3/webgump/lib/python/webgump/util/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Thu Apr 7 09:53:11 2005
@@ -0,0 +1,2 @@
+*.pyc
+*.pyo
Added: gump/branches/Gump3/webgump/lib/python/webgump/util/__init__.py
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/webgump/lib/python/webgump/util/__init__.py?view=auto&rev=160420
==============================================================================
(empty)
Added: gump/branches/Gump3/webgump/lib/python/webgump/util/log.py
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/webgump/lib/python/webgump/util/log.py?view=auto&rev=160420
==============================================================================
--- gump/branches/Gump3/webgump/lib/python/webgump/util/log.py (added)
+++ gump/branches/Gump3/webgump/lib/python/webgump/util/log.py Thu Apr 7 09:53:11 2005
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+import time
+import sys
+
+from mod_python import apache
+
+# log levels
+DEBUG = apache.APLOG_DEBUG #7
+INFO = apache.APLOG_INFO #6
+WARNING = apache.APLOG_WARNING #4
+ERROR = apache.APLOG_ERR #3
+CRITICAL = apache.APLOG_CRIT #2
+NONE = -1000
+
+LOG_LEVEL_NAMES = {
+ DEBUG:'DEBUG',
+ INFO:'INFO',
+ WARNING:'WARNING',
+ ERROR:'ERROR',
+ CRITICAL:'CRITICAL'
+}
+
+class Logger:
+ """Simple logger similar to PEP 249 that logs to an apache error log."""
+ def __init__(self, req=None, level=DEBUG, printLevel=ERROR, name=None):
+ self.req = req
+ if req:
+ self.error_func = req.log_error
+ else:
+ self.error_func = apache.log_error
+ self.level = level
+ self.printLevel = printLevel
+ self.name = name
+
+
+ def log(self, level, msg):
+ try:
+ logToWebPage = self.req and level <= self.printLevel
+ if logToWebPage: self.req.write("<pre>\n")
+
+ if self.name:
+ for line in msg.split("\n"):
+ self.error_func("[%s] %s" % (self.name, line), level)
+ if logToWebPage: self.req.write("[%s] %s: %s\n" % (self.name, LOG_LEVEL_NAMES[level], line))
+ else:
+ for line in msg.split("\n"):
+ self.error_func(msg, level)
+ if logToWebPage: self.req.write("%s: %s\n" % (LOG_LEVEL_NAMES[level], line))
+
+ if logToWebPage: self.req.write("</pre>\n")
+ except IOError:
+ # probably a client is "gone"
+ pass
+
+ def debug(self, msg):
+ if self.level >= DEBUG:
+ self.log(DEBUG, msg);
+
+ def info(self, msg):
+ if self.level >= INFO:
+ self.log(INFO, msg);
+
+ def warning(self, msg):
+ if self.level >= WARNING:
+ self.log(WARNING, msg);
+
+ def error(self, msg):
+ if self.level >= ERROR:
+ self.log(ERROR, msg);
+
+ def exception(self, msg):
+ info = sys.exc_info()
+ import traceback
+ from StringIO import StringIO
+ buf = StringIO()
+ traceback.print_exception( info[0], info[1], info[2], file=buf)
+ self.error("%s\n%s" % (msg, buf.getvalue()))
+
+ def critical(self, msg):
+ if self.level >= CRITICAL:
+ self.log(CRITICAL, msg);
+
Added: gump/branches/Gump3/webgump/lib/python/webgump/util/template.py
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/webgump/lib/python/webgump/util/template.py?view=auto&rev=160420
==============================================================================
--- gump/branches/Gump3/webgump/lib/python/webgump/util/template.py (added)
+++ gump/branches/Gump3/webgump/lib/python/webgump/util/template.py Thu Apr 7 09:53:11 2005
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+import os
+from ezt import Template
+
+class TemplateEngine:
+ def __init__(self, template_root):
+ self.template_root = template_root
+
+ def render_template(self, template, target, data):
+ if isinstance(template, Template):
+ template.generate(target, data)
+ else:
+ self.get_template(template).generate(target, data)
+
+ def get_template(self, name):
+ templatefile = os.path.abspath(os.path.join(self.template_root, "%s.ezt" % name))
+ template = Template(templatefile)
+ return template
Propchange: gump/branches/Gump3/webgump/logs/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Thu Apr 7 09:53:11 2005
@@ -0,0 +1 @@
+*
Added: gump/branches/Gump3/webgump/sql/webgump.sql
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/webgump/sql/webgump.sql?view=auto&rev=160420
==============================================================================
--- gump/branches/Gump3/webgump/sql/webgump.sql (added)
+++ gump/branches/Gump3/webgump/sql/webgump.sql Thu Apr 7 09:53:11 2005
@@ -0,0 +1,23 @@
+# CocoaMySQL dump
+# Version 0.5
+# http://cocoamysql.sourceforge.net
+#
+# Host: localhost (MySQL 4.0.23-standard)
+# Database: lsdblog
+# Generation Time: 2005-04-05 17:37:25 +0200
+# ************************************************************
+
+# Dump of table test
+# ------------------------------------------------------------
+
+DROP TABLE IF EXISTS `test`;
+
+CREATE TABLE `test` (
+ `id` int(11) NOT NULL auto_increment,
+ `content` text,
+ PRIMARY KEY (`id`)
+) TYPE=MyISAM;
+
+INSERT INTO `test` (`id`,`content`) VALUES ("1","mwuhahahaha!");
+
+
Added: gump/branches/Gump3/webgump/templates/view.ezt
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/webgump/templates/view.ezt?view=auto&rev=160420
==============================================================================
--- gump/branches/Gump3/webgump/templates/view.ezt (added)
+++ gump/branches/Gump3/webgump/templates/view.ezt Thu Apr 7 09:53:11 2005
@@ -0,0 +1,14 @@
+<html>
+ <head>
+ <title>[PAGE_TITLE]</title>
+ </head>
+ <body>
+
+ <h1>[PAGE_TITLE]</h1>
+
+[for content]
+ <pre>Piece of content: [content]</pre>
+[end]
+
+ </body>
+</html>