Pytest: Как найти FutureWarning и исправить его?

В моем текущем проекте, когда я запускаю свои тесты (с pytest), я получаю этот результат (помимо других):

ml_framework/tests/test_impute.py: 8 warnings
ml_framework/tests/test_transform_pipeline.py: 9 warnings
ml_framework/tests/test_data_transforms.py: 27 warnings
ml_framework/tests/test_jsonizer.py: 4 warnings
  /home/sondracek/anaconda3/envs/p3.8/lib/python3.8/site-packages/sklearn/utils/validation.py:70: FutureWarning: Pass standardize=False as keyword args. From version 1.0 (renaming of 0.25) passing these as positional arguments will result in an error
    warnings.warn(f"Pass {args_msg} as keyword args. From version "

Это FutureWarning совершенно ясно - начиная с версии 1.0 (в данный момент я использую 0.24) некоторые параметры будут изменены, и я не смогу передавать standardize в качестве позиционных аргументов.

Но возникает вопрос: Как мне найти, где мне изменить этот позиционный аргумент на ключевое слово?

Я мог бы перейти к validation.py, который упоминается в выводе предупреждения, но есть общая _deprecate_positional_args функция, которая не сообщает мне, какой код ее вызывает.

В данном конкретном случае я предполагаю, что это будет из PowerTransformer, импортированного из sklearn.preprocessing, который используется в моем коде, имеет параметр standardize и протестирован в перечисленных тестах. Есть ли общий способ легко найти причину этого для любого FutureWarning? Или мне нужно проверить все свои коды и попытаться как-то ее найти?


person Nerxis    schedule 01.04.2021    source источник


Ответы (1)


Pytest позволяет выдавать FutureWarning как ошибку, таким образом он указывает на место кода, в котором было поднято предупреждение, самый простой способ - через флаг предупреждения

pytest -W error::FutureWarning test_script.py

или внутри test_script.py через pytestmark

pytestmark = pytest.mark.filterwarnings("error::FutureWarning")

вы можете узнать больше об указании действия, выполняемого над предупреждением, здесь и здесь.

Пример

Рассмотрим следующий фиктивный пример test_transform.py скрипта

# test_transform.py
import pytest


pytestmark = pytest.mark.filterwarnings("error::FutureWarning")


def test_transformer_boxcox():

    from sklearn.preprocessing import PowerTransformer
    
    pt = PowerTransformer('box-cox', False)
    
    assert True
    

def test_transformer_yeo_johnson():

    from sklearn.preprocessing import PowerTransformer
    
    pt = PowerTransformer('yeo-johnson', False)
    
    assert True

и работает на терминале

pytest test_transform.py > logs.log

журналы показывают мне именно то, что PowerTransformer поднял FutureWarning.

===================================================== test session starts =====================================================
platform linux -- Python 3.7.6, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /home/jovyan/work
collected 2 items

test_transform.py FF                                                                                                    [100%]

========================================================== FAILURES ===========================================================
___________________________________________________ test_transformer_boxcox ___________________________________________________

    def test_transformer_boxcox():
    
        from sklearn.preprocessing import PowerTransformer
    
>       pt = PowerTransformer('box-cox', False)

test_transform.py:11: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

args = (<[AttributeError("'PowerTransformer' object has no attribute 'copy'") raised in repr()] PowerTransformer object at 0x7fa0bda14d50>, 'box-cox', False)
kwargs = {}, extra_args = 1, args_msg = 'standardize=False'

    @wraps(f)
    def inner_f(*args, **kwargs):
        extra_args = len(args) - len(all_args)
        if extra_args <= 0:
            return f(*args, **kwargs)
    
        # extra_args > 0
        args_msg = ['{}={}'.format(name, arg)
                    for name, arg in zip(kwonly_args[:extra_args],
                                         args[-extra_args:])]
        args_msg = ", ".join(args_msg)
        warnings.warn(f"Pass {args_msg} as keyword args. From version "
                      f"{version} passing these as positional arguments "
>                     "will result in an error", FutureWarning)
E       FutureWarning: Pass standardize=False as keyword args. From version 1.0 (renaming of 0.25) passing these as positional arguments will result in an error

/opt/conda/lib/python3.7/site-packages/sklearn/utils/validation.py:72: FutureWarning
________________________________________________ test_transformer_yeo_johnson _________________________________________________

    def test_transformer_yeo_johnson():
    
        from sklearn.preprocessing import PowerTransformer
    
>       pt = PowerTransformer('yeo-johnson', False)

test_transform.py:20: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

args = (<[AttributeError("'PowerTransformer' object has no attribute 'copy'") raised in repr()] PowerTransformer object at 0x7fa0bd02f750>, 'yeo-johnson', False)
kwargs = {}, extra_args = 1, args_msg = 'standardize=False'

    @wraps(f)
    def inner_f(*args, **kwargs):
        extra_args = len(args) - len(all_args)
        if extra_args <= 0:
            return f(*args, **kwargs)
    
        # extra_args > 0
        args_msg = ['{}={}'.format(name, arg)
                    for name, arg in zip(kwonly_args[:extra_args],
                                         args[-extra_args:])]
        args_msg = ", ".join(args_msg)
        warnings.warn(f"Pass {args_msg} as keyword args. From version "
                      f"{version} passing these as positional arguments "
>                     "will result in an error", FutureWarning)
E       FutureWarning: Pass standardize=False as keyword args. From version 1.0 (renaming of 0.25) passing these as positional arguments will result in an error

/opt/conda/lib/python3.7/site-packages/sklearn/utils/validation.py:72: FutureWarning
=================================================== short test summary info ===================================================
FAILED test_transform.py::test_transformer_boxcox - FutureWarning: Pass standardize=False as keyword args. From version 1.0 ...
FAILED test_transform.py::test_transformer_yeo_johnson - FutureWarning: Pass standardize=False as keyword args. From version...
====================================================== 2 failed in 0.71s ======================================================
person Miguel Trejo    schedule 01.04.2021
comment
Спасибо, хорошее решение! Я знаю о флаге -W, но не понимал, что его можно использовать таким образом. Знаете ли вы, можно ли использовать что-то подобное для поиска других предупреждений, возникающих во время импорта в test_something.py? - person Nerxis; 06.04.2021
comment
@Nerxis как предупреждение об устаревании? - person Miguel Trejo; 06.04.2021
comment
Да, именно этот. - person Nerxis; 06.04.2021
comment
@Nerxis Я думаю, что если вы импортируете функцию from module import function внутри тестовой функции, вы сможете улавливать предупреждения об устаревании, применяя ту же логику. - person Miguel Trejo; 07.04.2021
comment
Да, вы правы, просто вроде не работает, когда вы импортируете что-то вне тестовой функции. Но это немного другая проблема, поэтому я постараюсь разобраться. - person Nerxis; 08.04.2021