u-toolkit 0.1.18__tar.gz → 0.1.20__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- u_toolkit-0.1.20/PKG-INFO +5 -0
- {u_toolkit-0.1.18 → u_toolkit-0.1.20}/pyproject.toml +1 -14
- {u_toolkit-0.1.18 → u_toolkit-0.1.20}/src/u_toolkit/enum.py +7 -7
- {u_toolkit-0.1.18 → u_toolkit-0.1.20}/src/u_toolkit/function.py +1 -1
- {u_toolkit-0.1.18 → u_toolkit-0.1.20}/src/u_toolkit/helpers.py +2 -0
- u_toolkit-0.1.20/src/u_toolkit/merge.py +89 -0
- u_toolkit-0.1.20/src/u_toolkit/naming.py +94 -0
- u_toolkit-0.1.20/src/u_toolkit/signature.py +110 -0
- u_toolkit-0.1.20/tests/test_enum.py +130 -0
- u_toolkit-0.1.20/tests/test_function.py +26 -0
- u_toolkit-0.1.20/tests/test_helpers.py +13 -0
- u_toolkit-0.1.20/uv.lock +189 -0
- u_toolkit-0.1.18/PKG-INFO +0 -11
- u_toolkit-0.1.18/src/u_toolkit/__init__.py +0 -9
- u_toolkit-0.1.18/src/u_toolkit/alias_generators.py +0 -49
- u_toolkit-0.1.18/src/u_toolkit/fastapi/cbv.py +0 -440
- u_toolkit-0.1.18/src/u_toolkit/fastapi/config.py +0 -9
- u_toolkit-0.1.18/src/u_toolkit/fastapi/exception.py +0 -163
- u_toolkit-0.1.18/src/u_toolkit/fastapi/helpers.py +0 -18
- u_toolkit-0.1.18/src/u_toolkit/fastapi/lifespan.py +0 -67
- u_toolkit-0.1.18/src/u_toolkit/fastapi/pagination.py +0 -79
- u_toolkit-0.1.18/src/u_toolkit/fastapi/responses.py +0 -67
- u_toolkit-0.1.18/src/u_toolkit/logger.py +0 -11
- u_toolkit-0.1.18/src/u_toolkit/merge.py +0 -31
- u_toolkit-0.1.18/src/u_toolkit/object.py +0 -0
- u_toolkit-0.1.18/src/u_toolkit/pydantic/fields.py +0 -24
- u_toolkit-0.1.18/src/u_toolkit/pydantic/models.py +0 -41
- u_toolkit-0.1.18/src/u_toolkit/pydantic/type_vars.py +0 -6
- u_toolkit-0.1.18/src/u_toolkit/signature.py +0 -72
- u_toolkit-0.1.18/src/u_toolkit/sqlalchemy/__init__.py +0 -0
- u_toolkit-0.1.18/src/u_toolkit/sqlalchemy/fields.py +0 -0
- u_toolkit-0.1.18/src/u_toolkit/sqlalchemy/function.py +0 -12
- u_toolkit-0.1.18/src/u_toolkit/sqlalchemy/orm/__init__.py +0 -0
- u_toolkit-0.1.18/src/u_toolkit/sqlalchemy/orm/fields.py +0 -20
- u_toolkit-0.1.18/src/u_toolkit/sqlalchemy/orm/models.py +0 -23
- u_toolkit-0.1.18/src/u_toolkit/sqlalchemy/table_info.py +0 -17
- u_toolkit-0.1.18/src/u_toolkit/sqlalchemy/type_vars.py +0 -6
- u_toolkit-0.1.18/tests/__init__.py +0 -0
- u_toolkit-0.1.18/tests/fastapi/__init__.py +0 -0
- u_toolkit-0.1.18/tests/fastapi/test_cbv.py +0 -116
- u_toolkit-0.1.18/uv.lock +0 -565
- {u_toolkit-0.1.18 → u_toolkit-0.1.20}/.github/workflows/publish.yml +0 -0
- {u_toolkit-0.1.18 → u_toolkit-0.1.20}/.gitignore +0 -0
- {u_toolkit-0.1.18 → u_toolkit-0.1.20}/README.md +0 -0
- {u_toolkit-0.1.18/src/u_toolkit/fastapi → u_toolkit-0.1.20/src/u_toolkit}/__init__.py +0 -0
- {u_toolkit-0.1.18 → u_toolkit-0.1.20}/src/u_toolkit/datetime.py +0 -0
- {u_toolkit-0.1.18 → u_toolkit-0.1.20}/src/u_toolkit/decorators.py +0 -0
- {u_toolkit-0.1.18 → u_toolkit-0.1.20}/src/u_toolkit/path.py +0 -0
- {u_toolkit-0.1.18/src/u_toolkit/pydantic → u_toolkit-0.1.20/tests}/__init__.py +0 -0
@@ -1,25 +1,15 @@
|
|
1
1
|
[project]
|
2
2
|
name = "u-toolkit"
|
3
|
-
version = "0.1.
|
3
|
+
version = "0.1.20"
|
4
4
|
description = "Add your description here"
|
5
5
|
readme = "README.md"
|
6
6
|
requires-python = ">=3.11"
|
7
7
|
dependencies = [
|
8
|
-
"pydantic>=2.11.3",
|
9
8
|
]
|
10
9
|
|
11
10
|
[project.scripts]
|
12
11
|
u-toolkit = "u_toolkit:main"
|
13
12
|
|
14
|
-
[project.optional-dependencies]
|
15
|
-
sqlalchemy = [
|
16
|
-
"sqlalchemy>=2.0.40",
|
17
|
-
]
|
18
|
-
fastapi = [
|
19
|
-
"fastapi>=0.115.12",
|
20
|
-
"pydantic-settings>=2.9.1",
|
21
|
-
]
|
22
|
-
|
23
13
|
[build-system]
|
24
14
|
requires = ["hatchling"]
|
25
15
|
build-backend = "hatchling.build"
|
@@ -27,12 +17,9 @@ build-backend = "hatchling.build"
|
|
27
17
|
[dependency-groups]
|
28
18
|
dev = [
|
29
19
|
"devtools>=0.12.2",
|
30
|
-
"httpx>=0.28.1",
|
31
20
|
"pytest>=8.3.5",
|
32
|
-
"python-multipart>=0.0.20",
|
33
21
|
"rich>=14.0.0",
|
34
22
|
"ruff>=0.11.6",
|
35
|
-
"uvicorn>=0.34.2",
|
36
23
|
]
|
37
24
|
|
38
25
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from enum import StrEnum, auto
|
2
2
|
|
3
|
-
from .
|
3
|
+
from .naming import to_camel, to_pascal, to_snake
|
4
4
|
|
5
5
|
|
6
6
|
__all__ = [
|
@@ -15,29 +15,29 @@ __all__ = [
|
|
15
15
|
|
16
16
|
class NameEnum(StrEnum):
|
17
17
|
@staticmethod
|
18
|
-
def _generate_next_value_(name, *_, **__) -> str:
|
18
|
+
def _generate_next_value_(name: str, *_, **__) -> str:
|
19
19
|
return name
|
20
20
|
|
21
21
|
|
22
22
|
class PascalEnum(StrEnum):
|
23
23
|
@staticmethod
|
24
|
-
def _generate_next_value_(name, *_, **__) -> str:
|
24
|
+
def _generate_next_value_(name: str, *_, **__) -> str:
|
25
25
|
return to_pascal(name)
|
26
26
|
|
27
27
|
|
28
28
|
class CamelEnum(StrEnum):
|
29
29
|
@staticmethod
|
30
|
-
def _generate_next_value_(name, *_, **__) -> str:
|
30
|
+
def _generate_next_value_(name: str, *_, **__) -> str:
|
31
31
|
return to_camel(name)
|
32
32
|
|
33
33
|
|
34
34
|
class SnakeEnum(StrEnum):
|
35
35
|
@staticmethod
|
36
|
-
def _generate_next_value_(name, *_, **__) -> str:
|
36
|
+
def _generate_next_value_(name: str, *_, **__) -> str:
|
37
37
|
return to_snake(name)
|
38
38
|
|
39
39
|
|
40
40
|
class TitleEnum(StrEnum):
|
41
41
|
@staticmethod
|
42
|
-
def _generate_next_value_(name, *_, **__) -> str:
|
43
|
-
return name.replace("_", " ").title()
|
42
|
+
def _generate_next_value_(name: str, *_, **__) -> str:
|
43
|
+
return name.replace("_", " ").strip().title()
|
@@ -0,0 +1,89 @@
|
|
1
|
+
from collections.abc import Iterable, Mapping
|
2
|
+
from typing import overload
|
3
|
+
|
4
|
+
|
5
|
+
def _merge_dict(
|
6
|
+
target: dict,
|
7
|
+
source: Mapping,
|
8
|
+
):
|
9
|
+
"""深层合并两个字典
|
10
|
+
|
11
|
+
:param target: 存放合并内容的字典
|
12
|
+
:param source: 来源, 因为不会修改, 所以只读映射就可以
|
13
|
+
:param exclude_keys: 需要排除的 keys
|
14
|
+
"""
|
15
|
+
|
16
|
+
for ok, ov in source.items():
|
17
|
+
v = target.get(ok)
|
18
|
+
# 如果两边都是映射类型, 就可以合并
|
19
|
+
if isinstance(v, dict) and isinstance(ov, Mapping):
|
20
|
+
_merge_dict(v, ov)
|
21
|
+
elif isinstance(v, list) and isinstance(ov, Iterable):
|
22
|
+
_merge_list(v, ov)
|
23
|
+
# 如果当前值允许进行相加的操作
|
24
|
+
# 并且不是字符串和数字
|
25
|
+
# 并且旧字典与当前值类型相同
|
26
|
+
elif (
|
27
|
+
hasattr(v, "__add__")
|
28
|
+
and not isinstance(v, str | int)
|
29
|
+
and type(v) is type(ov)
|
30
|
+
):
|
31
|
+
target[ok] = v + ov
|
32
|
+
# 否则使用有效的值
|
33
|
+
else:
|
34
|
+
target[ok] = v or ov
|
35
|
+
|
36
|
+
|
37
|
+
def _merge_list(target: list, source: Iterable):
|
38
|
+
for oi, ov in enumerate(source):
|
39
|
+
try:
|
40
|
+
v = target[oi]
|
41
|
+
except IndexError:
|
42
|
+
target[oi] = ov
|
43
|
+
break
|
44
|
+
|
45
|
+
if isinstance(v, dict) and isinstance(ov, Mapping):
|
46
|
+
merge(v, ov)
|
47
|
+
|
48
|
+
elif isinstance(v, list) and isinstance(ov, Iterable):
|
49
|
+
_merge_list(v, ov)
|
50
|
+
# 如果当前值允许进行相加的操作
|
51
|
+
# 并且不是字符串和数字
|
52
|
+
# 并且旧字典与当前值类型相同
|
53
|
+
elif (
|
54
|
+
hasattr(v, "__add__")
|
55
|
+
and not isinstance(v, str | int)
|
56
|
+
and type(v) is type(ov)
|
57
|
+
):
|
58
|
+
target[oi] = v + ov
|
59
|
+
else:
|
60
|
+
target[oi] = v or ov
|
61
|
+
|
62
|
+
|
63
|
+
@overload
|
64
|
+
def merge(target: list, source: Iterable): ...
|
65
|
+
@overload
|
66
|
+
def merge(target: dict, source: Mapping): ...
|
67
|
+
|
68
|
+
|
69
|
+
def merge(target, source):
|
70
|
+
for ok, ov in source.items():
|
71
|
+
v = target.get(ok)
|
72
|
+
|
73
|
+
# 如果两边都是映射类型, 就可以合并
|
74
|
+
if isinstance(v, dict) and isinstance(ov, Mapping):
|
75
|
+
_merge_dict(v, ov)
|
76
|
+
|
77
|
+
# 如果当前值允许进行相加的操作
|
78
|
+
# 并且不是字符串和数字
|
79
|
+
# 并且旧字典与当前值类型相同
|
80
|
+
elif (
|
81
|
+
hasattr(v, "__add__")
|
82
|
+
and not isinstance(v, str | int)
|
83
|
+
and type(v) is type(ov)
|
84
|
+
):
|
85
|
+
target[ok] = v + ov
|
86
|
+
|
87
|
+
# 否则使用有效的值
|
88
|
+
else:
|
89
|
+
target[ok] = v or ov
|
@@ -0,0 +1,94 @@
|
|
1
|
+
import re
|
2
|
+
from collections.abc import Callable
|
3
|
+
from typing import TypeVar, TypeVarTuple, overload
|
4
|
+
|
5
|
+
|
6
|
+
__all__ = ("to_camel", "to_pascal", "to_snake")
|
7
|
+
|
8
|
+
Ts = TypeVarTuple("Ts")
|
9
|
+
T = TypeVar("T")
|
10
|
+
|
11
|
+
|
12
|
+
def _arguments_handle(transform: Callable, *args, kwd_name: str, **kwds):
|
13
|
+
if len(args) > 1:
|
14
|
+
return tuple(transform(a) for a in args)
|
15
|
+
|
16
|
+
if args:
|
17
|
+
return transform(args[0])
|
18
|
+
|
19
|
+
return transform(kwds[kwd_name])
|
20
|
+
|
21
|
+
|
22
|
+
@overload
|
23
|
+
def to_pascal(snake: str) -> str: ...
|
24
|
+
@overload
|
25
|
+
def to_pascal(*snake: str) -> tuple[str, ...]: ...
|
26
|
+
|
27
|
+
|
28
|
+
def to_pascal(*args, **kwds):
|
29
|
+
pattern = re.compile("([0-9A-Za-z])_(?=[0-9A-Z])")
|
30
|
+
|
31
|
+
def transform(arg: str) -> str:
|
32
|
+
camel = arg.title()
|
33
|
+
return pattern.sub(lambda m: m.group(1), camel)
|
34
|
+
|
35
|
+
return _arguments_handle(transform, *args, kwd_name="snake", **kwds)
|
36
|
+
|
37
|
+
|
38
|
+
@overload
|
39
|
+
def to_camel(snake: str) -> str: ...
|
40
|
+
@overload
|
41
|
+
def to_camel(*snake: str) -> tuple[str, ...]: ...
|
42
|
+
|
43
|
+
|
44
|
+
def to_camel(*args, **kwds):
|
45
|
+
# If the string is already in camelCase and does not contain
|
46
|
+
# a digit followed by a lowercase letter, return it as it is
|
47
|
+
match_pattern = re.compile("^[a-z]+[A-Za-z0-9]*$")
|
48
|
+
search_pattern = re.compile(r"\d[a-z]")
|
49
|
+
|
50
|
+
def transform(arg: str):
|
51
|
+
if match_pattern.match(arg) and not search_pattern.search(arg):
|
52
|
+
return arg
|
53
|
+
|
54
|
+
camel = to_pascal(arg)
|
55
|
+
return re.sub("(^_*[A-Z])", lambda m: m.group(1).lower(), camel)
|
56
|
+
|
57
|
+
return _arguments_handle(transform, *args, kwd_name="snake", **kwds)
|
58
|
+
|
59
|
+
|
60
|
+
@overload
|
61
|
+
def to_snake(camel: str) -> str: ...
|
62
|
+
@overload
|
63
|
+
def to_snake(*camel: str) -> tuple[str, ...]: ...
|
64
|
+
|
65
|
+
|
66
|
+
def to_snake(*args, **kwds):
|
67
|
+
"""Convert a PascalCase, camelCase, kebab-case string to snake_case.""" # noqa: W505
|
68
|
+
|
69
|
+
# Handle the sequence of uppercase letters followed by
|
70
|
+
# a lowercase letter
|
71
|
+
def transform(arg: str):
|
72
|
+
snake = re.sub(
|
73
|
+
r"([A-Z]+)([A-Z][a-z])",
|
74
|
+
lambda m: f"{m.group(1)}_{m.group(2)}",
|
75
|
+
arg,
|
76
|
+
)
|
77
|
+
# Insert an underscore between a lowercase letter and
|
78
|
+
# an uppercase letter
|
79
|
+
snake = re.sub(
|
80
|
+
r"([a-z])([A-Z])", lambda m: f"{m.group(1)}_{m.group(2)}", snake
|
81
|
+
)
|
82
|
+
# Insert an underscore between a digit and an uppercase letter
|
83
|
+
snake = re.sub(
|
84
|
+
r"([0-9])([A-Z])", lambda m: f"{m.group(1)}_{m.group(2)}", snake
|
85
|
+
)
|
86
|
+
# Insert an underscore between a lowercase letter and a digit
|
87
|
+
snake = re.sub(
|
88
|
+
r"([a-z])([0-9])", lambda m: f"{m.group(1)}_{m.group(2)}", snake
|
89
|
+
)
|
90
|
+
# Replace hyphens with underscores to handle kebab-case
|
91
|
+
snake = snake.replace("-", "_")
|
92
|
+
return snake.lower()
|
93
|
+
|
94
|
+
return _arguments_handle(transform, *args, kwd_name="camel", **kwds)
|
@@ -0,0 +1,110 @@
|
|
1
|
+
import inspect
|
2
|
+
from collections.abc import Callable, Sequence
|
3
|
+
from functools import partial, update_wrapper
|
4
|
+
from typing import Annotated, Any, NamedTuple, overload
|
5
|
+
|
6
|
+
|
7
|
+
class _Undefined: ...
|
8
|
+
|
9
|
+
|
10
|
+
def list_parameters(fn: Callable, /) -> list[inspect.Parameter]:
|
11
|
+
signature = inspect.signature(fn)
|
12
|
+
return list(signature.parameters.values())
|
13
|
+
|
14
|
+
|
15
|
+
class WithParameterResult(NamedTuple):
|
16
|
+
parameters: list[inspect.Parameter]
|
17
|
+
parameter: inspect.Parameter
|
18
|
+
parameter_index: int
|
19
|
+
|
20
|
+
|
21
|
+
@overload
|
22
|
+
def with_parameter(
|
23
|
+
fn: Callable, *, name: str, annotation: type | Annotated
|
24
|
+
) -> WithParameterResult: ...
|
25
|
+
@overload
|
26
|
+
def with_parameter(
|
27
|
+
fn: Callable, *, name: str, default: Any
|
28
|
+
) -> WithParameterResult: ...
|
29
|
+
@overload
|
30
|
+
def with_parameter(
|
31
|
+
fn: Callable, *, name: str, annotation: type | Annotated, default: Any
|
32
|
+
) -> WithParameterResult: ...
|
33
|
+
|
34
|
+
|
35
|
+
def with_parameter(
|
36
|
+
fn: Callable,
|
37
|
+
*,
|
38
|
+
name: str,
|
39
|
+
annotation: type | Annotated | _Undefined = _Undefined,
|
40
|
+
default: Any = _Undefined,
|
41
|
+
) -> WithParameterResult:
|
42
|
+
kwargs = {}
|
43
|
+
if annotation is not _Undefined:
|
44
|
+
kwargs["annotation"] = annotation
|
45
|
+
if default is not _Undefined:
|
46
|
+
kwargs["default"] = default
|
47
|
+
|
48
|
+
parameters = list_parameters(fn)
|
49
|
+
parameter = inspect.Parameter(
|
50
|
+
name=name, kind=inspect.Parameter.KEYWORD_ONLY, **kwargs
|
51
|
+
)
|
52
|
+
index = -1
|
53
|
+
if parameters and parameters[index].kind == inspect.Parameter.VAR_KEYWORD:
|
54
|
+
parameters.insert(index, parameter)
|
55
|
+
index = -2
|
56
|
+
else:
|
57
|
+
parameters.append(parameter)
|
58
|
+
|
59
|
+
return WithParameterResult(parameters, parameter, index)
|
60
|
+
|
61
|
+
|
62
|
+
def add_parameter(
|
63
|
+
fn: Callable,
|
64
|
+
*,
|
65
|
+
name: str,
|
66
|
+
annotation: type | Annotated = _Undefined,
|
67
|
+
default: Any = _Undefined,
|
68
|
+
):
|
69
|
+
"""添加参数, 会将添加参数后的新函数返回"""
|
70
|
+
p = with_parameter(
|
71
|
+
fn,
|
72
|
+
name=name,
|
73
|
+
annotation=annotation,
|
74
|
+
default=default,
|
75
|
+
)
|
76
|
+
|
77
|
+
new_fn = update_wrapper(partial(fn), fn)
|
78
|
+
update_parameters(fn, parameters=p.parameters)
|
79
|
+
return new_fn
|
80
|
+
|
81
|
+
|
82
|
+
def update_signature(
|
83
|
+
fn: Callable,
|
84
|
+
*,
|
85
|
+
parameters: Sequence[inspect.Parameter] | None = _Undefined, # type: ignore
|
86
|
+
return_annotation: type | None = _Undefined,
|
87
|
+
):
|
88
|
+
signature = inspect.signature(fn)
|
89
|
+
if parameters is not _Undefined:
|
90
|
+
signature = signature.replace(parameters=parameters)
|
91
|
+
if return_annotation is not _Undefined:
|
92
|
+
signature = signature.replace(return_annotation=return_annotation)
|
93
|
+
|
94
|
+
setattr(fn, "__signature__", signature)
|
95
|
+
|
96
|
+
|
97
|
+
def update_parameters(
|
98
|
+
fn: Callable,
|
99
|
+
*,
|
100
|
+
parameters: Sequence[inspect.Parameter] | None = _Undefined, # type: ignore
|
101
|
+
):
|
102
|
+
update_signature(fn, parameters=parameters)
|
103
|
+
|
104
|
+
|
105
|
+
def update_return_annotation(
|
106
|
+
fn: Callable,
|
107
|
+
*,
|
108
|
+
return_annotation: type | None = _Undefined,
|
109
|
+
):
|
110
|
+
update_signature(fn, return_annotation=return_annotation)
|
@@ -0,0 +1,130 @@
|
|
1
|
+
from u_toolkit import enum
|
2
|
+
|
3
|
+
|
4
|
+
def test_name_enum():
|
5
|
+
class _Enum(enum.NameEnum):
|
6
|
+
A = enum.auto()
|
7
|
+
Ab = enum.auto()
|
8
|
+
AbC = enum.auto()
|
9
|
+
ABCD = enum.auto()
|
10
|
+
ABCD_E = enum.auto()
|
11
|
+
aa = enum.auto()
|
12
|
+
a_a = enum.auto()
|
13
|
+
a2 = enum.auto()
|
14
|
+
Bb = enum.auto()
|
15
|
+
c_ = enum.auto()
|
16
|
+
|
17
|
+
assert _Enum.A == "A"
|
18
|
+
assert _Enum.Ab == "Ab"
|
19
|
+
assert _Enum.AbC == "AbC"
|
20
|
+
assert _Enum.ABCD == "ABCD"
|
21
|
+
assert _Enum.ABCD_E == "ABCD_E"
|
22
|
+
assert _Enum.aa == "aa"
|
23
|
+
assert _Enum.a_a == "a_a"
|
24
|
+
assert _Enum.a2 == "a2"
|
25
|
+
assert _Enum.Bb == "Bb"
|
26
|
+
assert _Enum.c_ == "c_"
|
27
|
+
|
28
|
+
|
29
|
+
def test_pascal_enum():
|
30
|
+
class _Enum(enum.PascalEnum):
|
31
|
+
A = enum.auto()
|
32
|
+
Ab = enum.auto()
|
33
|
+
AbC = enum.auto()
|
34
|
+
ABCD = enum.auto()
|
35
|
+
ABCD_E = enum.auto()
|
36
|
+
aa = enum.auto()
|
37
|
+
a_a = enum.auto()
|
38
|
+
a2 = enum.auto()
|
39
|
+
Bb = enum.auto()
|
40
|
+
c_ = enum.auto()
|
41
|
+
|
42
|
+
assert _Enum.A == "A"
|
43
|
+
assert _Enum.Ab == "Ab"
|
44
|
+
assert _Enum.AbC == "Abc"
|
45
|
+
assert _Enum.ABCD == "ABCD"
|
46
|
+
assert _Enum.ABCD_E == "ABCD_E"
|
47
|
+
assert _Enum.aa == "Aa"
|
48
|
+
assert _Enum.a_a == "AA"
|
49
|
+
assert _Enum.a2 == "A2"
|
50
|
+
assert _Enum.Bb == "Bb"
|
51
|
+
assert _Enum.c_ == "C_"
|
52
|
+
|
53
|
+
|
54
|
+
def test_camel_enum():
|
55
|
+
class _Enum(enum.CamelEnum):
|
56
|
+
A = enum.auto()
|
57
|
+
Ab = enum.auto()
|
58
|
+
AbC = enum.auto()
|
59
|
+
ABCD = enum.auto()
|
60
|
+
ABCD_E = enum.auto()
|
61
|
+
aa = enum.auto()
|
62
|
+
a_a = enum.auto()
|
63
|
+
a2 = enum.auto()
|
64
|
+
Bb = enum.auto()
|
65
|
+
c_ = enum.auto()
|
66
|
+
|
67
|
+
assert _Enum.A == "A"
|
68
|
+
assert _Enum.Ab == "ab"
|
69
|
+
assert _Enum.AbC == "abc"
|
70
|
+
assert _Enum.ABCD == "ABCD"
|
71
|
+
assert _Enum.ABCD_E == "ABCD_E"
|
72
|
+
assert _Enum.aa == "aa"
|
73
|
+
assert _Enum.a_a == "aA"
|
74
|
+
assert _Enum.a2 == "a2"
|
75
|
+
assert _Enum.Bb == "bb"
|
76
|
+
assert _Enum.c_ == "c_"
|
77
|
+
|
78
|
+
|
79
|
+
def test_snake_enum():
|
80
|
+
class _Enum(enum.SnakeEnum):
|
81
|
+
A = enum.auto()
|
82
|
+
Ab = enum.auto()
|
83
|
+
AbC = enum.auto()
|
84
|
+
ABCD = enum.auto()
|
85
|
+
ABCD_E = enum.auto()
|
86
|
+
aa = enum.auto()
|
87
|
+
a_a = enum.auto()
|
88
|
+
a2 = enum.auto()
|
89
|
+
aa3 = enum.auto()
|
90
|
+
Bb = enum.auto()
|
91
|
+
c_ = enum.auto()
|
92
|
+
|
93
|
+
assert _Enum.A == "A"
|
94
|
+
assert _Enum.Ab == "ab"
|
95
|
+
assert _Enum.AbC == "ab_c"
|
96
|
+
assert _Enum.ABCD == "ABCD"
|
97
|
+
assert _Enum.ABCD_E == "ABCD_E"
|
98
|
+
assert _Enum.aa == "aa"
|
99
|
+
assert _Enum.a_a == "a_a"
|
100
|
+
assert _Enum.a2 == "a_2"
|
101
|
+
assert _Enum.aa3 == "aa_3"
|
102
|
+
assert _Enum.Bb == "bb"
|
103
|
+
assert _Enum.c_ == "c_"
|
104
|
+
|
105
|
+
|
106
|
+
def test_title_enum():
|
107
|
+
class _Enum(enum.TitleEnum):
|
108
|
+
A = enum.auto()
|
109
|
+
Ab = enum.auto()
|
110
|
+
AbC = enum.auto()
|
111
|
+
ABCD = enum.auto()
|
112
|
+
ABCD_E = enum.auto()
|
113
|
+
aa = enum.auto()
|
114
|
+
a_a = enum.auto()
|
115
|
+
a2 = enum.auto()
|
116
|
+
aa3 = enum.auto()
|
117
|
+
Bb = enum.auto()
|
118
|
+
c_ = enum.auto()
|
119
|
+
|
120
|
+
assert _Enum.A == "A"
|
121
|
+
assert _Enum.Ab == "Ab"
|
122
|
+
assert _Enum.AbC == "Abc"
|
123
|
+
assert _Enum.ABCD == "Abcd"
|
124
|
+
assert _Enum.ABCD_E == "Abcd E"
|
125
|
+
assert _Enum.aa == "Aa"
|
126
|
+
assert _Enum.a_a == "A A"
|
127
|
+
assert _Enum.a2 == "A2"
|
128
|
+
assert _Enum.aa3 == "Aa3"
|
129
|
+
assert _Enum.Bb == "Bb"
|
130
|
+
assert _Enum.c_ == "C"
|
@@ -0,0 +1,26 @@
|
|
1
|
+
from u_toolkit import function
|
2
|
+
|
3
|
+
|
4
|
+
def test_get_name():
|
5
|
+
def fn(): ...
|
6
|
+
|
7
|
+
assert function.get_name(fn) == "fn"
|
8
|
+
assert function.get_name(lambda: 1) == "<lambda>"
|
9
|
+
|
10
|
+
|
11
|
+
def test_add_document():
|
12
|
+
def fn(): ...
|
13
|
+
|
14
|
+
assert fn.__doc__ is None
|
15
|
+
|
16
|
+
function.add_document(fn, "lalala")
|
17
|
+
|
18
|
+
assert fn.__doc__ == "lalala"
|
19
|
+
|
20
|
+
def fn2():
|
21
|
+
"""2333"""
|
22
|
+
|
23
|
+
assert fn2.__doc__ == "2333"
|
24
|
+
|
25
|
+
function.add_document(fn2, "lalala")
|
26
|
+
assert fn2.__doc__ == "2333\n\nlalala"
|
@@ -0,0 +1,13 @@
|
|
1
|
+
from typing import Annotated
|
2
|
+
|
3
|
+
from u_toolkit import helpers
|
4
|
+
|
5
|
+
|
6
|
+
def test_is_annotated():
|
7
|
+
assert helpers.is_annotated(type) is False
|
8
|
+
assert helpers.is_annotated(int) is False
|
9
|
+
assert helpers.is_annotated(object()) is False
|
10
|
+
assert helpers.is_annotated(Annotated) is False
|
11
|
+
assert helpers.is_annotated(Annotated[int, 1]) is True
|
12
|
+
assert helpers.is_annotated(Annotated[int, type]) is True
|
13
|
+
assert helpers.is_annotated(Annotated[int, object()]) is True
|