Skip to content

Commit

Permalink
upgrade typeguard
Browse files Browse the repository at this point in the history
  • Loading branch information
madhur-ob committed Feb 28, 2025
1 parent 7617add commit c5777ee
Show file tree
Hide file tree
Showing 31 changed files with 8,439 additions and 720 deletions.
354 changes: 259 additions & 95 deletions metaflow/_vendor/typeguard/_checkers.py

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions metaflow/_vendor/typeguard/_config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from collections.abc import Collection
from collections.abc import Iterable
from dataclasses import dataclass
from enum import Enum, auto
from typing import TYPE_CHECKING, TypeVar
Expand Down Expand Up @@ -49,11 +49,11 @@ class CollectionCheckStrategy(Enum):
FIRST_ITEM = auto()
ALL_ITEMS = auto()

def iterate_samples(self, collection: Collection[T]) -> Collection[T]:
def iterate_samples(self, collection: Iterable[T]) -> Iterable[T]:
if self is CollectionCheckStrategy.FIRST_ITEM:
if len(collection):
try:
return [next(iter(collection))]
else:
except StopIteration:
return ()
else:
return collection
Expand Down
20 changes: 8 additions & 12 deletions metaflow/_vendor/typeguard/_decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,18 @@
from ._transformer import TypeguardTransformer
from ._utils import Unset, function_name, get_stacklevel, is_method_of, unset

T_CallableOrType = TypeVar("T_CallableOrType", bound=Callable[..., Any])

if TYPE_CHECKING:
from typeshed.stdlib.types import _Cell

_F = TypeVar("_F")

def typeguard_ignore(f: _F) -> _F:
def typeguard_ignore(f: T_CallableOrType) -> T_CallableOrType:
"""This decorator is a noop during static type-checking."""
return f

else:
from typing import no_type_check as typeguard_ignore # noqa: F401

T_CallableOrType = TypeVar("T_CallableOrType", bound=Callable[..., Any])


def make_cell(value: object) -> _Cell:
return (lambda: value).__closure__[0] # type: ignore[index]
Expand Down Expand Up @@ -133,13 +131,11 @@ def typechecked(
typecheck_fail_callback: TypeCheckFailCallback | Unset = unset,
collection_check_strategy: CollectionCheckStrategy | Unset = unset,
debug_instrumentation: bool | Unset = unset,
) -> Callable[[T_CallableOrType], T_CallableOrType]:
...
) -> Callable[[T_CallableOrType], T_CallableOrType]: ...


@overload
def typechecked(target: T_CallableOrType) -> T_CallableOrType:
...
def typechecked(target: T_CallableOrType) -> T_CallableOrType: ...


def typechecked(
Expand Down Expand Up @@ -215,9 +211,9 @@ def typechecked(
return target

# Find either the first Python wrapper or the actual function
wrapper_class: type[classmethod[Any, Any, Any]] | type[
staticmethod[Any, Any]
] | None = None
wrapper_class: (
type[classmethod[Any, Any, Any]] | type[staticmethod[Any, Any]] | None
) = None
if isinstance(target, (classmethod, staticmethod)):
wrapper_class = target.__class__
target = target.__func__
Expand Down
65 changes: 33 additions & 32 deletions metaflow/_vendor/typeguard/_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import sys
import warnings
from typing import Any, Callable, NoReturn, TypeVar, overload
from typing import Any, Callable, NoReturn, TypeVar, Union, overload

from . import _suppression
from ._checkers import BINARY_MAGIC_METHODS, check_type_internal
Expand Down Expand Up @@ -32,8 +32,7 @@ def check_type(
forward_ref_policy: ForwardRefPolicy = ...,
typecheck_fail_callback: TypeCheckFailCallback | None = ...,
collection_check_strategy: CollectionCheckStrategy = ...,
) -> T:
...
) -> T: ...


@overload
Expand All @@ -44,16 +43,15 @@ def check_type(
forward_ref_policy: ForwardRefPolicy = ...,
typecheck_fail_callback: TypeCheckFailCallback | None = ...,
collection_check_strategy: CollectionCheckStrategy = ...,
) -> Any:
...
) -> Any: ...


def check_type(
value: object,
expected_type: Any,
*,
forward_ref_policy: ForwardRefPolicy = TypeCheckConfiguration().forward_ref_policy,
typecheck_fail_callback: (TypeCheckFailCallback | None) = (
typecheck_fail_callback: TypeCheckFailCallback | None = (
TypeCheckConfiguration().typecheck_fail_callback
),
collection_check_strategy: CollectionCheckStrategy = (
Expand All @@ -80,7 +78,7 @@ def check_type(
corresponding fields in :class:`TypeCheckConfiguration`.
:param value: value to be checked against ``expected_type``
:param expected_type: a class or generic type instance
:param expected_type: a class or generic type instance, or a tuple of such things
:param forward_ref_policy: see :attr:`TypeCheckConfiguration.forward_ref_policy`
:param typecheck_fail_callback:
see :attr`TypeCheckConfiguration.typecheck_fail_callback`
Expand All @@ -90,6 +88,9 @@ def check_type(
:raises TypeCheckError: if there is a type mismatch
"""
if type(expected_type) is tuple:
expected_type = Union[expected_type]

config = TypeCheckConfiguration(
forward_ref_policy=forward_ref_policy,
typecheck_fail_callback=typecheck_fail_callback,
Expand Down Expand Up @@ -244,7 +245,7 @@ def check_variable_assignment(
value: object, varname: str, annotation: Any, memo: TypeCheckMemo
) -> Any:
if _suppression.type_checks_suppressed:
return
return value

try:
check_type_internal(value, annotation, memo)
Expand All @@ -262,36 +263,36 @@ def check_variable_assignment(
def check_multi_variable_assignment(
value: Any, targets: list[dict[str, Any]], memo: TypeCheckMemo
) -> Any:
if _suppression.type_checks_suppressed:
return

if max(len(target) for target in targets) == 1:
iterated_values = [value]
else:
iterated_values = list(value)

for expected_types in targets:
value_index = 0
for ann_index, (varname, expected_type) in enumerate(expected_types.items()):
if varname.startswith("*"):
varname = varname[1:]
keys_left = len(expected_types) - 1 - ann_index
next_value_index = len(iterated_values) - keys_left
obj: object = iterated_values[value_index:next_value_index]
value_index = next_value_index
else:
obj = iterated_values[value_index]
value_index += 1

try:
check_type_internal(obj, expected_type, memo)
except TypeCheckError as exc:
qualname = qualified_name(obj, add_class_prefix=True)
exc.append_path_element(f"value assigned to {varname} ({qualname})")
if memo.config.typecheck_fail_callback:
memo.config.typecheck_fail_callback(exc, memo)
if not _suppression.type_checks_suppressed:
for expected_types in targets:
value_index = 0
for ann_index, (varname, expected_type) in enumerate(
expected_types.items()
):
if varname.startswith("*"):
varname = varname[1:]
keys_left = len(expected_types) - 1 - ann_index
next_value_index = len(iterated_values) - keys_left
obj: object = iterated_values[value_index:next_value_index]
value_index = next_value_index
else:
raise
obj = iterated_values[value_index]
value_index += 1

try:
check_type_internal(obj, expected_type, memo)
except TypeCheckError as exc:
qualname = qualified_name(obj, add_class_prefix=True)
exc.append_path_element(f"value assigned to {varname} ({qualname})")
if memo.config.typecheck_fail_callback:
memo.config.typecheck_fail_callback(exc, memo)
else:
raise

return iterated_values[0] if len(iterated_values) == 1 else iterated_values

Expand Down
53 changes: 40 additions & 13 deletions metaflow/_vendor/typeguard/_pytest_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,45 @@

import sys
import warnings

from pytest import Config, Parser
from typing import TYPE_CHECKING, Any, Literal

from metaflow._vendor.typeguard._config import CollectionCheckStrategy, ForwardRefPolicy, global_config
from metaflow._vendor.typeguard._exceptions import InstrumentationWarning
from metaflow._vendor.typeguard._importhook import install_import_hook
from metaflow._vendor.typeguard._utils import qualified_name, resolve_reference

if TYPE_CHECKING:
from pytest import Config, Parser


def pytest_addoption(parser: Parser) -> None:
def add_ini_option(
opt_type: (
Literal["string", "paths", "pathlist", "args", "linelist", "bool"] | None
),
) -> None:
parser.addini(
group.options[-1].names()[0][2:],
group.options[-1].attrs()["help"],
opt_type,
)

group = parser.getgroup("typeguard")
group.addoption(
"--typeguard-packages",
action="store",
help="comma separated name list of packages and modules to instrument for "
"type checking, or :all: to instrument all modules loaded after typeguard",
)
add_ini_option("linelist")

group.addoption(
"--typeguard-debug-instrumentation",
action="store_true",
help="print all instrumented code to stderr",
)
add_ini_option("bool")

group.addoption(
"--typeguard-typecheck-fail-callback",
action="store",
Expand All @@ -33,6 +50,8 @@ def pytest_addoption(parser: Parser) -> None:
"handle a TypeCheckError"
),
)
add_ini_option("string")

group.addoption(
"--typeguard-forward-ref-policy",
action="store",
Expand All @@ -42,21 +61,31 @@ def pytest_addoption(parser: Parser) -> None:
"annotations"
),
)
add_ini_option("string")

group.addoption(
"--typeguard-collection-check-strategy",
action="store",
choices=list(CollectionCheckStrategy.__members__),
help="determines how thoroughly to check collections (list, dict, etc)",
)
add_ini_option("string")


def pytest_configure(config: Config) -> None:
packages_option = config.getoption("typeguard_packages")
if packages_option:
if packages_option == ":all:":
packages: list[str] | None = None
def getoption(name: str) -> Any:
return config.getoption(name.replace("-", "_")) or config.getini(name)

packages: list[str] | None = []
if packages_option := config.getoption("typeguard_packages"):
packages = [pkg.strip() for pkg in packages_option.split(",")]
elif packages_ini := config.getini("typeguard-packages"):
packages = packages_ini

if packages:
if packages == [":all:"]:
packages = None
else:
packages = [pkg.strip() for pkg in packages_option.split(",")]
already_imported_packages = sorted(
package for package in packages if package in sys.modules
)
Expand All @@ -70,11 +99,11 @@ def pytest_configure(config: Config) -> None:

install_import_hook(packages=packages)

debug_option = config.getoption("typeguard_debug_instrumentation")
debug_option = getoption("typeguard-debug-instrumentation")
if debug_option:
global_config.debug_instrumentation = True

fail_callback_option = config.getoption("typeguard_typecheck_fail_callback")
fail_callback_option = getoption("typeguard-typecheck-fail-callback")
if fail_callback_option:
callback = resolve_reference(fail_callback_option)
if not callable(callback):
Expand All @@ -85,14 +114,12 @@ def pytest_configure(config: Config) -> None:

global_config.typecheck_fail_callback = callback

forward_ref_policy_option = config.getoption("typeguard_forward_ref_policy")
forward_ref_policy_option = getoption("typeguard-forward-ref-policy")
if forward_ref_policy_option:
forward_ref_policy = ForwardRefPolicy.__members__[forward_ref_policy_option]
global_config.forward_ref_policy = forward_ref_policy

collection_check_strategy_option = config.getoption(
"typeguard_collection_check_strategy"
)
collection_check_strategy_option = getoption("typeguard-collection-check-strategy")
if collection_check_strategy_option:
collection_check_strategy = CollectionCheckStrategy.__members__[
collection_check_strategy_option
Expand Down
8 changes: 3 additions & 5 deletions metaflow/_vendor/typeguard/_suppression.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,15 @@


@overload
def suppress_type_checks(func: Callable[P, T]) -> Callable[P, T]:
...
def suppress_type_checks(func: Callable[P, T]) -> Callable[P, T]: ...


@overload
def suppress_type_checks() -> ContextManager[None]:
...
def suppress_type_checks() -> ContextManager[None]: ...


def suppress_type_checks(
func: Callable[P, T] | None = None
func: Callable[P, T] | None = None,
) -> Callable[P, T] | ContextManager[None]:
"""
Temporarily suppress all type checking.
Expand Down
Loading

0 comments on commit c5777ee

Please sign in to comment.