u-toolkit 0.1.18__py3-none-any.whl → 0.1.20__py3-none-any.whl

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/__init__.py CHANGED
@@ -1,9 +0,0 @@
1
- def get(obj: dict, key: str, fallback=None):
2
- items = key.split(".", 1)
3
- if len(items) > 1:
4
- for i in items:
5
- return get(obj, i, fallback)
6
- elif items:
7
- return obj.get(key, fallback)
8
-
9
- return fallback
u_toolkit/enum.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from enum import StrEnum, auto
2
2
 
3
- from .alias_generators import to_camel, to_pascal, to_snake
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()
u_toolkit/function.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from collections.abc import Callable
2
2
 
3
3
 
4
- def get_name(fn: Callable, /):
4
+ def get_name(fn: Callable, /) -> str:
5
5
  return fn.__name__
6
6
 
7
7
 
u_toolkit/helpers.py CHANGED
@@ -2,4 +2,6 @@ from typing import Annotated, get_origin
2
2
 
3
3
 
4
4
  def is_annotated(target):
5
+ """判断值是否是 `typing.Annotated`"""
6
+
5
7
  return get_origin(target) is Annotated
u_toolkit/merge.py CHANGED
@@ -1,7 +1,8 @@
1
- from collections.abc import Mapping
1
+ from collections.abc import Iterable, Mapping
2
+ from typing import overload
2
3
 
3
4
 
4
- def deep_merge_dict(
5
+ def _merge_dict(
5
6
  target: dict,
6
7
  source: Mapping,
7
8
  ):
@@ -16,7 +17,9 @@ def deep_merge_dict(
16
17
  v = target.get(ok)
17
18
  # 如果两边都是映射类型, 就可以合并
18
19
  if isinstance(v, dict) and isinstance(ov, Mapping):
19
- deep_merge_dict(v, ov)
20
+ _merge_dict(v, ov)
21
+ elif isinstance(v, list) and isinstance(ov, Iterable):
22
+ _merge_list(v, ov)
20
23
  # 如果当前值允许进行相加的操作
21
24
  # 并且不是字符串和数字
22
25
  # 并且旧字典与当前值类型相同
@@ -29,3 +32,58 @@ def deep_merge_dict(
29
32
  # 否则使用有效的值
30
33
  else:
31
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
u_toolkit/naming.py ADDED
@@ -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)
u_toolkit/signature.py CHANGED
@@ -1,6 +1,10 @@
1
1
  import inspect
2
2
  from collections.abc import Callable, Sequence
3
- from typing import Annotated, Any, overload
3
+ from functools import partial, update_wrapper
4
+ from typing import Annotated, Any, NamedTuple, overload
5
+
6
+
7
+ class _Undefined: ...
4
8
 
5
9
 
6
10
  def list_parameters(fn: Callable, /) -> list[inspect.Parameter]:
@@ -8,31 +12,37 @@ def list_parameters(fn: Callable, /) -> list[inspect.Parameter]:
8
12
  return list(signature.parameters.values())
9
13
 
10
14
 
15
+ class WithParameterResult(NamedTuple):
16
+ parameters: list[inspect.Parameter]
17
+ parameter: inspect.Parameter
18
+ parameter_index: int
19
+
20
+
11
21
  @overload
12
22
  def with_parameter(
13
23
  fn: Callable, *, name: str, annotation: type | Annotated
14
- ) -> tuple[list[inspect.Parameter], inspect.Parameter, int]: ...
24
+ ) -> WithParameterResult: ...
15
25
  @overload
16
26
  def with_parameter(
17
27
  fn: Callable, *, name: str, default: Any
18
- ) -> tuple[list[inspect.Parameter], inspect.Parameter, int]: ...
28
+ ) -> WithParameterResult: ...
19
29
  @overload
20
30
  def with_parameter(
21
31
  fn: Callable, *, name: str, annotation: type | Annotated, default: Any
22
- ) -> tuple[list[inspect.Parameter], inspect.Parameter, int]: ...
32
+ ) -> WithParameterResult: ...
23
33
 
24
34
 
25
35
  def with_parameter(
26
36
  fn: Callable,
27
37
  *,
28
38
  name: str,
29
- annotation: type | Annotated | None = None,
30
- default: Any = None,
31
- ) -> tuple[list[inspect.Parameter], inspect.Parameter, int]:
39
+ annotation: type | Annotated | _Undefined = _Undefined,
40
+ default: Any = _Undefined,
41
+ ) -> WithParameterResult:
32
42
  kwargs = {}
33
- if annotation is not None:
43
+ if annotation is not _Undefined:
34
44
  kwargs["annotation"] = annotation
35
- if default is not None:
45
+ if default is not _Undefined:
36
46
  kwargs["default"] = default
37
47
 
38
48
  parameters = list_parameters(fn)
@@ -46,27 +56,55 @@ def with_parameter(
46
56
  else:
47
57
  parameters.append(parameter)
48
58
 
49
- return parameters, parameter, index
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
50
80
 
51
81
 
52
82
  def update_signature(
53
83
  fn: Callable,
54
84
  *,
55
- parameters: Sequence[inspect.Parameter] | None = None,
56
- return_annotation: type | None = None,
85
+ parameters: Sequence[inspect.Parameter] | None = _Undefined, # type: ignore
86
+ return_annotation: type | None = _Undefined,
57
87
  ):
58
88
  signature = inspect.signature(fn)
59
- if parameters is not None:
89
+ if parameters is not _Undefined:
60
90
  signature = signature.replace(parameters=parameters)
61
- if return_annotation is not None:
91
+ if return_annotation is not _Undefined:
62
92
  signature = signature.replace(return_annotation=return_annotation)
63
93
 
64
94
  setattr(fn, "__signature__", signature)
65
95
 
66
96
 
67
- def update_parameters(fn: Callable, *parameters: inspect.Parameter):
97
+ def update_parameters(
98
+ fn: Callable,
99
+ *,
100
+ parameters: Sequence[inspect.Parameter] | None = _Undefined, # type: ignore
101
+ ):
68
102
  update_signature(fn, parameters=parameters)
69
103
 
70
104
 
71
- def update_return_annotation(fn: Callable, return_annotation: type, /):
105
+ def update_return_annotation(
106
+ fn: Callable,
107
+ *,
108
+ return_annotation: type | None = _Undefined,
109
+ ):
72
110
  update_signature(fn, return_annotation=return_annotation)
@@ -0,0 +1,5 @@
1
+ Metadata-Version: 2.4
2
+ Name: u-toolkit
3
+ Version: 0.1.20
4
+ Summary: Add your description here
5
+ Requires-Python: >=3.11
@@ -0,0 +1,14 @@
1
+ u_toolkit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ u_toolkit/datetime.py,sha256=GOG0xa6yKeqvFXJkImK5Pt7wsPXnHMlNKju8deniGJ4,644
3
+ u_toolkit/decorators.py,sha256=rBZfXtWVfja6YMDqF9izjAiabRVyp4pAWOlwnN0HkUw,2396
4
+ u_toolkit/enum.py,sha256=S0qsklSm7rc53W_gheTCpopej5eGOeCK-HKK_ryR2No,885
5
+ u_toolkit/function.py,sha256=HgZcWd4IydNqUdUbS6A5pXVcAponGUZ2A8YKuiwHGjg,259
6
+ u_toolkit/helpers.py,sha256=QSkDZH90FfuNB0hWI1GA2KHFk1ajZMzNBr7ysuBdFTg,161
7
+ u_toolkit/merge.py,sha256=r_GhRIg5VHzXnqOVU9l0rpdT7XXV8FHWIp-nzP7-xnA,2550
8
+ u_toolkit/naming.py,sha256=WLK-kYFJUFvUGYK7rJrL1uBVxwXNJPcDQqOidXEwkqU,2746
9
+ u_toolkit/path.py,sha256=IkyIHcU9hKBCOZfF30FrKf4CfL-MH91fjeYF9EY7eos,128
10
+ u_toolkit/signature.py,sha256=vyZgo4lXWD282Sq3Svn36peOrUlBHwl0QIGOBghczAc,2869
11
+ u_toolkit-0.1.20.dist-info/METADATA,sha256=AHyOwnhZnOB1zNzOQNKZUQHvV6VB61YRq5E6J85_NkI,113
12
+ u_toolkit-0.1.20.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
13
+ u_toolkit-0.1.20.dist-info/entry_points.txt,sha256=hTfAYCd5vvRiqgnJk2eBsoRIiIVB9pK8WZm3Q3jjKFU,45
14
+ u_toolkit-0.1.20.dist-info/RECORD,,
@@ -1,49 +0,0 @@
1
- from typing import TypeVarTuple, overload
2
-
3
- from pydantic import alias_generators
4
- from pydantic.fields import ComputedFieldInfo, FieldInfo
5
-
6
-
7
- Ts = TypeVarTuple("Ts")
8
-
9
-
10
- @overload
11
- def to_camel(string: str, _: ComputedFieldInfo | FieldInfo) -> str: ...
12
-
13
-
14
- @overload
15
- def to_camel(string: str) -> str: ...
16
-
17
-
18
- def to_camel(string: str, *_, **__):
19
- if string.isupper():
20
- return string
21
- return alias_generators.to_camel(string)
22
-
23
-
24
- @overload
25
- def to_snake(string: str, _: ComputedFieldInfo | FieldInfo) -> str: ...
26
-
27
-
28
- @overload
29
- def to_snake(string: str) -> str: ...
30
-
31
-
32
- def to_snake(string: str, *_, **__):
33
- if string.isupper():
34
- return string
35
- return alias_generators.to_snake(string)
36
-
37
-
38
- @overload
39
- def to_pascal(string: str, _: ComputedFieldInfo | FieldInfo) -> str: ...
40
-
41
-
42
- @overload
43
- def to_pascal(string: str) -> str: ...
44
-
45
-
46
- def to_pascal(string: str, *_, **__):
47
- if string.isupper():
48
- return string
49
- return alias_generators.to_pascal(string)
File without changes