Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-gevent for openSUSE:Factory 
checked in at 2023-09-22 21:46:56
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-gevent (Old)
 and      /work/SRC/openSUSE:Factory/.python-gevent.new.1770 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-gevent"

Fri Sep 22 21:46:56 2023 rev:45 rq:1112068 version:23.9.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-gevent/python-gevent.changes      
2023-08-15 16:39:19.982711585 +0200
+++ /work/SRC/openSUSE:Factory/.python-gevent.new.1770/python-gevent.changes    
2023-09-22 21:47:28.925008396 +0200
@@ -1,0 +2,40 @@
+Mon Sep 18 19:07:56 UTC 2023 - Dirk Müller <[email protected]>
+
+- update to 23.9.0 (CVE-2023-41419):
+  * Make ``gevent.select.select`` accept arbitrary iterables, not
+    just sequences. That is, you can now pass in a generator of file
+    descriptors instead of a realized list. Internally, arbitrary
+    iterables are copied into lists. This better matches what the
+    standard library does.
+  * On Python 3.11 and newer, opt out of Cython's fast exception
+    manipulation, which *may* be causing problems in certain
+    circumstances when combined with greenlets.
+  * On all versions of Python, adjust some error handling in the
+    default * -based loop. This fixes several assertion failures
+    on debug versions of CPython. Hopefully it has a positive
+    impact under real conditions.
+  * Make ``gevent.pywsgi`` comply more closely with the HTTP
+    specification for chunked transfer encoding. In particular,
+    we are much stricter about trailers, and trailers that are
+    invalid (too long or featuring disallowed characters) forcibly
+    close the connection to the client *after* the results have
+    been sent.
+  * Trailers otherwise continue to be ignored and are not
+    available to the WSGI application.
+    Previously, carefully crafted invalid trailers in chunked
+    requests on keep-alive connections might appear as two
+    requests to ``gevent.pywsgi``. Because this was handled
+    exactly as a normal keep-alive connection with two requests,
+    the WSGI application should handle it normally. However, if
+    you were counting on some upstream server to filter incoming
+    requests based on paths or header fields, and the upstream
+    server simply passed trailers through without
+    validating them, then this embedded second request would
+    bypass those checks.
+    (If the upstream server validated that the trailers
+    meet the* HTTP specification, this could not occur,
+    because characters that are required in an HTTP request,
+    like a space, are not allowed in trailers.) CVE-2023-41419
+    was reserved for this.
+
+-------------------------------------------------------------------

Old:
----
  gevent-23.7.0.tar.gz

New:
----
  gevent-23.9.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-gevent.spec ++++++
--- /var/tmp/diff_new_pack.bLbV5Z/_old  2023-09-22 21:47:30.821077229 +0200
+++ /var/tmp/diff_new_pack.bLbV5Z/_new  2023-09-22 21:47:30.821077229 +0200
@@ -25,7 +25,7 @@
 %endif
 %{?sle15_python_module_pythons}
 Name:           python-gevent
-Version:        23.7.0
+Version:        23.9.0
 Release:        0
 Summary:        Python network library that uses greenlet and libevent
 License:        MIT

++++++ gevent-23.7.0.tar.gz -> gevent-23.9.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gevent-23.7.0/.github/workflows/ci.yml 
new/gevent-23.9.0/.github/workflows/ci.yml
--- old/gevent-23.7.0/.github/workflows/ci.yml  2023-07-11 17:34:08.000000000 
+0200
+++ new/gevent-23.9.0/.github/workflows/ci.yml  2023-09-02 01:22:30.000000000 
+0200
@@ -83,18 +83,19 @@
         # 3.10+ needs more work: dnspython for example doesn't work
         # with it. That means for the bulk of our testing we need to
         # stick to 3.9.
-        python-version: ["pypy-3.10", "3.12-dev", 3.8, 3.9, '3.10', '3.11']
+        python-version: ["3.12-dev", "pypy-3.10", 3.8, 3.9, '3.10', '3.11']
         os: [macos-latest, ubuntu-latest]
         exclude:
           # The bulk of the testing is on Linux and Windows (appveyor).
           # Experience shows that it's sufficient to only test the latest
-          # version on macOS.
-          - os: macos-latest
-            python-version: 3.8
-          - os: macos-latest
-            python-version: 3.9
-          - os: macos-latest
-            python-version: 3.10
+          # version on macOS. However, that does mean you need to
+          # manually upload macOS wheels for those versions.
+          # - os: macos-latest
+          #   python-version: 3.8
+          # - os: macos-latest
+          #   python-version: 3.9
+          # - os: macos-latest
+          #   python-version: 3.10
           - os: macos-latest
             python-version: "pypy-3.10"
 
@@ -169,8 +170,10 @@
           pip install -U pip
           pip install -U -q setuptools wheel twine
           pip install -q -U 'cffi;platform_python_implementation=="CPython"'
-          pip install -q -U 'cython>=3.0b3; python_version < "3.12"' "Cython @ 
https://github.com/cython/cython/archive/37f4dcdc04547875e2836fda076f5707ec50e579.zip;
 python_version >= '3.12'"
-          pip install 'greenlet>=2.0.0 
;platform_python_implementation=="CPython"' 'greenlet >= 3.0a1; python_version 
>="3.12"'
+          pip install -q -U 'cython>=3.0.2'
+          # Use a debug version of greenlet to help catch any errors earlier.
+          CFLAGS="$CFLAGS -Og -g -UNDEBUG" pip install -v --no-binary :all: 
'greenlet>=2.0.0;platform_python_implementation=="CPython" and python_version < 
"3.12"'
+          CFLAGS="$CFLAGS -Og -g -UNDEBUG" pip install -v --no-binary :all: 
'greenlet>=3.0rc1;platform_python_implementation=="CPython" and python_version 
>= "3.12"'
 
       - name: Build gevent (non-Mac)
         if: ${{ ! startsWith(runner.os, 'Mac') }}
@@ -193,6 +196,13 @@
           # output (pip install uses a random temporary directory, making this 
difficult)
           python setup.py build_ext -i
           python setup.py bdist_wheel
+          # Something in the build system isn't detecting that we're building 
for both,
+          # so we're getting tagged with just x86_64. Force the universal2 tag.
+          # (I've verified that the .so files are in fact universal, with both 
architectures.)
+          #wheel tags --abi-tag universal2 dist/*whl
+          # XXX: That can produce invalid filenames, for some reason. 3.11 
came up with
+          # gevent-23.7.1.dev0-cp311-universal2-macosx_10_9_universal2.whl, 
which is not valid.
+          # It's not clear why, because greenlet didn't do that. Maybe because 
it was already universal?
         env:
           # Unlike the above, we are actually distributing these
           # wheels, so they need to be built for production use.
@@ -202,7 +212,7 @@
       - name: Check gevent build
         run: |
           ls -l dist
-          twine check dist/*
+          twine check dist/*whl
       - name: Upload gevent wheel
         uses: actions/upload-artifact@v2
         with:
@@ -240,6 +250,7 @@
       # it's sufficient to run the full suite on the current version
       # and oldest version.
       - name: "Tests: subproccess and FileObjectThread"
+        if: startsWith(runner.os, 'Linux') || (startsWith(runner.os, 'Mac') && 
matrix.python-version == '3.12-dev')
         # Now, the non-default threaded file object.
         # In the past, we included all test files that had a reference to 
'subprocess'' somewhere in their
         # text. The monkey-patched stdlib tests were specifically included 
here.
@@ -378,7 +389,7 @@
           pip install -U pip
           pip install -U -q setuptools wheel twine
           pip install -q -U 'cffi;platform_python_implementation=="CPython"'
-          pip install -q -U 'cython>=3.0b3; python_version < "3.12"' "Cython @ 
https://github.com/cython/cython/archive/37f4dcdc04547875e2836fda076f5707ec50e579.zip;
 python_version >= '3.12'"
+          pip install -q -U 'cython>=3.0'
           pip install 'greenlet>=2.0.0; 
platform_python_implementation=="CPython"'
 
       - name: build libs and gevent
@@ -435,7 +446,7 @@
           - manylinux2014_aarch64
           - manylinux2014_ppc64le
           - manylinux2014_s390x
-          - manylinux2014_x86_64
+          - manylinux_2_28_x86_64
           - musllinux_1_1_x86_64
           - musllinux_1_1_aarch64
     name: ${{ matrix.image }}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gevent-23.7.0/CHANGES.rst 
new/gevent-23.9.0/CHANGES.rst
--- old/gevent-23.7.0/CHANGES.rst       2023-07-11 17:34:08.000000000 +0200
+++ new/gevent-23.9.0/CHANGES.rst       2023-09-02 01:22:30.000000000 +0200
@@ -6,6 +6,59 @@
 
 .. towncrier release notes start
 
+23.9.0 (2023-09-01)
+===================
+
+
+Bugfixes
+--------
+
+- Make ``gevent.select.select`` accept arbitrary iterables, not just
+  sequences. That is, you can now pass in a generator of file
+  descriptors instead of a realized list. Internally, arbitrary
+  iterables are copied into lists. This better matches what the standard
+  library does. Thanks to David Salvisberg.
+  See :issue:`1979`.
+- On Python 3.11 and newer, opt out of Cython's fast exception
+  manipulation, which *may* be causing problems in certain circumstances
+  when combined with greenlets.
+
+  On all versions of Python, adjust some error handling in the default
+  C-based loop. This fixes several assertion failures on debug versions
+  of CPython. Hopefully it has a positive impact under real conditions.
+  See :issue:`1985`.
+- Make ``gevent.pywsgi`` comply more closely with the HTTP specification
+  for chunked transfer encoding. In particular, we are much stricter
+  about trailers, and trailers that are invalid (too long or featuring
+  disallowed characters) forcibly close the connection to the client
+  *after* the results have been sent.
+
+  Trailers otherwise continue to be ignored and are not available to the
+  WSGI application.
+
+  Previously, carefully crafted invalid trailers in chunked requests on
+  keep-alive connections might appear as two requests to
+  ``gevent.pywsgi``. Because this was handled exactly as a normal
+  keep-alive connection with two requests, the WSGI application should
+  handle it normally. However, if you were counting on some upstream
+  server to filter incoming requests based on paths or header fields,
+  and the upstream server simply passed trailers through without
+  validating them, then this embedded second request would bypass those
+  checks. (If the upstream server validated that the trailers meet the
+  HTTP specification, this could not occur, because characters that are
+  required in an HTTP request, like a space, are not allowed in
+  trailers.) CVE-2023-41419 was reserved for this.
+
+  Our thanks to the original reporters, Keran Mu
+  ([email protected]) and Jianjun Chen
+  ([email protected]), from Tsinghua University and Zhongguancun
+  Laboratory.
+  See :issue:`1989`.
+
+
+----
+
+
 23.7.0 (2023-07-11)
 ===================
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gevent-23.7.0/_setuputils.py 
new/gevent-23.9.0/_setuputils.py
--- old/gevent-23.7.0/_setuputils.py    2023-07-11 17:34:08.000000000 +0200
+++ new/gevent-23.9.0/_setuputils.py    2023-09-02 01:22:30.000000000 +0200
@@ -23,6 +23,9 @@
 
 PYPY = hasattr(sys, 'pypy_version_info')
 WIN = sys.platform.startswith('win')
+PY311 = sys.version_info[:2] >= (3, 11)
+PY312 = sys.version_info[:2] >= (3, 12)
+
 
 RUNNING_ON_TRAVIS = os.environ.get('TRAVIS')
 RUNNING_ON_APPVEYOR = os.environ.get('APPVEYOR')
@@ -233,6 +236,15 @@
         # This is for generated include files; see below.
         '.',
     ]
+    if PY311:
+        # The "fast" code is Cython for manipulating
+        # exceptions is, unfortunately, broken, at least in 3.0.2.
+        # The implementation of __Pyx__GetException() doesn't properly set
+        # tstate->current_exception when it normalizes exceptions,
+        # causing assertion errors.
+        # This definitely seems to be a problem on 3.12, and MAY
+        # be a problem on 3.11 (#1985)
+        ext.define_macros.append(('CYTHON_FAST_THREAD_STATE', '0'))
     try:
         new_ext = cythonize(
             [ext],
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gevent-23.7.0/appveyor.yml 
new/gevent-23.9.0/appveyor.yml
--- old/gevent-23.7.0/appveyor.yml      2023-07-11 17:34:08.000000000 +0200
+++ new/gevent-23.9.0/appveyor.yml      2023-09-02 01:22:30.000000000 +0200
@@ -44,12 +44,12 @@
 
     # Pre-installed Python versions, which Appveyor may upgrade to
     # a later point release.
-    # XXX: Cython 3.0b3 won't build us on 3.12 yet.
-    # - PYTHON: "C:\\Python312-x64"
-    #   PYTHON_VERSION: "3.12.0b3"
-    #   PYTHON_ARCH: "64"
-    #   PYTHON_EXE: python
-    #   APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
+
+    - PYTHON: "C:\\Python312-x64"
+      PYTHON_VERSION: "3.12.0b4"
+      PYTHON_ARCH: "64"
+      PYTHON_EXE: python
+      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
 
     # 64-bit
     - PYTHON: "C:\\Python311-x64"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gevent-23.7.0/docs/servers.rst 
new/gevent-23.9.0/docs/servers.rst
--- old/gevent-23.7.0/docs/servers.rst  2023-07-11 17:34:08.000000000 +0200
+++ new/gevent-23.9.0/docs/servers.rst  2023-09-02 01:22:30.000000000 +0200
@@ -43,6 +43,13 @@
 :class:`WSGI server <gevent.pywsgi.WSGIServer>`. In addition,
 gunicorn_ is a stand-alone server that supports gevent.
 
+.. important::
+
+   The provided server implementations are intended primarily for
+   development and testing, or internal usage, and otherwise only
+   generally "safe" scenarios. They have not been security audited.
+   Expose them to the public Internet at your own risk.
+
 API Reference
 =============
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gevent-23.7.0/pyproject.toml 
new/gevent-23.9.0/pyproject.toml
--- old/gevent-23.7.0/pyproject.toml    2023-07-11 17:34:08.000000000 +0200
+++ new/gevent-23.9.0/pyproject.toml    2023-09-02 01:22:30.000000000 +0200
@@ -17,18 +17,8 @@
      # This was fixed in 3.0a5 (https://github.com/cython/cython/issues/3578)
      # 3.0a6 fixes an issue cythonizing source on 32-bit platforms.
      # 3.0a9 is needed for Python 3.10.
-     # Python 3.12 requires a snapshot at this writing (2023-06-29). This is 
just before
-     # 3.0rc1
-     # "Cython @ 
https://github.com/cython/cython/archive/37f4dcdc04547875e2836fda076f5707ec50e579.zip";,
-     # Unfortunately, markers like python_version do not work for remote 
installs,
-     # so we always install that, even if we then provide a different 
requirement where
-     # markers DO work. This is a problem beacuse the snapshot cannot be 
compiled on Appveyor.
-     # That's OK, because our tests.yml and make-manylinux install this stuff 
manually where markers
-     # DO work. It just means that we cannot be built from a sdist on Python 
3.12 without
-     # --no-build-isolation or using setup.py directly; but as soon as the 
Cython version is released,
-     # we can be.
-
-     "Cython >= 3.0b3",
+     # Python 3.12 requires at least 3.0rc2.
+     "Cython >= 3.0.2",
      # See version requirements in setup.py
      "cffi >= 1.12.3 ; platform_python_implementation == 'CPython'",
      # Python 3.7 requires at least 0.4.14, which is ABI incompatible with 
earlier
@@ -40,7 +30,7 @@
      # 3.0 is ABI compatible with earlier releases, so we can switch back and
      # forth between 2 and 3 without recompiling. 3.0 is required for
      # Python 3.12
-     "greenlet >= 3.0a1 ; platform_python_implementation == 'CPython'",
+     "greenlet >= 3.0rc1 ; platform_python_implementation == 'CPython'",
 ]
 
 [tool.towncrier]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gevent-23.7.0/scripts/releases/geventrel.sh 
new/gevent-23.9.0/scripts/releases/geventrel.sh
--- old/gevent-23.7.0/scripts/releases/geventrel.sh     2023-07-11 
17:34:08.000000000 +0200
+++ new/gevent-23.9.0/scripts/releases/geventrel.sh     2023-09-02 
01:22:30.000000000 +0200
@@ -19,7 +19,8 @@
 # because it's not available on earlier releases and leads to
 # segfaults because the symbol clock_gettime is NULL.
 # See https://github.com/gevent/gevent/issues/916
-export CPPFLAGS="-D_DARWIN_FEATURE_CLOCK_GETTIME=0"
+#export CPPFLAGS="-D_DARWIN_FEATURE_CLOCK_GETTIME=0"
+export ARCHFLAGS="-arch arm64 -arch x86_64"
 
 BASE=`pwd`/../../
 BASE=`greadlink -f $BASE`
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gevent-23.7.0/scripts/releases/geventreleases.sh 
new/gevent-23.9.0/scripts/releases/geventreleases.sh
--- old/gevent-23.7.0/scripts/releases/geventreleases.sh        2023-07-11 
17:34:08.000000000 +0200
+++ new/gevent-23.9.0/scripts/releases/geventreleases.sh        2023-09-02 
01:22:30.000000000 +0200
@@ -7,22 +7,10 @@
 mkdir /tmp/gevent/
 
 
-# 2.7 is a python.org build, builds a 10_6_intel wheel
-./geventrel.sh /usr/local/bin/python2.7
-
-# 3.5 is a python.org build, builds a 10_6_intel wheel
-./geventrel.sh /usr/local/bin/python3.5
-
-# 3.6 is a python.org build, builds a 10_6_intel wheel
-./geventrel.sh /usr/local/bin/python3.6
-
-# 3.7 is a python.org build, builds a 10_6_intel wheel
-./geventrel.sh /usr/local/bin/python3.7
 ./geventrel.sh /usr/local/bin/python3.8
 ./geventrel.sh /usr/local/bin/python3.9
-
-
-# PyPy 4.0
-./geventrel.sh `which pypy`
+./geventrel.sh /usr/local/bin/python3.10
+./geventrel.sh /usr/local/bin/python3.11
+./geventrel.sh /usr/local/bin/python3.12
 
 wait
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gevent-23.7.0/scripts/releases/make-manylinux 
new/gevent-23.9.0/scripts/releases/make-manylinux
--- old/gevent-23.7.0/scripts/releases/make-manylinux   2023-07-11 
17:34:08.000000000 +0200
+++ new/gevent-23.9.0/scripts/releases/make-manylinux   2023-09-02 
01:22:30.000000000 +0200
@@ -155,7 +155,7 @@
 
     # Start echoing commands (doing it earlier is too much)
     set -x
-    for variant in /opt/python/cp{312,38,39,310,311,312}*; do
+    for variant in /opt/python/cp{312,38,39,310,311}*; do
         echo $SEP
         export PATH="$variant/bin:$OPATH"
         if [ -n "$SLOW_BUILD" ]; then
@@ -172,8 +172,7 @@
         # The downside is that we must install dependencies manually.
         # NOTE: We can't upgrade ``wheel`` because ``auditwheel`` depends on
         # it, and auditwheel is installed in one of these environments.
-        time python -m pip install  -U 'cython>=3.0b3; python_version < "3.12"'
-        time python -m pip install -v -U "Cython @ 
https://github.com/cython/cython/archive/37f4dcdc04547875e2836fda076f5707ec50e579.zip;
 python_version >= '3.12'"
+        time python -m pip install  -U 'cython>=3.0'
         time python -mpip install -U cffi 'greenlet >= 2.0.0; python_version < 
"3.12"' 'greenlet >= 3.0a1; python_version >= "3.12"' setuptools
         echo "$variant: Building wheel"
         time (python setup.py bdist_wheel)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gevent-23.7.0/setup.py new/gevent-23.9.0/setup.py
--- old/gevent-23.7.0/setup.py  2023-07-11 17:34:08.000000000 +0200
+++ new/gevent-23.9.0/setup.py  2023-09-02 01:22:30.000000000 +0200
@@ -211,7 +211,7 @@
     # 3.0 is ABI compatible and adds support for Python 3.12 (but right
     # now it's alpha because of Cython, so we only require it on 3.12)
     'greenlet >= 2.0.0 ; platform_python_implementation=="CPython" and 
python_version < "3.12"',
-    'greenlet >= 3.0a1 ; platform_python_implementation=="CPython" and 
python_version >= "3.12"',
+    'greenlet >= 3.0rc1 ; platform_python_implementation=="CPython" and 
python_version >= "3.12"',
 ]
 
 # Note that we don't add cffi to install_requires, it's
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gevent-23.7.0/src/gevent/__init__.py 
new/gevent-23.9.0/src/gevent/__init__.py
--- old/gevent-23.7.0/src/gevent/__init__.py    2023-07-11 17:34:08.000000000 
+0200
+++ new/gevent-23.9.0/src/gevent/__init__.py    2023-09-02 01:22:30.000000000 
+0200
@@ -27,7 +27,7 @@
 #: Use ``pkg_resources.parse_version(__version__)`` or
 #: ``packaging.version.Version(__version__)`` to get a machine-usable
 #: value.
-__version__ = '23.7.0'
+__version__ = '23.9.0'
 
 
 __all__ = [
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gevent-23.7.0/src/gevent/_compat.h 
new/gevent-23.9.0/src/gevent/_compat.h
--- old/gevent-23.7.0/src/gevent/_compat.h      2023-07-11 17:34:08.000000000 
+0200
+++ new/gevent-23.9.0/src/gevent/_compat.h      2023-09-02 01:22:30.000000000 
+0200
@@ -24,13 +24,9 @@
 #  define _PyCFrame CFrame
 #endif
 
-/* FrameType and CodeType changed a lot in 3.11. */
-#if GREENLET_PY311
-   /* _PyInterpreterFrame moved to the internal C API in Python 3.11 */
-#  include <internal/pycore_frame.h>
-#else
 #include <frameobject.h>
-#if PY_MAJOR_VERSION < 3 || (PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION < 9)
+
+#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION < 9
 /* these were added in 3.9, though they officially became stable in 3.10 */
 /* the official versions of these functions return strong references, so we
    need to increment the refcount before returning, not just to match the
@@ -51,7 +47,7 @@
     return result;
 }
 #endif /* support 3.8 and below. */
-#endif
+
 
 /**
    Unlike PyFrame_GetBack, which can return NULL,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gevent-23.7.0/src/gevent/_compat.py 
new/gevent-23.9.0/src/gevent/_compat.py
--- old/gevent-23.7.0/src/gevent/_compat.py     2023-07-11 17:34:08.000000000 
+0200
+++ new/gevent-23.9.0/src/gevent/_compat.py     2023-09-02 01:22:30.000000000 
+0200
@@ -14,6 +14,7 @@
 
 PY39 = sys.version_info[:2] >= (3, 9)
 PY311 = sys.version_info[:2] >= (3, 11)
+PY312 = sys.version_info[:2] >= (3, 11)
 PYPY = hasattr(sys, 'pypy_version_info')
 WIN = sys.platform.startswith("win")
 LINUX = sys.platform.startswith('linux')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gevent-23.7.0/src/gevent/libev/callbacks.c 
new/gevent-23.9.0/src/gevent/libev/callbacks.c
--- old/gevent-23.7.0/src/gevent/libev/callbacks.c      2023-07-11 
17:34:08.000000000 +0200
+++ new/gevent-23.9.0/src/gevent/libev/callbacks.c      2023-09-02 
01:22:30.000000000 +0200
@@ -39,13 +39,16 @@
 #define GGIL_RELEASE  PyGILState_Release(___save);
 
 
-static CYTHON_INLINE void gevent_check_signals(struct PyGeventLoopObject* 
loop) {
+static CYTHON_INLINE void gevent_check_signals(struct PyGeventLoopObject* loop)
+{
     if (!ev_is_default_loop(loop->_ptr)) {
         /* only reporting signals on the default loop */
         return;
     }
     PyErr_CheckSignals();
-    if (PyErr_Occurred()) gevent_handle_error(loop, Py_None);
+    if (PyErr_Occurred()) {
+        gevent_handle_error(loop, Py_None);
+    }
 }
 
 #define GET_OBJECT(PY_TYPE, EV_PTR, MEMBER) \
@@ -54,7 +57,8 @@
 
 void gevent_noop(struct ev_loop* loop, void* watcher, int revents) {}
 
-static void gevent_stop(PyObject* watcher, struct PyGeventLoopObject* loop) {
+static void gevent_stop(PyObject* watcher, struct PyGeventLoopObject* loop)
+{
     PyObject *result, *method;
     int error;
     error = 1;
@@ -68,12 +72,14 @@
         Py_DECREF(method);
     }
     if (error) {
+        assert(PyErr_Occurred());
         gevent_handle_error(loop, watcher);
     }
 }
 
 
-static void gevent_callback(struct PyGeventLoopObject* loop, PyObject* 
callback, PyObject* args, PyObject* watcher, void *c_watcher, int revents) {
+static void gevent_callback(struct PyGeventLoopObject* loop, PyObject* 
callback, PyObject* args, PyObject* watcher, void *c_watcher, int revents)
+{
     GGIL_DECLARE;
     PyObject *result, *py_events;
     long length;
@@ -89,6 +95,8 @@
     }
     length = PyTuple_Size(args);
     if (length < 0) {
+        /* returns -1 and sets an error if args isn't a tuple. */
+        assert(PyErr_Occurred());
         gevent_handle_error(loop, watcher);
         goto end;
     }
@@ -108,6 +116,7 @@
         Py_DECREF(result);
     }
     else {
+        assert(PyErr_Occurred());
         gevent_handle_error(loop, watcher);
         if (revents & (EV_READ|EV_WRITE)) {
             /* io watcher: not stopping it may cause the failing callback to 
be called repeatedly */
@@ -134,7 +143,8 @@
 }
 
 
-void gevent_call(struct PyGeventLoopObject* loop, struct 
PyGeventCallbackObject* cb) {
+void gevent_call(struct PyGeventLoopObject* loop, struct 
PyGeventCallbackObject* cb)
+{
     /* no need for GIL here because it is only called from run_callbacks which 
already has GIL */
     PyObject *result, *callback, *args;
     if (!loop || !cb)
@@ -152,12 +162,29 @@
     Py_INCREF(Py_None);
     Py_DECREF(cb->callback);
     cb->callback = Py_None;
-
+    /*
+      you are not allowed to use PyObject_Call() with an error pending;
+      debug builds crash with an assertion.
+      How do we get here with an error pending? good question. It's been
+      seen in 3.12 before we stopped using CYTHON_FAST_THREAD_STATE.
+      Hopefully changing that makes this dead code.
+    */
+    if (PyErr_Occurred()) {
+        /* by now, cb->callback is None, so using it as the context doesn't
+           produce a useful output. The callable object is more helpful.
+           Writing unraisable clears the error, unless it gets an error of
+           its own.
+        */
+        PyErr_WriteUnraisable(callback);
+        PyErr_Clear();
+    }
+    assert(!PyErr_Occurred());
     result = PyObject_Call(callback, args, NULL);
     if (result) {
         Py_DECREF(result);
     }
     else {
+        assert(PyErr_Occurred());
         gevent_handle_error(loop, (PyObject*)cb);
     }
 
@@ -200,6 +227,14 @@
         Py_DECREF(result);
     }
     else {
+        /*
+         For reasons that are unclear, PyErr_WriteUnraisable, which invokes
+         sys.unraisablehook, isn't safe to call here, at least in some cases.
+         test__pool.TestCoroutinePool.test_stderr_raising fails its timeout
+         if we use that. Instead, we use PyErr_Print, which doesn't have any
+         context, but doesn't hang either. It calls sys.excepthook.
+         */
+
         PyErr_Print();
         PyErr_Clear();
     }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gevent-23.7.0/src/gevent/libev/corecext.pyx 
new/gevent-23.9.0/src/gevent/libev/corecext.pyx
--- old/gevent-23.7.0/src/gevent/libev/corecext.pyx     2023-07-11 
17:34:08.000000000 +0200
+++ new/gevent-23.9.0/src/gevent/libev/corecext.pyx     2023-09-02 
01:22:30.000000000 +0200
@@ -34,6 +34,10 @@
 from cpython.exc cimport PyErr_NormalizeException
 from cpython.exc cimport PyErr_WriteUnraisable
 from libc.errno cimport errno
+from cpython cimport PyErr_Fetch
+from cpython cimport PyErr_Occurred
+from cpython cimport PyObject
+from cpython cimport PyErr_Clear
 
 cdef extern from "Python.h":
     int    Py_ReprEnter(object)
@@ -199,10 +203,9 @@
     return result
 
 
-if sys.version_info[0] >= 3:
-    basestring = (bytes, str)
-else:
-    basestring = __builtins__.basestring
+
+basestring = (bytes, str)
+
 
 
 cpdef unsigned int _flags_to_int(object flags) except? -1:
@@ -483,19 +486,29 @@
         self.starting_timer_may_update_loop_time = True
         cdef libev.ev_tstamp now = libev.ev_now(self._ptr)
         cdef libev.ev_tstamp expiration = now + 
<libev.ev_tstamp>getswitchinterval()
-
+        cdef object cb_callable # for printing later
+        assert not PyErr_Occurred()
         try:
             libev.ev_timer_stop(self._ptr, &self._timer0)
             while self._callbacks.head is not None:
                 cb = self._callbacks.popleft()
-
                 libev.ev_unref(self._ptr)
                 # On entry, this will set cb.callback to None,
                 # changing cb.pending from True to False; on exit,
                 # this will set cb.args to None, changing bool(cb)
                 # from True to False.
                 # XXX: Why is this a C callback, not cython?
+                cb_callable = cb.callback
                 gevent_call(self, cb)
+                if PyErr_Occurred():
+                    # Exceptions should not escape gevent_call,
+                    # but just in case...
+                    # note we don't use gevent_handle_error here, between
+                    # running callbacks is a fairly fragile state and
+                    # that directs back up to the hub and user code.
+                    PyErr_WriteUnraisable(cb_callable)
+                    PyErr_Clear()
+                cb_callable = None
                 count -= 1
 
                 if count == 0 and self._callbacks.head is not None:
@@ -1377,8 +1390,7 @@
 
 # Things used in callbacks.c
 
-from cpython cimport PyErr_Fetch
-from cpython cimport PyObject
+from traceback import print_exception
 
 cdef public void gevent_handle_error(loop loop, object context):
     cdef PyObject* typep
@@ -1415,9 +1427,7 @@
         traceback = <object>tracebackp
         Py_DECREF(traceback)
 
-    # If this method fails by raising an exception,
-    # cython will print it for us because we don't return a
-    # Python object and we don't declare an `except` clause.
+
     # Prior to Cython 3.<something>, we relied on Cython printing an
     # uncaught exception here (because we don't return a Python object, and
     # we have no except clause). It seems that as-of 3.0b3 at least,
@@ -1426,7 +1436,17 @@
     try:
         loop.handle_error(context, type, value, traceback)
     except:
-        PyErr_WriteUnraisable(context)
+        # In an except: block, PyErr_Occurred() is actually false.
+        # Cython has captured the exception and moved it around. The
+        # exc_info is available at the python level, but
+        # the C level APIs aren't going to work. In debug builds,
+        # PyErr_WriteUnraisable will crash with an assertion.
+        #
+        # It would be nice to call ``sys.unraisablehook``, but the default
+        # implementation of that requires that the argument be of
+        # a specific private type we cannot construct.
+        print_exception(*sys.exc_info())
+
 
 cdef public tuple _empty_tuple = ()
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gevent-23.7.0/src/gevent/pywsgi.py 
new/gevent-23.9.0/src/gevent/pywsgi.py
--- old/gevent-23.7.0/src/gevent/pywsgi.py      2023-07-11 17:34:08.000000000 
+0200
+++ new/gevent-23.9.0/src/gevent/pywsgi.py      2023-09-02 01:22:30.000000000 
+0200
@@ -1,13 +1,32 @@
 # Copyright (c) 2005-2009, eventlet contributors
 # Copyright (c) 2009-2018, gevent contributors
 """
-A pure-Python, gevent-friendly WSGI server.
+A pure-Python, gevent-friendly WSGI server implementing HTTP/1.1.
 
 The server is provided in :class:`WSGIServer`, but most of the actual
 WSGI work is handled by :class:`WSGIHandler` --- a new instance is
 created for each request. The server can be customized to use
 different subclasses of :class:`WSGIHandler`.
 
+.. important::
+
+   This server is intended primarily for development and testing, and
+   secondarily for other "safe" scenarios where it will not be exposed to
+   potentially malicious input. The code has not been security audited,
+   and is not intended for direct exposure to the public Internet. For 
production
+   usage on the Internet, either choose a production-strength server such as
+   gunicorn, or put a reverse proxy between gevent and the Internet.
+
+.. versionchanged:: 23.9.0
+
+   Complies more closely with the HTTP specification for chunked transfer 
encoding.
+   In particular, we are much stricter about trailers, and trailers that
+   are invalid (too long or featuring disallowed characters) forcibly close
+   the connection to the client *after* the results have been sent.
+
+   Trailers otherwise continue to be ignored and are not available to the
+   WSGI application.
+
 """
 from __future__ import absolute_import
 
@@ -22,10 +41,7 @@
 import traceback
 from datetime import datetime
 
-try:
-    from urllib import unquote
-except ImportError:
-    from urllib.parse import unquote # python 2 
pylint:disable=import-error,no-name-in-module
+from urllib.parse import unquote
 
 from gevent import socket
 import gevent
@@ -50,29 +66,52 @@
 
 MAX_REQUEST_LINE = 8192
 # Weekday and month names for HTTP date/time formatting; always English!
-_WEEKDAYNAME = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
-_MONTHNAME = [None,  # Dummy so we can use 1-based month numbers
+_WEEKDAYNAME = ("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")
+_MONTHNAME = (None,  # Dummy so we can use 1-based month numbers
               "Jan", "Feb", "Mar", "Apr", "May", "Jun",
-              "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
+              "Jul", "Aug", "Sep", "Oct", "Nov", "Dec")
 
 # The contents of the "HEX" grammar rule for HTTP, upper and lowercase A-F 
plus digits,
 # in byte form for comparing to the network.
 _HEX = string.hexdigits.encode('ascii')
 
+# The characters allowed in "token" rules.
+
+# token          = 1*tchar
+# tchar          = "!" / "#" / "$" / "%" / "&" / "'" / "*"
+#                / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
+#                / DIGIT / ALPHA
+#                ; any VCHAR, except delimiters
+# ALPHA          =  %x41-5A / %x61-7A   ; A-Z / a-z
+_ALLOWED_TOKEN_CHARS = frozenset(
+    # Remember we have to be careful because bytestrings
+    # inexplicably iterate as integers, which are not equal to bytes.
+
+    # explicit chars then DIGIT
+    (c.encode('ascii') for c in "!#$%&'*+-.^_`|~0123456789")
+    # Then we add ALPHA
+) | {c.encode('ascii') for c in string.ascii_letters}
+assert b'A' in _ALLOWED_TOKEN_CHARS
+
+
 # Errors
 _ERRORS = {}
 _INTERNAL_ERROR_STATUS = '500 Internal Server Error'
 _INTERNAL_ERROR_BODY = b'Internal Server Error'
-_INTERNAL_ERROR_HEADERS = [('Content-Type', 'text/plain'),
-                           ('Connection', 'close'),
-                           ('Content-Length', str(len(_INTERNAL_ERROR_BODY)))]
+_INTERNAL_ERROR_HEADERS = (
+    ('Content-Type', 'text/plain'),
+    ('Connection', 'close'),
+    ('Content-Length', str(len(_INTERNAL_ERROR_BODY)))
+)
 _ERRORS[500] = (_INTERNAL_ERROR_STATUS, _INTERNAL_ERROR_HEADERS, 
_INTERNAL_ERROR_BODY)
 
 _BAD_REQUEST_STATUS = '400 Bad Request'
 _BAD_REQUEST_BODY = ''
-_BAD_REQUEST_HEADERS = [('Content-Type', 'text/plain'),
-                        ('Connection', 'close'),
-                        ('Content-Length', str(len(_BAD_REQUEST_BODY)))]
+_BAD_REQUEST_HEADERS = (
+    ('Content-Type', 'text/plain'),
+    ('Connection', 'close'),
+    ('Content-Length', str(len(_BAD_REQUEST_BODY)))
+)
 _ERRORS[400] = (_BAD_REQUEST_STATUS, _BAD_REQUEST_HEADERS, _BAD_REQUEST_BODY)
 
 _REQUEST_TOO_LONG_RESPONSE = b"HTTP/1.1 414 Request URI Too 
Long\r\nConnection: close\r\nContent-length: 0\r\n\r\n"
@@ -200,23 +239,37 @@
         # Read and return the next integer chunk length. If no
         # chunk length can be read, raises _InvalidClientInput.
 
-        # Here's the production for a chunk:
-        # (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html)
-        #   chunk          = chunk-size [ chunk-extension ] CRLF
-        #                    chunk-data CRLF
-        #   chunk-size     = 1*HEX
-        #   chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
-        #   chunk-ext-name = token
-        #   chunk-ext-val  = token | quoted-string
-
-        # To cope with malicious or broken clients that fail to send valid
-        # chunk lines, the strategy is to read character by character until we 
either reach
-        # a ; or newline. If at any time we read a non-HEX digit, we bail. If 
we hit a
-        # ;, indicating an chunk-extension, we'll read up to the next
-        # MAX_REQUEST_LINE characters
-        # looking for the CRLF, and if we don't find it, we bail. If we read 
more than 16 hex characters,
-        # (the number needed to represent a 64-bit chunk size), we bail (this 
protects us from
-        # a client that sends an infinite stream of `F`, for example).
+        # Here's the production for a chunk (actually the whole body):
+        # (https://www.rfc-editor.org/rfc/rfc7230#section-4.1)
+
+        # chunked-body   = *chunk
+        #                  last-chunk
+        #                  trailer-part
+        #                  CRLF
+        #
+        # chunk          = chunk-size [ chunk-ext ] CRLF
+        #                  chunk-data CRLF
+        # chunk-size     = 1*HEXDIG
+        # last-chunk     = 1*("0") [ chunk-ext ] CRLF
+        # trailer-part   = *( header-field CRLF )
+        # chunk-data     = 1*OCTET ; a sequence of chunk-size octets
+        #
+        # chunk-ext      = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
+        #
+        # chunk-ext-name = token
+        # chunk-ext-val  = token / quoted-string
+
+        # To cope with malicious or broken clients that fail to send
+        # valid chunk lines, the strategy is to read character by
+        # character until we either reach a ; or newline. If at any
+        # time we read a non-HEX digit, we bail. If we hit a ;,
+        # indicating an chunk-extension, we'll read up to the next
+        # MAX_REQUEST_LINE characters ("A server ought to limit the
+        # total length of chunk extensions received") looking for the
+        # CRLF, and if we don't find it, we bail. If we read more than
+        # 16 hex characters, (the number needed to represent a 64-bit
+        # chunk size), we bail (this protects us from a client that
+        # sends an infinite stream of `F`, for example).
 
         buf = BytesIO()
         while 1:
@@ -224,16 +277,20 @@
             if not char:
                 self._chunked_input_error = True
                 raise _InvalidClientInput("EOF before chunk end reached")
-            if char == b'\r':
-                break
-            if char == b';':
+
+            if char in (
+                b'\r', # Beginning EOL
+                b';', # Beginning extension
+            ):
                 break
 
-            if char not in _HEX:
+            if char not in _HEX: # Invalid data.
                 self._chunked_input_error = True
                 raise _InvalidClientInput("Non-hex data", char)
+
             buf.write(char)
-            if buf.tell() > 16:
+
+            if buf.tell() > 16: # Too many hex bytes
                 self._chunked_input_error = True
                 raise _InvalidClientInput("Chunk-size too large.")
 
@@ -253,11 +310,72 @@
         if char == b'\r':
             # We either got here from the main loop or from the
             # end of an extension
+            self.__read_chunk_size_crlf(rfile, newline_only=True)
+            result = int(buf.getvalue(), 16)
+            if result == 0:
+                # The only time a chunk size of zero is allowed is the final
+                # chunk. It is either followed by another \r\n, or some 
trailers
+                # which are then followed by \r\n.
+                while self.__read_chunk_trailer(rfile):
+                    pass
+            return result
+
+    # Trailers have the following production (they are a header-field followed 
by CRLF)
+    # See above for the definition of "token".
+    #
+    # header-field   = field-name ":" OWS field-value OWS
+    # field-name     = token
+    # field-value    = *( field-content / obs-fold )
+    # field-content  = field-vchar [ 1*( SP / HTAB ) field-vchar ]
+    # field-vchar    = VCHAR / obs-text
+    # obs-fold       = CRLF 1*( SP / HTAB )
+    #                ; obsolete line folding
+    #                ; see Section 3.2.4
+
+
+    def __read_chunk_trailer(self, rfile, ):
+        # With rfile positioned just after a \r\n, read a trailer line.
+        # Return a true value if a non-empty trailer was read, and
+        # return false if an empty trailer was read (meaning the trailers are
+        # done).
+        # If a single line exceeds the MAX_REQUEST_LINE, raise an exception.
+        # If the field-name portion contains invalid characters, raise an 
exception.
+
+        i = 0
+        empty = True
+        seen_field_name = False
+        while i < MAX_REQUEST_LINE:
             char = rfile.read(1)
-            if char != b'\n':
+            if char == b'\r':
+                # Either read the next \n or raise an error.
+                self.__read_chunk_size_crlf(rfile, newline_only=True)
+                break
+            # Not a \r, so we are NOT an empty chunk.
+            empty = False
+            if char == b':' and i > 0:
+                # We're ending the field-name part; stop validating characters.
+                # Unless : was the first character...
+                seen_field_name = True
+            if not seen_field_name and char not in _ALLOWED_TOKEN_CHARS:
+                raise _InvalidClientInput('Invalid token character: %r' % 
(char,))
+            i += 1
+        else:
+            # We read too much
+            self._chunked_input_error = True
+            raise _InvalidClientInput("Too large chunk trailer")
+        return not empty
+
+    def __read_chunk_size_crlf(self, rfile, newline_only=False):
+        # Also for safety, correctly verify that we get \r\n when expected.
+        if not newline_only:
+            char = rfile.read(1)
+            if char != b'\r':
                 self._chunked_input_error = True
-                raise _InvalidClientInput("Line didn't end in CRLF")
-            return int(buf.getvalue(), 16)
+                raise _InvalidClientInput("Line didn't end in CRLF: %r" % 
(char,))
+        char = rfile.read(1)
+        if char != b'\n':
+            self._chunked_input_error = True
+            raise _InvalidClientInput("Line didn't end in LF: %r" % (char,))
 
     def _chunked_read(self, length=None, use_readline=False):
         # pylint:disable=too-many-branches
@@ -290,7 +408,7 @@
 
                 self.position += datalen
                 if self.chunk_length == self.position:
-                    rfile.readline()
+                    self.__read_chunk_size_crlf(rfile)
 
                 if length is not None:
                     length -= datalen
@@ -303,9 +421,9 @@
                 # determine the next size to read
                 self.chunk_length = self.__read_chunk_length(rfile)
                 self.position = 0
-                if self.chunk_length == 0:
-                    # Last chunk. Terminates with a CRLF.
-                    rfile.readline()
+                # If chunk_length was 0, we already read any trailers and
+                # validated that we have ended with \r\n\r\n.
+
         return b''.join(response)
 
     def read(self, length=None):
@@ -524,7 +642,8 @@
         elif len(words) == 2:
             self.command, self.path = words
             if self.command != "GET":
-                raise _InvalidClientRequest('Expected GET method: %r' % 
(raw_requestline,))
+                raise _InvalidClientRequest('Expected GET method; Got 
command=%r; path=%r; raw=%r' % (
+                    self.command, self.path, raw_requestline,))
             self.request_version = "HTTP/0.9"
             # QQQ I'm pretty sure we can drop support for HTTP/0.9
         else:
@@ -989,14 +1108,28 @@
             finally:
                 try:
                     self.wsgi_input._discard()
+                except _InvalidClientInput:
+                    # This one is deliberately raised to the outer
+                    # scope, because, with the incoming stream in some bad 
state,
+                    # we can't be sure we can synchronize and properly parse 
the next
+                    # request.
+                    raise
                 except socket.error:
-                    # Don't let exceptions during discarding
+                    # Don't let socket exceptions during discarding
                     # input override any exception that may have been
                     # raised by the application, such as our own 
_InvalidClientInput.
                     # In the general case, these aren't even worth logging 
(see the comment
                     # just below)
                     pass
-        except _InvalidClientInput:
+        except _InvalidClientInput as ex:
+            # DO log this one because:
+            # - Some of the data may have been read and acted on by the
+            #   application;
+            # - The response may or may not have been sent;
+            # - It's likely that the client is bad, or malicious, and
+            #   users might wish to take steps to block the client.
+            self._handle_client_error(ex)
+            self.close_connection = True
             self._send_error_response_if_possible(400)
         except socket.error as ex:
             if ex.args[0] in self.ignored_socket_errors:
@@ -1039,17 +1172,22 @@
     def _handle_client_error(self, ex):
         # Called for invalid client input
         # Returns the appropriate error response.
-        if not isinstance(ex, ValueError):
+        if not isinstance(ex, (ValueError, _InvalidClientInput)):
             # XXX: Why not self._log_error to send it through the loop's
             # handle_error method?
+            # _InvalidClientRequest is a ValueError; _InvalidClientInput is an 
IOError.
             traceback.print_exc()
         if isinstance(ex, _InvalidClientRequest):
             # No formatting needed, that's already been handled. In fact, 
because the
             # formatted message contains user input, it might have a % in it, 
and attempting
             # to format that with no arguments would be an error.
-            self.log_error(ex.formatted_message)
+            # However, the error messages do not include the requesting IP
+            # necessarily, so we do add that.
+            self.log_error('(from %s) %s', self.client_address, 
ex.formatted_message)
         else:
-            self.log_error('Invalid request: %s', str(ex) or 
ex.__class__.__name__)
+            self.log_error('Invalid request (from %s): %s',
+                           self.client_address,
+                           str(ex) or ex.__class__.__name__)
         return ('400', _BAD_REQUEST_RESPONSE)
 
     def _headers(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gevent-23.7.0/src/gevent/select.py 
new/gevent-23.9.0/src/gevent/select.py
--- old/gevent-23.7.0/src/gevent/select.py      2023-07-11 17:34:08.000000000 
+0200
+++ new/gevent-23.9.0/src/gevent/select.py      2023-09-02 01:22:30.000000000 
+0200
@@ -159,6 +159,13 @@
         # forward compatible
         raise ValueError("timeout must be non-negative")
 
+    # since rlist and wlist can be any iterable we will have to first
+    # copy them into a list, so we can use them in both _original_select
+    # and in SelectResult.select. We don't need to do it for xlist, since
+    # that one will only be passed into _original_select
+    rlist = rlist if isinstance(rlist, (list, tuple)) else list(rlist)
+    wlist = wlist if isinstance(wlist, (list, tuple)) else list(wlist)
+
     # First, do a poll with the original select system call. This is
     # the most efficient way to check to see if any of the file
     # descriptors have previously been closed and raise the correct
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gevent-23.7.0/src/gevent/subprocess.py 
new/gevent-23.9.0/src/gevent/subprocess.py
--- old/gevent-23.7.0/src/gevent/subprocess.py  2023-07-11 17:34:08.000000000 
+0200
+++ new/gevent-23.9.0/src/gevent/subprocess.py  2023-09-02 01:22:30.000000000 
+0200
@@ -360,10 +360,11 @@
 
     To capture standard error in the result, use ``stderr=STDOUT``::
 
-        >>> print(check_output(["/bin/sh", "-c",
+        >>> output = check_output(["/bin/sh", "-c",
         ...               "ls -l non_existent_file ; exit 0"],
-        ...              stderr=STDOUT).decode('ascii').strip())
-        ls: non_existent_file: No such file or directory
+        ...              stderr=STDOUT).decode('ascii').strip()
+        >>> print(output.rsplit(':', 1)[1].strip())
+        No such file or directory
 
     There is an additional optional argument, "input", allowing you to
     pass a string to the subprocess's stdin.  If you use this argument
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gevent-23.7.0/src/gevent/testing/__init__.py 
new/gevent-23.9.0/src/gevent/testing/__init__.py
--- old/gevent-23.7.0/src/gevent/testing/__init__.py    2023-07-11 
17:34:08.000000000 +0200
+++ new/gevent-23.9.0/src/gevent/testing/__init__.py    2023-09-02 
01:22:30.000000000 +0200
@@ -112,6 +112,7 @@
 from .skipping import skipOnLibuvOnTravisOnCPython27
 from .skipping import skipOnPy37
 from .skipping import skipOnPy310
+from .skipping import skipOnPy312
 from .skipping import skipOnPy3
 from .skipping import skipWithoutResource
 from .skipping import skipWithoutExternalNetwork
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/gevent-23.7.0/src/gevent/testing/patched_tests_setup.py 
new/gevent-23.9.0/src/gevent/testing/patched_tests_setup.py
--- old/gevent-23.7.0/src/gevent/testing/patched_tests_setup.py 2023-07-11 
17:34:08.000000000 +0200
+++ new/gevent-23.9.0/src/gevent/testing/patched_tests_setup.py 2023-09-02 
01:22:30.000000000 +0200
@@ -1221,6 +1221,15 @@
             'test_threading.ThreadTests.test_gettrace_all_threads',
         ]
 
+    if WIN:
+        disabled_tests += [
+            # These three are looking for an error string that matches,
+            # and ours differs very slightly
+            
'test_socket.BasicHyperVTest.testCreateHyperVSocketAddrNotTupleFailure',
+            
'test_socket.BasicHyperVTest.testCreateHyperVSocketAddrServiceIdNotValidUUIDFailure',
+            
'test_socket.BasicHyperVTest.testCreateHyperVSocketAddrVmIdNotValidUUIDFailure',
+        ]
+
 if TRAVIS:
     disabled_tests += [
         # These tests frequently break when we try to use newer Travis CI 
images,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gevent-23.7.0/src/gevent/testing/skipping.py 
new/gevent-23.9.0/src/gevent/testing/skipping.py
--- old/gevent-23.7.0/src/gevent/testing/skipping.py    2023-07-11 
17:34:08.000000000 +0200
+++ new/gevent-23.9.0/src/gevent/testing/skipping.py    2023-09-02 
01:22:30.000000000 +0200
@@ -48,6 +48,7 @@
 skipOnPy3 = unittest.skip if sysinfo.PY3 else _do_not_skip
 skipOnPy37 = unittest.skip if sysinfo.PY37 else _do_not_skip
 skipOnPy310 = unittest.skip if sysinfo.PY310 else _do_not_skip
+skipOnPy312 = unittest.skip if sysinfo.PY312 else _do_not_skip
 
 skipOnPurePython = unittest.skip if sysinfo.PURE_PYTHON else _do_not_skip
 skipWithCExtensions = unittest.skip if not sysinfo.PURE_PYTHON else 
_do_not_skip
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gevent-23.7.0/src/gevent/testing/testcase.py 
new/gevent-23.9.0/src/gevent/testing/testcase.py
--- old/gevent-23.7.0/src/gevent/testing/testcase.py    2023-07-11 
17:34:08.000000000 +0200
+++ new/gevent-23.9.0/src/gevent/testing/testcase.py    2023-09-02 
01:22:30.000000000 +0200
@@ -151,7 +151,7 @@
         gevent.Timeout.__init__(
             self,
             timeout,
-            '%r: test timed out\n' % (method,),
+            '%r: test timed out (set class __timeout__ to increase)\n' % 
(method,),
             ref=False
         )
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gevent-23.7.0/src/gevent/tests/known_failures.py 
new/gevent-23.9.0/src/gevent/tests/known_failures.py
--- old/gevent-23.7.0/src/gevent/tests/known_failures.py        2023-07-11 
17:34:08.000000000 +0200
+++ new/gevent-23.9.0/src/gevent/tests/known_failures.py        2023-09-02 
01:22:30.000000000 +0200
@@ -92,6 +92,7 @@
 BIT_64 = ConstantCondition(struct.calcsize('P') * 8 == 64, 'BIT_64')
 PY380_EXACTLY = ConstantCondition(sys.version_info[:3] == (3, 8, 0), 
'PY380_EXACTLY')
 PY312B3_EXACTLY = ConstantCondition(sys.version_info == (3, 12, 0, 'beta', 3))
+PY312B4_EXACTLY = ConstantCondition(sys.version_info == (3, 12, 0, 'beta', 4))
 
 class _Definition(object):
     __slots__ = (
@@ -213,7 +214,7 @@
 
         So far, this is only seen on one version, in CI environment.
         """,
-        when=(CI & PY312B3_EXACTLY)
+        when=(CI & (PY312B3_EXACTLY | PY312B4_EXACTLY))
     )
 
     test__issue6 = Flaky(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gevent-23.7.0/src/gevent/tests/test__pool.py 
new/gevent-23.9.0/src/gevent/tests/test__pool.py
--- old/gevent-23.7.0/src/gevent/tests/test__pool.py    2023-07-11 
17:34:08.000000000 +0200
+++ new/gevent-23.9.0/src/gevent/tests/test__pool.py    2023-09-02 
01:22:30.000000000 +0200
@@ -108,6 +108,11 @@
             sys.stderr = FakeFile()
             waiter = pool.spawn(crash)
             with gevent.Timeout(2):
+                # Without the timeout, we can get caught...doing something?
+                # If we call PyErr_WriteUnraisable at a certain point,
+                # we appear to switch back to the hub and do nothing,
+                # meaning we sit forever. The timeout at least keeps us from
+                # doing that and fails the test if we mess up error handling.
                 self.assertRaises(RuntimeError, waiter.get)
             # the pool should have something free at this point since the
             # waiter returned
@@ -126,13 +131,13 @@
 
 
 def crash(*_args, **_kw):
-    raise RuntimeError("Whoa")
+    raise RuntimeError("Raising an error from the crash() function")
 
 
 class FakeFile(object):
 
     def write(self, *_args):
-        raise RuntimeError('Whaaa')
+        raise RuntimeError('Writing to the file failed')
 
 
 class PoolBasicTests(greentest.TestCase):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gevent-23.7.0/src/gevent/tests/test__pywsgi.py 
new/gevent-23.9.0/src/gevent/tests/test__pywsgi.py
--- old/gevent-23.7.0/src/gevent/tests/test__pywsgi.py  2023-07-11 
17:34:08.000000000 +0200
+++ new/gevent-23.9.0/src/gevent/tests/test__pywsgi.py  2023-09-02 
01:22:30.000000000 +0200
@@ -25,21 +25,11 @@
 monkey.patch_all()
 
 from contextlib import contextmanager
-try:
-    from urllib.parse import parse_qs
-except ImportError:
-    # Python 2
-    from urlparse import parse_qs
+from urllib.parse import parse_qs
 import os
 import sys
-try:
-    # On Python 2, we want the C-optimized version if
-    # available; it has different corner-case behaviour than
-    # the Python implementation, and it used by socket.makefile
-    # by default.
-    from cStringIO import StringIO
-except ImportError:
-    from io import BytesIO as StringIO
+from io import BytesIO as StringIO
+
 import weakref
 import unittest
 from wsgiref.validate import validator
@@ -156,6 +146,10 @@
     @classmethod
     def read(cls, fd, code=200, reason='default', version='1.1',
              body=None, chunks=None, content_length=None):
+        """
+        Read an HTTP response, optionally perform assertions,
+        and return the Response object.
+        """
         # pylint:disable=too-many-branches
         _status_line, headers = read_headers(fd)
         self = cls(_status_line, headers)
@@ -716,7 +710,14 @@
 
 class TestChunkedPost(TestCase):
 
+    calls = 0
+
+    def setUp(self):
+        super().setUp()
+        self.calls = 0
+
     def application(self, env, start_response):
+        self.calls += 1
         self.assertTrue(env.get('wsgi.input_terminated'))
         start_response('200 OK', [('Content-Type', 'text/plain')])
         if env['PATH_INFO'] == '/a':
@@ -730,6 +731,8 @@
         if env['PATH_INFO'] == '/c':
             return list(iter(lambda: env['wsgi.input'].read(1), b''))
 
+        return [b'We should not get here', env['PATH_INFO'].encode('ascii')]
+
     def test_014_chunked_post(self):
         data = (b'POST /a HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n'
                 b'Transfer-Encoding: chunked\r\n\r\n'
@@ -797,6 +800,170 @@
             fd.write(data)
             read_http(fd, code=400)
 
+    def test_trailers_keepalive_ignored(self):
+        # Trailers after a chunk are ignored.
+        data1 = (
+            b'POST /a HTTP/1.1\r\n'
+            b'Host: localhost\r\n'
+            b'Connection: keep-alive\r\n'
+            b'Transfer-Encoding: chunked\r\n'
+            b'\r\n'
+            b'2\r\noh\r\n'
+            b'4\r\n hai\r\n'
+            b'0\r\n' # last-chunk
+            # Normally the final CRLF would go here, but if you put in a
+            # trailer, it doesn't.
+            b'trailer1: value1\r\n'
+            b'trailer2: value2\r\n'
+            b'\r\n' # Really terminate the chunk.
+        )
+        data2 = (
+            b'POST /a HTTP/1.1\r\n'
+            b'Host: localhost\r\n'
+            b'Connection: close\r\n'
+            b'Transfer-Encoding: chunked\r\n'
+            b'\r\n'
+            b'2\r\noh\r\n'
+            b'4\r\n bye\r\n'
+            b'0\r\n' # last-chunk
+        )
+        with self.makefile() as fd:
+            fd.write(data1)
+            read_http(fd, body='oh hai')
+            fd.write(data2)
+            read_http(fd, body='oh bye')
+
+        self.assertEqual(self.calls, 2)
+
+    def test_trailers_close_ignored(self):
+        data = (
+            b'POST /a HTTP/1.1\r\n'
+            b'Host: localhost\r\n'
+            b'Connection: close\r\n'
+            b'Transfer-Encoding: chunked\r\n'
+            b'\r\n'
+            b'2\r\noh\r\n'
+            b'4\r\n hai\r\n'
+            b'0\r\n' # last-chunk
+            # Normally the final CRLF would go here, but if you put in a
+            # trailer, it doesn't.
+            # b'\r\n'
+            b'GETpath2a:123 HTTP/1.1\r\n'
+            b'Host: a.com\r\n'
+            b'Connection: close\r\n'
+            b'\r\n'
+        )
+        with self.makefile() as fd:
+            fd.write(data)
+            read_http(fd, body='oh hai')
+            with self.assertRaises(ConnectionClosed):
+                read_http(fd)
+
+    def test_trailers_too_long(self):
+        # Trailers after a chunk are ignored.
+        data = (
+            b'POST /a HTTP/1.1\r\n'
+            b'Host: localhost\r\n'
+            b'Connection: keep-alive\r\n'
+            b'Transfer-Encoding: chunked\r\n'
+            b'\r\n'
+            b'2\r\noh\r\n'
+            b'4\r\n hai\r\n'
+            b'0\r\n' # last-chunk
+            # Normally the final CRLF would go here, but if you put in a
+            # trailer, it doesn't.
+            b'trailer2: value2' # note lack of \r\n
+        )
+        data += b't' * pywsgi.MAX_REQUEST_LINE
+        # No termination, because we detect the trailer as being too
+        # long and abort the connection.
+        with self.makefile() as fd:
+            fd.write(data)
+            read_http(fd, body='oh hai')
+            with self.assertRaises(ConnectionClosed):
+                read_http(fd, body='oh bye')
+
+    def test_trailers_request_smuggling_missing_last_chunk_keep_alive(self):
+        # When something that looks like a request line comes in the trailer
+        # as the first line, immediately after an invalid last chunk.
+        # We detect this and abort the connection, because the
+        # whitespace in the GET line isn't a legal part of a trailer.
+        # If we didn't abort the connection, then, because we specified
+        # keep-alive, the server would be hanging around waiting for more 
input.
+        data = (
+            b'POST /a HTTP/1.1\r\n'
+            b'Host: localhost\r\n'
+            b'Connection: keep-alive\r\n'
+            b'Transfer-Encoding: chunked\r\n'
+            b'\r\n'
+            b'2\r\noh\r\n'
+            b'4\r\n hai\r\n'
+            b'0' # last-chunk, but missing the \r\n
+            # Normally the final CRLF would go here, but if you put in a
+            # trailer, it doesn't.
+            # b'\r\n'
+            b'GET /path2?a=:123 HTTP/1.1\r\n'
+            b'Host: a.com\r\n'
+            b'Connection: close\r\n'
+            b'\r\n'
+        )
+        with self.makefile() as fd:
+            fd.write(data)
+            read_http(fd, body='oh hai')
+            with self.assertRaises(ConnectionClosed):
+                read_http(fd)
+
+        self.assertEqual(self.calls, 1)
+
+    def test_trailers_request_smuggling_header_first(self):
+        # When something that looks like a header comes in the first line.
+        data = (
+            b'POST /a HTTP/1.1\r\n'
+            b'Host: localhost\r\n'
+            b'Connection: keep-alive\r\n'
+            b'Transfer-Encoding: chunked\r\n'
+            b'\r\n'
+            b'2\r\noh\r\n'
+            b'4\r\n hai\r\n'
+            b'0\r\n' # last-chunk, but only one CRLF
+            b'Header: value\r\n'
+            b'GET /path2?a=:123 HTTP/1.1\r\n'
+            b'Host: a.com\r\n'
+            b'Connection: close\r\n'
+            b'\r\n'
+        )
+        with self.makefile() as fd:
+            fd.write(data)
+            read_http(fd, body='oh hai')
+            with self.assertRaises(ConnectionClosed):
+                read_http(fd, code=400)
+
+        self.assertEqual(self.calls, 1)
+
+    def test_trailers_request_smuggling_request_terminates_then_header(self):
+        data = (
+            b'POST /a HTTP/1.1\r\n'
+            b'Host: localhost\r\n'
+            b'Connection: keep-alive\r\n'
+            b'Transfer-Encoding: chunked\r\n'
+            b'\r\n'
+            b'2\r\noh\r\n'
+            b'4\r\n hai\r\n'
+            b'0\r\n' # last-chunk
+            b'\r\n'
+            b'Header: value'
+            b'GET /path2?a=:123 HTTP/1.1\r\n'
+            b'Host: a.com\r\n'
+            b'Connection: close\r\n'
+            b'\r\n'
+        )
+        with self.makefile() as fd:
+            fd.write(data)
+            read_http(fd, body='oh hai')
+            read_http(fd, code=400)
+
+        self.assertEqual(self.calls, 1)
+
 
 class TestUseWrite(TestCase):
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gevent-23.7.0/src/gevent/tests/test__select.py 
new/gevent-23.9.0/src/gevent/tests/test__select.py
--- old/gevent-23.7.0/src/gevent/tests/test__select.py  2023-07-11 
17:34:08.000000000 +0200
+++ new/gevent-23.9.0/src/gevent/tests/test__select.py  2023-09-02 
01:22:30.000000000 +0200
@@ -106,6 +106,17 @@
             finally:
                 sock.close()
 
+    def test_iterable(self):
+        sock = socket.socket()
+
+        def fileno_iter():
+            yield int(sock.fileno())
+
+        try:
+            select.select(fileno_iter(), [], [], 0.001)
+        finally:
+            sock.close()
+
     def test_string(self):
         self.switch_expected = False
         self.assertRaises(TypeError, select.select, ['hello'], [], [], 0.001)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gevent-23.7.0/src/gevent/tests/test__util.py 
new/gevent-23.9.0/src/gevent/tests/test__util.py
--- old/gevent-23.7.0/src/gevent/tests/test__util.py    2023-07-11 
17:34:08.000000000 +0200
+++ new/gevent-23.9.0/src/gevent/tests/test__util.py    2023-09-02 
01:22:30.000000000 +0200
@@ -51,6 +51,7 @@
             io = NativeStrIO()
             g = gevent.spawn(util.print_run_info, file=io)
             g.join()
+
             return io.getvalue()
 
         g = gevent.spawn(root)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gevent-23.7.0/src/gevent/util.py 
new/gevent-23.9.0/src/gevent/util.py
--- old/gevent-23.7.0/src/gevent/util.py        2023-07-11 17:34:08.000000000 
+0200
+++ new/gevent-23.9.0/src/gevent/util.py        2023-09-02 01:22:30.000000000 
+0200
@@ -371,46 +371,17 @@
     def __str__(self):
         return self.format(False)
 
-    @staticmethod
-    def __render_tb(tree, label, frame, limit):
+    # Prior to greenlet 3.0rc1, getting tracebacks of inactive
+    # greenlets could crash on Python 3.12. So we  added a version-based
+    # setting here to disable it. That's now fixed, but leave the
+    # hook just in case.
+    _SUPPORTS_TRACEBACK = True
+
+    @classmethod
+    def __render_tb(cls, tree, label, frame, limit):
         tree.child_data(label)
-        # XXX: Issues with tblib? Seen with tblib 1.3 and 2.0.
-        # More likely, it's something wrong in greenlet and the way it's
-        # keeping track of the frames?
-        #
-        # In a test like this:
-        #
-        #   g = gevent.spawn(util.print_run_info, file=io)
-        #   g.join() (test__util.py, line 53)
-        #
-        # 3.12b3 is crashing walking the stack on macOS:
-        # It's a simple segfault on line 340, ``f = f.f_back``.
-        # I have confirmed that the object is a real frame object.
-        #
-        # It seems unlikely to be a greenlet thing though, because the frame 
we're
-        # crashing on is the root frame:
-        #
-        # <frame at 0x.., file '/gevent/tests/test__util.py', line 53, code 
root>
-        #
-        # Interestingly, we see the test case dump the stack of the greenlet 
(successfully),
-        # then dump the stack of the main thread (successfully) --- this ends 
in line 53 --,
-        # and then get _another_ frame for line 53, and this is where it 
crashes.
-        # The difference? The successful dump does not list it as a root frame,
-        # where the failed one does.
-        #
-        #
-        # on Linux CI (not sure what frame), it is failing with a nice 
attribute error
-        # (which watches where the macOS is failing, inside a call to
-        # Py_GetAttr):
-        #
-        # File "//python3.12/traceback.py", line 339, in walk_stack
-        #   yield f, f.f_lineno
-        # AttributeError: 'dict' object has no attribute 'f_lineno'
-        #
-        # A workaround on macOS is to not dump the root frame, but that only 
fixes
-        # test__util. test__threadpool:test_greenlet_class crashes similarly, 
but
-        # not 100% of the time.
-        if sys.version_info != (3, 12, 0, 'beta', 3):
+
+        if cls._SUPPORTS_TRACEBACK:
             tb = ''.join(traceback.format_stack(frame, limit))
         else:
             tb = ''

Reply via email to