Skip to content

Commit

Permalink
Added decorator to disable run time type checking (#180)
Browse files Browse the repository at this point in the history
  • Loading branch information
cjolowicz committed Mar 19, 2021
1 parent 56e44c3 commit a8d1c79
Show file tree
Hide file tree
Showing 8 changed files with 49 additions and 4 deletions.
2 changes: 2 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ API reference

.. autodecorator:: typechecked

.. autodecorator:: typeguard_ignore

.. autoclass:: TypeChecker

.. autoexception:: TypeHintWarning
Expand Down
11 changes: 11 additions & 0 deletions docs/userguide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,17 @@ You can also customize the logic used to select which modules to instrument::

install_import_hook('', cls=CustomFinder)

To exclude specific functions or classes from run time type checking, use the ``@typeguard_ignore`` decorator::

from typeguard import typeguard_ignore

@typeguard_ignore
def f(x: int) -> int:
return str(x)

Unlike :func:`~typing.no_type_check`, this decorator has no effect on static type checking.


Using the pytest plugin
-----------------------

Expand Down
5 changes: 5 additions & 0 deletions docs/versionhistory.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ Version history

This library adheres to `Semantic Versioning 2.0 <https://semver.org/#semantic-versioning-200>`_.

**UNRELEASED**

- Added ``@typeguard_ignore`` decorator to exclude specific functions and classes from
runtime type checking (PR by Claudio Jolowicz)

**2.11.1 (2021-02-16)**

- Fixed compatibility with Python 3.10
Expand Down
15 changes: 13 additions & 2 deletions src/typeguard/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
__all__ = ('ForwardRefPolicy', 'TypeHintWarning', 'typechecked', 'check_return_type',
'check_argument_types', 'check_type', 'TypeWarning', 'TypeChecker')
'check_argument_types', 'check_type', 'TypeWarning', 'TypeChecker',
'typeguard_ignore')

import collections.abc
import gc
Expand All @@ -16,7 +17,7 @@
from typing import (
Callable, Any, Union, Dict, List, TypeVar, Tuple, Set, Sequence, get_type_hints, TextIO,
Optional, IO, BinaryIO, Type, Generator, overload, Iterable, AsyncIterable, Iterator,
AsyncIterator, AbstractSet)
AsyncIterator, AbstractSet, TYPE_CHECKING)
from unittest.mock import Mock
from warnings import warn
from weakref import WeakKeyDictionary, WeakValueDictionary
Expand Down Expand Up @@ -61,6 +62,16 @@ def isasyncgenfunction(func):
evaluate_forwardref = ForwardRef._eval_type


if TYPE_CHECKING:
_F = TypeVar("_F")

def typeguard_ignore(f: _F) -> _F:
"""This decorator is a noop during static type-checking."""
return f
else:
from typing import no_type_check as typeguard_ignore


_type_hints_map = WeakKeyDictionary() # type: Dict[FunctionType, Dict[str, Any]]
_functions_map = WeakValueDictionary() # type: Dict[CodeType, FunctionType]
_missing = object()
Expand Down
7 changes: 7 additions & 0 deletions tests/dummymodule.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

from typing import no_type_check, no_type_check_decorator

from typeguard import typeguard_ignore


@no_type_check_decorator
def dummy_decorator(func):
Expand All @@ -24,6 +26,11 @@ def non_type_checked_decorated_func(x: int, y: str) -> 6:
return 'foo'


@typeguard_ignore
def non_typeguard_checked_func(x: int, y: str) -> 6:
return 'foo'


def dynamic_type_checking_func(arg, argtype, return_annotation):
def inner(x: argtype) -> return_annotation:
return str(x)
Expand Down
7 changes: 6 additions & 1 deletion tests/mypy/negative.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typeguard import check_argument_types, check_return_type, typechecked
from typeguard import check_argument_types, check_return_type, typechecked, typeguard_ignore


@typechecked
Expand All @@ -11,6 +11,11 @@ def bar(x: int) -> int:
return str(x) # error: Incompatible return value type (got "str", expected "int")


@typeguard_ignore
def non_typeguard_checked_func(x: int) -> int:
return str(x) # error: Incompatible return value type (got "str", expected "int")


def returns_str() -> str:
return bar(0) # error: Incompatible return value type (got "int", expected "str")

Expand Down
2 changes: 1 addition & 1 deletion tests/mypy/test_type_annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def get_expected_errors() -> Dict[int, str]:
expected[idx + 1] = line[line.index("# error") + 2:]

# Sanity check. Should update if negative.py changes.
assert len(expected) == 8
assert len(expected) == 9
return expected


Expand Down
4 changes: 4 additions & 0 deletions tests/test_importhook.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ def test_non_type_checked_decorated_func(dummymodule):
assert dummymodule.non_type_checked_decorated_func('bah', 9) == 'foo'


def test_typeguard_ignored_func(dummymodule):
assert dummymodule.non_typeguard_checked_func('bah', 9) == 'foo'


def test_type_checked_method(dummymodule):
instance = dummymodule.DummyClass()
pytest.raises(TypeError, instance.type_checked_method, 'bah', 9).\
Expand Down

0 comments on commit a8d1c79

Please sign in to comment.