diff --git a/Doc/library/cgi.rst b/Doc/library/cgi.rst --- a/Doc/library/cgi.rst +++ b/Doc/library/cgi.rst @@ -152,16 +152,19 @@ return bytes):: if fileitem.file: # It's an uploaded file; count lines linecount = 0 while True: line = fileitem.file.readline() if not line: break linecount = linecount + 1 +:class:`FieldStorage` objects also support being used in a :keyword:`with` +statement, which will automatically close them when done. + If an error is encountered when obtaining the contents of an uploaded file (for example, when the user interrupts the form submission by clicking on a Back or Cancel button) the :attr:`~FieldStorage.done` attribute of the object for the field will be set to the value -1. The file upload draft standard entertains the possibility of uploading multiple files from one field (using a recursive :mimetype:`multipart/\*` encoding). When this occurs, the item will be a dictionary-like :class:`FieldStorage` item. @@ -177,16 +180,20 @@ actually be instances of the class :clas A form submitted via POST that also has a query string will contain both :class:`FieldStorage` and :class:`MiniFieldStorage` items. .. versionchanged:: 3.4 The :attr:`~FieldStorage.file` attribute is automatically closed upon the garbage collection of the creating :class:`FieldStorage` instance. +.. versionchanged:: 3.5 + Added support for the context management protocol to the + :class:`FieldStorage` class. + Higher Level Interface ---------------------- The previous section explains how to read CGI form data using the :class:`FieldStorage` class. This section describes a higher level interface which was added to this class to allow one to do it in a more readable and intuitive way. The interface doesn't make the techniques described in previous diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst --- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -131,16 +131,22 @@ New Modules .. ----------- * None yet. Improved Modules ================ +cgi +--- + +* :class:`FieldStorage` now supports the context management protocol. + (Contributed by Berker Peksag in :issue:`20289`.) + code ---- * The :func:`code.InteractiveInterpreter.showtraceback` method now prints the full chained traceback, just like the interactive interpreter. (Contributed by Claudiu.Popa in :issue:`17442`.) compileall diff --git a/Lib/cgi.py b/Lib/cgi.py --- a/Lib/cgi.py +++ b/Lib/cgi.py @@ -561,16 +561,22 @@ class FieldStorage: self.read_single() def __del__(self): try: self.file.close() except AttributeError: pass + def __enter__(self): + return self + + def __exit__(self, *args): + self.file.close() + def __repr__(self): """Return a printable representation.""" return "FieldStorage(%r, %r, %r)" % ( self.name, self.filename, self.value) def __iter__(self): return iter(self.keys()) diff --git a/Lib/test/test_cgi.py b/Lib/test/test_cgi.py --- a/Lib/test/test_cgi.py +++ b/Lib/test/test_cgi.py @@ -1,9 +1,9 @@ -from test.support import run_unittest, check_warnings +from test.support import check_warnings import cgi import os import sys import tempfile import unittest import warnings from collections import namedtuple from io import StringIO, BytesIO @@ -302,16 +302,27 @@ Content-Type: text/plain self.assertEqual(len(files), 2) expect = [{'name': None, 'filename': 'file1.txt', 'value': b'... contents of file1.txt ...'}, {'name': None, 'filename': 'file2.gif', 'value': b'...contents of file2.gif...'}] for x in range(len(files)): for k, exp in expect[x].items(): got = getattr(files[x], k) self.assertEqual(got, exp) + def test_fieldstorage_as_context_manager(self): + fp = BytesIO(b'x' * 10) + env = {'REQUEST_METHOD': 'PUT'} + with cgi.FieldStorage(fp=fp, environ=env) as fs: + content = fs.file.read() + self.assertFalse(fs.file.closed) + self.assertTrue(fs.file.closed) + self.assertEqual(content, 'x' * 10) + with self.assertRaisesRegex(ValueError, 'I/O operation on closed file'): + fs.file.read() + _qs_result = { 'key1': 'value1', 'key2': ['value2x', 'value2y'], 'key3': 'value3', 'key4': 'value4' } def testQSAndUrlEncode(self): data = "key2=value2x&key3=value3&key4=value4" @@ -476,14 +487,10 @@ Content-Disposition: file; filename="fil Content-Type: image/gif Content-Transfer-Encoding: binary ...contents of file2.gif... --BbC04y-- --AaB03x-- """ - -def test_main(): - run_unittest(CgiTests) - if __name__ == '__main__': - test_main() + unittest.main()