Skip to content

Commit

Permalink
Made @typechecked a no-op in optimized mode (#350)
Browse files Browse the repository at this point in the history
Closes #348.
  • Loading branch information
agronholm committed May 1, 2023
1 parent 43d686e commit 8923b7b
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 0 deletions.
13 changes: 13 additions & 0 deletions docs/userguide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,19 @@ when the containing module is instrumented by the import hook::
.. warning:: The :func:`@no_type_check_decorator <typing.no_type_check_decorator>`
decorator is not currently recognized by Typeguard.

Suppressing the ``@typechecked`` decorator in production
--------------------------------------------------------

If you're using the :func:`@typechecked <typechecked>` decorator to gradually introduce
run-time type checks to your code base, you can disable the checks in production by
running Python in optimized mode (as opposed to debug mode which is the default mode).
You can do this by either starting Python with the ``-O`` or ``-OO`` option, or by
setting the PYTHONOPTIMIZE_ environment variable. This will cause
:func:`@typechecked <typechecked>` to become a no-op when the import hook is not being
used to instrument the code.

.. _PYTHONOPTIMIZE: https://docs.python.org/3/using/cmdline.html#envvar-PYTHONOPTIMIZE

Debugging instrumented code
---------------------------

Expand Down
2 changes: 2 additions & 0 deletions docs/versionhistory.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ This library adheres to `Semantic Versioning 2.0 <https://semver.org/#semantic-v
**UNRELEASED**

- Added ``InstrumentationWarning`` to the public API
- Changed ``@typechecked`` to skip instrumentation in optimized mode, as in typeguard
2.x
- Avoid type checks where the types in question are shadowed by local variables
- Fixed instrumentation using ``typing.Optional`` without a subscript when the subscript
value was erased due to being an ignored import
Expand Down
7 changes: 7 additions & 0 deletions src/typeguard/_decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ def typechecked(
:func:`@staticmethod <staticmethod>`, and :class:`@property <property>` decorated
methods in the class.
.. note:: When Python is run in optimized mode (``-O`` or ``-OO``, this decorator
is a no-op). This is a feature meant for selectively introducing type checking
into a code base where the checks aren't meant to be run in production.
:param target: the function or class to enable type checking for
:param forward_ref_policy: override for
:attr:`.TypeCheckConfiguration.forward_ref_policy`
Expand All @@ -170,6 +174,9 @@ def typechecked(
debug_instrumentation=debug_instrumentation,
)

if not __debug__:
return target

if isclass(target):
for key, attr in target.__dict__.items():
if is_method_of(attr, target):
Expand Down
37 changes: 37 additions & 0 deletions tests/test_typechecked.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import asyncio
import subprocess
import sys
from contextlib import contextmanager
from pathlib import Path
from textwrap import dedent
from typing import (
Any,
Expand Down Expand Up @@ -585,3 +587,38 @@ def method(self, x: int) -> None:

with Foo().method(6) as value:
assert value == 7


@pytest.mark.parametrize(
"flags, expected_return_code",
[
pytest.param([], 1, id="debug"),
pytest.param(["-O"], 0, id="O"),
pytest.param(["-OO"], 0, id="OO"),
],
)
def test_typechecked_disabled_in_optimized_mode(
tmp_path: Path, flags: List[str], expected_return_code: int
):
code = dedent(
"""
from typeguard import typechecked
@typechecked
def foo(x: int) -> None:
pass
foo("a")
"""
)
script_path = tmp_path / "code.py"
script_path.write_text(code)
process = subprocess.run(
[sys.executable, *flags, str(script_path)], capture_output=True
)
assert process.returncode == expected_return_code
if process.returncode == 1:
assert process.stderr.endswith(
b'typeguard.TypeCheckError: argument "x" (str) is not an instance of '
b"int\n"
)

0 comments on commit 8923b7b

Please sign in to comment.