1313
1414This module defines a number of utilities for use by CGI scripts
1515written in Python.
16+
17+ The global variable maxlen can be set to an integer indicating the maximum size
18+ of a POST request. POST requests larger than this size will result in a
19+ ValueError being raised during parsing. The default value of this variable is 0,
20+ meaning the request size is unlimited.
1621"""
1722
1823# History
4146import html
4247import locale
4348import tempfile
49+ import warnings
4450
4551__all__ = ["MiniFieldStorage" , "FieldStorage" , "parse" , "parse_multipart" ,
4652 "parse_header" , "test" , "print_exception" , "print_environ" ,
4753 "print_form" , "print_directory" , "print_arguments" ,
4854 "print_environ_usage" ]
4955
56+
57+ warnings ._deprecated (__name__ , remove = (3 ,13 ))
58+
5059# Logging support
5160# ===============
5261
@@ -77,9 +86,11 @@ def initlog(*allargs):
7786
7887 """
7988 global log , logfile , logfp
89+ warnings .warn ("cgi.log() is deprecated as of 3.10. Use logging instead" ,
90+ DeprecationWarning , stacklevel = 2 )
8091 if logfile and not logfp :
8192 try :
82- logfp = open (logfile , "a" )
93+ logfp = open (logfile , "a" , encoding = "locale" )
8394 except OSError :
8495 pass
8596 if not logfp :
@@ -115,7 +126,8 @@ def closelog():
115126# 0 ==> unlimited input
116127maxlen = 0
117128
118- def parse (fp = None , environ = os .environ , keep_blank_values = 0 , strict_parsing = 0 ):
129+ def parse (fp = None , environ = os .environ , keep_blank_values = 0 ,
130+ strict_parsing = 0 , separator = '&' ):
119131 """Parse a query in the environment or from a file (default stdin)
120132
121133 Arguments, all optional:
@@ -134,6 +146,9 @@ def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0):
134146 strict_parsing: flag indicating what to do with parsing errors.
135147 If false (the default), errors are silently ignored.
136148 If true, errors raise a ValueError exception.
149+
150+ separator: str. The symbol to use for separating the query arguments.
151+ Defaults to &.
137152 """
138153 if fp is None :
139154 fp = sys .stdin
@@ -154,7 +169,7 @@ def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0):
154169 if environ ['REQUEST_METHOD' ] == 'POST' :
155170 ctype , pdict = parse_header (environ ['CONTENT_TYPE' ])
156171 if ctype == 'multipart/form-data' :
157- return parse_multipart (fp , pdict )
172+ return parse_multipart (fp , pdict , separator = separator )
158173 elif ctype == 'application/x-www-form-urlencoded' :
159174 clength = int (environ ['CONTENT_LENGTH' ])
160175 if maxlen and clength > maxlen :
@@ -178,10 +193,10 @@ def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0):
178193 qs = ""
179194 environ ['QUERY_STRING' ] = qs # XXX Shouldn't, really
180195 return urllib .parse .parse_qs (qs , keep_blank_values , strict_parsing ,
181- encoding = encoding )
196+ encoding = encoding , separator = separator )
182197
183198
184- def parse_multipart (fp , pdict , encoding = "utf-8" , errors = "replace" ):
199+ def parse_multipart (fp , pdict , encoding = "utf-8" , errors = "replace" , separator = '&' ):
185200 """Parse multipart input.
186201
187202 Arguments:
@@ -194,15 +209,18 @@ def parse_multipart(fp, pdict, encoding="utf-8", errors="replace"):
194209 value is a list of values for that field. For non-file fields, the value
195210 is a list of strings.
196211 """
197- # RFC 2026 , Section 5.1 : The "multipart" boundary delimiters are always
212+ # RFC 2046 , Section 5.1 : The "multipart" boundary delimiters are always
198213 # represented as 7bit US-ASCII.
199214 boundary = pdict ['boundary' ].decode ('ascii' )
200215 ctype = "multipart/form-data; boundary={}" .format (boundary )
201216 headers = Message ()
202217 headers .set_type (ctype )
203- headers ['Content-Length' ] = pdict ['CONTENT-LENGTH' ]
218+ try :
219+ headers ['Content-Length' ] = pdict ['CONTENT-LENGTH' ]
220+ except KeyError :
221+ pass
204222 fs = FieldStorage (fp , headers = headers , encoding = encoding , errors = errors ,
205- environ = {'REQUEST_METHOD' : 'POST' })
223+ environ = {'REQUEST_METHOD' : 'POST' }, separator = separator )
206224 return {k : fs .getlist (k ) for k in fs }
207225
208226def _parseparam (s ):
@@ -312,7 +330,7 @@ class FieldStorage:
312330 def __init__ (self , fp = None , headers = None , outerboundary = b'' ,
313331 environ = os .environ , keep_blank_values = 0 , strict_parsing = 0 ,
314332 limit = None , encoding = 'utf-8' , errors = 'replace' ,
315- max_num_fields = None ):
333+ max_num_fields = None , separator = '&' ):
316334 """Constructor. Read multipart/* until last part.
317335
318336 Arguments, all optional:
@@ -360,6 +378,7 @@ def __init__(self, fp=None, headers=None, outerboundary=b'',
360378 self .keep_blank_values = keep_blank_values
361379 self .strict_parsing = strict_parsing
362380 self .max_num_fields = max_num_fields
381+ self .separator = separator
363382 if 'REQUEST_METHOD' in environ :
364383 method = environ ['REQUEST_METHOD' ].upper ()
365384 self .qs_on_post = None
@@ -586,7 +605,7 @@ def read_urlencoded(self):
586605 query = urllib .parse .parse_qsl (
587606 qs , self .keep_blank_values , self .strict_parsing ,
588607 encoding = self .encoding , errors = self .errors ,
589- max_num_fields = self .max_num_fields )
608+ max_num_fields = self .max_num_fields , separator = self . separator )
590609 self .list = [MiniFieldStorage (key , value ) for key , value in query ]
591610 self .skip_lines ()
592611
@@ -602,7 +621,7 @@ def read_multi(self, environ, keep_blank_values, strict_parsing):
602621 query = urllib .parse .parse_qsl (
603622 self .qs_on_post , self .keep_blank_values , self .strict_parsing ,
604623 encoding = self .encoding , errors = self .errors ,
605- max_num_fields = self .max_num_fields )
624+ max_num_fields = self .max_num_fields , separator = self . separator )
606625 self .list .extend (MiniFieldStorage (key , value ) for key , value in query )
607626
608627 klass = self .FieldStorageClass or self .__class__
@@ -646,7 +665,7 @@ def read_multi(self, environ, keep_blank_values, strict_parsing):
646665 else self .limit - self .bytes_read
647666 part = klass (self .fp , headers , ib , environ , keep_blank_values ,
648667 strict_parsing , limit ,
649- self .encoding , self .errors , max_num_fields )
668+ self .encoding , self .errors , max_num_fields , self . separator )
650669
651670 if max_num_fields is not None :
652671 max_num_fields -= 1
@@ -736,7 +755,8 @@ def read_lines_to_outerboundary(self):
736755 last_line_lfend = True
737756 _read = 0
738757 while 1 :
739- if self .limit is not None and _read >= self .limit :
758+
759+ if self .limit is not None and 0 <= self .limit <= _read :
740760 break
741761 line = self .fp .readline (1 << 16 ) # bytes
742762 self .bytes_read += len (line )
0 commit comments