Skip to content

Commit

Permalink
add warnings settings to control and filter warnings
Browse files Browse the repository at this point in the history
  • Loading branch information
voidZXL committed Oct 24, 2024
1 parent c3a84ef commit 940966e
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 58 deletions.
10 changes: 6 additions & 4 deletions utype/decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from .parser.rule import Lax, Rule
from .utils import exceptions as exc
from .utils.datastructures import Unprovided, unprovided
from .settings import warning_settings

T = TypeVar("T")
FUNC = TypeVar("FUNC")
Expand All @@ -34,9 +35,9 @@ def parse(
eager: bool = False,
) -> Union[T, Callable[[T], T]]:
if ignore_params and ignore_result:
warnings.warn(
warning_settings.warn(
f"@utype.parse: you turn off both params and result parse in @parse decorator,"
f" which is basically meaningless..."
f" which is basically meaningless...", warning_settings.parse_ignore_both_params_and_result
)

def decorator(func: T) -> T:
Expand Down Expand Up @@ -103,9 +104,10 @@ def decorator(cls: CLS) -> CLS:
post_setattr=post_setattr, post_delattr=post_delattr
)
elif post_setattr or post_delattr:
warnings.warn(
warning_settings.warn(
f"@utype.dataclass received post_delattr / post_setattr "
f'without "set_properties=True", these params won\'t take effect'
f'without "set_properties=True", these params won\'t take effect',
warning_settings.dataclass_setattr_delattr_not_effect
)

cls.__parser__ = parser
Expand Down
9 changes: 7 additions & 2 deletions utype/parser/cls.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,13 @@ def globals(self) -> dict:
current_obj = dic.get(name)
if current_obj:
if getattr(current_obj, '__qualname__', None) != getattr(self.obj, '__qualname__', None):
warnings.warn(f'Parser object: {self.obj} got conflict object: {current_obj} '
f'with same name: {repr(name)}, it may affect the ForwardRef resolve')
from ..settings import warning_settings
warning_settings.warn(
f'Parser object: {self.obj} got conflict object: {current_obj} '
f'with same name: {repr(name)}, it may affect the ForwardRef resolve',
warning_settings.globals_name_conflict
)

dic[name] = self.obj
# !IMPORTANT: we need to override __name__ for current obj
# cause in the locals, same name may be the different object, we should be careful about that
Expand Down
42 changes: 25 additions & 17 deletions utype/parser/field.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import inspect
import warnings
from collections.abc import Mapping
from datetime import date, datetime, time, timedelta, timezone
from decimal import Decimal
Expand All @@ -14,6 +13,7 @@
from ..utils.functional import copy_value, get_name, multi
from .options import Options, RuntimeContext
from .rule import ConstraintMode, Lax, LogicalType, Rule, resolve_forward_type
from ..settings import warning_settings

represent = repr

Expand Down Expand Up @@ -563,10 +563,11 @@ def setup(self, options: Options):
trans = Rule.transformer_cls.resolver_transformer(self.type)
if not trans:
if options.unresolved_types == options.THROW:
warnings.warn(
warning_settings.warn(
f"Field(name={repr(self.name)}) got unresolved type: {self.type}, "
f'and Options.unresolved_types == "throw", which will raise error'
f" in the runtime if the input value type does not match"
f" in the runtime if the input value type does not match",
warning_settings.field_unresolved_types_with_throw_options
)
else:
self.input_transformer = trans
Expand All @@ -578,10 +579,11 @@ def setup(self, options: Options):
trans = Rule.transformer_cls.resolver_transformer(self.output_type)
if not trans:
if options.unresolved_types == options.THROW:
warnings.warn(
warning_settings.warn(
f"Field(name={repr(self.name)}) got unresolved output type: {self.output_type}, "
f'and Options.unresolved_types == "throw", which will raise error'
f" in the runtime if the input value type does not match"
f" in the runtime if the input value type does not match",
warning_settings.field_unresolved_types_with_throw_options
)
else:
self.output_transformer = trans
Expand Down Expand Up @@ -918,40 +920,46 @@ def check_function(self, func):

if self.positional_only:
if self.field.alias:
warnings.warn(
warning_settings.warn(
f"{func}: Field(name={repr(self.name)}).alias ({repr(self.field.alias)}) "
f"has no meanings in positional only params,"
f" please consider move it"
f" please consider move it",
warning_settings.field_alias_on_positional_args
)
if self.field.alias_from:
warnings.warn(
warning_settings.warn(
f"{func}: Field(name={repr(self.name)}).alias_from ({repr(self.field.alias_from)}) "
f"has no meanings in positional only params,"
f" please consider move it"
f" please consider move it",
warning_settings.field_alias_on_positional_args
)
if self.case_insensitive:
warnings.warn(
warning_settings.warn(
f"{func}: Field(name={repr(self.name)}).case_insensitive "
f"has no meanings in positional only params,"
f" please consider move it"
f" please consider move it",
warning_settings.field_case_sensitive_on_positional_args
)

if self.no_output:
warnings.warn(
warning_settings.warn(
f"{func}: Field(name={repr(self.name)}).no_output has no meanings in function params,"
f" please consider move it"
f" please consider move it",
warning_settings.field_invalid_params_in_function
)

if self.immutable:
warnings.warn(
warning_settings.warn(
f"{func}: Field(name={repr(self.name)}).immutable has no meanings in function params, "
f"please consider move it"
f"please consider move it",
warning_settings.field_invalid_params_in_function
)

if self.field.repr:
warnings.warn(
warning_settings.warn(
f"{func}: Field(name={repr(self.name)}).repr has no meanings in function params,"
f" please consider move it"
f" please consider move it",
warning_settings.field_invalid_params_in_function
)

# 1. deal with parse: use context
Expand Down
34 changes: 20 additions & 14 deletions utype/parser/func.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import inspect
import warnings
from collections.abc import (AsyncGenerator, AsyncIterable, AsyncIterator,
Callable, Generator, Iterable, Iterator, Mapping)
from functools import wraps
Expand All @@ -13,6 +12,7 @@
from .field import ParserField
from .options import Options, RuntimeContext
from .rule import Rule, resolve_forward_type
from ..settings import warning_settings

LAMBDA_NAME = (lambda: None).__name__
LOCALS_NAME = "<locals>"
Expand Down Expand Up @@ -256,9 +256,10 @@ def __init__(self, func, options: Options = None, from_class: type = None):
f"without declaring the **kwargs variable"
)
if self.kw_var and not self.options.addition:
warnings.warn(
warning_settings.warn(
f"FunctionParser: {func}, specified **{self.kw_var}"
f" but set addition=False, {self.kw_var} will always be empty"
f" but set addition=False, {self.kw_var} will always be empty",
warning_settings.function_kwargs_With_no_addition,
)

if self.options.no_default:
Expand All @@ -272,12 +273,14 @@ def __init__(self, func, options: Options = None, from_class: type = None):
)

if self.options.immutable:
warnings.warn(
f"FunctionParser: {func}, specified immutable=True in Options, which is useless"
warning_settings(
f"FunctionParser: {func}, specified immutable=True in Options, which is useless",
warning_settings.function_invalid_options
)
if self.options.secret_names:
warnings.warn(
f"FunctionParser: {func}, specified secret_names in Options, which is useless"
warning_settings.warn(
f"FunctionParser: {func}, specified secret_names in Options, which is useless",
warning_settings.function_invalid_options
)

self.position_type = None
Expand Down Expand Up @@ -327,9 +330,10 @@ def generate_return_types(self):
self.generator_return_type,
) = self.return_type.__args__
else:
warnings.warn(
warning_settings.warn(
f"Invalid return type annotation: {self.return_annotation} "
f"for generator function, should be Generator[...] / Iterator[...] / Iterable[...]"
f"for generator function, should be Generator[...] / Iterator[...] / Iterable[...]",
warning_settings.function_invalid_return_annotation
)
elif self.is_async_generator:
if self.return_type.__origin__ in (AsyncIterable, AsyncIterator):
Expand All @@ -340,10 +344,11 @@ def generate_return_types(self):
self.generator_send_type,
) = self.return_type.__args__
else:
warnings.warn(
warning_settings.warn(
f"Invalid return type annotation: {self.return_annotation} "
f"for async generator function, should be "
f"AsyncGenerator[...] / AsyncIterator[...] / AsyncIterable[...]"
f"AsyncGenerator[...] / AsyncIterator[...] / AsyncIterable[...]",
warning_settings.function_invalid_return_annotation
)

@cached_property
Expand Down Expand Up @@ -401,9 +406,10 @@ def generate_fields(self):
else:
annotation = param.annotation
if is_final(annotation) or is_classvar(annotation):
warnings.warn(
warning_settings.warn(
f"{self.obj}: param: {repr(name)} invalid annotation: {annotation}, "
f"this is only for class variables, please use the type directly"
f"this is only for class variables, please use the type directly",
warning_settings.function_invalid_params_annotation
)
# args = get_args(annotation)
# annotation = args[0] if args else None
Expand Down Expand Up @@ -494,7 +500,7 @@ def validate_fields(self):
if v.kind == v.POSITIONAL_ONLY:
raise SyntaxError(msg)
else:
warnings.warn(msg)
warning_settings.warn(msg, warning_settings.function_non_default_follows_default_args)
else:
optional_name = k

Expand Down
9 changes: 5 additions & 4 deletions utype/parser/options.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import inspect
import warnings
from typing import Any, Callable, List, Optional, Set, Type, Union

from ..utils import exceptions as exc
# from ..utils.base import ParamsCollector
from ..utils.compat import Literal
from ..utils.datastructures import unprovided
from ..utils.functional import multi
from ..utils.transform import TypeTransformer
from ..settings import warning_settings

DEFAULT_SECRET_NAMES = (
"password",
Expand Down Expand Up @@ -175,8 +174,9 @@ def __init__(

if not collect_errors:
if max_errors:
warnings.warn(
f"Options with max_errors: {max_errors} should turn on collect_errors=True"
warning_settings.warn(
f"Options with max_errors: {max_errors} should turn on collect_errors=True",
warning_settings.options_max_errors_with_no_collect_errors
)
max_errors = None

Expand Down Expand Up @@ -477,5 +477,6 @@ def handle_error(
raise exc.CollectedParseError(errors=errors)

def collect_waring(self, warning, category=None):
import warnings
warnings.warn(warning, category=category)
self.warnings.append(warning)
42 changes: 25 additions & 17 deletions utype/parser/rule.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import re
import typing
import warnings
from collections import deque
from decimal import Decimal
from enum import Enum, EnumMeta
Expand All @@ -14,6 +13,7 @@
from ..utils.datastructures import unprovided
from ..utils.functional import multi, pop
from ..utils.transform import TypeTransformer
from ..settings import warning_settings
from .options import RuntimeContext

T = typing.TypeVar("T")
Expand Down Expand Up @@ -535,15 +535,17 @@ def valid_length(self, bounds: dict):
if not hasattr(self.origin_type, "__len__"):
# just warning here, we will coerce to str in runtime
if issubclass(self.origin_type, (int, float, Decimal)):
warnings.warn(
warning_settings.warn(
f"Rule specify length constraints for type: {self.origin_type} "
f"that does not support length, we recommend to use "
f'"max_digits" and "round" for number types'
f'"max_digits" and "round" for number types',
warning_settings.rule_length_constraints_on_unsupported_types
)
else:
warnings.warn(
warning_settings.warn(
f"Rule specify length constraints for type: {self.origin_type} "
f"that does not support length, value will be convert to str to validate length"
f"that does not support length, value will be convert to str to validate length",
warning_settings.rule_length_constraints_on_unsupported_types
)

def valid_bounds(self, bounds: dict):
Expand Down Expand Up @@ -1188,9 +1190,10 @@ def __init_subclass__(cls, **kwargs):
cls.__origin__
)
if not cls.__origin_transformer__:
warnings.warn(
warning_settings.warn(
f"{cls}: origin type: {cls.__origin__} got no transformer resolved, "
f"will just pass {cls.__origin__}(data) at runtime"
f"will just pass {cls.__origin__}(data) at runtime",
warning_settings.rule_no_origin_transformer
)

if cls.__args__:
Expand Down Expand Up @@ -1221,28 +1224,31 @@ def _cannot_getitem(*_args):
cls.__origin__, NONE_ARG_ALLOWED_TYPES
):
continue
warnings.warn(
warning_settings(
f"None arg: {arg} detected where origin type: {cls.__origin__} is not in "
f"{NONE_ARG_ALLOWED_TYPES}"
f"{NONE_ARG_ALLOWED_TYPES}",
warning_settings.rule_none_arg_in_unsupported_origin
)
continue

if not isinstance(arg, type):
raise TypeError(f"Invalid arg: {arg}, must be a class")
transformer = cls.transformer_cls.resolver_transformer(arg)
if not transformer:
warnings.warn(
warning_settings.warn(
f"{cls}: arg type: {arg} got no transformer resolved, "
f"will just pass {arg}(data) at runtime"
f"will just pass {arg}(data) at runtime",
warning_settings.rule_no_arg_transformer
)

arg_transformers.append(transformer)
cls.__arg_transformers__ = tuple(arg_transformers)
cls.__args_parser__ = cls.resolve_args_parser()
if not cls.__args_parser__:
warnings.warn(
warning_settings.warn(
f"{cls}: type: {cls.__origin__} with __args__ cannot resolve an args parser, "
f"you should inherit resolve_args_parser and specify yourself"
f"you should inherit resolve_args_parser and specify yourself",
warning_settings.rule_none_arg_in_unsupported_origin
)
else:
if class_getitem and not cls.__dict__.get("__class_getitem__"):
Expand Down Expand Up @@ -1278,9 +1284,10 @@ def annotate(

if type_ == Any:
if args_:
warnings.warn(f"Any type cannot specify args: {args_}")
warning_settings.warn(f"Any type cannot specify args: {args_}", warning_settings.rule_args_in_any)
if constraints:
warnings.warn(f"Any type cannot specify constraints: {constraints}")
warning_settings.warn(f"Any type cannot specify constraints: {constraints}",
warning_settings.rule_args_in_any)
return Rule

elif type_ == Literal:
Expand Down Expand Up @@ -1805,9 +1812,10 @@ def resolve_forward_refs(cls):
resolved = True
transformer = cls.transformer_cls.resolver_transformer(arg)
if not transformer:
warnings.warn(
warning_settings.warn(
f"{cls}: arg type: {arg} got no transformer resolved, "
f"will just pass {arg}(data) at runtime"
f"will just pass {arg}(data) at runtime",
warning_settings.rule_no_arg_transformer
)
trans = transformer or trans
args.append(arg)
Expand Down
Loading

0 comments on commit 940966e

Please sign in to comment.