In pytest, the pytest_report_teststatus hook executes after session-scoped teardown fixtures with yield statements.
I have a pytest_report_teststatus hook to log test results for setup, call, and teardown phases:
def pytest_report_teststatus(report): 
    sum_log = dnet_generic.get_logger()
    if report.when == 'setup' and report.outcome == 'failed':
        sum_log.info("---- Test Setup Stage FAILED!")
    elif report.when == 'call':
        sum_log.info(f'---- Test: {report.nodeid}, Result: {report.outcome.upper()}')
    elif report.when == 'teardown' and report.outcome == 'failed':
        sum_log.info("---- Test Teardown Stage FAILED!")
And a session-scoped fixture using yield:
@pytest.fixture(scope="session")
def logger(testinfo):
    sum_log = dnet_generic.get_logger(initialize=True)
    # log session start...
    yield sum_log
    # log session end...
The problem: the teardown portion of the hook gets executed after the session-scoped fixture writes its “Session Ended” messages.
Expected order:
---- Test Teardown Stage FAILED!
-----Pytest Session Ended
Actual order:
-----Pytest Session Ended
---- Test Teardown Stage FAILED!
How can I make pytest_report_teststatus execute before session-scoped fixture teardown?
             
            
              
              
              
            
           
          
            
            
              I ran into this exact ordering problem and realized that session-scoped fixtures with yield always tear down after the reporting hooks.
To get pytest_report_teststatus to log before the fixture teardown, I wrapped my logging in a function- or module-scoped autouse fixture. For example:
@pytest.fixture(autouse=True)
def log_test_results():
    yield
    # This runs after each test but before session teardown
    report = get_last_test_report()
    if report.when == 'teardown' and report.outcome == 'failed':
        sum_log = dnet_generic.get_logger()
        sum_log.info("---- Test Teardown Stage FAILED!")
By moving the teardown logging to a smaller-scope fixture, it executes before the session-scoped logger’s yield cleanup.
             
            
              
              
              
            
           
          
            
            
              From my experience, pytest_report_teststatus hooks run too late for session-scoped fixtures because they run after all teardown is finished. Switching to pytest_runtest_teardown(item, nextitem) gave me better control:
def pytest_runtest_teardown(item, nextitem):
    report = item._report_sections[-1][2]  # or use item._store to track
    if report.failed:
        sum_log = dnet_generic.get_logger()
        sum_log.info("---- Test Teardown Stage FAILED!")
This hook executes immediately after the test teardown, so it naturally comes before any session-scoped fixture cleanup.
             
            
              
              
              
            
           
          
            
            
              Another trick that worked for me: instead of using yield in the session fixture, I used request.addfinalizer to schedule teardown after the reporting is done:
@pytest.fixture(scope="session")
def logger(request):
    sum_log = dnet_generic.get_logger(initialize=True)
    # log session start...
    def teardown():
        sum_log.info("-----Pytest Session Ended")
    request.addfinalizer(teardown)
    return sum_log
Because pytest_report_teststatus hooks run before finalizers registered via addfinalizer, your “Teardown Stage FAILED” logs now appear before the session-end log.