You are viewing a plain text version of this content. The canonical link for it is here.
Posted to mod_python-commits@quetz.apache.org by gr...@apache.org on 2006/04/13 12:50:47 UTC
svn commit: r393781 - in /httpd/mod_python/branches/3.2.x: Doc/appendixc.tex
Doc/modpython4.tex lib/python/mod_python/__init__.py
lib/python/mod_python/util.py src/include/mpversion.h
Author: grahamd
Date: Thu Apr 13 03:50:36 2006
New Revision: 393781
URL: http://svn.apache.org/viewcvs?rev=393781&view=rev
Log:
Backported MODPYTHON-93 from trunk to branches/3.2.x.
Modified:
httpd/mod_python/branches/3.2.x/Doc/appendixc.tex
httpd/mod_python/branches/3.2.x/Doc/modpython4.tex
httpd/mod_python/branches/3.2.x/lib/python/mod_python/__init__.py
httpd/mod_python/branches/3.2.x/lib/python/mod_python/util.py
httpd/mod_python/branches/3.2.x/src/include/mpversion.h
Modified: httpd/mod_python/branches/3.2.x/Doc/appendixc.tex
URL: http://svn.apache.org/viewcvs/httpd/mod_python/branches/3.2.x/Doc/appendixc.tex?rev=393781&r1=393780&r2=393781&view=diff
==============================================================================
--- httpd/mod_python/branches/3.2.x/Doc/appendixc.tex (original)
+++ httpd/mod_python/branches/3.2.x/Doc/appendixc.tex Thu Apr 13 03:50:36 2006
@@ -44,6 +44,9 @@
(\citetitle[http://issues.apache.org/jira/browse/MODPYTHON-119]{MODPYTHON-119})
DbmSession unit test no longer uses the default directory for the dbm file,
so the test will not interfer with the user's current apache instance.
+ \item
+ (\citetitle[http://issues.apache.org/jira/browse/MODPYTHON-93]{MODPYTHON-93})
+ Improved util.FieldStorage efficiency.
\end{itemize}
Bug Fixes
Modified: httpd/mod_python/branches/3.2.x/Doc/modpython4.tex
URL: http://svn.apache.org/viewcvs/httpd/mod_python/branches/3.2.x/Doc/modpython4.tex?rev=393781&r1=393780&r2=393781&view=diff
==============================================================================
--- httpd/mod_python/branches/3.2.x/Doc/modpython4.tex (original)
+++ httpd/mod_python/branches/3.2.x/Doc/modpython4.tex Thu Apr 13 03:50:36 2006
@@ -1522,7 +1522,7 @@
class. This class is similar to the standard library module
\module{cgi} \class{FieldStorage}.
-\begin{classdesc}{FieldStorage}{req\optional{, keep_blank_values, strict_parsing}}
+\begin{classdesc}{FieldStorage}{req\optional{, keep_blank_values, strict_parsing, file_callback, field_callback}}
This class provides uniform access to HTML form data submitted by the
client. \var{req} is an instance of the mod_python request object.
@@ -1533,6 +1533,14 @@
The optional argument \var{strict_parsing} is not yet implemented.
+ The optional argument \var{file_callback} allows the application to override
+ both file creation/deletion semantics and location. See
+ \ref{pyapi-util-fstor-examples} ``FieldStorage Examples'' for additional
+ information. \emph{New in version 3.2}
+
+ The optional argument \var{field_callback} allows the application to override
+ both the creation/deletion semantics and behaviour. \emph{New in version 3.2}
+
During initialization, \class{FieldStorage} class reads all of the
data provided by the client. Since all data provided by the client is
consumed at this point, there should be no more than one
@@ -1599,6 +1607,90 @@
\end{methoddesc}
\end{classdesc}
+
+\subsection{FieldStorage Examples\label{pyapi-util-fstor-examples}}
+
+ The following examples demonstrate how to use the \var{file_callback}
+ parameter of the \class{FieldStorage} constructor to control file
+ object creation. The \class{Storage} classes created in both
+ examples derive from FileType, thereby providing extended file
+ functionality.
+
+ These examples are provided for demonstration purposes only. The
+ issue of temporary file location and security must be considered
+ when providing such overrides with mod_python in production use.
+
+\begin{description}
+ \item[Simple file control using class constructor]
+
+ This example uses the \class{FieldStorage} class constructor to
+ create the file object, allowing simple control. It is not
+ advisable to add class variables to this if serving multiple sites
+ from apache. In that case use the factory method instead.
+
+\begin{verbatim}
+class Storage(file):
+
+ def __init__(self, advisory_filename):
+ self.advisory_filename = advisory_filename
+ self.delete_on_close = True
+ self.already_deleted = False
+ self.real_filename = '/someTempDir/thingy-unique-thingy'
+ super(Storage, self).__init__(self.real_filename, 'w+b')
+
+ def close(self):
+ if self.already_deleted:
+ return
+ super(Storage, self).close()
+ if self.delete_on_close:
+ self.already_deleted = True
+ os.remove(self.real_filename)
+
+request_data = util.FieldStorage(request, keep_blank_values=True, file_callback=Storage)
+\end{verbatim}
+
+
+ \item[Advanced file control using object factory]
+
+ Using a object factory can provide greater control over the
+ constructor parameters.
+
+\begin{verbatim}
+import os
+
+class Storage(file):
+
+ def __init__(self, directory, advisory_filename):
+ self.advisory_filename = advisory_filename
+ self.delete_on_close = True
+ self.already_deleted = False
+ self.real_filename = directory + '/thingy-unique-thingy'
+ super(Storage, self).__init__(self.real_filename, 'w+b')
+
+ def close(self):
+ if self.already_deleted:
+ return
+ super(Storage, self).close()
+ if self.delete_on_close:
+ self.already_deleted = True
+ os.remove(self.real_filename)
+
+class StorageFactory:
+
+ def __init__(self, directory):
+ self.dir = directory
+
+ def create(self, advisory_filename):
+ return Storage(self.dir, advisory_filename)
+
+file_factory = StorageFactory(someDirectory)
+[...sometime later...]
+request_data = util.FieldStorage(request, keep_blank_values=True,
+ file_callback=file_factory.create)
+\end{verbatim}
+
+\end{description}
+
\subsection{Field class\label{pyapi-util-fstor-fld}}
Modified: httpd/mod_python/branches/3.2.x/lib/python/mod_python/__init__.py
URL: http://svn.apache.org/viewcvs/httpd/mod_python/branches/3.2.x/lib/python/mod_python/__init__.py?rev=393781&r1=393780&r2=393781&view=diff
==============================================================================
--- httpd/mod_python/branches/3.2.x/lib/python/mod_python/__init__.py (original)
+++ httpd/mod_python/branches/3.2.x/lib/python/mod_python/__init__.py Thu Apr 13 03:50:36 2006
@@ -20,5 +20,5 @@
__all__ = ["apache", "cgihandler", "psp",
"publisher", "util", "python22"]
-version = "3.2.9-unreleased-20060412"
+version = "3.2.9-unreleased-20060413"
Modified: httpd/mod_python/branches/3.2.x/lib/python/mod_python/util.py
URL: http://svn.apache.org/viewcvs/httpd/mod_python/branches/3.2.x/lib/python/mod_python/util.py?rev=393781&r1=393780&r2=393781&view=diff
==============================================================================
--- httpd/mod_python/branches/3.2.x/lib/python/mod_python/util.py (original)
+++ httpd/mod_python/branches/3.2.x/lib/python/mod_python/util.py Thu Apr 13 03:50:36 2006
@@ -48,19 +48,8 @@
"""
class Field:
- def __init__(self, name, file, ctype, type_options,
- disp, disp_options, headers = {}):
+ def __init__(self, name):
self.name = name
- self.file = file
- self.type = ctype
- self.type_options = type_options
- self.disposition = disp
- self.disposition_options = disp_options
- if disp_options.has_key("filename"):
- self.filename = disp_options["filename"]
- else:
- self.filename = None
- self.headers = headers
def __repr__(self):
"""Return printable representation."""
@@ -81,13 +70,34 @@
self.file.close()
class StringField(str):
- """ This class is basically a string with
- a value attribute for compatibility with std lib cgi.py
- """
-
- def __init__(self, str=""):
- str.__init__(self, str)
- self.value = self.__str__()
+ """ This class is basically a string with
+ added attributes for compatibility with std lib cgi.py. Basically, this
+ works the opposite of Field, as it stores its data in a string, but creates
+ a file on demand. Field creates a value on demand and stores data in a file.
+ """
+ filename = None
+ headers = {}
+ ctype = "text/plain"
+ type_options = {}
+ disposition = None
+ disp_options = None
+
+ # I wanted __init__(name, value) but that does not work (apparently, you
+ # cannot subclass str with a constructor that takes >1 argument)
+ def __init__(self, value):
+ '''Create StringField instance. You'll have to set name yourself.'''
+ str.__init__(self, value)
+ self.value = value
+
+ def __getattr__(self, name):
+ if name != 'file':
+ raise AttributeError, name
+ self.file = cStringIO.StringIO(self.value)
+ return self.file
+
+ def __repr__(self):
+ """Return printable representation (to pass unit tests)."""
+ return "Field(%s, %s)" % (`self.name`, `self.value`)
class FieldStorage:
@@ -103,8 +113,7 @@
if req.args:
pairs = parse_qsl(req.args, keep_blank_values)
for pair in pairs:
- file = cStringIO.StringIO(pair[1])
- self.list.append(Field(pair[0], file, "text/plain", {}, None, {}))
+ self.add_field(pair[0], pair[1])
if req.method != "POST":
return
@@ -123,9 +132,7 @@
if ctype.startswith("application/x-www-form-urlencoded"):
pairs = parse_qsl(req.read(clen), keep_blank_values)
for pair in pairs:
- # TODO : isn't this a bit heavyweight just for form fields ?
- file = cStringIO.StringIO(pair[1])
- self.list.append(Field(pair[0], file, "text/plain", {}, None, {}))
+ self.add_field(pair[0], pair[1])
return
if not ctype.startswith("multipart/"):
@@ -205,32 +212,43 @@
else:
name = None
+ # create a file object
# is this a file?
if disp_options.has_key("filename"):
if file_callback and callable(file_callback):
file = file_callback(disp_options["filename"])
else:
- file = self.make_file()
+ file = tempfile.TemporaryFile("w+b")
else:
if field_callback and callable(field_callback):
file = field_callback()
else:
- file = self.make_field()
+ file = cStringIO.StringIO()
# read it in
- end_of_stream = self.read_to_boundary(req, boundary, file)
+ self.read_to_boundary(req, boundary, file)
file.seek(0)
-
+
# make a Field
- field = Field(name, file, ctype, type_options, disp, disp_options, headers)
-
+ if disp_options.has_key("filename"):
+ field = Field(name)
+ field.filename = disp_options["filename"]
+ else:
+ field = StringField(file.read())
+ field.name = name
+ field.file = file
+ field.type = ctype
+ field.type_options = type_options
+ field.disposition = disp
+ field.disposition_options = disp_options
+ field.headers = headers
self.list.append(field)
- def make_file(self):
- return tempfile.TemporaryFile("w+b")
-
- def make_field(self):
- return cStringIO.StringIO()
+ def add_field(self, key, value):
+ """Insert a field as key/value pair"""
+ item = StringField(value)
+ item.name = key
+ self.list.append(item)
def read_to_boundary(self, req, boundary, file):
previous_delimiter = None
@@ -283,18 +301,7 @@
def __getitem__(self, key):
"""Dictionary style indexing."""
- if self.list is None:
- raise TypeError, "not indexable"
- found = []
- for item in self.list:
- if item.name == key:
- if isinstance(item.file, FileType) or \
- isinstance(getattr(item.file, 'file', None), FileType):
- found.append(item)
- else:
- found.append(StringField(item.value))
- if not found:
- raise KeyError, key
+ found = self.dictionary[key]
if len(found) == 1:
return found[0]
else:
@@ -303,56 +310,56 @@
def get(self, key, default):
try:
return self.__getitem__(key)
- except KeyError:
+ except (TypeError, KeyError):
return default
def keys(self):
"""Dictionary style keys() method."""
- if self.list is None:
- raise TypeError, "not indexable"
- keys = []
- for item in self.list:
- if item.name not in keys: keys.append(item.name)
- return keys
+ return self.dictionary.keys()
def has_key(self, key):
"""Dictionary style has_key() method."""
- if self.list is None:
- raise TypeError, "not indexable"
- for item in self.list:
- if item.name == key: return 1
- return 0
+ return (key in self.dictionary)
__contains__ = has_key
def __len__(self):
"""Dictionary style len(x) support."""
- return len(self.keys())
+ return len(self.dictionary.keys())
def getfirst(self, key, default=None):
""" return the first value received """
- for item in self.list:
- if item.name == key:
- if isinstance(item.file, FileType) or \
- isinstance(getattr(item.file, 'file', None), FileType):
- return item
- else:
- return StringField(item.value)
- return default
+ try:
+ return self.dictionary[key][0]
+ except KeyError:
+ return default
def getlist(self, key):
""" return a list of received values """
- if self.list is None:
+ try:
+ return self.dictionary[key]
+ except KeyError:
+ return []
+
+ def items(self):
+ """Dictionary-style items(), except that items are returned in the same
+ order as they were supplied in the form."""
+ return [(item.name, item) for item in self.list]
+
+ def __getattr__(self, name):
+ if name != 'dictionary':
+ raise AttributeError, name
+ list = self.list
+ if list is None:
raise TypeError, "not indexable"
- found = []
- for item in self.list:
- if item.name == key:
- if isinstance(item.file, FileType) or \
- isinstance(getattr(item.file, 'file', None), FileType):
- found.append(item)
- else:
- found.append(StringField(item.value))
- return found
+ result = {}
+ self.dictionary = result
+ for item in list:
+ if item.name in result:
+ result[item.name].append(item)
+ else:
+ result[item.name] = [item]
+ return result
def parse_header(line):
"""Parse a Content-type like header.
Modified: httpd/mod_python/branches/3.2.x/src/include/mpversion.h
URL: http://svn.apache.org/viewcvs/httpd/mod_python/branches/3.2.x/src/include/mpversion.h?rev=393781&r1=393780&r2=393781&view=diff
==============================================================================
--- httpd/mod_python/branches/3.2.x/src/include/mpversion.h (original)
+++ httpd/mod_python/branches/3.2.x/src/include/mpversion.h Thu Apr 13 03:50:36 2006
@@ -1,5 +1,5 @@
#define MPV_MAJOR 3
#define MPV_MINOR 2
#define MPV_PATCH 9
-#define MPV_BUILD 20060412
-#define MPV_STRING "3.2.9-unreleased-20060412"
+#define MPV_BUILD 20060413
+#define MPV_STRING "3.2.9-unreleased-20060413"