diff --git a/webob/compat.py b/webob/compat.py
index d40ee63..0337ac6 100644
--- a/webob/compat.py
+++ b/webob/compat.py
@@ -131,64 +131,82 @@ else:
     from cgi import escape
 
 
-# We only need this on Python3 but the issue was fixed in Pytohn 3.4.4 and 3.5.
-if PY3 and sys.version_info[:3] < (3, 4, 4):  # pragma no cover
-    # Work around http://bugs.python.org/issue23801
+if PY3:
     import cgi
+    import tempfile
     from cgi import FieldStorage as _cgi_FieldStorage
 
-    class cgi_FieldStorage(_cgi_FieldStorage):
-        # This is taken exactly from Python 3.5's cgi.py module, and patched
-        # with the patch from http://bugs.python.org/issue23801.
-        def read_multi(self, environ, keep_blank_values, strict_parsing):
-            """Internal: read a part that is itself multipart."""
-            ib = self.innerboundary
-            if not cgi.valid_boundary(ib):
-                raise ValueError(
-                    'Invalid boundary in multipart form: %r' % (ib,))
-            self.list = []
-            if self.qs_on_post:
-                query = cgi.urllib.parse.parse_qsl(
-                    self.qs_on_post, self.keep_blank_values,
-                    self.strict_parsing,
-                    encoding=self.encoding, errors=self.errors)
-                for key, value in query:
-                    self.list.append(cgi.MiniFieldStorage(key, value))
-
-            klass = self.FieldStorageClass or self.__class__
-            first_line = self.fp.readline()  # bytes
-            if not isinstance(first_line, bytes):
-                raise ValueError("%s should return bytes, got %s"
-                                 % (self.fp, type(first_line).__name__))
-            self.bytes_read += len(first_line)
-
-            # Ensure that we consume the file until we've hit our innerboundary
-            while (first_line.strip() != (b"--" + self.innerboundary) and
-                    first_line):
-                first_line = self.fp.readline()
+    # Various different FieldStorage work-arounds required on Python 3.x
+
+    if sys.version_info[:3] < (3, 7, 0):  # pragma no cover
+        # Assume that this will be fixed in Python 3.7
+
+        class cgi_FieldStorage(_cgi_FieldStorage):
+
+            # Work around https://bugs.python.org/issue27777
+            def make_file(self):
+                if self._binary_file or self.length >= 0:
+                    return tempfile.TemporaryFile("wb+")
+                else:
+                    return tempfile.TemporaryFile(
+                        "w+",
+                        encoding=self.encoding, newline='\n'
+                    )
+
+            # Work around http://bugs.python.org/issue23801
+            # This is taken exactly from Python 3.5's cgi.py module
+            def read_multi(self, environ, keep_blank_values, strict_parsing):
+                """Internal: read a part that is itself multipart."""
+                ib = self.innerboundary
+                if not cgi.valid_boundary(ib):
+                    raise ValueError(
+                        'Invalid boundary in multipart form: %r' % (ib,))
+                self.list = []
+                if self.qs_on_post:
+                    query = cgi.urllib.parse.parse_qsl(
+                        self.qs_on_post, self.keep_blank_values,
+                        self.strict_parsing,
+                        encoding=self.encoding, errors=self.errors)
+                    for key, value in query:
+                        self.list.append(cgi.MiniFieldStorage(key, value))
+
+                klass = self.FieldStorageClass or self.__class__
+                first_line = self.fp.readline()  # bytes
+                if not isinstance(first_line, bytes):
+                    raise ValueError("%s should return bytes, got %s"
+                                     % (self.fp, type(first_line).__name__))
                 self.bytes_read += len(first_line)
 
-            while True:
-                parser = cgi.FeedParser()
-                hdr_text = b""
+                # Ensure that we consume the file until we've hit our innerboundary
+                while (first_line.strip() != (b"--" + self.innerboundary) and
+                        first_line):
+                    first_line = self.fp.readline()
+                    self.bytes_read += len(first_line)
+
                 while True:
-                    data = self.fp.readline()
-                    hdr_text += data
-                    if not data.strip():
+                    parser = cgi.FeedParser()
+                    hdr_text = b""
+                    while True:
+                        data = self.fp.readline()
+                        hdr_text += data
+                        if not data.strip():
+                            break
+                    if not hdr_text:
+                        break
+                    # parser takes strings, not bytes
+                    self.bytes_read += len(hdr_text)
+                    parser.feed(hdr_text.decode(self.encoding, self.errors))
+                    headers = parser.close()
+                    part = klass(self.fp, headers, ib, environ, keep_blank_values,
+                                 strict_parsing, self.limit-self.bytes_read,
+                                 self.encoding, self.errors)
+                    self.bytes_read += part.bytes_read
+                    self.list.append(part)
+                    if part.done or self.bytes_read >= self.length > 0:
                         break
-                if not hdr_text:
-                    break
-                # parser takes strings, not bytes
-                self.bytes_read += len(hdr_text)
-                parser.feed(hdr_text.decode(self.encoding, self.errors))
-                headers = parser.close()
-                part = klass(self.fp, headers, ib, environ, keep_blank_values,
-                             strict_parsing, self.limit-self.bytes_read,
-                             self.encoding, self.errors)
-                self.bytes_read += part.bytes_read
-                self.list.append(part)
-                if part.done or self.bytes_read >= self.length > 0:
-                    break
-            self.skip_lines()
+                self.skip_lines()
+    else:  # pragma no cover
+        cgi_FieldStorage = _cgi_FieldStorage
+
 else:
     from cgi import FieldStorage as cgi_FieldStorage

Problem with multipart request when content-length is defined for content part (Python 3.4)
The bug occurs with Python 3.4 (see https://bugs.python.org/issue27777).

I will create a PR.

diff --git a/tests/test_compat.py b/tests/test_compat.py
index d995182..321fefc 100644
--- a/tests/test_compat.py
+++ b/tests/test_compat.py
@@ -1,4 +1,8 @@
 import unittest
+from io import BytesIO
+
+import pytest
+import sys
 
 from webob.compat import text_type
 
@@ -46,3 +50,87 @@ class bytes_Tests(unittest.TestCase):
         result = self._callFUT(val, 'utf-8')
         self.assertTrue(isinstance(result, bytes))
         self.assertEqual(result, b'La Pe\xc3\xb1a')
+
+class Test_cgi_FieldStorage_Py3_tests(object):
+
+    def test_fieldstorage_not_multipart(self):
+        from webob.compat import cgi_FieldStorage
+
+        POSTDATA = b'{"name": "Bert"}'
+
+        env = {
+            'REQUEST_METHOD': 'POST',
+            'CONTENT_TYPE': 'text/plain',
+            'CONTENT_LENGTH': str(len(POSTDATA))
+        }
+        fp = BytesIO(POSTDATA)
+        fs = cgi_FieldStorage(fp, environ=env)
+        assert fs.list is None
+        assert fs.value == b'{"name": "Bert"}'
+
+    @pytest.mark.skipif(
+        sys.version_info < (3, 0),
+        reason="FieldStorage on Python 2.7 is broken, see "
+               "https://github.com/Pylons/webob/issues/293"
+    )
+    def test_fieldstorage_part_content_length(self):
+        from webob.compat import cgi_FieldStorage
+        BOUNDARY = "JfISa01"
+        POSTDATA = """--JfISa01
+Content-Disposition: form-data; name="submit-name"
+Content-Length: 5
+
+Larry
+--JfISa01"""
+        env = {
+            'REQUEST_METHOD': 'POST',
+            'CONTENT_TYPE': 'multipart/form-data; boundary={}'.format(BOUNDARY),
+            'CONTENT_LENGTH': str(len(POSTDATA))}
+        fp = BytesIO(POSTDATA.encode('latin-1'))
+        fs = cgi_FieldStorage(fp, environ=env)
+        assert len(fs.list) == 1
+        assert fs.list[0].name == 'submit-name'
+        assert fs.list[0].value == b'Larry'
+
+    def test_fieldstorage_multipart_leading_whitespace(self):
+        from webob.compat import cgi_FieldStorage
+
+        BOUNDARY = "---------------------------721837373350705526688164684"
+        POSTDATA = """-----------------------------721837373350705526688164684
+Content-Disposition: form-data; name="id"
+
+1234
+-----------------------------721837373350705526688164684
+Content-Disposition: form-data; name="title"
+
+
+-----------------------------721837373350705526688164684
+Content-Disposition: form-data; name="file"; filename="test.txt"
+Content-Type: text/plain
+
+Testing 123.
+
+-----------------------------721837373350705526688164684
+Content-Disposition: form-data; name="submit"
+
+ Add\x20
+-----------------------------721837373350705526688164684--
+"""
+
+        env = {
+            'REQUEST_METHOD': 'POST',
+            'CONTENT_TYPE': 'multipart/form-data; boundary={}'.format(BOUNDARY),
+            'CONTENT_LENGTH': '560'}
+        # Add some leading whitespace to our post data that will cause the
+        # first line to not be the innerboundary.
+        fp = BytesIO(b"\r\n" + POSTDATA.encode('latin-1'))
+        fs = cgi_FieldStorage(fp, environ=env)
+        assert len(fs.list) == 4
+        expect = [{'name':'id', 'filename':None, 'value':'1234'},
+                  {'name':'title', 'filename':None, 'value':''},
+                  {'name':'file', 'filename':'test.txt', 'value':b'Testing 123.\n'},
+                  {'name':'submit', 'filename':None, 'value':' Add '}]
+        for x in range(len(fs.list)):
+            for k, exp in expect[x].items():
+                got = getattr(fs.list[x], k)
+                assert got == exp

pip install -e .[testing,docs]
parse_log_pytest
pytest
pytest
3.5
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
alabaster==0.7.13
attrs @ file:///opt/conda/conda-bld/attrs_1642510447205/work
Babel==2.11.0
certifi==2021.5.30
charset-normalizer==2.0.12
coverage==6.2
docutils==0.18.1
idna==3.10
imagesize==1.4.1
importlib-metadata @ file:///tmp/build/80754af9/importlib-metadata_1631916693255/work
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
Jinja2==3.0.3
MarkupSafe==2.0.1
more-itertools @ file:///tmp/build/80754af9/more-itertools_1637733554872/work
packaging @ file:///tmp/build/80754af9/packaging_1637314298585/work
pluggy @ file:///tmp/build/80754af9/pluggy_1615976315926/work
py @ file:///opt/conda/conda-bld/py_1644396412707/work
Pygments==2.14.0
pylons-sphinx-themes==1.0.13
pyparsing @ file:///tmp/build/80754af9/pyparsing_1635766073266/work
pytest==6.2.4
pytest-cov==4.0.0
pytz==2025.2
requests==2.27.1
snowballstemmer==2.2.0
Sphinx==5.3.0
sphinxcontrib-applehelp==1.0.2
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==2.0.0
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.5
toml @ file:///tmp/build/80754af9/toml_1616166611790/work
tomli==1.2.3
typing_extensions @ file:///opt/conda/conda-bld/typing_extensions_1647553014482/work
urllib3==1.26.20
-e git+https://github.com/Pylons/webob.git@5ec5ca2e45b70ff4ee9a2c74c77a04b71d6290fd#egg=WebOb
zipp @ file:///tmp/build/80754af9/zipp_1633618647012/work

name: webob
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - attrs=21.4.0=pyhd3eb1b0_0
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - importlib-metadata=4.8.1=py36h06a4308_0
  - importlib_metadata=4.8.1=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - more-itertools=8.12.0=pyhd3eb1b0_0
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - packaging=21.3=pyhd3eb1b0_0
  - pip=21.2.2=py36h06a4308_0
  - pluggy=0.13.1=py36h06a4308_0
  - py=1.11.0=pyhd3eb1b0_0
  - pyparsing=3.0.4=pyhd3eb1b0_0
  - pytest=6.2.4=py36h06a4308_2
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - toml=0.10.2=pyhd3eb1b0_0
  - typing_extensions=4.1.1=pyh06a4308_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.6.0=pyhd3eb1b0_0
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - alabaster==0.7.13
      - babel==2.11.0
      - charset-normalizer==2.0.12
      - coverage==6.2
      - docutils==0.18.1
      - idna==3.10
      - imagesize==1.4.1
      - jinja2==3.0.3
      - markupsafe==2.0.1
      - pygments==2.14.0
      - pylons-sphinx-themes==1.0.13
      - pytest-cov==4.0.0
      - pytz==2025.2
      - requests==2.27.1
      - snowballstemmer==2.2.0
      - sphinx==5.3.0
      - sphinxcontrib-applehelp==1.0.2
      - sphinxcontrib-devhelp==1.0.2
      - sphinxcontrib-htmlhelp==2.0.0
      - sphinxcontrib-jsmath==1.0.1
      - sphinxcontrib-qthelp==1.0.3
      - sphinxcontrib-serializinghtml==1.1.5
      - tomli==1.2.3
      - urllib3==1.26.20
prefix: /opt/conda/envs/webob

tests/test_compat.py::Test_cgi_FieldStorage_Py3_tests::test_fieldstorage_not_multipart tests/test_compat.py::Test_cgi_FieldStorage_Py3_tests::test_fieldstorage_part_content_length
tests/test_compat.py::text_Tests::test_binary tests/test_compat.py::text_Tests::test_binary_alternate_decoding tests/test_compat.py::text_Tests::test_binary_decoding_error tests/test_compat.py::text_Tests::test_text tests/test_compat.py::bytes_Tests::test_binary tests/test_compat.py::bytes_Tests::test_text tests/test_compat.py::bytes_Tests::test_text_alternate_encoding tests/test_compat.py::Test_cgi_FieldStorage_Py3_tests::test_fieldstorage_multipart_leading_whitespace
webob/compat.py
webob/compat.py