|
4 | 4 | import enum
|
5 | 5 | import functools
|
6 | 6 | import inspect
|
7 |
| -import os |
8 | 7 | import socket
|
9 | 8 | import sys
|
10 | 9 | import warnings
|
11 | 10 | from asyncio import AbstractEventLoopPolicy
|
12 |
| -from pathlib import Path |
13 |
| -from tempfile import NamedTemporaryFile |
14 | 11 | from textwrap import dedent
|
15 | 12 | from typing import (
|
16 | 13 | Any,
|
|
31 | 28 | )
|
32 | 29 |
|
33 | 30 | import pytest
|
34 |
| -from _pytest.pathlib import visit |
35 | 31 | from pytest import (
|
36 | 32 | Class,
|
37 | 33 | Collector,
|
@@ -627,100 +623,28 @@ def _patched_collect():
|
627 | 623 | collector.__original_collect = collector.collect
|
628 | 624 | collector.collect = _patched_collect
|
629 | 625 | elif type(collector) is Package:
|
630 |
| - if not collector.funcnamefilter(collector.name): |
631 |
| - return |
632 | 626 |
|
633 | 627 | def _patched_collect():
|
634 |
| - # pytest.Package collects all files and sub-packages. Pytest 8 changes |
635 |
| - # this logic to only collect a single directory. Sub-packages are then |
636 |
| - # collected by a separate Package collector. Therefore, this logic can be |
637 |
| - # dropped, once we move to pytest 8. |
638 |
| - collector_dir = Path(collector.path.parent) |
639 |
| - for direntry in visit(str(collector_dir), recurse=collector._recurse): |
640 |
| - if not direntry.name == "__init__.py": |
641 |
| - # No need to register a package-scoped fixture, if we aren't |
642 |
| - # collecting a (sub-)package |
643 |
| - continue |
644 |
| - pkgdir = Path(direntry.path).parent |
645 |
| - pkg_nodeid = str(pkgdir.relative_to(collector_dir)) |
646 |
| - if pkg_nodeid == ".": |
647 |
| - pkg_nodeid = "" |
648 |
| - # Pytest's fixture matching algorithm compares a fixture's baseid with |
649 |
| - # an Item's nodeid to determine whether a fixture is available for a |
650 |
| - # specific Item. Package.nodeid ends with __init__.py, so the |
651 |
| - # fixture's baseid will also end with __init__.py and prevents |
652 |
| - # the fixture from being matched to test items in the package. |
653 |
| - # Furthermore, Package also collects any sub-packages, which means |
654 |
| - # the ID of the scoped event loop for the package must change for |
655 |
| - # each sub-package. |
656 |
| - # As the fixture matching is purely based on string comparison, we |
657 |
| - # can assemble a path based on the root package path |
658 |
| - # (i.e. Package.path.parent) and the sub-package path |
659 |
| - # (i.e. Path(direntry.path).parent)). This makes the fixture visible |
660 |
| - # to all items in the package. |
661 |
| - # see also https://github.com/pytest-dev/pytest/issues/11662#issuecomment-1879310072 # noqa |
662 |
| - # Possibly related to https://github.com/pytest-dev/pytest/issues/4085 |
663 |
| - fixture_id = f"{pkg_nodeid}/__init__.py::<event_loop>".lstrip("/") |
664 |
| - # When collector is a Package, collector.obj is the package's |
665 |
| - # __init__.py. Accessing the __init__.py to attach the fixture function |
666 |
| - # may trigger additional module imports or change the order of imports, |
667 |
| - # which leads to a number of problems. |
668 |
| - # see https://github.com/pytest-dev/pytest-asyncio/issues/729 |
669 |
| - # Moreover, Package.obj has been removed in pytest 8. |
670 |
| - # Therefore, pytest-asyncio creates a temporary Python module inside the |
671 |
| - # collected package. The sole purpose of that module is to house a |
672 |
| - # fixture function for the pacakge-scoped event loop fixture. Once the |
673 |
| - # fixture has been evaluated by pytest, the temporary module |
674 |
| - # can be removed. |
675 |
| - with NamedTemporaryFile( |
676 |
| - dir=pkgdir, |
677 |
| - prefix="pytest_asyncio_virtual_module_", |
678 |
| - suffix=".py", |
679 |
| - delete=False, # Required for Windows compatibility |
680 |
| - ) as virtual_module_file: |
681 |
| - virtual_module = Module.from_parent( |
682 |
| - collector, path=Path(virtual_module_file.name) |
683 |
| - ) |
684 |
| - virtual_module_file.write( |
685 |
| - dedent( |
686 |
| - f"""\ |
687 |
| - # This is a temporary file created by pytest-asyncio |
688 |
| - # If you see this file, a pytest run has crashed and |
689 |
| - # wasn't able to clean up the file in time. |
690 |
| - # You can safely remove this file. |
691 |
| - import asyncio |
692 |
| - import pytest |
693 |
| - from pytest_asyncio.plugin \ |
694 |
| - import _temporary_event_loop_policy |
695 |
| - @pytest.fixture( |
696 |
| - scope="{collector_scope}", |
697 |
| - name="{fixture_id}", |
698 |
| - ) |
699 |
| - def scoped_event_loop( |
700 |
| - *args, |
701 |
| - event_loop_policy, |
702 |
| - ): |
703 |
| - new_loop_policy = event_loop_policy |
704 |
| - with _temporary_event_loop_policy(new_loop_policy): |
705 |
| - loop = asyncio.new_event_loop() |
706 |
| - loop.__pytest_asyncio = True |
707 |
| - asyncio.set_event_loop(loop) |
708 |
| - yield loop |
709 |
| - loop.close() |
710 |
| - """ |
711 |
| - ).encode() |
712 |
| - ) |
713 |
| - virtual_module_file.flush() |
714 |
| - fixturemanager = collector.config.pluginmanager.get_plugin( |
715 |
| - "funcmanage" |
| 628 | + # When collector is a Package, collector.obj is the package's |
| 629 | + # __init__.py. Accessing the __init__.py to attach the fixture function |
| 630 | + # may trigger additional module imports or change the order of imports, |
| 631 | + # which leads to a number of problems. |
| 632 | + # see https://github.com/pytest-dev/pytest-asyncio/issues/729 |
| 633 | + # Moreover, Package.obj has been removed in pytest 8. |
| 634 | + # Therefore, pytest-asyncio attaches the packages-scoped event loop |
| 635 | + # fixture to the first collected module in that package. |
| 636 | + package_scoped_loop_added = False |
| 637 | + for subcollector in collector.__original_collect(): |
| 638 | + if ( |
| 639 | + not package_scoped_loop_added |
| 640 | + and isinstance(subcollector, Module) |
| 641 | + and getattr(subcollector, "obj", None) |
| 642 | + ): |
| 643 | + subcollector.obj.__pytest_asyncio_package_scoped_event_loop = ( |
| 644 | + scoped_event_loop |
716 | 645 | )
|
717 |
| - # Collect the fixtures in the virtual module with the node ID of |
718 |
| - # the current sub-package to ensure correct fixture matching. |
719 |
| - # see also https://github.com/pytest-dev/pytest/issues/11662#issuecomment-1879310072 # noqa |
720 |
| - fixturemanager.parsefactories(virtual_module.obj, nodeid=pkg_nodeid) |
721 |
| - yield virtual_module |
722 |
| - os.unlink(virtual_module_file.name) |
723 |
| - yield from collector.__original_collect() |
| 646 | + package_scoped_loop_added = True |
| 647 | + yield subcollector |
724 | 648 |
|
725 | 649 | collector.__original_collect = collector.collect
|
726 | 650 | collector.collect = _patched_collect
|
|
0 commit comments