From dc50e2f74d79ad8f860eab09e47133495d801b41 Mon Sep 17 00:00:00 2001 From: Tage Johansson Date: Wed, 31 Jul 2024 18:47:51 +0200 Subject: [PATCH] Setting environment variable TYPEGUARD_DISABLE has same effect as __debug__=False. --- docs/userguide.rst | 4 ++-- docs/versionhistory.rst | 3 +++ src/typeguard/_decorators.py | 8 +++++--- tests/test_typechecked.py | 16 ++++++++++------ 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/docs/userguide.rst b/docs/userguide.rst index fa2dc16f..9983f539 100644 --- a/docs/userguide.rst +++ b/docs/userguide.rst @@ -261,8 +261,8 @@ Suppressing the ``@typechecked`` decorator in production If you're using the :func:`@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 +You can do this by either starting Python with the ``-O`` or ``-OO`` option, or setting +any of the PYTHONOPTIMIZE_ or TYPEGUARD_DISABLE environment variables. This will cause :func:`@typechecked ` to become a no-op when the import hook is not being used to instrument the code. diff --git a/docs/versionhistory.rst b/docs/versionhistory.rst index afedbaa1..ec83b1b6 100644 --- a/docs/versionhistory.rst +++ b/docs/versionhistory.rst @@ -3,6 +3,9 @@ Version history This library adheres to `Semantic Versioning 2.0 `_. +**UNRELEASED** + +- Setting the TYPEGUARD_DISABLE environment variable has same effect as __debug__=False. **4.3.0** (2024-05-27) diff --git a/src/typeguard/_decorators.py b/src/typeguard/_decorators.py index cf325335..043f9dc9 100644 --- a/src/typeguard/_decorators.py +++ b/src/typeguard/_decorators.py @@ -2,6 +2,7 @@ import ast import inspect +import os import sys from collections.abc import Sequence from functools import partial @@ -160,8 +161,9 @@ def typechecked( :func:`@staticmethod `, and :class:`@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 + .. note:: When Python is run in optimized mode (``-O`` or ``-OO``, or when the + environment variable `TYPEGUARD_DISABLE` is set, 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 @@ -184,7 +186,7 @@ def typechecked( debug_instrumentation=debug_instrumentation, ) - if not __debug__: + if not __debug__ or "TYPEGUARD_DISABLE" in os.environ: return target if isclass(target): diff --git a/tests/test_typechecked.py b/tests/test_typechecked.py index dbb516f0..40933744 100644 --- a/tests/test_typechecked.py +++ b/tests/test_typechecked.py @@ -591,15 +591,19 @@ def method(self, x: int) -> None: @pytest.mark.parametrize( - "flags, expected_return_code", + "flags, envs, expected_return_code", [ - pytest.param([], 1, id="debug"), - pytest.param(["-O"], 0, id="O"), - pytest.param(["-OO"], 0, id="OO"), + pytest.param([], {}, 1, id="debug"), + pytest.param([], {"TYPEGUARD_DISABLE": "1"}, 0, id="TYPEGUARD_DISABLE"), + pytest.param(["-O"], {}, 0, id="O"), + pytest.param( + ["-O"], {"TYPEGUARD_DISABLE": "1"}, 0, id="TYPEGUARD_DISABLE and -O" + ), + pytest.param(["-OO"], {}, 0, id="OO"), ], ) def test_typechecked_disabled_in_optimized_mode( - tmp_path: Path, flags: List[str], expected_return_code: int + tmp_path: Path, flags: List[str], envs: dict[str, str], expected_return_code: int ): code = dedent( """ @@ -615,7 +619,7 @@ def foo(x: int) -> None: script_path = tmp_path / "code.py" script_path.write_text(code) process = subprocess.run( - [sys.executable, *flags, str(script_path)], capture_output=True + [sys.executable, *flags, str(script_path)], env=envs, capture_output=True ) assert process.returncode == expected_return_code if process.returncode == 1: