diff -Nru quixote-2.5/CHANGES.txt quixote-2.7~b2/CHANGES.txt --- quixote-2.5/CHANGES.txt 2007-11-16 22:36:29.000000000 +0000 +++ quixote-2.7~b2/CHANGES.txt 2010-01-22 19:38:12.000000000 +0000 @@ -1,65 +1,318 @@ Summary of changes ================== -2.5 (released 2007-11-16) -------------------------- +v2.7b2 +------ + +Author: Neil Schemenauer +Date: Fri Jan 22 13:35:21 2010 -0600 + + Update version to 2.7b2 + +Author: Neil Schemenauer +Date: Fri Jan 22 13:32:44 2010 -0600 + + Use the StringIO module rather than cStringIO. + + cStringIO is gone in Python 3 and also does not handle unicode + strings properly. + +Author: Neil Schemenauer +Date: Fri Jan 22 13:29:46 2010 -0600 + + By default, set Cache-Control in addition to the Expires header. + + The Expires header is sufficient for HTTP 1.0 but for HTTP 1.1 we + must add a must-revalidate directive. Clients and proxies are + allowed to ignore Expires in certain cases and use stale pages (RFC + 2616 sections 13.1.5 and 14.9.4). + +Author: Neil Schemenauer +Date: Fri Jan 22 13:28:58 2010 -0600 + + Disable cimport module for Python >= 2.6. + + The current version of the cimport module does not support relative + imports. Disable it for now. + +Author: Neil Schemenauer +Date: Sun Dec 13 14:18:45 2009 -0600 + + Fix reference to compile_file function (fixes compile_dir function). + + +v2.7b1 +------ + +Author: Neil Schemenauer +Date: Mon Sep 7 00:41:44 2009 -0600 + + Update version for 2.7b1 release. + +Author: Neil Schemenauer +Date: Mon Sep 7 00:42:51 2009 -0600 + + Add session iterator. + +Author: Neil Schemenauer +Date: Wed Dec 3 14:41:05 2008 -0600 + + Don't use callable(). + +Author: Neil Schemenauer +Date: Wed Dec 3 12:43:38 2008 -0600 + + Use __contains__ instead of has_key. + +Author: Neil Schemenauer +Date: Wed Dec 3 12:41:18 2008 -0600 + + Use utf-8 as default encoding. + +Author: Neil Schemenauer +Date: Fri Nov 28 23:00:40 2008 -0600 + + Use built-in set type. + +Author: Neil Schemenauer +Date: Mon Sep 7 01:30:26 2009 -0600 + + Work around broken ihooks module in Python 2.6. + + Using the import hook is still the most convenient way of using PTL + modules. + +Author: Neil Schemenauer +Date: Sun Apr 12 10:57:06 2009 -0600 + + Remove spurious kwargs from WidgetDict.__init__. + +Author: Neil Schemenauer +Date: Tue Jun 16 09:55:31 2009 -0600 + + Add options to sendmail so it can be used without a Quixote config. + + Also, remove broken encode() call since it can't handle Unicode properly + as implemented. + +Author: Neil Schemenauer +Date: Sun May 31 19:09:53 2009 -0600 + + Add SESSION_COOKIE_SECURE and SESSION_COOKIE_HTTPONLY. + + Based on a suggestion from Emmanuel Dreyfus , add + the SESSION_COOKIE_SECURE and SESSION_COOKIE_HTTPONLY options. + Setting them to true will cause the corresponding flag to be set + on the session cookie. + +Author: Hamish Lawson +Date: Mon Feb 2 10:04:04 2009 -0600 + + Check for other possible values of HTTPS. + + Currently HTTPRequest only checks whether the HTTPS environment + variable has a value of 'on', but other possible positive values are + '1' (as set by mod_wsgi) and 'yes'. + +Author: Neil Schemenauer +Date: Tue Jan 6 20:16:39 2009 -0600 + + Avoid infinite redirect when PATH_INFO is empty. + + +v2.6 +---- + +Author: Neil Schemenauer +Date: Tue Nov 25 22:22:12 2008 -0600 + + Add quixote.ptl.compile_package function. + + The ihooks module is broken in Python 2.6 and will be gone in Python 3. + Provide compile_package() as an alternative method of compiling PTL + templates. + +Author: Neil Schemenauer +Date: Sat Jul 5 17:51:20 2008 -0600 + + Prepare for v2.6 release. + + Remove CHANGES.txt file and generate it as necessary. Update + version numbers and remove setup.py version check. + +Author: Neil Schemenauer +Date: Sat Jul 5 17:50:14 2008 -0600 + + Improve README.txt. + + Add link to wiki. Add instructions on how to run the mini demo. + +Author: Neil Schemenauer +Date: Sat Apr 5 20:05:33 2008 -0600 + + Use SCGIMount directive in documentation. + +Author: Neil Schemenauer +Date: Sat Apr 5 15:15:40 2008 -0600 + + Remove a reference to _q_exception_handler. + +Author: Neil Schemenauer +Date: Sat Feb 16 15:09:45 2008 -0600 + + Silence struck.pack warning. + + Silence struct.pack warning by forcing gzipped response CRC to be + a positive integer. + + +v2.5 +---- + +Author: Neil Schemenauer +Date: Fri Nov 16 16:36:30 2007 -0600 + + Update CHANGES.txt for 2.5 release. + +Author: Neil Schemenauer +Date: Fri Nov 16 16:35:33 2007 -0600 -Neil Schemenauer : Fix PTL handling of __future__ statements. -2.5b1 (released 2007-04-05) ---------------------------- +v2.5b1 +------ + +Author: Neil Schemenauer +Date: Fri Apr 6 00:01:32 2007 -0600 + + Improve setup.py for 2.5b1 release. -C. Titus Brown : +Author: Neil Schemenauer +Date: Thu Apr 5 23:33:22 2007 -0600 + + Prepare for 2.5b1 release. + +Author: C. Titus Brown +Date: Fri Feb 9 17:00:37 2007 -0600 + + Reorganize source + Added some simple WSGI documentation; cleaned up test code a bit. + Added test server status check. Moved the quixote package into the + quixote/ subdirectory. Added nose+twill tests under tests/. + +Author: C. Titus Brown +Date: Tue Jan 30 09:25:26 2007 -0600 + + Add WSGI and qpy support. + + Added quixote.html.use_qpy to switch Quixote to using qpy instead of + htmltext (code contributed by Mike Orr). Added quixote.cleanup() + to clear _publisher to support test fixtures. Added wsgi.py, + quixote.get_wsgi_app() for WSGI support. -C. Titus Brown : - Moved package contents into quixote/ subdirectory; added tests/ - subdirectory with some minimal tests. +Author: Neil Schemenauer +Date: Tue Jan 30 09:23:25 2007 -0600 -C. Titus Brown : - Added quixote.html.use_qpy to switch Quixote over to using qpy - instead of htmltext. (Code contributed by Mike Orr.) + In scgi_server.py, make host default to 'localhost'. + + It seems better to default to a more secure setup. The previous code + caused the server to listen on all interfaces by default. -C. Titus Brown : - Added quixote.cleanup() to clear _publisher to support test fixtures. +Author: Neil Schemenauer +Date: Tue Jan 30 09:21:51 2007 -0600 -C. Titus Brown : - Added wsgi.py, quixote.get_wsgi_app() for WSGI support. + Make setup.py URL point to quixote.ca site. -Neil Schemenauer : - In scgi_server.py, make the "host" default to "localhost" for - the run() function. +Author: Neil Schemenauer +Date: Thu Sep 7 10:59:51 2006 -0600 -Neil Schemenauer : - Allow setup.py to work without the "quixote" package being - available. + Add comment explaining why single-select options are required. +Author: Neil Schemenauer +Date: Wed Aug 23 20:39:49 2006 -0600 -2.5a1 (released 2006-08-08) -------------------------- + Allow setup.py to work without the "quixote" package. + + Also, improve the PyPI information. -Neil Schemenauer : - Update PTL to work with Python 2.5. -Neil Schemenauer : - In simple_server.py, ensure that server_close() gets called. +v2.5a1 +------ -Neil Schemenauer : - Remove name="" from the noscript input tag rendered for - OptionSelectWidget. This change does not seem to effect behavior +Author: Neil Schemenauer +Date: Tue Aug 15 12:02:13 2006 -0600 + + Do version check only when building source distribution. + +Author: Neil Schemenauer +Date: Tue Aug 8 20:43:03 2006 -0600 + + Prepare for 2.5a1 release. + +Author: Neil Schemenauer +Date: Mon Aug 7 16:19:28 2006 -0600 + + Have setup.py check version numbers + + Make setup.py check that quixote.__version__ matches the one in + CHANGES.txt. Also, stop distributing MANIFEST since it is a pain to + ensure that it is up-to-date. + +Author: Neil Schemenauer +Date: Mon Aug 7 16:01:12 2006 -0600 + + Make PTL work with Python 2.5. + +Author: Neil Schemenauer +Date: Mon Aug 7 15:53:48 2006 -0600 + + Don't try to process non-ReST text files with rst2html + +Author: Neil Schemenauer +Date: Mon Aug 7 15:51:52 2006 -0600 + + Make MANIFEST.in more accurate. + +Author: Neil Schemenauer +Date: Fri May 19 11:55:30 2006 -0600 + + Have simple_server.py call server_close(). + + Use a try/finally block to ensure that server_close() is called by + simple_server.py (as requested by Michele Simionato). + +Author: Neil Schemenauer +Date: Fri May 19 11:54:24 2006 -0600 + + Remove empty "name" attribute from an input field. + + Remove the empty "name" attribute from the noscript input field created + by OptionSelectWidget. This change does not seem to effect behavior and makes Tidy happier. -Neil Schemenauer : +Author: Neil Schemenauer +Date: Fri May 19 11:51:55 2006 -0600 + Add get_size() method to the Upload class. -Neil Schemenauer : - Remove Subversion keywords. Update README. +Author: Neil Schemenauer +Date: Thu Mar 16 19:48:55 2006 -0700 + + Update README and LICENSE. + +Author: Neil Schemenauer +Date: Thu Mar 16 13:18:21 2006 -0700 + + Remove Subversion keywords. + +v2.4 +---- -Older changes -============= +Author: Neil Schemenauer +Date: Wed Mar 15 17:58:21 2006 -0700 - See doc/CHANGES_24.txt for changes up to version 2.4. + Import Quixote 2.4. diff -Nru quixote-2.5/debian/changelog quixote-2.7~b2/debian/changelog --- quixote-2.5/debian/changelog 2010-12-10 17:13:24.000000000 +0000 +++ quixote-2.7~b2/debian/changelog 2010-12-10 01:23:13.000000000 +0000 @@ -1,14 +1,32 @@ -quixote (2.5-5build2) natty; urgency=low +quixote (2.7~b2-1) unstable; urgency=low - * Rebuild with python 2.7 as the python default. + * New upstream beta release (Closes: #592151). + * debian/control: + - Changed to DPMT maintainer; put myself in uploaders. + - Updated Standards-Version to 3.9.1. + - Moved appropriate build-depends to build-depends-indep. + - Removed build-depends on dpatch. + - Updated python-quixote-doc package description (Closes: #564332). + - Added ${misc:Depends} to python-quixote depends per lintian warning. + - Added ${shlibs:Depends} per lintian warning. + * debian/source/format: Switched to source format 3.0. + * Removed debian/pyversions as only 2.6 is currently available. + * debian/rules: + - Only build docs in the binary-indep target (Closes: #514762). + - Removed patch handling code. + - Added commands to strip binaries. + * debian/patches: Converted patches to quilt format. + * debian/copyright: Removed the encoded copyright symbol per lintian + warning. - -- Matthias Klose Wed, 08 Dec 2010 15:09:56 +0000 + -- Oleksandr Moskalenko Thu, 09 Dec 2010 16:33:33 -0600 -quixote (2.5-5build1) natty; urgency=low +quixote (2.6-1) unstable; urgency=low - * Rebuild to add support for python 2.7. + * New upstream release. + * Fix the build targets (Closes: #514762). - -- Matthias Klose Fri, 03 Dec 2010 00:13:41 +0000 + -- Oleksandr Moskalenko Wed, 18 Feb 2009 09:30:01 -0700 quixote (2.5-5) unstable; urgency=low diff -Nru quixote-2.5/debian/compat quixote-2.7~b2/debian/compat --- quixote-2.5/debian/compat 2010-12-10 17:13:24.000000000 +0000 +++ quixote-2.7~b2/debian/compat 2010-12-10 00:49:07.000000000 +0000 @@ -1 +1 @@ -5 +7 diff -Nru quixote-2.5/debian/control quixote-2.7~b2/debian/control --- quixote-2.5/debian/control 2010-12-10 17:13:24.000000000 +0000 +++ quixote-2.7~b2/debian/control 2010-12-10 01:24:06.000000000 +0000 @@ -1,10 +1,10 @@ Source: quixote -Section: web +Section: python Priority: optional -Maintainer: Oleksandr Moskalenko -Uploaders: Neil Schemenauer , Debian Python Modules Team -Build-Depends: dpatch, debhelper (>= 5), python-all-dev, python-setuptools (>= 0.6b3), python-support (>= 0.6.4) -Standards-Version: 3.8.0 +Maintainer: Debian Python Modules Team +Uploaders: Neil Schemenauer , Oleksandr Moskalenko +Build-Depends: debhelper (>= 7), python-all, python-setuptools (>= 0.6b3-1~), python-support (>= 0.6.4), python-all-dev +Standards-Version: 3.9.1 XS-Python-Version: all Homepage: http://www.mems-exchange.org/software/quixote/ Vcs-Svn: svn://svn.debian.org/python-modules/packages/quixote/trunk/ @@ -12,7 +12,7 @@ Package: python-quixote Architecture: any -Depends: ${python:Depends}, python +Depends: ${python:Depends}, python, ${misc:Depends}, ${shlibs:Depends} Recommends: python-quixote-doc, python (>=2.4) Replaces: quixote (<< 2.4-6), python2.3-quixote (<< 2.4-6), python2.4-quixote (<< 2.4-6) Conflicts: quixote (<< 2.4-6), python2.3-quixote (<< 2.4-6), python2.4-quixote (<< 2.4-6) @@ -20,7 +20,7 @@ Provides: ${python:Provides} Description: A highly Pythonic Web application framework Quixote is yet another framework for developing Web applications in - Python. The design goals were: + Python. The design goals were: . 1) To allow easy development of Web applications where the emphasis is more on complicated programming logic than @@ -41,9 +41,18 @@ Package: python-quixote-doc Section: doc +Depends: python-quixote, ${misc:Depends} Architecture: all XB-Python-Version: ${python:Versions} Replaces: quixote-doc (<< 2.4-6) Conflicts: quixote-doc (<< 2.4-6) Description: Quixote web application framework documentation - Contains the documentation and examples for Quixote. + This package contains the documentation and examples for Quixote. + Quixote is yet another framework for developing Web applications in + Python. If you view a web site as a program, and web pages as + subroutines, Quixote just might be the tool for you. If you view a + web site as a graphic design showcase, and each web page as an + individual work of art, Quixote is probably not what you're looking for. + The full list of its design goals is listed in the description to + python-quixote package. + diff -Nru quixote-2.5/debian/copyright quixote-2.7~b2/debian/copyright --- quixote-2.5/debian/copyright 2010-12-10 17:13:24.000000000 +0000 +++ quixote-2.7~b2/debian/copyright 2010-12-09 22:45:27.000000000 +0000 @@ -32,7 +32,7 @@ license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Quixote-2.0 alone or in any derivative version, provided, however, that CNRI's -License Agreement and CNRI's notice of copyright, i.e., "Copyright © +License Agreement and CNRI's notice of copyright, i.e., "Copyright 2005 Corporation for National Research Initiatives; All Rights Reserved" are retained in Quixote-2.0 alone or in any derivative version prepared by Licensee. diff -Nru quixote-2.5/debian/patches/00list quixote-2.7~b2/debian/patches/00list --- quixote-2.5/debian/patches/00list 2010-12-10 17:13:24.000000000 +0000 +++ quixote-2.7~b2/debian/patches/00list 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -01-ptl_compile.py.dpatch -02-egg_support.dpatch diff -Nru quixote-2.5/debian/patches/01_ptl_compile.py quixote-2.7~b2/debian/patches/01_ptl_compile.py --- quixote-2.5/debian/patches/01_ptl_compile.py 1970-01-01 00:00:00.000000000 +0000 +++ quixote-2.7~b2/debian/patches/01_ptl_compile.py 2010-12-09 22:52:06.000000000 +0000 @@ -0,0 +1,8 @@ +--- quixote-2.7~b2.orig/quixote/ptl/ptl_compile.py ++++ quixote-2.7~b2/quixote/ptl/ptl_compile.py +@@ -1,4 +1,4 @@ +-#!/www/python/bin/python ++#!/usr/bin/env python + """Compile a PTL template. + + First template function names are mangled, noting the template type. diff -Nru quixote-2.5/debian/patches/01-ptl_compile.py.dpatch quixote-2.7~b2/debian/patches/01-ptl_compile.py.dpatch --- quixote-2.5/debian/patches/01-ptl_compile.py.dpatch 2010-12-10 17:13:24.000000000 +0000 +++ quixote-2.7~b2/debian/patches/01-ptl_compile.py.dpatch 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -#! /bin/sh /usr/share/dpatch/dpatch-run -## 01_ptl_compile.py.dpatch by Oleksandr Moskalenko -## -## All lines beginning with `## DP:' are a description of the patch. -## DP: Patch to fix the hardcoded path to python executable - -@DPATCH@ - ---- Quixote-2.5/quixote/ptl/ptl_compile.py 2007-10-01 16:53:14.000000000 -0600 -+++ ptl_compile.py 2007-11-24 13:36:50.000000000 -0700 -@@ -1,4 +1,4 @@ --#!/www/python/bin/python -+#!/usr/bin/env python - """Compile a PTL template. - - First template function names are mangled, noting the template type. diff -Nru quixote-2.5/debian/patches/02-egg_support.dpatch quixote-2.7~b2/debian/patches/02-egg_support.dpatch --- quixote-2.5/debian/patches/02-egg_support.dpatch 2010-12-10 17:13:24.000000000 +0000 +++ quixote-2.7~b2/debian/patches/02-egg_support.dpatch 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -#! /bin/sh /usr/share/dpatch/dpatch-run -## 02-egg_support.dpatch by Piotr Ożarowski - -@DPATCH@ - ---- quixote-2.5/setup.py 2007-01-25 20:26:49.000000000 +0100 -+++ quixote-2.5/setup.py 2007-02-08 13:30:45.000000000 +0100 -@@ -6,6 +6,7 @@ - - import sys, os - from distutils import core -+from setuptools import setup - from distutils.extension import Extension - from ptl.qx_distutils import qx_build_py - -@@ -60,4 +61,4 @@ - kw['download_url'] = ('http://www.mems-exchange.org/software/files' - '/quixote/Quixote-%s.tar.gz' % kw['version']) - --core.setup(**kw) -+setup(**kw) diff -Nru quixote-2.5/debian/patches/02_setup.py quixote-2.7~b2/debian/patches/02_setup.py --- quixote-2.5/debian/patches/02_setup.py 1970-01-01 00:00:00.000000000 +0000 +++ quixote-2.7~b2/debian/patches/02_setup.py 2010-12-09 22:55:05.000000000 +0000 @@ -0,0 +1,16 @@ +--- quixote-2.7~b2.orig/setup.py ++++ quixote-2.7~b2/setup.py +@@ -10,6 +10,7 @@ + import sys + import os + from distutils import core ++from setuptools import setup + from distutils.extension import Extension + from quixote.ptl.qx_distutils import qx_build_py + from quixote import __version__ +@@ -71,4 +72,4 @@ + if 'platforms' in core.setup_keywords: + kw['platforms'] = 'Most' + +-core.setup(**kw) ++setup(**kw) diff -Nru quixote-2.5/debian/patches/series quixote-2.7~b2/debian/patches/series --- quixote-2.5/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 +++ quixote-2.7~b2/debian/patches/series 2010-12-09 23:27:21.000000000 +0000 @@ -0,0 +1,2 @@ +01_ptl_compile.py +02_setup.py diff -Nru quixote-2.5/debian/python-quixote-doc.docs quixote-2.7~b2/debian/python-quixote-doc.docs --- quixote-2.5/debian/python-quixote-doc.docs 2010-12-10 17:13:24.000000000 +0000 +++ quixote-2.7~b2/debian/python-quixote-doc.docs 2010-12-10 00:05:32.000000000 +0000 @@ -3,8 +3,6 @@ doc/demo.txt doc/form2conversion.html doc/form2conversion.txt -doc/INSTALL.html -doc/INSTALL.txt doc/multi-threaded.html doc/multi-threaded.txt doc/programming.html diff -Nru quixote-2.5/debian/rules quixote-2.7~b2/debian/rules --- quixote-2.5/debian/rules 2010-12-10 17:13:24.000000000 +0000 +++ quixote-2.7~b2/debian/rules 2010-12-10 00:38:31.000000000 +0000 @@ -3,9 +3,7 @@ #export DH_VERBOSE=1 package=python-quixote -include /usr/share/dpatch/dpatch.make - -clean: unpatch +clean: dh_testdir dh_testroot rm -f build-stamp @@ -19,38 +17,49 @@ python setup.py build touch $@ -build: patch-stamp build-stamp +build: build-stamp install: build dh_testdir dh_testroot - dh_clean -k + dh_prep dh_installdirs python setup.py install \ --no-compile \ - --single-version-externally-managed \ - --root $(CURDIR)/debian/$(package) + --root $(CURDIR)/debian/$(package) \ + --single-version-externally-managed -binary-indep: build install +binary-arch: build install dh_testdir dh_testroot - dh_installchangelogs CHANGES.txt - dh_installdocs - dh_installexamples - dh_compress -X.cgi -X.py -X.conf -# install -m 644 debian/python-quixote.lintian \ - $(DEBIAN_DIR)/$(package)/usr/share/lintian/overrides/python-quixote -# install -m 644 debian/python-quixote-doc.lintian \ - $(DEBIAN_DIR)/$(package)-doc/usr/share/lintian/overrides/python-quixote-doc - dh_fixperms - dh_pysupport - rm -f $(DEBIAN_DIR)/python-quixote-doc/usr/share/doc/python-quixote-doc/INSTALL.* - dh_installdeb - dh_gencontrol - dh_md5sums - dh_builddeb + dh_installdirs + dh_installchangelogs CHANGES.txt -a + dh_installdocs -a + dh_installexamples -a + dh_compress -X.cgi -X.py -X.conf -a + dh_fixperms -a + dh_pysupport -a + dh_makeshlibs -a + dh_shlibdeps -a + dh_strip -a + dh_installdeb -a + dh_gencontrol -a + dh_md5sums -a + dh_builddeb -a -binary-arch: binary-indep +binary-indep: + dh_testdir + dh_testroot + dh_installchangelogs CHANGES.txt -i + dh_installdocs -i + dh_installexamples -i + dh_compress -X.cgi -X.py -X.conf -i + dh_fixperms -i + dh_pysupport -i + dh_installdeb -i + dh_gencontrol -i + dh_md5sums -i + dh_builddeb -i -binary: binary-indep binary-arch +binary: binary-arch binary-indep .PHONY: build clean binary-indep binary-arch binary install patch unpatch diff -Nru quixote-2.5/debian/source/format quixote-2.7~b2/debian/source/format --- quixote-2.5/debian/source/format 1970-01-01 00:00:00.000000000 +0000 +++ quixote-2.7~b2/debian/source/format 2010-12-10 17:13:24.000000000 +0000 @@ -0,0 +1 @@ +3.0 (quilt) diff -Nru quixote-2.5/doc/web-server.txt quixote-2.7~b2/doc/web-server.txt --- quixote-2.5/doc/web-server.txt 2006-03-17 02:49:51.000000000 +0000 +++ quixote-2.7~b2/doc/web-server.txt 2008-11-29 05:50:56.000000000 +0000 @@ -44,7 +44,7 @@ ----------------------- Throughout this document, I'm going to assume that: - + * CGI scripts live in the ``/www/cgi-bin`` directory of your web server, and have the extension ``.cgi`` @@ -174,11 +174,11 @@ already-running SCGI server on a different TCP port, and doesn't try to start or stop processes, leaving that up to the SCGI server. -The SCGI code is available from http://www.mems-exchange.org/software/scgi/ . +The SCGI code is available from . The quixote.server.scgi_server module is a script that publishes the demo quixote application via SCGI. You can use -it for your application by importing it and calling the ``run()`` +it for your application by importing it and calling the ``run()`` function with arguments to run your application, on the port you choose. Here is an example:: @@ -195,13 +195,7 @@ The following Apache directive will direct requests to an SCGI server running on port 3001:: - - SCGIServer 127.0.0.1 3001 - SCGIHandler On - - -[Note: the mod_scgi module for Apache 2 requires a colon, instead of a -space, between the host and port on the SCGIServer line.] + SCGIMount / 127.0.0.1:3001 SCGI through CGI @@ -255,4 +249,3 @@ application is, the more code it loads, and the more work it does at startup, the bigger a win FastCGI will be for you (in comparison to CGI). - diff -Nru quixote-2.5/PKG-INFO quixote-2.7~b2/PKG-INFO --- quixote-2.5/PKG-INFO 2007-11-16 22:38:52.000000000 +0000 +++ quixote-2.7~b2/PKG-INFO 2010-01-22 19:38:12.000000000 +0000 @@ -1,12 +1,12 @@ Metadata-Version: 1.0 Name: Quixote -Version: 2.5 -Summary: A highly Pythonic Web application framework +Version: 2.7b2 +Summary: A small and flexible Python Web application framework Home-page: http://www.quixote.ca/ Author: The Quixote developers Author-email: webmaster@quixote.ca -License: DFSG approved open source (see LICENSE.txt) -Download-URL: http://quixote.ca/releases/Quixote-2.5.tar.gz +License: DFSG approved (see LICENSE.txt) +Download-URL: http://quixote.ca/releases/Quixote-2.7b2.tar.gz Description: UNKNOWN Platform: Most Classifier: Development Status :: 5 - Production/Stable diff -Nru quixote-2.5/quixote/config.py quixote-2.7~b2/quixote/config.py --- quixote-2.5/quixote/config.py 2006-03-17 02:49:51.000000000 +0000 +++ quixote-2.7~b2/quixote/config.py 2009-06-01 01:23:38.000000000 +0000 @@ -70,6 +70,8 @@ # controlled by your application, but no other. SESSION_COOKIE_DOMAIN = None # eg. ".example.com" SESSION_COOKIE_PATH = None # eg. "/" +SESSION_COOKIE_SECURE = False +SESSION_COOKIE_HTTPONLY = False # Mail-related variables @@ -123,6 +125,8 @@ 'session_cookie_domain', 'session_cookie_name', 'session_cookie_path', + 'session_cookie_secure', + 'session_cookie_httponly', 'mail_from', 'mail_server', 'mail_debug_addr', diff -Nru quixote-2.5/quixote/demo/root.ptl quixote-2.7~b2/quixote/demo/root.ptl --- quixote-2.5/quixote/demo/root.ptl 2006-03-17 02:49:51.000000000 +0000 +++ quixote-2.7~b2/quixote/demo/root.ptl 2008-11-29 05:50:56.000000000 +0000 @@ -37,7 +37,8 @@
  • publish_error: A Python function that raises a PublishError exception. This exception - will be caught by a _q_exception_handler method. + will be caught and formatted by the + Publisher.format_publish_error() method.
  • dumpreq: Print out the contents of the HTTPRequest object.
  • css: diff -Nru quixote-2.5/quixote/directory.py quixote-2.7~b2/quixote/directory.py --- quixote-2.5/quixote/directory.py 2006-03-17 02:49:51.000000000 +0000 +++ quixote-2.7~b2/quixote/directory.py 2009-12-07 17:59:29.000000000 +0000 @@ -63,7 +63,7 @@ return obj._q_traverse(path) else: raise TraversalError - elif callable(obj): + elif hasattr(obj, '__call__'): return obj() else: return obj diff -Nru quixote-2.5/quixote/form/form.py quixote-2.7~b2/quixote/form/form.py --- quixote-2.5/quixote/form/form.py 2006-03-17 02:49:51.000000000 +0000 +++ quixote-2.7~b2/quixote/form/form.py 2009-12-07 17:59:29.000000000 +0000 @@ -121,7 +121,10 @@ def has_key(self, name): """Return true if the widget named 'name' is in the form.""" - return self._names.has_key(name) + return name in self._names + + def __contains__(self, name): + return self.has_key(name) def get(self, name, default=None): """(name:string, default=None) -> any @@ -226,7 +229,7 @@ # -- Form population methods --------------------------------------- def add(self, widget_class, name, *args, **kwargs): - if self._names.has_key(name): + if name in self._names: raise ValueError, "form already has '%s' widget" % name widget = widget_class(name, *args, **kwargs) self._names[name] = widget diff -Nru quixote-2.5/quixote/form/widget.py quixote-2.7~b2/quixote/form/widget.py --- quixote-2.5/quixote/form/widget.py 2006-09-07 16:59:07.000000000 +0000 +++ quixote-2.7~b2/quixote/form/widget.py 2009-12-07 17:59:29.000000000 +0000 @@ -266,7 +266,7 @@ """ def _parse(self, request): - self.value = request.form.has_key(self.name) + self.value = self.name in request.form def render_content(self): return htmltag("input", xml_end=True, @@ -342,7 +342,7 @@ used_keys = {} keys = map(stringify, descriptions) for key in keys: - if used_keys.has_key(key): + if key in used_keys: raise ValueError, "duplicated descriptions (provide keys)" used_keys[key] = 1 return keys @@ -584,7 +584,7 @@ name=self.name, value=value, **self.attrs) def _parse(self, request): - self.value = request.form.has_key(self.name) + self.value = self.name in request.form class SubmitWidget(ButtonWidget): @@ -757,7 +757,7 @@ return has_error def add(self, widget_class, name, *args, **kwargs): - if self._names.has_key(name): + if name in self._names: raise ValueError, 'the name %r is already used' % name if self.attrs.get('disabled') and 'disabled' not in kwargs: kwargs['disabled'] = True @@ -868,7 +868,7 @@ element_names : [string] """ - def __init__(self, name, value=None, title='', hint='', + def __init__(self, name, value=None, element_key_type=StringWidget, element_value_type=StringWidget, element_key_kwargs={}, diff -Nru quixote-2.5/quixote/form1/form.py quixote-2.7~b2/quixote/form1/form.py --- quixote-2.5/quixote/form1/form.py 2006-03-17 02:49:51.000000000 +0000 +++ quixote-2.7~b2/quixote/form1/form.py 2009-12-07 17:59:29.000000000 +0000 @@ -375,7 +375,7 @@ the request, use the first submit button registered. """ for button in self.submit_buttons: - if request.form.has_key(button.name): + if button.name in request.form: return button.name else: if request.form and self.submit_buttons: @@ -478,7 +478,7 @@ Returns the new Widget. """ - if self.widgets.has_key(name): + if name in self.widgets: raise ValueError, "form already has '%s' variable" % name klass = get_widget_class(widget_type) new_widget = apply(klass, (name, value), args) @@ -492,7 +492,7 @@ def add_submit_button(self, name, value): global _widget_class - if self.widgets.has_key(name): + if name in self.widgets: raise ValueError, "form already has '%s' variable" % name new_widget = _widget_class['submit_button'](name, value) @@ -519,7 +519,7 @@ def get_widget_class(widget_type): global _widget_class - if callable(widget_type): + if hasattr(widget_type, '__call__'): # Presumably someone passed a widget class object to # Widget.create_subwidget() or Form.add_widget() -- # don't bother with the widget class registry at all. diff -Nru quixote-2.5/quixote/form1/widget.py quixote-2.7~b2/quixote/form1/widget.py --- quixote-2.5/quixote/form1/widget.py 2006-03-17 02:49:51.000000000 +0000 +++ quixote-2.7~b2/quixote/form1/widget.py 2009-12-07 17:59:29.000000000 +0000 @@ -238,7 +238,7 @@ def parse(self, request): - self.value = request.form.has_key(self.name) + self.value = self.name in request.form return self.value @@ -327,7 +327,7 @@ used_keys = {} keys = map(str, descriptions) for key in keys: - if used_keys.has_key(key): + if key in used_keys: raise ValueError, "duplicated descriptions (provide keys)" used_keys[key] = 1 return keys diff -Nru quixote-2.5/quixote/http_request.py quixote-2.7~b2/quixote/http_request.py --- quixote-2.5/quixote/http_request.py 2006-05-19 17:48:55.000000000 +0000 +++ quixote-2.7~b2/quixote/http_request.py 2010-01-22 19:32:26.000000000 +0000 @@ -10,7 +10,7 @@ import tempfile import urllib import rfc822 -from cStringIO import StringIO +from StringIO import StringIO import quixote from quixote.http_response import HTTPResponse @@ -155,7 +155,7 @@ # sets this environment variable to "0" for non-SSL requests # (most web servers -- well, Apache at least -- simply don't set # it in that case). - if (environ.get('HTTPS', 'off').lower() == 'on' or + if (environ.get('HTTPS', 'off').lower() in ('on', 'yes', '1') or environ.get('SERVER_PORT_SECURE', '0') != '0'): self.scheme = "https" else: @@ -364,6 +364,17 @@ """ return self.environ.get('QUERY_STRING', '') + def get_path_query(self): + """() -> string + + Return the path and the query string (if any). + """ + path = self.get_path() + query = self.get_query() + if query: + path += '?' + query + return path + def get_url(self, n=0): """get_url(n : int = 0) -> string @@ -397,7 +408,7 @@ found_encodings = self._parse_pref_header(accept_encoding) if found_encodings: for encoding in encodings: - if found_encodings.has_key(encoding): + if encoding in found_encodings: return encoding return None diff -Nru quixote-2.5/quixote/http_response.py quixote-2.7~b2/quixote/http_response.py --- quixote-2.5/quixote/http_response.py 2006-03-17 02:49:51.000000000 +0000 +++ quixote-2.7~b2/quixote/http_response.py 2010-01-22 19:28:45.000000000 +0000 @@ -4,7 +4,6 @@ """ import time -from sets import Set try: import zlib except ImportError: @@ -70,7 +69,7 @@ "\002" "\377") -_GZIP_EXCLUDE = Set(["application/pdf", +_GZIP_EXCLUDE = set(["application/pdf", "application/zip", "audio/mpeg", "image/gif", @@ -81,6 +80,9 @@ "video/x-msvideo", ]) +def _LOWU32(i): + return i & 0xFFFFFFFFL + class HTTPResponse: """ An object representation of an HTTP response. @@ -122,10 +124,10 @@ future requests. The cookie value is stored as the "value" attribute. The other attributes are as specified by RFC 2109. cache : int | None - the number of seconds the response may be cached. The default is 0, - meaning don't cache at all. This variable is used to set the HTTP - expires header. If set to None then the expires header will not be - added. + the number of seconds the response may be cached. The default + is 0, meaning don't cache at all. This variable is used to set + the HTTP expires and cache-control headers. If set to None then + no headers will be added. javascript_code : { string : string } a collection of snippets of JavaScript code to be included in the response. The collection is built by calling add_javascript(), @@ -136,7 +138,7 @@ DEFAULT_CONTENT_TYPE = 'text/html' DEFAULT_CHARSET = None # defaults to quixote.DEFAULT_CHARSET - + def __init__(self, status=200, body=None, content_type=None, charset=None): """ Creates a new HTTP response. @@ -195,7 +197,7 @@ self.status_code = status if reason is None: - if status_reasons.has_key(status): + if status in status_reasons: reason = status_reasons[status] else: # Eg. for generic 4xx failures, use the reason @@ -250,10 +252,11 @@ n = len(body) co = zlib.compressobj(6, zlib.DEFLATED, -zlib.MAX_WBITS, zlib.DEF_MEM_LEVEL, 0) + crc = zlib.crc32(body) chunks = [_GZIP_HEADER, co.compress(body), co.flush(), - struct.pack(" 0: expire_date = formatdate(now + self.cache) + cache_control = "max-age=%d" % self.cache else: - expire_date = "-1" # allowed by HTTP spec and may work better - # with some clients - headers.append(("Expires", expire_date)) + # This is the default case and makes sense for a + # dynamically generated response that can change on each + # request. + # + # Using the current date is not a good idea since clocks + # might not be synchronized. Any invalid date is treated + # as in the past but Microsoft recommends "-1" for + # Internet Explorer so that's what we use. + expire_date = "-1" + # The Expires header is sufficient for HTTP 1.0 but + # for HTTP 1.1 we must add a must-revalidate directive. + # Clients and proxies are allowed to ignore Expires in + # certain cases and use stale pages (RFC 2616 sections + # 13.1.5 and 14.9.4). + cache_control = "max-age=0, must-revalidate" + if ("expires" not in self.headers and + "cache-control" not in self.headers): + # If either of these headers are set then don't add + # any of them. We assume the programmer knows what he + # is doing in that case. + headers.append(("Expires", expire_date)) + headers.append(("Cache-Control", cache_control)) # Content-type if "content-type" not in self.headers: diff -Nru quixote-2.5/quixote/__init__.py quixote-2.7~b2/quixote/__init__.py --- quixote-2.5/quixote/__init__.py 2007-11-16 22:34:31.000000000 +0000 +++ quixote-2.7~b2/quixote/__init__.py 2010-01-22 19:35:03.000000000 +0000 @@ -1,9 +1,9 @@ -"""quixote +"""Quixote -A highly Pythonic web application framework. +A small and flexible Python web application framework. """ -__version__ = '2.5' +__version__ = '2.7b2' # These are frequently needed by Quixote applications. from quixote.publish import \ @@ -14,7 +14,7 @@ # This is the default charset used by the HTTPRequest, HTTPResponse, # DefaultLogger, and sendmail components. -DEFAULT_CHARSET = 'iso-8859-1' +DEFAULT_CHARSET = 'utf-8' def enable_ptl(): """ diff -Nru quixote-2.5/quixote/ptl/ihooks_local.py quixote-2.7~b2/quixote/ptl/ihooks_local.py --- quixote-2.5/quixote/ptl/ihooks_local.py 1970-01-01 00:00:00.000000000 +0000 +++ quixote-2.7~b2/quixote/ptl/ihooks_local.py 2009-09-07 08:13:10.000000000 +0000 @@ -0,0 +1,557 @@ +# Based on ihooks.py from the Python 2.6 distribution. Fixes for relative +# imports applied (see Python issue #6855) +"""Import hook support. + +Consistent use of this module will make it possible to change the +different mechanisms involved in loading modules independently. + +While the built-in module imp exports interfaces to the built-in +module searching and loading algorithm, and it is possible to replace +the built-in function __import__ in order to change the semantics of +the import statement, until now it has been difficult to combine the +effect of different __import__ hacks, like loading modules from URLs +by rimport.py, or restricted execution by rexec.py. + +This module defines three new concepts: + +1) A "file system hooks" class provides an interface to a filesystem. + +One hooks class is defined (Hooks), which uses the interface provided +by standard modules os and os.path. It should be used as the base +class for other hooks classes. + +2) A "module loader" class provides an interface to search for a +module in a search path and to load it. It defines a method which +searches for a module in a single directory; by overriding this method +one can redefine the details of the search. If the directory is None, +built-in and frozen modules are searched instead. + +Two module loader class are defined, both implementing the search +strategy used by the built-in __import__ function: ModuleLoader uses +the imp module's find_module interface, while HookableModuleLoader +uses a file system hooks class to interact with the file system. Both +use the imp module's load_* interfaces to actually load the module. + +3) A "module importer" class provides an interface to import a +module, as well as interfaces to reload and unload a module. It also +provides interfaces to install and uninstall itself instead of the +default __import__ and reload (and unload) functions. + +One module importer class is defined (ModuleImporter), which uses a +module loader instance passed in (by default HookableModuleLoader is +instantiated). + +The classes defined here should be used as base classes for extended +functionality along those lines. + +If a module importer class supports dotted names, its import_module() +must return a different value depending on whether it is called on +behalf of a "from ... import ..." statement or not. (This is caused +by the way the __import__ hook is used by the Python interpreter.) It +would also do wise to install a different version of reload(). + +""" + +from warnings import warnpy3k, warn +warnpy3k("the ihooks module has been removed in Python 3.0", stacklevel=2) +del warnpy3k + +import __builtin__ +import imp +import os +import sys + +__all__ = ["BasicModuleLoader","Hooks","ModuleLoader","FancyModuleLoader", + "BasicModuleImporter","ModuleImporter","install","uninstall"] + +VERBOSE = 0 + + +from imp import C_EXTENSION, PY_SOURCE, PY_COMPILED +from imp import C_BUILTIN, PY_FROZEN, PKG_DIRECTORY +BUILTIN_MODULE = C_BUILTIN +FROZEN_MODULE = PY_FROZEN + + +class _Verbose: + + def __init__(self, verbose = VERBOSE): + self.verbose = verbose + + def get_verbose(self): + return self.verbose + + def set_verbose(self, verbose): + self.verbose = verbose + + # XXX The following is an experimental interface + + def note(self, *args): + if self.verbose: + self.message(*args) + + def message(self, format, *args): + if args: + print format%args + else: + print format + + +class BasicModuleLoader(_Verbose): + + """Basic module loader. + + This provides the same functionality as built-in import. It + doesn't deal with checking sys.modules -- all it provides is + find_module() and a load_module(), as well as find_module_in_dir() + which searches just one directory, and can be overridden by a + derived class to change the module search algorithm when the basic + dependency on sys.path is unchanged. + + The interface is a little more convenient than imp's: + find_module(name, [path]) returns None or 'stuff', and + load_module(name, stuff) loads the module. + + """ + + def find_module(self, name, path = None): + if path is None: + path = [None] + self.default_path() + for dir in path: + stuff = self.find_module_in_dir(name, dir) + if stuff: return stuff + return None + + def default_path(self): + return sys.path + + def find_module_in_dir(self, name, dir): + if dir is None: + return self.find_builtin_module(name) + else: + try: + return imp.find_module(name, [dir]) + except ImportError: + return None + + def find_builtin_module(self, name): + # XXX frozen packages? + if imp.is_builtin(name): + return None, '', ('', '', BUILTIN_MODULE) + if imp.is_frozen(name): + return None, '', ('', '', FROZEN_MODULE) + return None + + def load_module(self, name, stuff): + file, filename, info = stuff + try: + return imp.load_module(name, file, filename, info) + finally: + if file: file.close() + + +class Hooks(_Verbose): + + """Hooks into the filesystem and interpreter. + + By deriving a subclass you can redefine your filesystem interface, + e.g. to merge it with the URL space. + + This base class behaves just like the native filesystem. + + """ + + # imp interface + def get_suffixes(self): return imp.get_suffixes() + def new_module(self, name): return imp.new_module(name) + def is_builtin(self, name): return imp.is_builtin(name) + def init_builtin(self, name): return imp.init_builtin(name) + def is_frozen(self, name): return imp.is_frozen(name) + def init_frozen(self, name): return imp.init_frozen(name) + def get_frozen_object(self, name): return imp.get_frozen_object(name) + def load_source(self, name, filename, file=None): + return imp.load_source(name, filename, file) + def load_compiled(self, name, filename, file=None): + return imp.load_compiled(name, filename, file) + def load_dynamic(self, name, filename, file=None): + return imp.load_dynamic(name, filename, file) + def load_package(self, name, filename, file=None): + return imp.load_module(name, file, filename, ("", "", PKG_DIRECTORY)) + + def add_module(self, name): + d = self.modules_dict() + if name in d: return d[name] + d[name] = m = self.new_module(name) + return m + + # sys interface + def modules_dict(self): return sys.modules + def default_path(self): return sys.path + + def path_split(self, x): return os.path.split(x) + def path_join(self, x, y): return os.path.join(x, y) + def path_isabs(self, x): return os.path.isabs(x) + # etc. + + def path_exists(self, x): return os.path.exists(x) + def path_isdir(self, x): return os.path.isdir(x) + def path_isfile(self, x): return os.path.isfile(x) + def path_islink(self, x): return os.path.islink(x) + # etc. + + def openfile(self, *x): return open(*x) + openfile_error = IOError + def listdir(self, x): return os.listdir(x) + listdir_error = os.error + # etc. + + +class ModuleLoader(BasicModuleLoader): + + """Default module loader; uses file system hooks. + + By defining suitable hooks, you might be able to load modules from + other sources than the file system, e.g. from compressed or + encrypted files, tar files or (if you're brave!) URLs. + + """ + + def __init__(self, hooks = None, verbose = VERBOSE): + BasicModuleLoader.__init__(self, verbose) + self.hooks = hooks or Hooks(verbose) + + def default_path(self): + return self.hooks.default_path() + + def modules_dict(self): + return self.hooks.modules_dict() + + def get_hooks(self): + return self.hooks + + def set_hooks(self, hooks): + self.hooks = hooks + + def find_builtin_module(self, name): + # XXX frozen packages? + if self.hooks.is_builtin(name): + return None, '', ('', '', BUILTIN_MODULE) + if self.hooks.is_frozen(name): + return None, '', ('', '', FROZEN_MODULE) + return None + + def find_module_in_dir(self, name, dir, allow_packages=1): + if dir is None: + return self.find_builtin_module(name) + if allow_packages: + fullname = self.hooks.path_join(dir, name) + if self.hooks.path_isdir(fullname): + stuff = self.find_module_in_dir("__init__", fullname, 0) + if stuff: + file = stuff[0] + if file: file.close() + return None, fullname, ('', '', PKG_DIRECTORY) + for info in self.hooks.get_suffixes(): + suff, mode, type = info + fullname = self.hooks.path_join(dir, name+suff) + try: + fp = self.hooks.openfile(fullname, mode) + return fp, fullname, info + except self.hooks.openfile_error: + pass + return None + + def load_module(self, name, stuff): + file, filename, info = stuff + (suff, mode, type) = info + try: + if type == BUILTIN_MODULE: + return self.hooks.init_builtin(name) + if type == FROZEN_MODULE: + return self.hooks.init_frozen(name) + if type == C_EXTENSION: + m = self.hooks.load_dynamic(name, filename, file) + elif type == PY_SOURCE: + m = self.hooks.load_source(name, filename, file) + elif type == PY_COMPILED: + m = self.hooks.load_compiled(name, filename, file) + elif type == PKG_DIRECTORY: + m = self.hooks.load_package(name, filename, file) + else: + raise ImportError, "Unrecognized module type (%r) for %s" % \ + (type, name) + finally: + if file: file.close() + m.__file__ = filename + return m + + +class FancyModuleLoader(ModuleLoader): + + """Fancy module loader -- parses and execs the code itself.""" + + def load_module(self, name, stuff): + file, filename, (suff, mode, type) = stuff + realfilename = filename + path = None + + if type == PKG_DIRECTORY: + initstuff = self.find_module_in_dir("__init__", filename, 0) + if not initstuff: + raise ImportError, "No __init__ module in package %s" % name + initfile, initfilename, initinfo = initstuff + initsuff, initmode, inittype = initinfo + if inittype not in (PY_COMPILED, PY_SOURCE): + if initfile: initfile.close() + raise ImportError, \ + "Bad type (%r) for __init__ module in package %s" % ( + inittype, name) + path = [filename] + file = initfile + realfilename = initfilename + type = inittype + + if type == FROZEN_MODULE: + code = self.hooks.get_frozen_object(name) + elif type == PY_COMPILED: + import marshal + file.seek(8) + code = marshal.load(file) + elif type == PY_SOURCE: + data = file.read() + code = compile(data, realfilename, 'exec') + else: + return ModuleLoader.load_module(self, name, stuff) + + m = self.hooks.add_module(name) + if path: + m.__path__ = path + m.__file__ = filename + try: + exec code in m.__dict__ + except: + d = self.hooks.modules_dict() + if name in d: + del d[name] + raise + return m + + +class BasicModuleImporter(_Verbose): + + """Basic module importer; uses module loader. + + This provides basic import facilities but no package imports. + + """ + + def __init__(self, loader = None, verbose = VERBOSE): + _Verbose.__init__(self, verbose) + self.loader = loader or ModuleLoader(None, verbose) + self.modules = self.loader.modules_dict() + + def get_loader(self): + return self.loader + + def set_loader(self, loader): + self.loader = loader + + def get_hooks(self): + return self.loader.get_hooks() + + def set_hooks(self, hooks): + return self.loader.set_hooks(hooks) + + def import_module(self, name, globals={}, locals={}, fromlist=[]): + name = str(name) + if name in self.modules: + return self.modules[name] # Fast path + stuff = self.loader.find_module(name) + if not stuff: + raise ImportError, "No module named %s" % name + return self.loader.load_module(name, stuff) + + def reload(self, module, path = None): + name = str(module.__name__) + stuff = self.loader.find_module(name, path) + if not stuff: + raise ImportError, "Module %s not found for reload" % name + return self.loader.load_module(name, stuff) + + def unload(self, module): + del self.modules[str(module.__name__)] + # XXX Should this try to clear the module's namespace? + + def install(self): + self.save_import_module = __builtin__.__import__ + self.save_reload = __builtin__.reload + if not hasattr(__builtin__, 'unload'): + __builtin__.unload = None + self.save_unload = __builtin__.unload + __builtin__.__import__ = self.import_module + __builtin__.reload = self.reload + __builtin__.unload = self.unload + + def uninstall(self): + __builtin__.__import__ = self.save_import_module + __builtin__.reload = self.save_reload + __builtin__.unload = self.save_unload + if not __builtin__.unload: + del __builtin__.unload + + +class ModuleImporter(BasicModuleImporter): + + """A module importer that supports packages.""" + + def import_module(self, name, globals=None, locals=None, fromlist=None, + level=-1): + parent = self.determine_parent(globals, level) + q, tail = self.find_head_package(parent, str(name)) + m = self.load_tail(q, tail) + if not fromlist: + return q + if hasattr(m, "__path__"): + self.ensure_fromlist(m, fromlist) + return m + + def determine_parent(self, globals, level=-1): + if not globals or not level: + return None + pkgname = globals.get('__package__') + if pkgname is not None: + if not pkgname and level > 0: + raise ValueError, 'Attempted relative import in non-package' + else: + # __package__ not set, figure it out and set it + modname = globals.get('__name__') + if modname is None: + return None + if "__path__" in globals: + # __path__ is set so modname is already the package name + pkgname = modname + else: + # normal module, work out package name if any + if '.' not in modname: + if level > 0: + raise ValueError, ('Attempted relative import in ' + 'non-package') + globals['__package__'] = None + return None + pkgname = modname.rpartition('.')[0] + globals['__package__'] = pkgname + if level > 0: + dot = len(pkgname) + for x in range(level, 1, -1): + try: + dot = pkgname.rindex('.', 0, dot) + except ValueError: + raise ValueError('attempted relative import beyond ' + 'top-level package') + pkgname = pkgname[:dot] + try: + return sys.modules[pkgname] + except KeyError: + if level < 1: + warn("Parent module '%s' not found while handling " + "absolute import" % pkgname, RuntimeWarning, 1) + return None + else: + raise SystemError, ("Parent module '%s' not loaded, cannot " + "perform relative import" % pkgname) + + def find_head_package(self, parent, name): + if '.' in name: + i = name.find('.') + head = name[:i] + tail = name[i+1:] + else: + head = name + tail = "" + if parent: + qname = "%s.%s" % (parent.__name__, head) + else: + qname = head + q = self.import_it(head, qname, parent) + if q: return q, tail + if parent: + qname = head + parent = None + q = self.import_it(head, qname, parent) + if q: return q, tail + raise ImportError, "No module named '%s'" % qname + + def load_tail(self, q, tail): + m = q + while tail: + i = tail.find('.') + if i < 0: i = len(tail) + head, tail = tail[:i], tail[i+1:] + mname = "%s.%s" % (m.__name__, head) + m = self.import_it(head, mname, m) + if not m: + raise ImportError, "No module named '%s'" % mname + return m + + def ensure_fromlist(self, m, fromlist, recursive=0): + for sub in fromlist: + if sub == "*": + if not recursive: + try: + all = m.__all__ + except AttributeError: + pass + else: + self.ensure_fromlist(m, all, 1) + continue + if sub != "*" and not hasattr(m, sub): + subname = "%s.%s" % (m.__name__, sub) + submod = self.import_it(sub, subname, m) + if not submod: + raise ImportError, "No module named '%s'" % subname + + def import_it(self, partname, fqname, parent, force_load=0): + if not partname: + # completely empty module name should only happen in + # 'from . import' or __import__("") + return parent + if not force_load: + try: + return self.modules[fqname] + except KeyError: + pass + try: + path = parent and parent.__path__ + except AttributeError: + return None + partname = str(partname) + stuff = self.loader.find_module(partname, path) + if not stuff: + return None + fqname = str(fqname) + m = self.loader.load_module(fqname, stuff) + if parent: + setattr(parent, partname, m) + return m + + def reload(self, module): + name = str(module.__name__) + if '.' not in name: + return self.import_it(name, name, None, force_load=1) + i = name.rfind('.') + pname = name[:i] + parent = self.modules[pname] + return self.import_it(name[i+1:], name, parent, force_load=1) + + +default_importer = None +current_importer = None + +def install(importer = None): + global current_importer + current_importer = importer or default_importer or ModuleImporter() + current_importer.install() + +def uninstall(): + global current_importer + current_importer.uninstall() diff -Nru quixote-2.5/quixote/ptl/infinite_reload.py quixote-2.7~b2/quixote/ptl/infinite_reload.py --- quixote-2.5/quixote/ptl/infinite_reload.py 1970-01-01 00:00:00.000000000 +0000 +++ quixote-2.7~b2/quixote/ptl/infinite_reload.py 2009-05-31 22:12:44.000000000 +0000 @@ -0,0 +1,7 @@ +# For testing http://python.org/sf/742342, which reports that Python +# segfaults (infinite recursion in C) in the presence of infinite +# reload()ing. This module is imported by test_import.py:test_infinite_reload +# to make sure this doesn't happen any more. + +import infinite_reload +reload(infinite_reload) diff -Nru quixote-2.5/quixote/ptl/__init__.py quixote-2.7~b2/quixote/ptl/__init__.py --- quixote-2.5/quixote/ptl/__init__.py 2006-03-17 02:49:51.000000000 +0000 +++ quixote-2.7~b2/quixote/ptl/__init__.py 2009-09-07 08:13:10.000000000 +0000 @@ -219,24 +219,30 @@ Two implementations of ``htmltext`` are provided, one written in pure Python and a second one implemented as a C extension. Both versions -have seen production use. +have seen production use. PTL modules ----------- -PTL templates are kept in files with the extension .ptl. Like Python -files, they are byte-compiled on import, and the byte-code is written to +PTL templates are kept in files with the extension .ptl. Like +Python files, they are byte-compiled and the byte-code is written to a compiled file with the extension ``.pyc``. Since vanilla Python -doesn't know anything about PTL, this package provides an import hook to let -you import PTL files just like regular Python modules. The import -hook is installed when you import *this* package. - -(Note: if you're using ZODB, always import ZODB *before* installing the -PTL import hook. There's some interaction which causes importing the -TimeStamp module to fail when the PTL import hook is installed; we -haven't debugged the problem. A similar problem has been reported for -BioPython and win32com.client imports.) -''' +doesn't know anything about PTL, this package provides an import hook. To +enable it, use the following code in the __init__ module of a package +containing PTL: + + from quixote.ptl import enable_ptl + enable_ptl() + +Python's import mechanism is complicated and it is possible that certain +combinations of packages do not work well with an import hook. An +alternative mechanism is provided that does not use an import hook. To +compile all the PTL files in a package, use the following code in the +__init__ module: + from quixote.ptl import compile_package + compile_package(__path__) +''' +from quixote.ptl.ptl_compile import compile_package diff -Nru quixote-2.5/quixote/ptl/ptl_compile.py quixote-2.7~b2/quixote/ptl/ptl_compile.py --- quixote-2.5/quixote/ptl/ptl_compile.py 2007-10-01 22:53:14.000000000 +0000 +++ quixote-2.7~b2/quixote/ptl/ptl_compile.py 2009-12-13 20:18:09.000000000 +0000 @@ -254,6 +254,20 @@ os.unlink(outputname) raise +def compile_file(filename, force=0, verbose=0): + if filename.endswith(PTL_EXT): + cfile = filename[:-4] + '.pyc' + ftime = os.stat(filename)[stat.ST_MTIME] + try: + ctime = os.stat(cfile)[stat.ST_MTIME] + except os.error: + ctime = 0 + if (ctime > ftime) and not force: + return + if verbose: + print 'Compiling', filename, '...' + ok = compile(filename, cfile) + def compile_dir(dir, maxlevels=10, force=0): """Byte-compile all PTL modules in the given directory tree. (Adapted from compile_dir in Python module: compileall.py) @@ -275,37 +289,40 @@ for name in names: fullname = os.path.join(dir, name) if os.path.isfile(fullname): - head, tail = name[:-4], name[-4:] - if tail == PTL_EXT: - cfile = fullname[:-4] + '.pyc' - ftime = os.stat(fullname)[stat.ST_MTIME] - try: - ctime = os.stat(cfile)[stat.ST_MTIME] - except os.error: ctime = 0 - if (ctime > ftime) and not force: - continue - print 'Compiling', fullname, '...' - try: - ok = compile(fullname, cfile) - except KeyboardInterrupt: - raise KeyboardInterrupt - except: - # XXX compile catches SyntaxErrors - if type(sys.exc_type) == type(''): - exc_type_name = sys.exc_type - else: exc_type_name = sys.exc_type.__name__ - print 'Sorry:', exc_type_name + ':', - print sys.exc_value + try: + ok = compile_file(fullname, force=force, verbose=1) + except KeyboardInterrupt: + raise KeyboardInterrupt + except: + # XXX compile catches SyntaxErrors + if type(sys.exc_type) == type(''): + exc_type_name = sys.exc_type + else: exc_type_name = sys.exc_type.__name__ + print 'Sorry:', exc_type_name + ':', + print sys.exc_value + success = 0 + else: + if ok == 0: success = 0 - else: - if ok == 0: - success = 0 elif (maxlevels > 0 and name != os.curdir and name != os.pardir and os.path.isdir(fullname) and not os.path.islink(fullname)): if not compile_dir(fullname, maxlevels - 1, force): success = 0 return success +def compile_package(path, force=0, verbose=0): + """Compile all PTL files in a package. 'path' should be a list + of directory names containing the files of the package (i.e. __path__). + """ + for package_dir in path: + for dirpath, dirnames, filenames in os.walk(package_dir): + for dirname in dirnames: + compile_file(os.path.join(dirpath, dirname), force=force, + verbose=verbose) + for filename in filenames: + compile_file(os.path.join(dirpath, filename), force=force, + verbose=verbose) + def main(): args = sys.argv[1:] if not args: diff -Nru quixote-2.5/quixote/ptl/ptl_import.py quixote-2.7~b2/quixote/ptl/ptl_import.py --- quixote-2.5/quixote/ptl/ptl_import.py 2006-03-17 02:49:51.000000000 +0000 +++ quixote-2.7~b2/quixote/ptl/ptl_import.py 2010-01-22 19:28:45.000000000 +0000 @@ -13,12 +13,21 @@ import marshal import __builtin__ +# Check for a deficient ihooks module. Python 2.6 was released without +# ihooks.py being updated to support relative imports. Any library that uses +# relative imports will cause the import hook to fail. Use our local copy of +# ihooks module which does have support for relative imports. +if sys.hexversion >= 0x20600b0: + _m = ihooks.ModuleImporter.import_module + if _m.im_func.func_code.co_argcount == 5: + import ihooks_local as ihooks + from quixote.ptl.ptl_compile import compile_template, PTL_EXT assert sys.hexversion >= 0x20000b1, "need Python 2.0b1 or later" def _exec_module_code(code, name, filename): - if sys.modules.has_key(name): + if name in sys.modules: mod = sys.modules[name] # necessary for reload() else: mod = new.module(name) @@ -99,9 +108,13 @@ # Otherwise, use the default handler for loading return ihooks.ModuleLoader.load_module(self, name, stuff) -try: - import cimport -except ImportError: +if sys.hexversion <= 0x20600b0: + try: + import cimport + except ImportError: + cimport = None +else: + # cimport module doesn't handle relative imports cimport = None class cModuleImporter(ihooks.ModuleImporter): diff -Nru quixote-2.5/quixote/ptl/relimport.py quixote-2.7~b2/quixote/ptl/relimport.py --- quixote-2.5/quixote/ptl/relimport.py 1970-01-01 00:00:00.000000000 +0000 +++ quixote-2.7~b2/quixote/ptl/relimport.py 2009-05-31 22:11:57.000000000 +0000 @@ -0,0 +1 @@ +from .test_import import * diff -Nru quixote-2.5/quixote/ptl/test/utest_ptl.py quixote-2.7~b2/quixote/ptl/test/utest_ptl.py --- quixote-2.5/quixote/ptl/test/utest_ptl.py 2006-03-17 02:49:51.000000000 +0000 +++ quixote-2.7~b2/quixote/ptl/test/utest_ptl.py 2010-01-22 19:32:22.000000000 +0000 @@ -1,7 +1,7 @@ #!/usr/bin/env python from sancho.utest import UTest from quixote.ptl.ptl_compile import compile_template -from cStringIO import StringIO +from StringIO import StringIO from quixote.html import TemplateIO, htmltext def run_ptl(*source): diff -Nru quixote-2.5/quixote/ptl/test_import.py quixote-2.7~b2/quixote/ptl/test_import.py --- quixote-2.5/quixote/ptl/test_import.py 1970-01-01 00:00:00.000000000 +0000 +++ quixote-2.7~b2/quixote/ptl/test_import.py 2009-05-31 23:41:59.000000000 +0000 @@ -0,0 +1,421 @@ +import unittest +import os +import random +import shutil +import sys +import py_compile +import warnings +import marshal +from test.test_support import unlink, TESTFN, unload, run_unittest, check_warnings + + +def remove_files(name): + for f in (name + os.extsep + "py", + name + os.extsep + "pyc", + name + os.extsep + "pyo", + name + os.extsep + "pyw", + name + "$py.class"): + if os.path.exists(f): + os.remove(f) + + +class ImportTest(unittest.TestCase): + + def testCaseSensitivity(self): + # Brief digression to test that import is case-sensitive: if we got this + # far, we know for sure that "random" exists. + try: + import RAnDoM + except ImportError: + pass + else: + self.fail("import of RAnDoM should have failed (case mismatch)") + + def testDoubleConst(self): + # Another brief digression to test the accuracy of manifest float constants. + from test import double_const # don't blink -- that *was* the test + + def testImport(self): + def test_with_extension(ext): + # ext normally ".py"; perhaps ".pyw" + source = TESTFN + ext + pyo = TESTFN + os.extsep + "pyo" + if sys.platform.startswith('java'): + pyc = TESTFN + "$py.class" + else: + pyc = TESTFN + os.extsep + "pyc" + + f = open(source, "w") + print >> f, "# This tests Python's ability to import a", ext, "file." + a = random.randrange(1000) + b = random.randrange(1000) + print >> f, "a =", a + print >> f, "b =", b + f.close() + + try: + try: + mod = __import__(TESTFN) + except ImportError, err: + self.fail("import from %s failed: %s" % (ext, err)) + + self.assertEquals(mod.a, a, + "module loaded (%s) but contents invalid" % mod) + self.assertEquals(mod.b, b, + "module loaded (%s) but contents invalid" % mod) + finally: + os.unlink(source) + + try: + try: + reload(mod) + except ImportError, err: + self.fail("import from .pyc/.pyo failed: %s" % err) + finally: + try: + os.unlink(pyc) + except OSError: + pass + try: + os.unlink(pyo) + except OSError: + pass + del sys.modules[TESTFN] + + sys.path.insert(0, os.curdir) + try: + test_with_extension(os.extsep + "py") + if sys.platform.startswith("win"): + for ext in ".PY", ".Py", ".pY", ".pyw", ".PYW", ".pYw": + test_with_extension(ext) + finally: + del sys.path[0] + + def testImpModule(self): + # Verify that the imp module can correctly load and find .py files + import imp + x = imp.find_module("os") + os = imp.load_module("os", *x) + + def test_module_with_large_stack(self, module='longlist'): + # create module w/list of 65000 elements to test bug #561858 + filename = module + os.extsep + 'py' + + # create a file with a list of 65000 elements + f = open(filename, 'w+') + f.write('d = [\n') + for i in range(65000): + f.write('"",\n') + f.write(']') + f.close() + + # compile & remove .py file, we only need .pyc (or .pyo) + f = open(filename, 'r') + py_compile.compile(filename) + f.close() + os.unlink(filename) + + # need to be able to load from current dir + sys.path.append('') + + # this used to crash + exec 'import ' + module + + # cleanup + del sys.path[-1] + for ext in 'pyc', 'pyo': + fname = module + os.extsep + ext + if os.path.exists(fname): + os.unlink(fname) + + def test_failing_import_sticks(self): + source = TESTFN + os.extsep + "py" + f = open(source, "w") + print >> f, "a = 1/0" + f.close() + + # New in 2.4, we shouldn't be able to import that no matter how often + # we try. + sys.path.insert(0, os.curdir) + try: + for i in 1, 2, 3: + try: + mod = __import__(TESTFN) + except ZeroDivisionError: + if TESTFN in sys.modules: + self.fail("damaged module in sys.modules on %i. try" % i) + else: + self.fail("was able to import a damaged module on %i. try" % i) + finally: + sys.path.pop(0) + remove_files(TESTFN) + + def test_failing_reload(self): + # A failing reload should leave the module object in sys.modules. + source = TESTFN + os.extsep + "py" + f = open(source, "w") + print >> f, "a = 1" + print >> f, "b = 2" + f.close() + + sys.path.insert(0, os.curdir) + try: + mod = __import__(TESTFN) + self.assert_(TESTFN in sys.modules, "expected module in sys.modules") + self.assertEquals(mod.a, 1, "module has wrong attribute values") + self.assertEquals(mod.b, 2, "module has wrong attribute values") + + # On WinXP, just replacing the .py file wasn't enough to + # convince reload() to reparse it. Maybe the timestamp didn't + # move enough. We force it to get reparsed by removing the + # compiled file too. + remove_files(TESTFN) + + # Now damage the module. + f = open(source, "w") + print >> f, "a = 10" + print >> f, "b = 20//0" + f.close() + + self.assertRaises(ZeroDivisionError, reload, mod) + + # But we still expect the module to be in sys.modules. + mod = sys.modules.get(TESTFN) + self.failIf(mod is None, "expected module to still be in sys.modules") + + # We should have replaced a w/ 10, but the old b value should + # stick. + self.assertEquals(mod.a, 10, "module has wrong attribute values") + self.assertEquals(mod.b, 2, "module has wrong attribute values") + + finally: + sys.path.pop(0) + remove_files(TESTFN) + if TESTFN in sys.modules: + del sys.modules[TESTFN] + + def __test_infinite_reload(self): + # Bug #742342 reports that Python segfaults (infinite recursion in C) + # when faced with self-recursive reload()ing. + + sys.path.insert(0, os.path.dirname(__file__)) + try: + import infinite_reload + finally: + sys.path.pop(0) + + def test_import_name_binding(self): + # import x.y.z binds x in the current namespace + import test as x + import test.test_support + self.assert_(x is test, x.__name__) + self.assert_(hasattr(test.test_support, "__file__")) + + # import x.y.z as w binds z as w + import test.test_support as y + self.assert_(y is test.test_support, y.__name__) + + def test_import_initless_directory_warning(self): + with warnings.catch_warnings(): + # Just a random non-package directory we always expect to be + # somewhere in sys.path... + warnings.simplefilter('error', ImportWarning) + self.assertRaises(ImportWarning, __import__, "site-packages") + + def __test_importbyfilename(self): + path = os.path.abspath(TESTFN) + try: + __import__(path) + except ImportError, err: + self.assertEqual("Import by filename is not supported.", + err.args[0]) + else: + self.fail("import by path didn't raise an exception") + +class TestPycRewriting(unittest.TestCase): + # Test that the `co_filename` attribute on code objects always points + # to the right file, even when various things happen (e.g. both the .py + # and the .pyc file are renamed). + + module_name = "unlikely_module_name" + module_source = """ +import sys +code_filename = sys._getframe().f_code.co_filename +module_filename = __file__ +constant = 1 +def func(): + pass +func_filename = func.func_code.co_filename +""" + dir_name = os.path.abspath(TESTFN) + file_name = os.path.join(dir_name, module_name) + os.extsep + "py" + compiled_name = file_name + ("c" if __debug__ else "o") + + def setUp(self): + self.sys_path = sys.path[:] + self.orig_module = sys.modules.pop(self.module_name, None) + os.mkdir(self.dir_name) + with open(self.file_name, "w") as f: + f.write(self.module_source) + sys.path.insert(0, self.dir_name) + + def tearDown(self): + sys.path[:] = self.sys_path + if self.orig_module is not None: + sys.modules[self.module_name] = self.orig_module + else: + del sys.modules[self.module_name] + for file_name in self.file_name, self.compiled_name: + if os.path.exists(file_name): + os.remove(file_name) + if os.path.exists(self.dir_name): + shutil.rmtree(self.dir_name) + + def import_module(self): + ns = globals() + __import__(self.module_name, ns, ns) + return sys.modules[self.module_name] + + def test_basics(self): + mod = self.import_module() + self.assertEqual(mod.module_filename, self.file_name) + self.assertEqual(mod.code_filename, self.file_name) + self.assertEqual(mod.func_filename, self.file_name) + del sys.modules[self.module_name] + mod = self.import_module() + self.assertEqual(mod.module_filename, self.compiled_name) + self.assertEqual(mod.code_filename, self.file_name) + self.assertEqual(mod.func_filename, self.file_name) + + def __test_incorrect_code_name(self): + py_compile.compile(self.file_name, dfile="another_module.py") + mod = self.import_module() + self.assertEqual(mod.module_filename, self.compiled_name) + self.assertEqual(mod.code_filename, self.file_name) + self.assertEqual(mod.func_filename, self.file_name) + + def test_module_without_source(self): + target = "another_module.py" + py_compile.compile(self.file_name, dfile=target) + os.remove(self.file_name) + mod = self.import_module() + self.assertEqual(mod.module_filename, self.compiled_name) + self.assertEqual(mod.code_filename, target) + self.assertEqual(mod.func_filename, target) + + def test_foreign_code(self): + py_compile.compile(self.file_name) + with open(self.compiled_name, "rb") as f: + header = f.read(8) + code = marshal.load(f) + constants = list(code.co_consts) + foreign_code = test_main.func_code + pos = constants.index(1) + constants[pos] = foreign_code + code = type(code)(code.co_argcount, code.co_nlocals, code.co_stacksize, + code.co_flags, code.co_code, tuple(constants), + code.co_names, code.co_varnames, code.co_filename, + code.co_name, code.co_firstlineno, code.co_lnotab, + code.co_freevars, code.co_cellvars) + with open(self.compiled_name, "wb") as f: + f.write(header) + marshal.dump(code, f) + mod = self.import_module() + self.assertEqual(mod.constant.co_filename, foreign_code.co_filename) + +class PathsTests(unittest.TestCase): + path = TESTFN + + def setUp(self): + os.mkdir(self.path) + self.syspath = sys.path[:] + + def tearDown(self): + shutil.rmtree(self.path) + sys.path = self.syspath + + # http://bugs.python.org/issue1293 + def test_trailing_slash(self): + f = open(os.path.join(self.path, 'test_trailing_slash.py'), 'w') + f.write("testdata = 'test_trailing_slash'") + f.close() + sys.path.append(self.path+'/') + mod = __import__("test_trailing_slash") + self.assertEqual(mod.testdata, 'test_trailing_slash') + unload("test_trailing_slash") + + # http://bugs.python.org/issue3677 + def _test_UNC_path(self): + f = open(os.path.join(self.path, 'test_trailing_slash.py'), 'w') + f.write("testdata = 'test_trailing_slash'") + f.close() + #create the UNC path, like \\myhost\c$\foo\bar + path = os.path.abspath(self.path) + import socket + hn = socket.gethostname() + drive = path[0] + unc = "\\\\%s\\%s$"%(hn, drive) + unc += path[2:] + sys.path.append(path) + mod = __import__("test_trailing_slash") + self.assertEqual(mod.testdata, 'test_trailing_slash') + unload("test_trailing_slash") + + if sys.platform == "win32": + test_UNC_path = _test_UNC_path + + +class RelativeImport(unittest.TestCase): + def tearDown(self): + try: + del sys.modules["test.relimport"] + except: + pass + + def test_relimport_star(self): + # This will import * from .test_import. + from . import relimport + self.assertTrue(hasattr(relimport, "RelativeImport")) + + def test_issue3221(self): + def check_absolute(): + exec "from os import path" in ns + def check_relative(): + exec "from . import relimport" in ns + # Check both OK with __package__ and __name__ correct + ns = dict(__package__='test', __name__='test.notarealmodule') + check_absolute() + check_relative() + # Check both OK with only __name__ wrong + ns = dict(__package__='test', __name__='notarealpkg.notarealmodule') + check_absolute() + check_relative() + # Check relative fails with only __package__ wrong + ns = dict(__package__='foo', __name__='test.notarealmodule') + with check_warnings() as w: + check_absolute() + self.assert_('foo' in str(w.message)) + self.assertEqual(w.category, RuntimeWarning) + self.assertRaises(SystemError, check_relative) + # Check relative fails with __package__ and __name__ wrong + ns = dict(__package__='foo', __name__='notarealpkg.notarealmodule') + with check_warnings() as w: + check_absolute() + self.assert_('foo' in str(w.message)) + self.assertEqual(w.category, RuntimeWarning) + self.assertRaises(SystemError, check_relative) + # Check both fail with package set to a non-string + ns = dict(__package__=object()) + self.assertRaises(ValueError, check_absolute) + self.assertRaises(ValueError, check_relative) + +def test_main(verbose=None): + import ihooks + ihooks.install(ihooks.ModuleImporter()) + run_unittest(ImportTest, TestPycRewriting, PathsTests, RelativeImport) + +if __name__ == '__main__': + # test needs to be a package, so we can do relative import + #from test.test_import import test_main + from quixote.ptl.test_import import test_main + test_main() diff -Nru quixote-2.5/quixote/publish1.py quixote-2.7~b2/quixote/publish1.py --- quixote-2.5/quixote/publish1.py 2006-03-17 02:49:51.000000000 +0000 +++ quixote-2.7~b2/quixote/publish1.py 2009-12-07 17:59:29.000000000 +0000 @@ -64,7 +64,7 @@ output = object # ...or a callable. - elif callable(object): + elif hasattr(object, '__call__'): output = object(request) if output is None: raise RuntimeError, 'callable %s returned None' % repr(object) @@ -133,7 +133,7 @@ component = "_q_index" object = _get_component(object, component, request, namespace_stack) - if not (isstring(object) or callable(object)): + if not (isstring(object) or hasattr(object, '__call__')): # We went through all the components of the path and ended up at # something which isn't callable, like a module or an instance # without a __call__ method. diff -Nru quixote-2.5/quixote/publish.py quixote-2.7~b2/quixote/publish.py --- quixote-2.5/quixote/publish.py 2007-01-30 15:23:41.000000000 +0000 +++ quixote-2.7~b2/quixote/publish.py 2009-12-07 17:59:29.000000000 +0000 @@ -94,7 +94,7 @@ raise RuntimeError, "only one instance of Publisher allowed" _publisher = self - if not callable(getattr(root_directory, '_q_traverse')): + if not hasattr(getattr(root_directory, '_q_traverse'), '__call__'): raise TypeError( 'Expected something with a _q_traverse method, got %r' % root_directory) @@ -245,7 +245,7 @@ """ self.start_request() path = request.get_environ('PATH_INFO', '') - if path[:1] != '/': + if path and path[:1] != '/': return redirect( request.get_environ('SCRIPT_NAME', '') + '/' + path, permanent=True) diff -Nru quixote-2.5/quixote/sendmail.py quixote-2.7~b2/quixote/sendmail.py --- quixote-2.5/quixote/sendmail.py 2006-03-17 02:49:51.000000000 +0000 +++ quixote-2.7~b2/quixote/sendmail.py 2009-12-07 17:59:03.000000000 +0000 @@ -115,17 +115,9 @@ from_addr=None, cc_addrs=None, extra_headers=None, smtp_sender=None, smtp_recipients=None, + mail_server=None, mail_debug_addr=None, config=None): - """sendmail(subject : string, - msg_body : string, - to_addrs : [email_address], - from_addr : email_address = config.MAIL_SENDER, - cc_addrs : [email_address] = None, - extra_headers : [string] = None, - smtp_sender : email_address = (derived from from_addr) - smtp_recipients : [email_address] = (derived from to_addrs), - config : quixote.config.Config = (current publisher's config)): - + """ Send an email message to a list of recipients via a local SMTP server. In normal use, you supply a list of primary recipient e-mail addresses in 'to_addrs', an optional list of secondary @@ -192,10 +184,15 @@ Generally raises an exception on any SMTP errors; see smtplib (in the standard library documentation) for details. """ - if config is None: + if not mail_server and config is None: from quixote import get_publisher config = get_publisher().config + from_addr = from_addr or config.mail_from + mail_server = mail_server or config.mail_server + if config is not None: + mail_debug_addr = mail_debug_addr or config.mail_debug_addr + if not isinstance(to_addrs, ListType): raise TypeError("'to_addrs' must be a list") if not (cc_addrs is None or isinstance(cc_addrs, ListType)): @@ -203,8 +200,6 @@ # Make sure we have a "From" address if from_addr is None: - from_addr = config.mail_from - if from_addr is None: raise RuntimeError( "no from_addr supplied, and MAIL_FROM not set in config file") @@ -218,18 +213,15 @@ headers = ["From: %s" % from_addr.format(), "Subject: %s" % subject] _add_recip_headers(headers, "To", to_addrs) - if quixote.DEFAULT_CHARSET != 'iso-8859-1': - headers.append('Content-Type: text/plain; charset=%s' % - quixote.DEFAULT_CHARSET) if cc_addrs: _add_recip_headers(headers, "Cc", cc_addrs) if extra_headers: headers.extend(extra_headers) - if config.mail_debug_addr: + if mail_debug_addr: debug1 = ("[debug mode, message actually sent to %s]\n" - % config.mail_debug_addr) + % mail_debug_addr) if smtp_recipients: debug2 = ("[original SMTP recipients: %s]\n" % ", ".join(smtp_recipients)) @@ -239,7 +231,7 @@ sep = ("-"*72) + "\n" msg_body = debug1 + debug2 + sep + msg_body - smtp_recipients = [config.mail_debug_addr] + smtp_recipients = [mail_debug_addr] if smtp_sender is None: smtp_sender = from_addr.addr_spec @@ -255,10 +247,8 @@ for recip in smtp_recipients] message = "\n".join(headers) + "\n\n" + msg_body - if quixote.DEFAULT_CHARSET != 'iso-8859-1': - message = message.encode(quixote.DEFAULT_CHARSET) - smtp = SMTP(config.mail_server) + smtp = SMTP(mail_server) smtp.sendmail(smtp_sender, smtp_recipients, message) smtp.quit() diff -Nru quixote-2.5/quixote/server/_fcgi.py quixote-2.7~b2/quixote/server/_fcgi.py --- quixote-2.5/quixote/server/_fcgi.py 2006-03-17 02:49:51.000000000 +0000 +++ quixote-2.7~b2/quixote/server/_fcgi.py 2010-01-22 19:32:29.000000000 +0000 @@ -33,7 +33,7 @@ #------------------------------------------------------------------------ import os, sys, string, socket, errno, struct -from cStringIO import StringIO +from StringIO import StringIO import cgi #--------------------------------------------------------------------------- @@ -199,7 +199,8 @@ 'FCGI_MAX_REQS' : FCGI_MAX_REQS, 'FCGI_MPXS_CONNS': FCGI_MPXS_CONNS} for i in r.values.keys(): - if vars.has_key(i): v[i] = vars[i] + if i in vars: + v[i] = vars[i] r.values = vars r.writeRecord(conn) @@ -233,7 +234,7 @@ self.env = os.environ return - if os.environ.has_key('FCGI_WEB_SERVER_ADDRS'): + if 'FCGI_WEB_SERVER_ADDRS' in os.environ: good_addrs = string.split(os.environ['FCGI_WEB_SERVER_ADDRS'], ',') good_addrs = map(string.strip, good_addrs) # Remove whitespace else: @@ -357,7 +358,7 @@ def getFieldStorage(self): method = 'GET' - if self.env.has_key('REQUEST_METHOD'): + if 'REQUEST_METHOD' in self.env: method = string.upper(self.env['REQUEST_METHOD']) if method == 'GET': return cgi.FieldStorage(environ=self.env, keep_blank_values=1) @@ -415,7 +416,7 @@ doc.append('

    FCGI TestApp

    ') doc.append('request count = %d
    ' % counter) doc.append('pid = %s
    ' % os.getpid()) - if req.env.has_key('CONTENT_LENGTH'): + if 'CONTENT_LENGTH' in req.env: cl = string.atoi(req.env['CONTENT_LENGTH']) doc.append('
    POST data (%s):

    ' % cl)
                         keys = fs.keys()
    diff -Nru quixote-2.5/quixote/session.py quixote-2.7~b2/quixote/session.py
    --- quixote-2.5/quixote/session.py	2007-04-06 05:32:14.000000000 +0000
    +++ quixote-2.7~b2/quixote/session.py	2009-12-07 18:03:25.000000000 +0000
    @@ -138,6 +138,9 @@
             """
             return self.sessions.get(session_id, default)
     
    +    def __iter__(self):
    +        return self.sessions.itervalues()
    +
         def __getitem__(self, session_id):
             """(session_id : string) -> Session
     
    @@ -152,11 +155,13 @@
             Return true if a session identified by 'session_id' exists in
             the session manager.
             """
    -        return self.sessions.has_key(session_id)
    +        return session_id in self.sessions
    +
    +    def __contains__(self, session_id):
    +        return self.has_key(session_id)
     
    -    # has_session() is a synonym for has_key() -- if you override
    -    # has_key(), be sure to repeat this alias!
    -    has_session = has_key
    +    def has_session(self, session_id):
    +        return self.has_key(session_id)
     
         def __setitem__(self, session_id, session):
             """(session_id : string, session : Session)
    @@ -308,6 +313,11 @@
                 if not path.endswith("/"):
                     path += "/"
             domain = config.session_cookie_domain
    +        attrs = attrs.copy()
    +        if config.session_cookie_secure:
    +            attrs['secure'] = 1
    +        if config.session_cookie_httponly:
    +            attrs['httponly'] = 1
             get_response().set_cookie(name, value, domain=domain,
                                       path=path, **attrs)
             return name
    diff -Nru quixote-2.5/quixote/util.py quixote-2.7~b2/quixote/util.py
    --- quixote-2.5/quixote/util.py	2007-02-07 17:43:57.000000000 +0000
    +++ quixote-2.7~b2/quixote/util.py	2009-12-07 17:59:29.000000000 +0000
    @@ -272,7 +272,8 @@
                         obj = self._q_lookup(name)
                     except errors.TraversalError:
                         continue
    -                if not isinstance(obj, StaticDirectory) and callable(obj):
    +                if (not isinstance(obj, StaticDirectory)
    +                        and hasattr(obj, '__call__')):
                         return obj()
             r = TemplateIO(html=True)
             if self.list_directory:
    @@ -302,7 +303,7 @@
             """
             if name in ('.', '..'):
                 raise errors.TraversalError(private_msg="Attempt to use '.', '..'")
    -        if self.cache.has_key(name):
    +        if name in self.cache:
                 # Get item from cache
                 item = self.cache[name]
             else:
    diff -Nru quixote-2.5/quixote/wsgi.py quixote-2.7~b2/quixote/wsgi.py
    --- quixote-2.5/quixote/wsgi.py	2007-01-30 15:23:41.000000000 +0000
    +++ quixote-2.7~b2/quixote/wsgi.py	2010-01-22 19:32:24.000000000 +0000
    @@ -12,7 +12,7 @@
     import sys
     
     from http_request import HTTPRequest
    -from cStringIO import StringIO
    +from StringIO import StringIO
     
     ###### QWIP: WSGI COMPATIBILITY WRAPPER FOR QUIXOTE #####################
     
    @@ -29,7 +29,7 @@
                 getattr(self.publisher, 'is_thread_safe', False):
                 reason =  "%r is not thread safe" % self.publisher
                 raise AssertionError(reason)
    -        if not env.has_key('REQUEST_URI'):
    +        if 'REQUEST_URI' not in env:
                 env['REQUEST_URI'] = env['SCRIPT_NAME'] + env['PATH_INFO']
             input = env['wsgi.input']
             request = self.request_class(input, env)
    diff -Nru quixote-2.5/README.txt quixote-2.7~b2/README.txt
    --- quixote-2.5/README.txt	2006-08-08 21:10:40.000000000 +0000
    +++ quixote-2.7~b2/README.txt	2008-11-29 05:50:56.000000000 +0000
    @@ -3,33 +3,26 @@
     
     Quixote is a framework for developing Web applications in Python.
     The target is web applications that are developed and maintained by
    -Python programmers.  See http://www.quixote.ca/users for a list of
    +Python programmers.  See http://quixote.ca/users for a list of some
     applications using Quixote.
     
    -Quixote requires Python 2.3 or greater to run.  For installation
    -instructions, see the doc/INSTALL.txt file.
    +For installation instructions, see the doc/INSTALL.txt file.  For
    +the impatient, install the package using setup.py and then run:
    +
    +    python quixote/demo/mini_demo.py
    +
    +Note that you can copy the mini_demo.py somewhere and use it as a
    +starting point for your application if you like.  See the Quixote
    +wiki page  for more hints.
    +
    +Documentation is available in the doc/ directory and on the Quixote
    +web site.
     
     Quixote includes PTL, the Python Template Language for producing
     HTML with Python code.  Note that the use of PTL is not required in
     Quixote applications.  Details about PTL are provided in
     doc/PTL.txt.
     
    -If you're switching to a newer version of Quixote from an older
    -version, please refer to doc/upgrading.txt for explanations of any
    -backward-incompatible changes.  
    -
    -
    -Installation
    -=============
    -
    -See doc/INSTALL.txt.
    -
    -
    -Documentation
    -=============
    -
    -Look in the doc/ directory.
    -
     
     Authors, copyright, and license
     ===============================
    @@ -45,10 +38,16 @@
     ==========================================
     
     The Quixote home page is:
    -    http://www.quixote.ca/
    +    http://quixote.ca/
    +
    +There is wiki at:
    +
    +    http://quixote.ca/qx/
     
     Discussion of Quixote occurs on the quixote-users mailing list:
         http://mail.mems-exchange.org/mailman/listinfo/quixote-users/
     
    -The source code is managed using bazaar-ng:
    -    http://quixote.python.ca/quixote.dev/
    +The source code is managed using git.  You can checkout a copy using
    +the command:
    +
    +    git clone http://quixote.ca/src/quixote.git
    diff -Nru quixote-2.5/setup.py quixote-2.7~b2/setup.py
    --- quixote-2.5/setup.py	2007-04-06 05:46:23.000000000 +0000
    +++ quixote-2.7~b2/setup.py	2008-11-29 05:50:56.000000000 +0000
    @@ -14,16 +14,6 @@
     from quixote.ptl.qx_distutils import qx_build_py
     from quixote import __version__
     
    -# Ensure that version number is correct.
    -def _check_version_numbers():
    -    import re
    -    PAT = re.compile(r'^%s\b' % re.escape(__version__), re.MULTILINE)
    -    if not PAT.search(open("CHANGES.txt").read(400)):
    -        raise AssertionError("version number mismatch in CHANGES.txt")
    -
    -if 'sdist' in sys.argv[1:]:
    -    _check_version_numbers()
    -
     # a fast htmltext type
     htmltext = Extension(name="quixote.html._c_htmltext",
                          sources=["quixote/html/_c_htmltext.c"])
    @@ -34,11 +24,11 @@
     
     kw = {'name': "Quixote",
           'version': __version__,
    -      'description': "A highly Pythonic Web application framework",
    +      'description': "A small and flexible Python Web application framework",
           'author': "The Quixote developers",
           'author_email': "webmaster@quixote.ca",
           'url': "http://www.quixote.ca/",
    -      'license': "DFSG approved open source (see LICENSE.txt)",
    +      'license': "DFSG approved (see LICENSE.txt)",
     
           'package_dir': {'quixote': 'quixote'},
           'packages': ['quixote',  'quixote.demo', 'quixote.form',
    diff -Nru quixote-2.5/tests/qx_testlib.py quixote-2.7~b2/tests/qx_testlib.py
    --- quixote-2.5/tests/qx_testlib.py	2007-02-09 22:59:41.000000000 +0000
    +++ quixote-2.7~b2/tests/qx_testlib.py	2010-01-22 19:32:33.000000000 +0000
    @@ -2,7 +2,7 @@
     
     import quixote
     from quixote.server.simple_server import run
    -from cStringIO import StringIO
    +from StringIO import StringIO
     import os
     import socket
     import urllib
    diff -Nru quixote-2.5/tests/qx_testserver.py quixote-2.7~b2/tests/qx_testserver.py
    --- quixote-2.5/tests/qx_testserver.py	2007-02-09 22:59:41.000000000 +0000
    +++ quixote-2.7~b2/tests/qx_testserver.py	2008-11-29 05:50:56.000000000 +0000
    @@ -3,9 +3,6 @@
     """
     
     import os
    -import quixote
    -assert quixote.__version__ == '2.5a1'
    -
     from quixote.publish import Publisher
     from quixote.directory import Directory