diff --git a/dataclasses_json/api.py b/dataclasses_json/api.py index 3481e93f..29134816 100644 --- a/dataclasses_json/api.py +++ b/dataclasses_json/api.py @@ -2,7 +2,7 @@ import json from typing import Any, Callable, Dict, List, Optional, Tuple, Type, TypeVar, Union, overload -from dataclasses_json.cfg import config, LetterCase +from dataclasses_json.cfg import config, LetterCase, LetterCaseCallable from dataclasses_json.core import (Json, _ExtendedEncoder, _asdict, _decode_dataclass) from dataclasses_json.mm import (JsonData, SchemaType, build_schema) @@ -103,16 +103,16 @@ def schema(cls: Type[A], @overload -def dataclass_json(_cls: None = ..., *, letter_case: Optional[LetterCase] = ..., +def dataclass_json(_cls: None = ..., *, letter_case: Union[LetterCaseCallable, LetterCase, None] = ..., undefined: Optional[Union[str, Undefined]] = ...) -> Callable[[Type[T]], Type[T]]: ... @overload -def dataclass_json(_cls: Type[T], *, letter_case: Optional[LetterCase] = ..., +def dataclass_json(_cls: Type[T], *, letter_case: Union[LetterCaseCallable, LetterCase, None] = ..., undefined: Optional[Union[str, Undefined]] = ...) -> Type[T]: ... -def dataclass_json(_cls: Optional[Type[T]] = None, *, letter_case: Optional[LetterCase] = None, +def dataclass_json(_cls: Optional[Type[T]] = None, *, letter_case: Union[LetterCaseCallable, LetterCase, None] = None, undefined: Optional[Union[str, Undefined]] = None) -> Union[Callable[[Type[T]], Type[T]], Type[T]]: """ Based on the code in the `dataclasses` module to handle optional-parens @@ -132,7 +132,7 @@ def wrap(cls: Type[T]) -> Type[T]: return wrap(_cls) -def _process_class(cls: Type[T], letter_case: Optional[LetterCase], +def _process_class(cls: Type[T], letter_case: Union[LetterCaseCallable, LetterCase, None], undefined: Optional[Union[str, Undefined]]) -> Type[T]: if letter_case is not None or undefined is not None: cls.dataclass_json_config = config(letter_case=letter_case, # type: ignore[attr-defined] diff --git a/dataclasses_json/cfg.py b/dataclasses_json/cfg.py index 930e51e0..6c182ec2 100644 --- a/dataclasses_json/cfg.py +++ b/dataclasses_json/cfg.py @@ -47,11 +47,13 @@ def __init__(self): global_config = _GlobalConfig() +LetterCaseCallable = Callable[[str], str] + class LetterCase(Enum): - CAMEL = camelcase - KEBAB = spinalcase - SNAKE = snakecase - PASCAL = pascalcase + CAMEL: LetterCaseCallable = camelcase + KEBAB: LetterCaseCallable = spinalcase + SNAKE: LetterCaseCallable = snakecase + PASCAL: LetterCaseCallable = pascalcase def config(metadata: Optional[dict] = None, *, @@ -60,7 +62,7 @@ def config(metadata: Optional[dict] = None, *, encoder: Optional[Callable] = None, decoder: Optional[Callable] = None, mm_field: Optional[MarshmallowField] = None, - letter_case: Union[Callable[[str], str], LetterCase, None] = None, + letter_case: Union[LetterCaseCallable, LetterCase, None] = None, undefined: Optional[Union[str, Undefined]] = None, field_name: Optional[str] = None, exclude: Optional[Callable[[T], bool]] = None, diff --git a/dataclasses_json/stringcase.py b/dataclasses_json/stringcase.py index da0f546f..611e92e4 100644 --- a/dataclasses_json/stringcase.py +++ b/dataclasses_json/stringcase.py @@ -24,9 +24,10 @@ # Copyright © 2020 Louis-Philippe Véronneau import re +from typing import Literal, cast -def uplowcase(string, case): +def uplowcase(string: str, case: Literal["up", "low"]) -> str: """Convert string into upper or lower case. Args: @@ -42,7 +43,7 @@ def uplowcase(string, case): return str(string).lower() -def capitalcase(string): +def capitalcase(string: str) -> str: """Convert string into capital case. First letters will be uppercase. @@ -60,7 +61,7 @@ def capitalcase(string): return uplowcase(string[0], 'up') + string[1:] -def camelcase(string): +def camelcase(string: str) -> str: """ Convert string into camel case. Args: @@ -76,11 +77,11 @@ def camelcase(string): return string return (uplowcase(string[0], 'low') + re.sub(r"[\-_\.\s]([a-z0-9])", - lambda matched: uplowcase(matched.group(1), 'up'), + lambda matched: uplowcase(cast(str, matched.group(1)), 'up'), string[1:])) -def snakecase(string): +def snakecase(string: str) -> str: """Convert string into snake case. Join punctuation with underscore @@ -97,11 +98,11 @@ def snakecase(string): return string return (uplowcase(string[0], 'low') + re.sub(r"[A-Z0-9]", - lambda matched: '_' + uplowcase(matched.group(0), 'low'), + lambda matched: '_' + uplowcase(cast(str, matched.group(0)), 'low'), string[1:])) -def spinalcase(string): +def spinalcase(string: str) -> str: """Convert string into spinal case. Join punctuation with hyphen. @@ -116,7 +117,7 @@ def spinalcase(string): return re.sub(r"_", "-", snakecase(string)) -def pascalcase(string): +def pascalcase(string: str) -> str: """Convert string into pascal case. Args: