Repository: byllyfish/shellous (language: python)

Issue:
Warn on regression bug in Python 3.11.1, 3.10.9
shellous tests are affected by the asyncio regression in Python 3.11.1 and 3.10.9 (https://github.com/python/cpython/issues/100133).

CI tests are intentionally using 3.11.0 and 3.10.8; I am waiting for 3.11.2 and 3.10.10.



Discussion / hints:

Fix patch:
diff --git a/.cirrus.yml b/.cirrus.yml
index 05f24623..0ab8700a 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -22,7 +22,7 @@ freebsd_task:
   install_script:
     - pkg install -y python39 lsof
     - python3.9 -m venv .venv
-    - . .venv/bin/activate && pip install wheel poetry && poetry install --no-root --no-interaction
+    - . .venv/bin/activate && pip install -U setuptools wheel && pip install -r ./ci/requirements-dev.txt
     # We need to enable the /dev/fd file system on FreeBSD.
     - mount -t fdescfs null /dev/fd
 
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 506c96e6..8dbb3238 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -22,7 +22,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [ ubuntu-22.04, ubuntu-20.04, ubuntu-18.04, macos-12, macos-11, windows-2022 ]
-        python-version: [ '3.9', '3.10.8', '3.11.0' ]
+        python-version: [ '3.9', '3.10', '3.11']
 
     env:
       SHELLOUS_DEBUG: 1
diff --git a/shellous/__init__.py b/shellous/__init__.py
index 7e331d0a..f33843e7 100644
--- a/shellous/__init__.py
+++ b/shellous/__init__.py
@@ -8,6 +8,7 @@
 __version__ = "0.21.0"
 
 import sys
+import warnings
 
 from .command import CmdContext, Command, Options  # noqa: F401
 from .pipeline import Pipeline  # noqa: F401
@@ -19,6 +20,15 @@
     from .watcher import DefaultChildWatcher  # noqa: F401
 
 
+if sys.version_info[:3] in [(3, 10, 9), (3, 11, 1)]:
+    # Warn about these specific Python releases: 3.10.9 and 3.11.1
+    # These releases have a known race condition.
+    warnings.warn(
+        "Python 3.10.9 and Python 3.11.1 are unreliable with respect to asyncio subprocesses. Consider a newer Python release: 3.10.10+ or 3.11.2+. (https://github.com/python/cpython/issues/100133)",
+        RuntimeWarning,
+    )
+
+
 sh = CmdContext()
 """`sh` is the default command context (`CmdContext`).
 


Test patch:
diff --git a/tests/unix/test_unix.py b/tests/unix/test_unix.py
index e95d4069..c6eedfde 100644
--- a/tests/unix/test_unix.py
+++ b/tests/unix/test_unix.py
@@ -21,8 +21,8 @@
 # True if we're running on alpine linux.
 _IS_ALPINE = os.path.exists("/etc/alpine-release")
 
-# True if we're running on Python 3.11.
-_IS_PY3_11 = sys.version_info[0:2] == (3, 11)
+# True if we're running on Python 3.10.9 or later.
+_IS_PY3_NO_FD_COUNT = sys.version_info[:3] >= (3, 10, 9)
 
 
 def _is_uvloop():
@@ -1589,7 +1589,10 @@ async def test_open_file_descriptors():
     elif sys.platform.startswith("freebsd"):
         assert result == "0u unix \n1u PIPE \n2u VCHR /dev/null\n"
     else:
-        assert result == "0u unix \n1 PIPE \n2u CHR /dev/null\n"
+        assert result in (
+            "0u unix \n1 PIPE \n2u CHR /dev/null\n",
+            "0 PIPE \n1 PIPE \n2u CHR /dev/null\n",
+        )
 
 
 @pytest.mark.skipif(_is_lsof_unsupported(), reason="uvloop,codecov,alpine")
@@ -1612,7 +1615,10 @@ async def test_open_file_descriptors_unclosed_fds():
     elif sys.platform.startswith("freebsd"):
         assert result == "0u unix \n1u PIPE \n2u VCHR /dev/null\n"
     else:
-        assert result == "0u unix \n1 PIPE \n2u CHR /dev/null\n"
+        assert result in (
+            "0u unix \n1 PIPE \n2u CHR /dev/null\n",
+            "0 PIPE \n1 PIPE \n2u CHR /dev/null\n",
+        )
 
 
 @pytest.mark.skipif(_is_lsof_unsupported(), reason="uvloop,codecov,alpine")
@@ -1674,7 +1680,7 @@ def _limited_descriptors(limit):
         resource.setrlimit(resource.RLIMIT_NOFILE, (soft, hard))
 
 
-@pytest.mark.skipif(_is_uvloop() or _IS_PY3_11, reason="uvloop")
+@pytest.mark.skipif(_is_uvloop() or _IS_PY3_NO_FD_COUNT, reason="uvloop/no_fd_count")
 async def test_limited_file_descriptors(report_children):
     "Test running out of file descriptors."
     cmds = [sh("sleep", "1").stderr(sh.DEVNULL)] * 2