--- C:\DOCUME~1\PROPRI~1\LOCALS~1\Temp\cgi.py-revBASE.svn000.tmp.py sam. janv. 29 21:57:47 2011 +++ C:\python-dev\cgi.py sam. janv. 22 08:33:16 2011 @@ -106,72 +106,21 @@ # 0 ==> unlimited input maxlen = 0 -def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0): - """Parse a query in the environment or from a file (default stdin) - - Arguments, all optional: - - fp : file pointer; default: sys.stdin.buffer - - environ : environment dictionary; default: os.environ - - keep_blank_values: flag indicating whether blank values in - percent-encoded forms should be treated as blank strings. - A true value indicates that blanks should be retained as - blank strings. The default false value indicates that - blank values are to be ignored and treated as if they were - not included. - - strict_parsing: flag indicating what to do with parsing errors. - If false (the default), errors are silently ignored. - If true, errors raise a ValueError exception. - """ - if fp is None: - fp = sys.stdin - - # field keys and values (except for files) are returned as strings - # an encoding is required to decode the bytes read from self.fp - if hasattr(fp,'encoding'): - encoding = fp.encoding - else: - encoding = 'latin-1' - - # fp.read() must return bytes - if isinstance(fp, TextIOWrapper): - fp = fp.buffer - - if not 'REQUEST_METHOD' in environ: - environ['REQUEST_METHOD'] = 'GET' # For testing stand-alone - if environ['REQUEST_METHOD'] == 'POST': - ctype, pdict = parse_header(environ['CONTENT_TYPE']) - if ctype == 'multipart/form-data': - return parse_multipart(fp, pdict) - elif ctype == 'application/x-www-form-urlencoded': - clength = int(environ['CONTENT_LENGTH']) - if maxlen and clength > maxlen: - raise ValueError('Maximum content length exceeded') - qs = fp.read(clength).decode(encoding) +def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0, + encoding="utf-8",errors="replace"): + """Parse a query in the environment or from a file + Return a dictionary mapping field names to a list of values""" + fs = FieldStorage(fp=fp,environ=environ, + keep_blank_values=keep_blank_values, + strict_parsing=strict_parsing,encoding=encoding,errors=errors) + result = {} + for item in fs.list: + if item.name in result: + result[item.name].append(item.value) else: - qs = '' # Unknown content-type - if 'QUERY_STRING' in environ: - if qs: qs = qs + '&' - qs = qs + environ['QUERY_STRING'] - elif sys.argv[1:]: - if qs: qs = qs + '&' - qs = qs + sys.argv[1] - environ['QUERY_STRING'] = qs # XXX Shouldn't, really - elif 'QUERY_STRING' in environ: - qs = environ['QUERY_STRING'] - else: - if sys.argv[1:]: - qs = sys.argv[1] - else: - qs = "" - environ['QUERY_STRING'] = qs # XXX Shouldn't, really - return urllib.parse.parse_qs(qs, keep_blank_values, strict_parsing, - encoding=encoding) + result[item.name] = [item.value] + return result - # parse query string function called from urlparse, # this is done in order to maintain backward compatiblity. @@ -192,101 +141,10 @@ Arguments: fp : input file - pdict: dictionary containing other parameters of content-type header + pdict: dictionary containing other parameters of content-type header""" + fs = FieldStorage(fp,environ=pdict) + return fs - Returns a dictionary just like parse_qs(): keys are the field names, each - value is a list of values for that field. This is easy to use but not - much good if you are expecting megabytes to be uploaded -- in that case, - use the FieldStorage class instead which is much more flexible. Note - that content-type is the raw, unparsed contents of the content-type - header. - - XXX This does not parse nested multipart parts -- use FieldStorage for - that. - - XXX This should really be subsumed by FieldStorage altogether -- no - point in having two implementations of the same parsing algorithm. - Also, FieldStorage protects itself better against certain DoS attacks - by limiting the size of the data read in one chunk. The API here - does not support that kind of protection. This also affects parse() - since it can call parse_multipart(). - - """ - import http.client - - boundary = "" - if 'boundary' in pdict: - boundary = pdict['boundary'] - if not valid_boundary(boundary): - raise ValueError('Invalid boundary in multipart form: %r' - % (boundary,)) - - nextpart = "--" + boundary - lastpart = "--" + boundary + "--" - partdict = {} - terminator = "" - - while terminator != lastpart: - bytes = -1 - data = None - if terminator: - # At start of next part. Read headers first. - headers = http.client.parse_headers(fp) - clength = headers.get('content-length') - if clength: - try: - bytes = int(clength) - except ValueError: - pass - if bytes > 0: - if maxlen and bytes > maxlen: - raise ValueError('Maximum content length exceeded') - data = fp.read(bytes) - else: - data = "" - # Read lines until end of part. - lines = [] - while 1: - line = fp.readline() - if not line: - terminator = lastpart # End outer loop - break - if line.startswith("--"): - terminator = line.rstrip() - if terminator in (nextpart, lastpart): - break - lines.append(line) - # Done with part. - if data is None: - continue - if bytes < 0: - if lines: - # Strip final line terminator - line = lines[-1] - if line[-2:] == "\r\n": - line = line[:-2] - elif line[-1:] == "\n": - line = line[:-1] - lines[-1] = line - data = "".join(lines) - line = headers['content-disposition'] - if not line: - continue - key, params = parse_header(line) - if key != 'form-data': - continue - if 'name' in params: - name = params['name'] - else: - continue - if name in partdict: - partdict[name].append(data) - else: - partdict[name] = [data] - - return partdict - - def _parseparam(s): while s[:1] == ';': s = s[1:] @@ -320,6 +178,39 @@ return key, pdict +class IOMix(): + def __init__( self, fh, encoding="UTF-8"): + if hasattr( fh, 'buffer'): + self._bio = fh.buffer + fh.flush() + self._last = 'b' + self._txt = TextIOWrapper( self._bio, encoding, None, '\r\n') + self._encoding = encoding + else: + raise ValueError("not a buffered stream") + + def write( self, param ): + if isinstance( param, str ): + self._last = 't' + self._txt.write( param ) + else: + if self._last == 't': + self._txt.flush() + self._last = 'b' + self._bio.write( param ) + + def flush( self ): + self._txt.flush() + + def close( self ): + self.flush() + self._txt.close() + self._bio.close() + +def set_stdout_encoding(encoding): + sys.stdout = IOMix( sys.stdout, encoding ) + sys.stderr = IOMix( sys.stderr, encoding ) + # Classes for field storage # =========================