Skip to content

Commit

Permalink
Merge branch '4.0-dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
agronholm committed Apr 1, 2023
2 parents 972bed0 + 67c544e commit 8fbee4c
Show file tree
Hide file tree
Showing 20 changed files with 1,157 additions and 716 deletions.
9 changes: 5 additions & 4 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,16 @@ Configuration

The global configuration object.

Used by :func:`@typechecked <.typechecked>` and :func:`.install_import_hook`, and
notably **not used** by :func:`.check_type`.

.. autoclass:: TypeCheckConfiguration
:members:

.. autoclass:: CollectionCheckStrategy

.. autoclass:: Unset

.. autoclass:: ForwardRefPolicy

.. autofunction:: warn_on_error
Expand All @@ -53,10 +58,6 @@ Custom checkers
.. autoclass:: TypeCheckMemo
:members:

.. autoclass:: CallMemo
:show-inheritance:
:members:

Type check suppression
----------------------

Expand Down
8 changes: 6 additions & 2 deletions docs/extending.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ The checker function receives four arguments:
#. The origin type
#. The generic arguments from the annotation (empty tuple when the annotation was not
parametrized)
#. The memo object, either :class:`~.TypeCheckMemo` or :class:`~.CallMemo`
#. The memo object (:class:`~.TypeCheckMemo`)

There are a couple of things to take into account when writing a type checker:

Expand All @@ -38,7 +38,11 @@ There are a couple of things to take into account when writing a type checker:
along ``memo`` to it)
#. If you're type checking collections, your checker function should respect the
:attr:`~.TypeCheckConfiguration.collection_check_strategy` setting, available from
:data:`typeguard.config`
:attr:`~.TypeCheckMemo.config`

.. versionchanged:: 4.0
In Typeguard 4.0, checker functions **must** respect the settings in
``memo.config``, rather than the global configuration

The following example contains a lookup function and type checker for a custom class
(``MySpecialType``)::
Expand Down
34 changes: 6 additions & 28 deletions docs/features.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,43 +29,21 @@ The following type checks are not yet supported in Typeguard:
Other limitations
-----------------

Non-local forward references
++++++++++++++++++++++++++++
Local references to nested classes
++++++++++++++++++++++++++++++++++

Forward references pointing to non-local types (class defined inside a function, and a
nested function within the same parent function referring to that class) cannot
currently be resolved::

def outer():
class Inner:
pass

instance = Inner()

# Inner cannot be resolved because it is not in the __globals__ of inner() or
# its closure
def inner() -> "Inner":
return instance

return inner()

However, if you explicitly reference the type in the nested function, that will work::

# Inner is part of the closure of inner() now so it can be resolved
def inner() -> "Inner":
return Inner()

A similar corner case would be a forward reference to a nested class::
Forward references from methods pointing to non-local nested classes cannot currently be
resolved::

class Outer:
class Inner:
pass

# Cannot be resolved as the name is no longer available
def method() -> "Inner":
def method(self) -> "Inner":
return Outer.Inner()

Both these shortcomings may be resolved in a future release.
This shortcoming may be resolved in a future release.

Using :func:`@typechecked <typechecked>` on top of other decorators
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Expand Down
71 changes: 64 additions & 7 deletions docs/userguide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,6 @@ User guide

.. py:currentmodule:: typeguard
Setting configuration options
-----------------------------

There are several configuration options that can be set that influence how type checking
is done. To change the options, import :data:`typeguard.config` (which is of type
:class:`~.TypeCheckConfiguration`) and set the attributes you want to change.

Checking types directly
-----------------------

Expand Down Expand Up @@ -74,6 +67,19 @@ abstract syntax tree using :func:`ast.parse`, modifying it to add type checking,
finally compiling the modified AST into byte code. This code is then used to make a new
function object that is used to replace the original one.

To explicitly set type checking options on a per-function basis, you can pass them as
keyword arguments to :func:`@typechecked <typechecked>`::

from typeguard import CollectionCheckStrategy, typechecked

@typechecked(collection_check_strategy=CollectionCheckStrategy.ALL_ITEMS)
def some_function(a: int, b: float, c: str, *args: str) -> bool:
...
return retval

This also allows you to override the global options for specific functions when using
the import hook.

.. note:: You should always place this decorator closest to the original function,
as it will not work when there is another decorator wrapping the function.
For the same reason, when you use it on a class that has wrapping decorators on
Expand Down Expand Up @@ -120,6 +126,23 @@ You can also customize the logic used to select which modules to instrument::

install_import_hook('', cls=CustomFinder)

.. _forwardrefs:

Notes on forward reference handling
-----------------------------------

The internal type checking functions, injected to instrumented code by either
:func:`@typechecked <typechecked>` or the import hook, use the "naked" versions of any
annotations, undoing any quotations in them (and the effects of
``from __future__ import annotations``). As such, the
:attr:`~.TypeCheckConfiguration.forward_ref_policy` does not apply to instrumented code.

To facilitate the use of types only available to static type checkers, Typeguard
recognizes module-level imports guarded by ``if typing.TYPE_CHECKING:`` or
``if TYPE_CHECKING:`` (add the appropriate :mod:`typing` imports). Imports made within
such blocks on the module level will be replaced in calls to internal type checking
functions with :data:`~typing.Any`.

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

Expand All @@ -134,6 +157,40 @@ previous section). To use it, run ``pytest`` with the appropriate
There is currently no support for specifying a customized module finder.

Setting configuration options
-----------------------------

There are several configuration options that can be set that influence how type checking
is done. The :data:`typeguard.config` (which is of type
:class:`~.TypeCheckConfiguration`) controls the options applied to code instrumented via
either :func:`@typechecked <.typechecked>` or the import hook. The
:func:`~.check_type`, function, however, uses the built-in defaults and is not affected
by the global configuration, so you must pass any configuration overrides explicitly
with each call.

You can also override specific configuration options in instrumented functions (or
entire classes) by passing keyword arguments to :func:`@typechecked <.typechecked>`.
You can do this even if you're using the import hook, as the import hook will remove the
decorator to ensure that no double instrumentation takes place. If you're using the
import hook to type check your code only during tests and don't want to include
``typeguard`` as a run-time dependency, you can use a dummy replacement for the
decorator.

For example, the following snippet will only import the decorator during a pytest_ run::

import sys

if "pytest" in sys.modules:
from typeguard import typechecked
else:
from typing import TypeVar
_T = TypeVar("_T")

def typechecked(target: _T, **kwargs) -> _T:
return target if target else typechecked

.. _pytest: https://docs.pytest.org/

Suppressing type checks
-----------------------

Expand Down
20 changes: 18 additions & 2 deletions docs/versionhistory.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,27 @@ This library adheres to `Semantic Versioning 2.0 <https://semver.org/#semantic-v

**UNRELEASED**

- **BACKWARD INCOMPATIBLE** ``check_type()`` no longer uses the global configuration.
It now uses the default configuration values, unless overridden with an explicit
``config`` argument.
- **BACKWARD INCOMPATIBLE** Removed ``CallMemo`` from the API
- **BACKWARD INCOMPATIBLE** Required checkers to use the configuration from
``memo.config``, rather than the global configuration
- Added keyword arguments to ``@typechecked``, allowing users to override settings on a
per-function basis
- Added support for using ``suppress_type_checks()`` as a decorator
- Added support for type checking against nonlocal classes defined within the same
parent function as the instrumented function
- Changed instrumentation to statically copy the function annotations to avoid having to
look up the function object at run time
- Improved support for avoiding type checks against imports declared in
``if TYPE_CHECKING:`` blocks
- Fixed ``check_type`` not returning the passed value when checking against ``Any``, or
when type checking is being suppressed
- Fixed ``suppress_type_checks()`` not ending the suppression if the context block
raises an exception
- Fixed checking non-dictionary objects against a ``TypedDict`` annotation
(PR by Tolker-KU)
- Fixed ``suppress_type_checks()`` stopping type check suppression if an exception is
raised in the context manager block

**3.0.2** (2023-03-22)

Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ select = [
"B0", # flake8-bugbear
]
ignore = [
"PGH001"
"PGH001",
"B008",
]
target-version = "py37"
src = ["src"]
Expand Down
2 changes: 1 addition & 1 deletion src/typeguard/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
from ._importhook import ImportHookManager as ImportHookManager
from ._importhook import TypeguardFinder as TypeguardFinder
from ._importhook import install_import_hook as install_import_hook
from ._memo import CallMemo as CallMemo
from ._memo import TypeCheckMemo as TypeCheckMemo
from ._suppression import suppress_type_checks as suppress_type_checks
from ._utils import Unset as Unset

# Re-export imports so they look like they live directly in this package
for value in list(locals().values()):
Expand Down

0 comments on commit 8fbee4c

Please sign in to comment.