internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: python-trio/trio

Issue:
Erroring in Runner.spawn_impl() with an async generator should explain itself better
Ran into the situation where I made a mistake and called `nursery.start_soon(async_fn)` where `async_fn` was an async generator but got the following error:

```
# Give good error for: nursery.start_soon(some_sync_fn)
    raise TypeError(
         "trio expected an async function, but {!r} appears to be "
         "synchronous"
>               .format(getattr(async_fn, "__qualname__", async_fn))
 )
TypeError: trio expected an async function, but functools.partial(<function test_local_actor_async_gen.<locals>.print_loop at 0x7f45a6e78d90>) appears to be synchronous
```

Instead I propose having a separate explicit error message for the case where a user mistakenly passes an async generator:

```
TypeError: start_soon expected an async function but got an async generator <async_generator object test_local_actor_async_gen.<locals>.print_loop at 0x7f00a5504e80>
```


Discussion:

Resolving patch:
diff --git a/trio/_core/_run.py b/trio/_core/_run.py
--- a/trio/_core/_run.py
+++ b/trio/_core/_run.py
@@ -14,7 +14,9 @@
 from time import monotonic
 
 import attr
-from async_generator import async_generator, yield_, asynccontextmanager
+from async_generator import (
+    async_generator, yield_, asynccontextmanager, isasyncgen
+)
 from sortedcontainers import SortedDict
 from outcome import Error, Value
 
@@ -804,6 +806,13 @@ def _return_value_looks_like_wrong_library(value):
                     "That won't work without some sort of compatibility shim."
                     .format(coro)
                 )
+
+            if isasyncgen(coro):
+                raise TypeError(
+                    "start_soon expected an async function but got an async "
+                    "generator {!r}".format(coro)
+                )
+
             # Give good error for: nursery.start_soon(some_sync_fn)
             raise TypeError(
                 "trio expected an async function, but {!r} appears to be "


Test patch:
diff --git a/trio/_core/tests/test_run.py b/trio/_core/tests/test_run.py
--- a/trio/_core/tests/test_run.py
+++ b/trio/_core/tests/test_run.py
@@ -11,6 +11,7 @@
 import attr
 import outcome
 import pytest
+from async_generator import async_generator
 
 from .tutil import check_sequence_matches, gc_collect_harder
 from ... import _core
@@ -1590,6 +1591,15 @@ def generator_based_coro():  # pragma: no cover
                 bad_call(len, [1, 2, 3])
             assert "appears to be synchronous" in str(excinfo.value)
 
+            @async_generator
+            async def async_gen(arg):  # pragma: no cover
+                pass
+
+            with pytest.raises(TypeError) as excinfo:
+                bad_call(async_gen, 0)
+            msg = "expected an async function but got an async generator"
+            assert msg in str(excinfo.value)
+
             # Make sure no references are kept around to keep anything alive
             del excinfo
Repository: python-trio/trio

Issue:
Check CapacityLimiter's handling of _pending_borrowers
I was just reading the code for an unrelated reason, and realized that this code looks very suspicious:

https://github.com/python-trio/trio/blob/65729b121c55ff0aa1f5d43bd9cdcd6a81e54f0c/trio/_sync.py#L286-L296

Specifically, if the call to `await self._lot.park()` gets cancelled, it looks like we don't clear `self._pending_borrowers[task]`, which could be a memory leak (since it pins task objects in memory).


Discussion:

Resolving patch:
diff --git a/trio/_sync.py b/trio/_sync.py
--- a/trio/_sync.py
+++ b/trio/_sync.py
@@ -288,7 +288,11 @@ async def acquire_on_behalf_of(self, borrower):
         except _core.WouldBlock:
             task = _core.current_task()
             self._pending_borrowers[task] = borrower
-            await self._lot.park()
+            try:
+                await self._lot.park()
+            except _core.Cancelled:
+                self._pending_borrowers.pop(task)
+                raise
         except:
             await _core.cancel_shielded_checkpoint()
             raise


Test patch:
diff --git a/trio/tests/test_sync.py b/trio/tests/test_sync.py
--- a/trio/tests/test_sync.py
+++ b/trio/tests/test_sync.py
@@ -147,6 +147,21 @@ async def test_CapacityLimiter_change_total_tokens():
         assert c.statistics().tasks_waiting == 0
 
 
+# regression test for issue #548
+async def test_CapacityLimiter_memleak_548():
+    limiter = CapacityLimiter(total_tokens=1)
+    await limiter.acquire()
+
+    async with _core.open_nursery() as n:
+        n.start_soon(limiter.acquire)
+        await wait_all_tasks_blocked()  # give it a chance to run the task
+        n.cancel_scope.cancel()
+
+    # if this is 1, the acquire call (despite being killed) is still there in the task, and will
+    # leak memory all the while the limiter is active
+    assert len(limiter._pending_borrowers) == 0
+
+
 async def test_Semaphore():
     with pytest.raises(TypeError):
         Semaphore(1.0)
Repository: python-trio/trio

Issue:
Update dependency yapf to v0.24.0
This PR contains the following updates:

| Package | Update | New value |
|---|---|---|
| yapf | minor | ==0.24.0 |

---

### Renovate configuration

:date: **Schedule**: No schedule defined.

:vertical_traffic_light: **Automerge**: Enabled.

:recycle: **Rebasing**: Renovate will regenerate this PR any time it develops a merge conflict.

:no_bell: **Ignore**: Close this PR and you won't be reminded about this update again.

---

This PR has been generated by [Renovate Bot](https://renovatebot.com/gh/marketplace/renovate). View repository job log [here](https://renovatebot.com/dashboard#python-trio/trio).


Discussion:

Resolving patch:
diff --git a/trio/_ssl.py b/trio/_ssl.py
--- a/trio/_ssl.py
+++ b/trio/_ssl.py
@@ -652,7 +652,7 @@ async def receive_some(self, max_bytes):
                 # stream, and closing doesn't care about the state.
                 if (
                     self._https_compatible
-                    and isinstance(exc.__cause__, SSLEOFError)
+                    and isinstance(exc.__cause__, _stdlib_ssl.SSLEOFError)
                 ):
                     return b""
                 else:


Test patch:
Repository: python-trio/trio

Issue:
Documentation should have previous and next button on top as well as bottom.
I have been going through the trio documentation and I have noticed that the page is really big and if someone just wants to get from one page to either previous or next then he/she will have to scroll down to the bottom of the page then click the next or previous button.

It would be nice to have previous and next button on top as well as on bottom so that anyone can navigate through the documentation more easily.



Discussion:
That would be nice, yeah. We're currently using the theme `sphinx_rtd_theme` (+ some custom CSS tweaks)... and [it sounds like](https://sphinx-rtd-theme.readthedocs.io/en/latest/configuring.html) we can do this by adding `'prev_next_buttons_location': 'both'` to our `html_theme_options`?

@RatanShreshtha Want to try putting together a PR to do that?
@njsmith Yeah I am putting together a PR for the same. 

Resolving patch:
diff --git a/docs/source/conf.py b/docs/source/conf.py
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -157,6 +157,7 @@ def setup(app):
     # versions/settings...
     "navigation_depth": 4,
     "logo_only": True,
+    'prev_next_buttons_location': 'both'
 }
 
 # Add any paths that contain custom static files (such as style sheets) here,


Test patch:
Repository: python-trio/trio

Issue:
trio.Cancelled missing readable str()
Trying to print a trio.Cancelled exception e.g. with `f'{exc}'` yields an empty string.  I usually resort to `type(exc)` when I need to see what's going on.


Discussion:
Yeah, this is an annoying general quirk in Python, that for exceptions, `__str__` just returns the bare exception message... which is almost never what you want. Instead you usually want `__repr__`, e.g., by doing `f"{exc!r}"`.

This is a bit worse for `Cancelled` than other exceptions. Most exceptions IME are merely vague and confusing when printed without the type for context; `Cancelled` OTOH has no message string associated with it at all.

Options:

We could do nothing at all, and tell people to use `__repr__`, since that's the general rule for exceptions anyway.

We could give `Cancelled` exceptions some boilerplate message like `"cancelled"`, so `f"{exc}"` would be `"cancelled"`, and tracebacks would look like:

```python
trio.Cancelled: cancelled
```

We could define a custom `__str__` (maybe just `__str__ = __repr__`), so that you get something useful out of `f"{exc}"` without adding redundancy to tracebacks.

I don't have a strong opinion here; all three options seem reasonable enough.
Thank you for explaining.

Custom `__str__` approach sounds good.  I would not want add "trio.Cancelled: cancelled" noise to tracebacks.

Resolving patch:
diff --git a/trio/_core/_exceptions.py b/trio/_core/_exceptions.py
--- a/trio/_core/_exceptions.py
+++ b/trio/_core/_exceptions.py
@@ -73,6 +73,9 @@ def __init__(self, _marker=None):
             )
         super().__init__()
 
+    def __str__(self):
+        return "Cancelled"
+
     @classmethod
     def _init(cls):
         """A private constructor so that a user-created instance of Cancelled


Test patch:
diff --git a/trio/_core/tests/test_run.py b/trio/_core/tests/test_run.py
--- a/trio/_core/tests/test_run.py
+++ b/trio/_core/tests/test_run.py
@@ -2070,6 +2070,11 @@ def test_Cancelled_init():
     _core.Cancelled._init()
 
 
+def test_Cancelled_str():
+    cancelled = _core.Cancelled._init()
+    assert str(cancelled) == 'Cancelled'
+
+
 def test_sniffio_integration():
     with pytest.raises(sniffio.AsyncLibraryNotFoundError):
         sniffio.current_async_library()
Repository: python-trio/trio

Issue:
MultiError.exceptions foils linter
Access of `exceptions` on a MultiError instance elicits "no-member" error from Pylint.

I believe it's because MultiError `__new__` is merely mutating the result of `BaseException.__new__(MultiError)`.  I'd address it by making a proper `__init__()` constructor.


Discussion:

Resolving patch:
diff --git a/trio/_core/_multierror.py b/trio/_core/_multierror.py
--- a/trio/_core/_multierror.py
+++ b/trio/_core/_multierror.py
@@ -176,6 +176,15 @@ class MultiError(BaseException):
 
     """
 
+    def __init__(self, exceptions):
+        # Avoid recursion when exceptions[0] returned by __new__() happens
+        # to be a MultiError and subsequently __init__() is called.
+        if hasattr(self, "exceptions"):
+            # __init__ was already called on this object
+            assert len(exceptions) == 1 and exceptions[0] is self
+            return
+        self.exceptions = exceptions
+
     def __new__(cls, exceptions):
         exceptions = list(exceptions)
         for exc in exceptions:
@@ -184,11 +193,18 @@ def __new__(cls, exceptions):
                     "Expected an exception object, not {!r}".format(exc)
                 )
         if len(exceptions) == 1:
+            # If this lone object happens to itself be a MultiError, then
+            # Python will implicitly call our __init__ on it again.  See
+            # special handling in __init__.
             return exceptions[0]
         else:
-            self = BaseException.__new__(cls)
-            self.exceptions = exceptions
-            return self
+            # The base class __new__() implicitly invokes our __init__, which
+            # is what we want.
+            #
+            # In an earlier version of the code, we didn't define __init__ and
+            # simply set the `exceptions` attribute directly on the new object.
+            # However, linters expect attributes to be initialized in __init__.
+            return BaseException.__new__(cls, exceptions)
 
     def __str__(self):
         return ", ".join(repr(exc) for exc in self.exceptions)


Test patch:
diff --git a/trio/_core/tests/test_multierror.py b/trio/_core/tests/test_multierror.py
--- a/trio/_core/tests/test_multierror.py
+++ b/trio/_core/tests/test_multierror.py
@@ -112,6 +112,16 @@ def test_MultiError():
         MultiError([KeyError(), ValueError])
 
 
+def test_MultiErrorOfSingleMultiError():
+    # For MultiError([MultiError]), ensure there is no bad recursion by the
+    # constructor where __init__ is called if __new__ returns a bare MultiError.
+    exceptions = [KeyError(), ValueError()]
+    a = MultiError(exceptions)
+    b = MultiError([a])
+    assert b == a
+    assert b.exceptions == exceptions
+
+
 async def test_MultiErrorNotHashable():
     exc1 = NotHashableException(42)
     exc2 = NotHashableException(4242)
Repository: python-trio/trio

Issue:
New cancellation logic doesn't handle in-flight shielding changes correctly
Reproducer:
```python
import trio

async def main():
    with trio.CancelScope() as outer:
        with trio.CancelScope() as inner:
            outer.cancel()
            try:
                await trio.hazmat.checkpoint()
            finally:
                inner.shield = True
    print(outer.cancel_called, inner.cancel_called)
    print(outer.cancelled_caught, inner.cancelled_caught)

trio.run(main)
```
Prints:
```
True False
False True
```
It shouldn't be possible for a scope that wasn't cancelled to swallow a Cancelled.


Discussion:

Resolving patch:
diff --git a/trio/_core/_run.py b/trio/_core/_run.py
--- a/trio/_core/_run.py
+++ b/trio/_core/_run.py
@@ -426,7 +426,7 @@ def _close(self, exc):
         else:
             scope_task._activate_cancel_status(self._cancel_status.parent)
         if (
-            exc is not None
+            exc is not None and self._cancel_status.effectively_cancelled
             and not self._cancel_status.parent_cancellation_is_visible_to_us
         ):
             exc = MultiError.filter(self._exc_filter, exc)


Test patch:
diff --git a/trio/_core/tests/test_run.py b/trio/_core/tests/test_run.py
--- a/trio/_core/tests/test_run.py
+++ b/trio/_core/tests/test_run.py
@@ -849,6 +849,18 @@ async def test_cancel_scope_nesting():
     assert not scope.cancelled_caught
 
 
+# Regression test for https://github.com/python-trio/trio/issues/1175
+async def test_unshield_while_cancel_propagating():
+    with _core.CancelScope() as outer:
+        with _core.CancelScope() as inner:
+            outer.cancel()
+            try:
+                await _core.checkpoint()
+            finally:
+                inner.shield = True
+    assert outer.cancelled_caught and not inner.cancelled_caught
+
+
 async def test_cancel_unbound():
     async def sleep_until_cancelled(scope):
         with scope, fail_after(1):
Repository: python-trio/trio

Issue:
trio.open_signal_receiver should give an error if no arguments are passed
User confusion spotted in the wild: https://gitter.im/python-trio/general?at=5ebfa9d613878c30b581b9fe



Discussion:

Resolving patch:
diff --git a/trio/_signals.py b/trio/_signals.py
--- a/trio/_signals.py
+++ b/trio/_signals.py
@@ -129,6 +129,8 @@ def open_signal_receiver(*signals):
       signals: the signals to listen for.
 
     Raises:
+      TypeError: if no signals were provided.
+
       RuntimeError: if you try to use this anywhere except Python's main
           thread. (This is a Python limitation.)
 
@@ -144,6 +146,9 @@ def open_signal_receiver(*signals):
                  reload_configuration()
 
     """
+    if not signals:
+        raise TypeError("No signals were provided")
+
     if not is_main_thread():
         raise RuntimeError(
             "Sorry, open_signal_receiver is only possible when running in "


Test patch:
diff --git a/trio/tests/test_signals.py b/trio/tests/test_signals.py
--- a/trio/tests/test_signals.py
+++ b/trio/tests/test_signals.py
@@ -41,6 +41,12 @@ async def test_open_signal_receiver_restore_handler_after_one_bad_signal():
     assert signal.getsignal(signal.SIGILL) is orig
 
 
+async def test_open_signal_receiver_empty_fail():
+    with pytest.raises(TypeError, match="No signals were provided"):
+        with open_signal_receiver():
+            pass
+
+
 async def test_open_signal_receiver_restore_handler_after_duplicate_signal():
     orig = signal.getsignal(signal.SIGILL)
     with open_signal_receiver(signal.SIGILL, signal.SIGILL):
Repository: python-trio/trio

Issue:
Give a better diagnostic if user does trio.to_thread.run_sync(async_fn)
#1513 fixed the `trio.from_thread` functions to give nice diagnostics here, but skipped `trio.to_thread`.

Here's a real user who got confused by this: https://gitter.im/python-trio/general?at=5ed860e2b101510b203789ef



Discussion:

Resolving patch:
diff --git a/trio/_threads.py b/trio/_threads.py
--- a/trio/_threads.py
+++ b/trio/_threads.py
@@ -179,7 +179,17 @@ def do_release_then_return_result():
     def worker_fn():
         TOKEN_LOCAL.token = current_trio_token
         try:
-            return sync_fn(*args)
+            ret = sync_fn(*args)
+
+            if inspect.iscoroutine(ret):
+                # Manually close coroutine to avoid RuntimeWarnings
+                ret.close()
+                raise TypeError(
+                    "Trio expected a sync function, but {!r} appears to be "
+                    "asynchronous".format(getattr(sync_fn, "__qualname__", sync_fn))
+                )
+
+            return ret
         finally:
             del TOKEN_LOCAL.token
 


Test patch:
diff --git a/trio/tests/test_threads.py b/trio/tests/test_threads.py
--- a/trio/tests/test_threads.py
+++ b/trio/tests/test_threads.py
@@ -457,6 +457,15 @@ def thread_fn():
     assert callee_token == caller_token
 
 
+async def test_trio_to_thread_run_sync_expected_error():
+    # Test correct error when passed async function
+    async def async_fn():  # pragma: no cover
+        pass
+
+    with pytest.raises(TypeError, match="expected a sync function"):
+        await to_thread_run_sync(async_fn)
+
+
 async def test_trio_from_thread_run_sync():
     # Test that to_thread_run_sync correctly "hands off" the trio token to
     # trio.from_thread.run_sync()
Repository: python-trio/trio

Issue:
to_thread.run_sync() foils meta-class destructors 
Invoking `to_thread.run_sync()` seems to prevent meta-class destructors of completely unrelated classes from being called on shutdown.

```python
import trio

class deltype(type):
    def __del__(self):
        print(f"{self.__name__} class deleted")

Test = deltype("Test", (object,), {})

async def async_main():
    await trio.to_thread.run_sync(lambda: print('hi'))
    await trio.sleep(0)

trio.run(async_main)
```

expected output:
```
hi
Test class deleted
```

actual output:
```
hi
```

if the `run_sync()` is commented out, the meta-class destructor is called as expected:
```
Test class deleted
```



Discussion:
"git bisect" says the thread cache is the culprit.

1d1f871153b636fb18efe7091f7382e4fa57c41d

Resolving patch:
diff --git a/trio/_core/_thread_cache.py b/trio/_core/_thread_cache.py
--- a/trio/_core/_thread_cache.py
+++ b/trio/_core/_thread_cache.py
@@ -69,6 +69,8 @@ def _work(self):
                 # instead of spawning a new thread.
                 self._thread_cache._idle_workers[self] = None
                 deliver(result)
+                del fn
+                del deliver
             else:
                 # Timeout acquiring lock, so we can probably exit. But,
                 # there's a race condition: we might be assigned a job *just*


Test patch:
diff --git a/trio/_core/tests/test_thread_cache.py b/trio/_core/tests/test_thread_cache.py
--- a/trio/_core/tests/test_thread_cache.py
+++ b/trio/_core/tests/test_thread_cache.py
@@ -4,7 +4,7 @@
 import time
 import sys
 
-from .tutil import slow
+from .tutil import slow, gc_collect_harder
 from .. import _thread_cache
 from .._thread_cache import start_thread_soon, ThreadCache
 
@@ -25,6 +25,29 @@ def deliver(outcome):
         outcome.unwrap()
 
 
+def test_thread_cache_deref():
+    res = [False]
+
+    class del_me:
+        def __call__(self):
+            return 42
+
+        def __del__(self):
+            res[0] = True
+
+    q = Queue()
+
+    def deliver(outcome):
+        q.put(outcome)
+
+    start_thread_soon(del_me(), deliver)
+    outcome = q.get()
+    assert outcome.unwrap() == 42
+
+    gc_collect_harder()
+    assert res[0]
+
+
 @slow
 def test_spawning_new_thread_from_deliver_reuses_starting_thread():
     # We know that no-one else is using the thread cache, so if we keep
Repository: python-trio/trio

Issue:
Embedded use vs. signal handler
If you install your own signal handler from C and then run an embedded Python interpreter, you can't use Trio:
```
Traceback (most recent call last):
  File "/etc/kamailio/main.py", line 65, in background
    trio.run(bg_main)
  File "/usr/lib/python3/dist-packages/trio/_core/_run.py", line 1929, in run
    runner = setup_runner(
  File "/usr/lib/python3/dist-packages/trio/_core/_run.py", line 1846, in setup_runner
    ki_manager.install(runner.deliver_ki, restrict_keyboard_interrupt_to_checkpoints)
  File "/usr/lib/python3/dist-packages/trio/_core/_ki.py", line 180, in install
    not is_main_thread()
  File "/usr/lib/python3/dist-packages/trio/_util.py", line 79, in is_main_thread
    signal.signal(signal.SIGINT, signal.getsignal(signal.SIGINT))
  File "/usr/lib/python3.9/signal.py", line 56, in signal
    handler = _signal.signal(_enum_to_int(signalnum), _enum_to_int(handler))
TypeError: signal handler must be signal.SIG_IGN, signal.SIG_DFL, or a callable object
```



Discussion:

Resolving patch:
diff --git a/trio/_util.py b/trio/_util.py
--- a/trio/_util.py
+++ b/trio/_util.py
@@ -78,7 +78,7 @@ def is_main_thread():
     try:
         signal.signal(signal.SIGINT, signal.getsignal(signal.SIGINT))
         return True
-    except ValueError:
+    except (TypeError, ValueError):
         return False
 
 


Test patch: