2000-08-18 11:21:45 -04:00
|
|
|
|
PEP: 222
|
|
|
|
|
Title: Web Library Enhancements
|
|
|
|
|
Version: $Revision$
|
2000-08-22 01:15:51 -04:00
|
|
|
|
Author: akuchlin@mems-exchange.org (Andrew Kuchling)
|
2000-08-18 11:21:45 -04:00
|
|
|
|
Status: Active
|
|
|
|
|
Type: Standards Track
|
2000-08-23 02:04:33 -04:00
|
|
|
|
Python-Version: 2.1
|
2000-08-18 11:21:45 -04:00
|
|
|
|
Created: 18-Aug-2000
|
|
|
|
|
Post-History:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Abstract
|
|
|
|
|
|
|
|
|
|
This PEP proposes a set of enhancements to the CGI development
|
|
|
|
|
facilities in the Python standard library. Enhancements might be
|
|
|
|
|
new features, new modules for tasks such as cookie support, or
|
|
|
|
|
removal of obsolete code.
|
|
|
|
|
|
2000-10-28 20:16:37 -04:00
|
|
|
|
The intent is to incorporate the proposals emerging from this
|
|
|
|
|
document into Python 2.1, due to be released in the first half of
|
|
|
|
|
2001.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Proposed Changes
|
|
|
|
|
|
|
|
|
|
This section lists changes that have been suggested, but about
|
|
|
|
|
which no firm decision has yet been made. In the final version of
|
|
|
|
|
this PEP, this section should be empty, as all the changes should
|
|
|
|
|
be classified as accepted or rejected.
|
|
|
|
|
|
|
|
|
|
fcgi.py : A new module adding support for the FastCGI protocol
|
|
|
|
|
|
|
|
|
|
Better debugging support for CGIs from the Unix command line.
|
|
|
|
|
Using Perl's CGI.pm [1], if you run a script from the command
|
|
|
|
|
line, you can enter name=value pairs on standard input.
|
|
|
|
|
cgimodel [2] provides this already.
|
|
|
|
|
|
|
|
|
|
Should the existing cgi.py be deprecated and everything moved into
|
|
|
|
|
a 'web' or 'cgi' package? That would allow removing some
|
|
|
|
|
backward-compatibility cruft.
|
|
|
|
|
|
|
|
|
|
cgi.py: keep_blank_values should be on by default. The 'if
|
|
|
|
|
form.has_key()/if form[key].value' nested syntax is unnecessarily
|
|
|
|
|
convoluted.
|
|
|
|
|
|
|
|
|
|
cgi.py: We should not be told to create our own subclass just so
|
|
|
|
|
we can handle file uploads. As a practical matter, I have yet to
|
|
|
|
|
find the time to do this right, so I end up reading cgi.py's temp
|
|
|
|
|
file into, at best, another file. Some of our legacy code actually
|
|
|
|
|
reads it into a second temp file, then into a final destination!
|
|
|
|
|
And even if we did, that would mean creating yet another object
|
|
|
|
|
with its __init__ call and associated overhead.
|
|
|
|
|
|
2000-11-10 12:06:53 -05:00
|
|
|
|
cgi.py: Delete the references to self.lines for file uploads
|
|
|
|
|
|
2000-10-28 20:16:37 -04:00
|
|
|
|
cgi.py: Ideally, the pseudo-dictionary syntax would go away. It
|
|
|
|
|
seems to me that the main reason it is there is to accomodate
|
|
|
|
|
|
2000-12-12 12:34:39 -05:00
|
|
|
|
form['field'].file syntax. How about the following:
|
|
|
|
|
form['field'] = '' #no values for key
|
|
|
|
|
form['field'] = 'string' #one value in submission for key
|
|
|
|
|
form['field'] = ['string', 'string', ....] #multiple values
|
|
|
|
|
form['field'] = {'fileName':'remote/path',
|
|
|
|
|
'binaryValue':'@UIHJBV29489erht...'} #one file
|
|
|
|
|
form['field'] = [{'fileName':'remote/path',
|
|
|
|
|
'binaryValue':'@UIHJBV29489erht...'},
|
|
|
|
|
{'fileName':'another/path',
|
|
|
|
|
'binaryValue':'7r7w7@@@@'}] #multiple files
|
2000-10-28 20:16:37 -04:00
|
|
|
|
|
|
|
|
|
cgi.py: I'd prefer "input" or "submission" for the suggested
|
|
|
|
|
FieldStorage() name. The html page into which the data represented
|
|
|
|
|
by FieldStorage() is input is commonly known as a "form", which
|
|
|
|
|
means that when someone refers to "the form" you aren't always
|
|
|
|
|
sure what they are talking about.
|
|
|
|
|
|
|
|
|
|
cgi.py: Allow a combination of query data and POST data.
|
|
|
|
|
Currently, if there is POST data, then any query data encoded in
|
|
|
|
|
the URL is ignored. It would be more convenient if the data from
|
|
|
|
|
those two sources were merged into one dictionary. (XXX but is
|
|
|
|
|
this standard at all?)
|
|
|
|
|
|
|
|
|
|
cgi.py: Currently, query data with no `=' are ignored. Even if
|
|
|
|
|
keep_blank_values is set, queries like `...?value=&...' are
|
|
|
|
|
returned with blank values but queries like `...?value&...' are
|
|
|
|
|
completely lost. It would be great if such data were made
|
|
|
|
|
available through the FieldStorage interface, either as entries
|
|
|
|
|
with None as values, or in a separate list.
|
|
|
|
|
|
2000-11-10 12:06:53 -05:00
|
|
|
|
Utility function: build a query string from a list of 2-tuples
|
|
|
|
|
|
2000-10-28 20:16:37 -04:00
|
|
|
|
Higher-level frameworks: add something like Webware or Zope's
|
|
|
|
|
HTTPReqest/HTTPResponse objects.
|
|
|
|
|
|
|
|
|
|
An HTML templating module. (But which one? There's no clear, or
|
|
|
|
|
even vague, indication of which templating module to enshrine.)
|
|
|
|
|
|
|
|
|
|
Dictionary-related utility classes: NoKeyErrors (returns an empty
|
|
|
|
|
string, never a KeyError), PartialStringSubstitution (returns
|
|
|
|
|
the original key string, never a KeyError)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
New Modules
|
|
|
|
|
|
|
|
|
|
This section lists details about entire new packages or modules
|
|
|
|
|
that should be added to the Python standard library.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Major Changes to Existing Modules
|
|
|
|
|
|
|
|
|
|
This section lists details of major changes to existing modules,
|
|
|
|
|
whether in implementation or in interface. The changes in this
|
|
|
|
|
section therefore carry greater degrees of risk, either in
|
|
|
|
|
introducing bugs or a backward incompatibility.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Minor Changes to Existing Modules
|
|
|
|
|
|
|
|
|
|
This section lists details of minor changes to existing modules.
|
|
|
|
|
These changes should have relatively small implementations, and
|
|
|
|
|
have little risk of introducing incompatibilities with previous
|
|
|
|
|
versions.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Rejected Changes
|
|
|
|
|
|
|
|
|
|
The changes listed in this section were proposed for Python 2.1,
|
|
|
|
|
but were rejected as unsuitable. For each rejected change, a
|
|
|
|
|
rationale is given describing why the change was deemed
|
|
|
|
|
inappropriate.
|
|
|
|
|
|
2000-12-12 12:34:39 -05:00
|
|
|
|
* None yet
|
2000-10-28 20:16:37 -04:00
|
|
|
|
|
|
|
|
|
|
2000-12-12 12:34:39 -05:00
|
|
|
|
Proposed Interface
|
|
|
|
|
|
|
|
|
|
XXX open issues: naming convention (studlycaps or underline-separated?);
|
|
|
|
|
no interface for file uploads yet; need to look at all the various
|
|
|
|
|
packages to see if there's anything else missing; need to look at
|
|
|
|
|
the cgi.parse*() functions and see if they can be simplified, too.
|
|
|
|
|
|
|
|
|
|
Parsing functions: carry over most of the parse* functions from cgi.py
|
|
|
|
|
|
|
|
|
|
# The Response class borrows most of its methods from Zope's
|
|
|
|
|
# HTTPResponse class.
|
|
|
|
|
|
|
|
|
|
class Response:
|
|
|
|
|
"""
|
|
|
|
|
Attributes:
|
|
|
|
|
status: HTTP status code to return
|
|
|
|
|
headers: dictionary of response headers
|
|
|
|
|
body: string containing the body of the HTTP response
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self, status=200, headers={}, body=""):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def setStatus(self, status, reason=None):
|
|
|
|
|
"Set the numeric HTTP response code"
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def setHeader(self, name, value):
|
|
|
|
|
"Set an HTTP header"
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def setBody(self, body):
|
|
|
|
|
"Set the body of the response"
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def setCookie(self, name, value,
|
|
|
|
|
path = XXX, # What to use as defaults?
|
|
|
|
|
comment = XXX,
|
|
|
|
|
domain = XXX,
|
|
|
|
|
max-age = XXX,
|
|
|
|
|
expires = XXX,
|
|
|
|
|
secure = XXX
|
|
|
|
|
):
|
|
|
|
|
"Set a cookie"
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def expireCookie(self, name):
|
|
|
|
|
"Remove a cookie from the user"
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def redirect(self, url):
|
|
|
|
|
"Redirect the browser to another URL"
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
|
"Convert entire response to a string"
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def dump(self):
|
|
|
|
|
"Return a string representation useful for debugging"
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
# XXX methods for specific classes of error:serverError, badRequest, etc.?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Request:
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
Attributes:
|
|
|
|
|
.headers : dictionary containing HTTP headers
|
|
|
|
|
.cookies : dictionary of cookies
|
|
|
|
|
.form : data from the form
|
|
|
|
|
.env : environment dictionary
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self, environ=os.environ, stdin=sys.stdin,
|
|
|
|
|
keep_blank_values=0, strict_parsing=0):
|
|
|
|
|
"""Initialize the request object, using the provided environment
|
|
|
|
|
and standard input."""
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
# Should people just use the dictionaries directly?
|
|
|
|
|
def getHeader(self, name, default=None):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def getCookie(self, name, default=None):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def getField(self, name, default=None):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def getURL(self, n=0, query_string=0):
|
|
|
|
|
"""Return the URL of the current request, chopping off 'n' path
|
|
|
|
|
components from the right. Eg. if the URL is
|
|
|
|
|
"http://foo.com/bar/baz/qux", n=2 would return
|
|
|
|
|
"http://foo.com/bar". Does not include the query string (if
|
|
|
|
|
any)
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def getBaseURL(self, n=0):
|
|
|
|
|
"""Return the base URL of the current request, adding 'n' path
|
|
|
|
|
components to the end to recreate more of the whole URL.
|
|
|
|
|
|
|
|
|
|
Eg. if the request URL is
|
|
|
|
|
"http://foo.com/q/bar/baz/qux", n=0 would return
|
|
|
|
|
"http://foo.com/", and n=2 "http://foo.com/q/bar".
|
|
|
|
|
|
|
|
|
|
Returned URL does not include the query string, if any.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def dump(self):
|
|
|
|
|
"String representation suitable for debugging output"
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
# Possibilities?
|
|
|
|
|
def getBrowser(self):
|
|
|
|
|
"Returns Mozilla/IE/Lynx/Opera/whatever"
|
|
|
|
|
|
|
|
|
|
def isSecure(self):
|
|
|
|
|
"Return true if this is an SSLified request"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def wrapper(func, logfile=None):
|
|
|
|
|
"""
|
|
|
|
|
Calls the function 'func', passing it the arguments
|
|
|
|
|
(request, response, logfile). Exceptions are trapped and
|
|
|
|
|
sent to the file 'logfile'.
|
|
|
|
|
"""
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Copyright
|
|
|
|
|
|
2000-10-28 20:16:37 -04:00
|
|
|
|
This document has been placed in the public domain.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
References and Footnotes
|
|
|
|
|
|
|
|
|
|
[1] CGI.pm:
|
|
|
|
|
|
|
|
|
|
[2] http://www.embl-heidelberg.de/~chenna/pythonpages/
|
|
|
|
|
|
2000-08-18 11:21:45 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Local Variables:
|
|
|
|
|
mode: indented-text
|
|
|
|
|
indent-tabs-mode: nil
|
|
|
|
|
End:
|