首页 > 编程问答 >@singledispatch 和 @typechecked 的 PyTest 不会引发预期错误

@singledispatch 和 @typechecked 的 PyTest 不会引发预期错误

时间:2024-07-24 12:51:13浏览次数:12  
标签:python pytest single-dispatch

目标:成功通过测试用例,对于 NotImplementedError test_score_not_implemented_error()

@singledispatch def score() 的目的是提高 NotImplementedError ,如果当 count_neg count_pos 提供的参数不匹配||时|也 Tuple[int, int] . Tuple[List[int], List[int]] 我想通过以下方式测试此异常处理。

. test_score_not_implemented_error() 但是,出乎意料的是,我因在其他多态函数上实现

而收到错误。 @typechecked I我对需要多态函数的方法充满信心,并且我的测试函数具有适当的测试用例。我怀疑问题在于我如何实现

的多态函数。 def score() 调整: 从多态函数中删除

会抛出: @typechecked :

FAILED tests/test_tps.py::test_score_not_implemented_error[0-01] - TypeError: can only concatenate str (not "int") to str
FAILED tests/test_tps.py::test_score_not_implemented_error[count_neg3-count_pos3] - TypeError: unsupported operand type(s) for +: 'int' and 'str'
FAILED tests/test_tps.py::test_score_not_implemented_error[count_neg4-count_pos4] - TypeError: unsupported operand type(s) for +: 'int' and 'str'
FAILED tests/test_tps.py::test_score_not_implemented_error[count_neg5-count_pos5] - TypeError: unsupported operand type(s) for +: 'int' and 'str'

tests/test_score.py 回溯:

from typeguard import typechecked
from functools import singledispatch

import pytest
from pytest_cases import parametrize
from typing import Any, List, Tuple, Type, Union


@singledispatch
def score(count_neg: Any, count_pos: Any) -> None:
    raise NotImplementedError(f'{type(count_neg)} and or {type(count_pos)} are not supported.')


@score.register(int)
@typechecked
def score_int(count_neg: int, count_pos: int) -> float:
    return round(100 * count_pos / (count_pos + count_neg), 1)


@score.register(list)
@typechecked
def score_list(count_neg: List[int], count_pos: List[int]) -> float:
    return round(100 * sum(count_pos) / (sum(count_pos) + sum(count_neg)), 1)


@parametrize('count_neg, count_pos',
             [('0', 0),
              (0, '0'),
              ('0', '0'),
              (['0'], [0]),
              ([0], ['0']),
              (['0'], ['0']),
              (None, None)])
def test_score_not_implemented_error(count_neg: Union[str, int, List[str], List[int], None],
                                                       count_pos: Union[str, int, List[str], List[int], None],
                                                       error: Type[BaseException] = NotImplementedError):
    with pytest.raises(error) as exc_info:
        score(count_neg, count_pos)

    assert exc_info.type is error

Traceback:

(venv) me@laptop:~/BitBucket/project $ python -m pytest tests/test_score.py 
===================================================================================================================== test session starts =====================================================================================================================
platform linux -- Python 3.9.16, pytest-7.4.0, pluggy-1.0.0
rootdir: /home/danielbell/BitBucket/pdl1-lung
plugins: hydra-core-1.3.2, typeguard-3.0.2, mock-3.11.1, cases-3.6.13, dvc-3.2.3, anyio-3.5.0
collected 7 items                                                                                                                                                                                                                                             

tests/test_tps.py .F.FFF.                                                                                                                                                                                                                               [100%]

========================================================================================================================== FAILURES ===========================================================================================================================
___________________________________________________________________________________________________________ test_score_not_implemented_error[0-01] ____________________________________________________________________________________________________________

count_neg = 0, count_pos = '0', error = <class 'NotImplementedError'>

    @parametrize('count_neg, count_pos',
                 [('0', 0),
                  (0, '0'),
                  ('0', '0'),
                  (['0'], [0]),
                  ([0], ['0']),
                  (['0'], ['0']),
                  (None, None)])
    def test_score_not_implemented_error(count_neg: Union[str, int, List[str], List[int], None],
                                                           count_pos: Union[str, int, List[str], List[int], None],
                                                           error: Type[BaseException] = NotImplementedError):
        with pytest.raises(error) as exc_info:
>           score(count_neg, count_pos)

tests/test_tps.py:38: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../miniconda3/envs/pdl1lung/lib/python3.9/functools.py:888: in wrapper
    return dispatch(args[0].__class__)(*args, **kw)
tests/test_tps.py:16: in score_int
    def score_int(count_neg: int, count_pos: int) -> float:
../../miniconda3/envs/pdl1lung/lib/python3.9/site-packages/typeguard/_functions.py:113: in check_argument_types
    check_type_internal(value, expected_type, memo=memo)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

value = '0', annotation = <class 'int'>, memo = <typeguard.CallMemo object at 0x7f591213ccc0>

    def check_type_internal(value: Any, annotation: Any, memo: TypeCheckMemo) -> None:
        """
        Check that the given object is compatible with the given type annotation.
    
        This function should only be used by type checker callables. Applications should use
        :func:`~.check_type` instead.
    
        :param value: the value to check
        :param annotation: the type annotation to check against
        :param memo: a memo object containing configuration and information necessary for
            looking up forward references
        """
    
        if isinstance(annotation, ForwardRef):
            try:
                annotation = evaluate_forwardref(annotation, memo)
            except NameError:
                if global_config.forward_ref_policy is ForwardRefPolicy.ERROR:
                    raise
                elif global_config.forward_ref_policy is ForwardRefPolicy.WARN:
                    warnings.warn(
                        f"Cannot resolve forward reference {annotation.__forward_arg__!r}",
                        TypeHintWarning,
                        stacklevel=get_stacklevel(),
                    )
    
                return
    
        if annotation is Any or annotation is SubclassableAny or isinstance(value, Mock):
            return
    
        # Skip type checks if value is an instance of a class that inherits from Any
        if not isclass(value) and SubclassableAny in type(value).__bases__:
            return
    
        extras: tuple[Any, ...]
        origin_type = get_origin(annotation)
        if origin_type is Annotated:
            annotation, *extras_ = get_args(annotation)
            extras = tuple(extras_)
            origin_type = get_origin(annotation)
        else:
            extras = ()
    
        if origin_type is not None:
            args = get_args(annotation)
    
            # Compatibility hack to distinguish between unparametrized and empty tuple
            # (tuple[()]), necessary due to https://github.com/python/cpython/issues/91137
            if origin_type in (tuple, Tuple) and annotation is not Tuple and not args:
                args = ((),)
        else:
            origin_type = annotation
            args = ()
    
        for lookup_func in checker_lookup_functions:
            checker = lookup_func(origin_type, args, extras)
            if checker:
                checker(value, origin_type, args, memo)
                return
    
        if not isinstance(value, origin_type):
>           raise TypeCheckError(f"is not an instance of {qualified_name(origin_type)}")
E           typeguard.TypeCheckError: argument "count_pos" (str) is not an instance of int

../../miniconda3/envs/pdl1lung/lib/python3.9/site-packages/typeguard/_checkers.py:680: TypeCheckError
___________________________________________________________________________________________________ test_score_not_implemented_error[count_neg3-count_pos3] ___________________________________________________________________________________________________

count_neg = ['0'], count_pos = [0], error = <class 'NotImplementedError'>

    @parametrize('count_neg, count_pos',
                 [('0', 0),
                  (0, '0'),
                  ('0', '0'),
                  (['0'], [0]),
                  ([0], ['0']),
                  (['0'], ['0']),
                  (None, None)])
    def test_score_not_implemented_error(count_neg: Union[str, int, List[str], List[int], None],
                                                           count_pos: Union[str, int, List[str], List[int], None],
                                                           error: Type[BaseException] = NotImplementedError):
        with pytest.raises(error) as exc_info:
>           score(count_neg, count_pos)

tests/test_tps.py:38: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../miniconda3/envs/pdl1lung/lib/python3.9/functools.py:888: in wrapper
    return dispatch(args[0].__class__)(*args, **kw)
tests/test_tps.py:22: in score_list
    def score_list(count_neg: List[int], count_pos: List[int]) -> float:
../../miniconda3/envs/pdl1lung/lib/python3.9/site-packages/typeguard/_functions.py:113: in check_argument_types
    check_type_internal(value, expected_type, memo=memo)
../../miniconda3/envs/pdl1lung/lib/python3.9/site-packages/typeguard/_checkers.py:676: in check_type_internal
    checker(value, origin_type, args, memo)
../../miniconda3/envs/pdl1lung/lib/python3.9/site-packages/typeguard/_checkers.py:273: in check_list
    check_type_internal(v, args[0], memo)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

value = '0', annotation = <class 'int'>, memo = <typeguard.CallMemo object at 0x7f5854383770>

    def check_type_internal(value: Any, annotation: Any, memo: TypeCheckMemo) -> None:
        """
        Check that the given object is compatible with the given type annotation.
    
        This function should only be used by type checker callables. Applications should use
        :func:`~.check_type` instead.
    
        :param value: the value to check
        :param annotation: the type annotation to check against
        :param memo: a memo object containing configuration and information necessary for
            looking up forward references
        """
    
        if isinstance(annotation, ForwardRef):
            try:
                annotation = evaluate_forwardref(annotation, memo)
            except NameError:
                if global_config.forward_ref_policy is ForwardRefPolicy.ERROR:
                    raise
                elif global_config.forward_ref_policy is ForwardRefPolicy.WARN:
                    warnings.warn(
                        f"Cannot resolve forward reference {annotation.__forward_arg__!r}",
                        TypeHintWarning,
                        stacklevel=get_stacklevel(),
                    )
    
                return
    
        if annotation is Any or annotation is SubclassableAny or isinstance(value, Mock):
            return
    
        # Skip type checks if value is an instance of a class that inherits from Any
        if not isclass(value) and SubclassableAny in type(value).__bases__:
            return
    
        extras: tuple[Any, ...]
        origin_type = get_origin(annotation)
        if origin_type is Annotated:
            annotation, *extras_ = get_args(annotation)
            extras = tuple(extras_)
            origin_type = get_origin(annotation)
        else:
            extras = ()
    
        if origin_type is not None:
            args = get_args(annotation)
    
            # Compatibility hack to distinguish between unparametrized and empty tuple
            # (tuple[()]), necessary due to https://github.com/python/cpython/issues/91137
            if origin_type in (tuple, Tuple) and annotation is not Tuple and not args:
                args = ((),)
        else:
            origin_type = annotation
            args = ()
    
        for lookup_func in checker_lookup_functions:
            checker = lookup_func(origin_type, args, extras)
            if checker:
                checker(value, origin_type, args, memo)
                return
    
        if not isinstance(value, origin_type):
>           raise TypeCheckError(f"is not an instance of {qualified_name(origin_type)}")
E           typeguard.TypeCheckError: item 0 of argument "count_neg" (list) is not an instance of int

../../miniconda3/envs/pdl1lung/lib/python3.9/site-packages/typeguard/_checkers.py:680: TypeCheckError
___________________________________________________________________________________________________ test_score_not_implemented_error[count_neg4-count_pos4] ___________________________________________________________________________________________________

count_neg = [0], count_pos = ['0'], error = <class 'NotImplementedError'>

    @parametrize('count_neg, count_pos',
                 [('0', 0),
                  (0, '0'),
                  ('0', '0'),
                  (['0'], [0]),
                  ([0], ['0']),
                  (['0'], ['0']),
                  (None, None)])
    def test_score_not_implemented_error(count_neg: Union[str, int, List[str], List[int], None],
                                                           count_pos: Union[str, int, List[str], List[int], None],
                                                           error: Type[BaseException] = NotImplementedError):
        with pytest.raises(error) as exc_info:
>           score(count_neg, count_pos)

tests/test_tps.py:38: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../miniconda3/envs/pdl1lung/lib/python3.9/functools.py:888: in wrapper
    return dispatch(args[0].__class__)(*args, **kw)
tests/test_tps.py:22: in score_list
    def score_list(count_neg: List[int], count_pos: List[int]) -> float:
../../miniconda3/envs/pdl1lung/lib/python3.9/site-packages/typeguard/_functions.py:113: in check_argument_types
    check_type_internal(value, expected_type, memo=memo)
../../miniconda3/envs/pdl1lung/lib/python3.9/site-packages/typeguard/_checkers.py:676: in check_type_internal
    checker(value, origin_type, args, memo)
../../miniconda3/envs/pdl1lung/lib/python3.9/site-packages/typeguard/_checkers.py:273: in check_list
    check_type_internal(v, args[0], memo)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

value = '0', annotation = <class 'int'>, memo = <typeguard.CallMemo object at 0x7f58540d45e0>

    def check_type_internal(value: Any, annotation: Any, memo: TypeCheckMemo) -> None:
        """
        Check that the given object is compatible with the given type annotation.
    
        This function should only be used by type checker callables. Applications should use
        :func:`~.check_type` instead.
    
        :param value: the value to check
        :param annotation: the type annotation to check against
        :param memo: a memo object containing configuration and information necessary for
            looking up forward references
        """
    
        if isinstance(annotation, ForwardRef):
            try:
                annotation = evaluate_forwardref(annotation, memo)
            except NameError:
                if global_config.forward_ref_policy is ForwardRefPolicy.ERROR:
                    raise
                elif global_config.forward_ref_policy is ForwardRefPolicy.WARN:
                    warnings.warn(
                        f"Cannot resolve forward reference {annotation.__forward_arg__!r}",
                        TypeHintWarning,
                        stacklevel=get_stacklevel(),
                    )
    
                return
    
        if annotation is Any or annotation is SubclassableAny or isinstance(value, Mock):
            return
    
        # Skip type checks if value is an instance of a class that inherits from Any
        if not isclass(value) and SubclassableAny in type(value).__bases__:
            return
    
        extras: tuple[Any, ...]
        origin_type = get_origin(annotation)
        if origin_type is Annotated:
            annotation, *extras_ = get_args(annotation)
            extras = tuple(extras_)
            origin_type = get_origin(annotation)
        else:
            extras = ()
    
        if origin_type is not None:
            args = get_args(annotation)
    
            # Compatibility hack to distinguish between unparametrized and empty tuple
            # (tuple[()]), necessary due to https://github.com/python/cpython/issues/91137
            if origin_type in (tuple, Tuple) and annotation is not Tuple and not args:
                args = ((),)
        else:
            origin_type = annotation
            args = ()
    
        for lookup_func in checker_lookup_functions:
            checker = lookup_func(origin_type, args, extras)
            if checker:
                checker(value, origin_type, args, memo)
                return
    
        if not isinstance(value, origin_type):
>           raise TypeCheckError(f"is not an instance of {qualified_name(origin_type)}")
E           typeguard.TypeCheckError: item 0 of argument "count_pos" (list) is not an instance of int

../../miniconda3/envs/pdl1lung/lib/python3.9/site-packages/typeguard/_checkers.py:680: TypeCheckError
___________________________________________________________________________________________________ test_score_not_implemented_error[count_neg5-count_pos5] ___________________________________________________________________________________________________

count_neg = ['0'], count_pos = ['0'], error = <class 'NotImplementedError'>

    @parametrize('count_neg, count_pos',
                 [('0', 0),
                  (0, '0'),
                  ('0', '0'),
                  (['0'], [0]),
                  ([0], ['0']),
                  (['0'], ['0']),
                  (None, None)])
    def test_score_not_implemented_error(count_neg: Union[str, int, List[str], List[int], None],
                                                           count_pos: Union[str, int, List[str], List[int], None],
                                                           error: Type[BaseException] = NotImplementedError):
        with pytest.raises(error) as exc_info:
>           score(count_neg, count_pos)

tests/test_tps.py:38: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../miniconda3/envs/pdl1lung/lib/python3.9/functools.py:888: in wrapper
    return dispatch(args[0].__class__)(*args, **kw)
tests/test_tps.py:22: in score_list
    def score_list(count_neg: List[int], count_pos: List[int]) -> float:
../../miniconda3/envs/pdl1lung/lib/python3.9/site-packages/typeguard/_functions.py:113: in check_argument_types
    check_type_internal(value, expected_type, memo=memo)
../../miniconda3/envs/pdl1lung/lib/python3.9/site-packages/typeguard/_checkers.py:676: in check_type_internal
    checker(value, origin_type, args, memo)
../../miniconda3/envs/pdl1lung/lib/python3.9/site-packages/typeguard/_checkers.py:273: in check_list
    check_type_internal(v, args[0], memo)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

value = '0', annotation = <class 'int'>, memo = <typeguard.CallMemo object at 0x7f58541444f0>

    def check_type_internal(value: Any, annotation: Any, memo: TypeCheckMemo) -> None:
        """
        Check that the given object is compatible with the given type annotation.
    
        This function should only be used by type checker callables. Applications should use
        :func:`~.check_type` instead.
    
        :param value: the value to check
        :param annotation: the type annotation to check against
        :param memo: a memo object containing configuration and information necessary for
            looking up forward references
        """
    
        if isinstance(annotation, ForwardRef):
            try:
                annotation = evaluate_forwardref(annotation, memo)
            except NameError:
                if global_config.forward_ref_policy is ForwardRefPolicy.ERROR:
                    raise
                elif global_config.forward_ref_policy is ForwardRefPolicy.WARN:
                    warnings.warn(
                        f"Cannot resolve forward reference {annotation.__forward_arg__!r}",
                        TypeHintWarning,
                        stacklevel=get_stacklevel(),
                    )
    
                return
    
        if annotation is Any or annotation is SubclassableAny or isinstance(value, Mock):
            return
    
        # Skip type checks if value is an instance of a class that inherits from Any
        if not isclass(value) and SubclassableAny in type(value).__bases__:
            return
    
        extras: tuple[Any, ...]
        origin_type = get_origin(annotation)
        if origin_type is Annotated:
            annotation, *extras_ = get_args(annotation)
            extras = tuple(extras_)
            origin_type = get_origin(annotation)
        else:
            extras = ()
    
        if origin_type is not None:
            args = get_args(annotation)
    
            # Compatibility hack to distinguish between unparametrized and empty tuple
            # (tuple[()]), necessary due to https://github.com/python/cpython/issues/91137
            if origin_type in (tuple, Tuple) and annotation is not Tuple and not args:
                args = ((),)
        else:
            origin_type = annotation
            args = ()
    
        for lookup_func in checker_lookup_functions:
            checker = lookup_func(origin_type, args, extras)
            if checker:
                checker(value, origin_type, args, memo)
                return
    
        if not isinstance(value, origin_type):
>           raise TypeCheckError(f"is not an instance of {qualified_name(origin_type)}")
E           typeguard.TypeCheckError: item 0 of argument "count_neg" (list) is not an instance of int

../../miniconda3/envs/pdl1lung/lib/python3.9/site-packages/typeguard/_checkers.py:680: TypeCheckError
====================================================================================================================== warnings summary =======================================================================================================================
../../miniconda3/envs/pdl1lung/lib/python3.9/site-packages/cytomine/models/collection.py:26
  /home/danielbell/miniconda3/envs/pdl1lung/lib/python3.9/site-packages/cytomine/models/collection.py:26: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated since Python 3.3, and in 3.10 it will stop working
    from collections import MutableSequence

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=================================================================================================================== short test summary info ===================================================================================================================
FAILED tests/test_tps.py::test_score_not_implemented_error[0-01] - typeguard.TypeCheckError: argument "count_pos" (str) is not an instance of int
FAILED tests/test_tps.py::test_score_not_implemented_error[count_neg3-count_pos3] - typeguard.TypeCheckError: item 0 of argument "count_neg" (list) is not an instance of int
FAILED tests/test_tps.py::test_score_not_implemented_error[count_neg4-count_pos4] - typeguard.TypeCheckError: item 0 of argument "count_pos" (list) is not an instance of int
FAILED tests/test_tps.py::test_score_not_implemented_error[count_neg5-count_pos5] - typeguard.TypeCheckError: item 0 of argument "count_neg" (list) is not an instance of int
=========================================================================================================== 4 failed, 3 passed, 1 warning in 0.94s ============================================================================================================

你的测试函数中存在类型提示问题。告诉它期望 Union[str, int, List[str], List[int], None] 类型的 count_neg count_pos

这告诉 typeguard 这些类型是可以接受的,因此它不会引发期望的 TypeError 。然后,它会根据传递的第一个参数的类型将输入调度到 score_int score_list ,这会导致在尝试对字符串执行数学运算时出现错误。

需要做两件事来解决此问题:

  1. 从测试函数的参数化中删除错误类型。 只希望测试的函数是否在收到无效类型时引发 NotImplementedError ,因此不需要在类型提示中包含有效类型。
  2. 使用 @singledispatch 注册通用类型。 这将确保在没有为特定类型注册处理程序时,始终调用泛型函数。

以下是如何更新代码以实现此目的的方法:

from typeguard import typechecked
from functools import singledispatch

import pytest
from pytest_cases import parametrize
from typing import Any, List, Tuple, Type, Union


@singledispatch
def score(count_neg: Any, count_pos: Any) -> None:
    raise NotImplementedError(f'{type(count_neg)} and or {type(count_pos)} are not supported.')


@score.register
@typechecked
def score_int(count_neg: int, count_pos: int) -> float:
    return round(100 * count_pos / (count_pos + count_neg), 1)


@score.register
@typechecked
def score_list(count_neg: List[int], count_pos: List[int]) -> float:
    return round(100 * sum(count_pos) / (sum(count_pos) + sum(count_neg)), 1)


@parametrize('count_neg, count_pos',
             [('0', 0),
              (0, '0'),
              ('0', '0'),
              (['0'], [0]),
              ([0], ['0']),
              (['0'], ['0']),
              (None, None)])
def test_score_not_implemented_error(count_neg: Any, count_pos: Any):
    with pytest.raises(NotImplementedError) as exc_info:
        score(count_neg, count_pos)

    assert exc_info.type is NotImplementedError

这些更改将确保在向 score 函数传递无效类型时,始终引发 NotImplementedError

标签:python,pytest,single-dispatch
From: 76779124

相关文章

  • 初学Python时需要认识清楚的几个概念:对象、函数、圆括号给、点取、方括号取
    这是我在自学Python的过程中自己挑选和提炼出来的几个重要的概念,之所以特意介绍这些概念,其中包含了我自己的思维方式和我对Python设计理念的认识,有其独特性和局限性。我希望这篇文章能够给喜爱Python的朋友们带来一些启发。1、对象(Object)对象是Python编程的基本单元。就像音是......
  • 如何接受文件或路径作为python中方法的参数
    我正在尝试编写一个方法,该方法将接受打开的文件myFile=open("myFile.txt")obj.writeTo(myFile)myFile.close()或带有路径的字符串obj.writeTo("myFile.txt")该方法的实现如下:defwriteTo(self,hessianFile):ifisinstance(hessianFile,file):pr......
  • Python,“pathlib.Path.open()”方法和内置函数“open()”不返回“typing.IO”的实例
    我读过一些其他答案,它们似乎与现实生活中发生的事情相矛盾。尝试以下操作:fromtypingimportIOfrompathlibimportPathexample_path=Path(r"D:\Example.txt")withexample_path.open("r")asf:print(isinstance(f,IO))withopen(example_path)a......
  • 【Dison夏令营 Day 28】用 Python 创建恐龙游戏
    谁没有玩过谷歌著名的“恐龙游戏”?也许每个人都玩过这个游戏。今天,在这篇文章中,我们将帮助你用Python开发一个恐龙游戏。本教程将深入讲解每一行代码,并提供参考资料。我们将尽力让读者详细、透彻地理解这个项目。Python版恐龙游戏的任务记录包括图片文档和Python资料......
  • Python 无法 pickle 自定义类型
    我正在尝试在ProcessPool中运行一个函数,该函数将通过读取python文件并运行生成的类中的方法来加载一些自定义类。我遇到的错误是TypeError:cannotpickle'generator'object该方法需要返回一个生成器。我该如何解决这个问题,谢谢。我用谷歌搜索但没有运气。......
  • python 语法无效?
    我试图编写一些Python代码,但由于某些奇怪的原因,它重复了无效的语法,我不知道最大的问题是什么。这些行是文件中唯一的代码行。Age=int(input("Howoldareyou?:"))ifAge>=18:print("YouareaAdult!")我尝试更改行,因为这似乎是我的生气,但它没有做任何帮......
  • 如何在Python的matplotlib中将条形标签绘制到右侧并为条形标签添加标题?
    我已经在python中的matplotlib中创建了一个图表,但是以下代码中的最后一行不允许在图表之外对齐条形标签。importmatplotlib.pyplotaspltg=df.plot.barh(x=name,y=days)g.set_title("Dayspeopleshowedup")g.bar_label(g.containers[0],label_type='edge')我得......
  • 19、Python之容器:快来数一数,24678?Counter能数得更好
    引言关于数据的分组计数,前面的文章中已经涉及了很多次。眼下要进行分组计数,我们可用的方法有:1、直接使用dict进行计数,需要对首次出现的键进行判断初始化的操作;2、使用dict的setdefault()方法进行计数,代码可以简化一些,虽然方法名有点怪;3、defaultdict进行计数,可以设置自动......
  • 如何使用 C# 检查用户是否安装了最低 Python 版本并且可以访问我的代码?
    我正在开发一个C#程序,该程序必须为一项特定任务运行一些Python代码。(Python代码很复杂,是由另一个团队开发的。无法在C#中重现其功能。)我正在尝试更新我的程序的安装程序文件以解决此问题:我希望它检查用户是否(谁正在安装我的程序)已安装Python并且它满足我的最低版......
  • 如何优雅地将复杂的Python对象和SQLAlchemy对象模型类结合起来?
    我有一个相当复杂的类,具有从提供的df到init计算的复杂属性,这些属性可能是最终可以序列化为字符串的其他类类型。在Python中,我想处理对象而不是原始类型,但也想使用SQLAlchemy与数据库交互。表中的列与许多类属性相同,如何优雅地组合这两个类?我可以使用组合并将数据......