Repository: scrapy/scrapy
Pull request: Get eventloop from event_loop_policy to avoid deprecation warning.
Fixes #5685

Use `policy.get_event_loop()` instead of `asyncio.get_event_loop()`.  
If there is no event loop running, policy will create new_event_loop.



Problem statement:
DeprecationWarning: There is no current event loop
```
/home/runner/work/scrapy/scrapy/scrapy/utils/reactor.py:70: DeprecationWarning: There is no current event loop
  event_loop = asyncio.get_event_loop()
tests/test_downloadermiddleware.py: 2 warnings
tests/test_utils_asyncgen.py: 2 warnings
tests/test_utils_defer.py: 4 warnings
tests/test_utils_python.py: 2 warnings
tests/test_utils_signal.py: 2 warnings
  /home/runner/work/scrapy/scrapy/scrapy/utils/defer.py:272: DeprecationWarning: There is no current event loop
    return Deferred.fromFuture(asyncio.ensure_future(o))
tests/test_utils_asyncio.py::AsyncioTest::test_install_asyncio_reactor
  /home/runner/work/scrapy/scrapy/scrapy/utils/reactor.py:70: DeprecationWarning: There is no current event loop
    event_loop = asyncio.get_event_loop()
```

Not sure yet what does this imply and when can this break (this is from tests on 3.10 and 3.11).

Hints from issue discussion:
> asyncio.get_event_loop() now emits a deprecation warning if there is no running event loop. In the future it will be an alias of get_running_loop(). asyncio functions which implicitly create Future or Task objects now emit a deprecation warning if there is no running event loop and no explicit loop argument is passed
can I add an exception handling to this then? @wRAR 
@shashwata27 what kind of exception handling?
Maybe we can just switch to [`get_running_loop()`](https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.get_running_loop), it has been around since Python 3.7, which is our minimum Python version now.

~~I wonder, though, if there is any case where we can get a `RuntimeError` when using it.~~ Looking at the deprecation warnings, they explicitly say that there is no running event loop, which would trigger a `RuntimeError` with `get_running_loop()`.
For:

https://github.com/scrapy/scrapy/blob/82f25bc44acd2599115fa339967b436189eec9c1/scrapy/utils/reactor.py#L70

A try-except like this may make sense:

```python
try:
    event_loop = asyncio.get_running_loop()
except RuntimeError:
    event_loop = asyncio.new_event_loop()
```
This seems problematic, though:

https://github.com/scrapy/scrapy/blob/52d93490f57d4b2284e3d95efa5c622ab8ad16cb/scrapy/utils/defer.py#L323 
In the latter, maybe we could start by switching to `get_running_loop()`, and see what breaks. It should really only be called if there is a running loop, as far as I can tell. If not, maybe we are doing something wrong.

Commit message: Get eventloop from event_loop_policy to avoid deprecation warning.

Closes #5685

Patch to review:
diff --git a/scrapy/utils/reactor.py b/scrapy/utils/reactor.py
index 652733ce8b3..e0f9295392f 100644
--- a/scrapy/utils/reactor.py
+++ b/scrapy/utils/reactor.py
@@ -58,16 +58,19 @@ def install_reactor(reactor_path, event_loop_path=None):
     reactor_class = load_object(reactor_path)
     if reactor_class is asyncioreactor.AsyncioSelectorReactor:
         with suppress(error.ReactorAlreadyInstalledError):
+            policy = asyncio.get_event_loop_policy()
             if sys.version_info >= (3, 8) and sys.platform == "win32":
-                policy = asyncio.get_event_loop_policy()
                 if not isinstance(policy, asyncio.WindowsSelectorEventLoopPolicy):
-                    asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
+                    policy = asyncio.WindowsSelectorEventLoopPolicy()
+                    asyncio.set_event_loop_policy(policy)
+
             if event_loop_path is not None:
                 event_loop_class = load_object(event_loop_path)
                 event_loop = event_loop_class()
                 asyncio.set_event_loop(event_loop)
             else:
-                event_loop = asyncio.get_event_loop()
+                event_loop = policy.get_event_loop()
+
             asyncioreactor.install(eventloop=event_loop)
     else:
         *module, _ = reactor_path.split(".")


Code review comments:
diff_hunk: @@ -58,16 +58,19 @@ def install_reactor(reactor_path, event_loop_path=None):
     reactor_class = load_object(reactor_path)
     if reactor_class is asyncioreactor.AsyncioSelectorReactor:
         with suppress(error.ReactorAlreadyInstalledError):
+            policy = asyncio.get_event_loop_policy()
             if sys.version_info >= (3, 8) and sys.platform == "win32":
path: scrapy/utils/reactor.py
text: @user1:
This condition could now be refactored as
```python
if (
    sys.version_info >= (3, 8)
    and sys.platform == "win32"
    and not isinstance(policy, asyncio.WindowsSelectorEventLoopPolicy)
):
```