adiumentum 0.1.1__py3-none-any.whl → 0.3.1__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.
- adiumentum/__init__.py +35 -14
- adiumentum/dependency_sorting.py +84 -0
- adiumentum/display.py +49 -0
- adiumentum/functional.py +56 -5
- adiumentum/io_utils.py +63 -0
- adiumentum/markers.py +67 -84
- adiumentum/merge.py +113 -0
- adiumentum/paths_manager.py +19 -0
- adiumentum/pydantic_extensions.md +839 -0
- adiumentum/pydantic_extensions.py +410 -0
- adiumentum/{string.py → string_utils.py} +39 -11
- adiumentum/typing_utils.py +115 -2
- adiumentum-0.3.1.dist-info/METADATA +60 -0
- adiumentum-0.3.1.dist-info/RECORD +26 -0
- adiumentum-0.3.1.dist-info/entry_points.txt +3 -0
- adiumentum/io.py +0 -33
- adiumentum-0.1.1.dist-info/METADATA +0 -236
- adiumentum-0.1.1.dist-info/RECORD +0 -19
- {adiumentum-0.1.1.dist-info → adiumentum-0.3.1.dist-info}/WHEEL +0 -0
adiumentum/__init__.py
CHANGED
@@ -11,15 +11,22 @@ from .file_modification_time import (
|
|
11
11
|
from .frozendict import FrozenDefaultDict
|
12
12
|
from .functional import (
|
13
13
|
dmap,
|
14
|
+
endofilter,
|
15
|
+
endomap,
|
14
16
|
fold_dictionaries,
|
15
17
|
identity,
|
18
|
+
kfilter,
|
16
19
|
kmap,
|
20
|
+
lfilter,
|
17
21
|
lmap,
|
22
|
+
sfilter,
|
18
23
|
smap,
|
24
|
+
tfilter,
|
19
25
|
tmap,
|
26
|
+
vfilter,
|
20
27
|
vmap,
|
21
28
|
)
|
22
|
-
from .
|
29
|
+
from .io_utils import (
|
23
30
|
list_full,
|
24
31
|
read_json,
|
25
32
|
read_raw,
|
@@ -28,20 +35,24 @@ from .io import (
|
|
28
35
|
write_raw_bytes,
|
29
36
|
)
|
30
37
|
from .markers import (
|
31
|
-
|
38
|
+
endo,
|
32
39
|
impure,
|
33
40
|
mutates,
|
34
41
|
mutates_and_returns_instance,
|
35
42
|
mutates_instance,
|
36
43
|
pure,
|
37
44
|
refactor,
|
38
|
-
|
39
|
-
|
40
|
-
|
45
|
+
)
|
46
|
+
from .merge import (
|
47
|
+
join_as_sequence,
|
48
|
+
make_hashable,
|
49
|
+
merge_dicts,
|
41
50
|
)
|
42
51
|
from .numerical import evenly_spaced, ihash, round5
|
43
|
-
from .
|
44
|
-
from .
|
52
|
+
from .paths_manager import PathsManager
|
53
|
+
from .performance_logging import log_perf # type: ignore
|
54
|
+
from .pydantic_extensions import BaseDict, BaseList, BaseSet
|
55
|
+
from .string_utils import (
|
45
56
|
MixedValidated,
|
46
57
|
PromptTypeName,
|
47
58
|
as_json,
|
@@ -49,6 +60,7 @@ from .string import (
|
|
49
60
|
flexsplit,
|
50
61
|
indent_lines,
|
51
62
|
parse_sequence,
|
63
|
+
re_split,
|
52
64
|
)
|
53
65
|
from .timestamping import insert_timestamp, make_timestamp
|
54
66
|
from .typing_utils import (
|
@@ -61,55 +73,64 @@ DELIMITER = "᜶"
|
|
61
73
|
|
62
74
|
__all__ = [
|
63
75
|
"DELIMITER",
|
76
|
+
"BaseDict",
|
77
|
+
"BaseList",
|
78
|
+
"BaseSet",
|
64
79
|
"Colorizer",
|
65
80
|
"CustomValidationError",
|
66
81
|
"FrozenDefaultDict",
|
67
82
|
"MixedValidated",
|
68
|
-
"
|
69
|
-
"NoneTime",
|
83
|
+
"PathsManager",
|
70
84
|
"PromptTypeName",
|
71
85
|
"areinstances",
|
72
|
-
"args_to_dict",
|
73
86
|
"as_json",
|
74
87
|
"call_fallback_if_none",
|
75
88
|
"cast_as",
|
76
89
|
"dmap",
|
90
|
+
"endo",
|
91
|
+
"endofilter",
|
92
|
+
"endomap",
|
77
93
|
"equal_within",
|
78
94
|
"evenly_spaced",
|
79
95
|
"fallback_if_none",
|
80
96
|
"first_newer",
|
81
97
|
"flexsplit",
|
82
98
|
"fold_dictionaries",
|
83
|
-
"helper",
|
84
99
|
"identity",
|
85
100
|
"ihash",
|
86
101
|
"impure",
|
87
102
|
"indent_lines",
|
88
103
|
"insert_timestamp",
|
104
|
+
"join_as_sequence",
|
105
|
+
"kfilter",
|
89
106
|
"kmap",
|
107
|
+
"lfilter",
|
90
108
|
"list_full",
|
91
109
|
"lmap",
|
92
110
|
"log_perf",
|
111
|
+
"make_hashable",
|
93
112
|
"make_timestamp",
|
113
|
+
"merge_dicts",
|
94
114
|
"mutates",
|
95
115
|
"mutates_and_returns_instance",
|
96
116
|
"mutates_instance",
|
97
117
|
"nearly_equal",
|
98
118
|
"parse_sequence",
|
99
119
|
"pure",
|
120
|
+
"re_split",
|
100
121
|
"read_json",
|
101
122
|
"read_raw",
|
102
123
|
"refactor",
|
103
124
|
"round5",
|
125
|
+
"sfilter",
|
104
126
|
"smap",
|
105
|
-
"
|
106
|
-
"step_transition",
|
127
|
+
"tfilter",
|
107
128
|
"time_created",
|
108
129
|
"time_created_readable",
|
109
130
|
"time_modified",
|
110
131
|
"time_modified_readable",
|
111
132
|
"tmap",
|
112
|
-
"
|
133
|
+
"vfilter",
|
113
134
|
"vmap",
|
114
135
|
"write_json",
|
115
136
|
"write_raw",
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# for tests
|
2
|
+
sample = [
|
3
|
+
{"id": "a", "priority": 0.4, "prerequisites": []},
|
4
|
+
{"id": "b", "priority": 0.7, "prerequisites": []},
|
5
|
+
{"id": "c", "priority": 0.4, "prerequisites": []},
|
6
|
+
{"id": "d", "priority": 0.3, "prerequisites": []},
|
7
|
+
{"id": "e", "priority": 0.8, "prerequisites": ["a", "b"]},
|
8
|
+
{"id": "f", "priority": 0.9, "prerequisites": []},
|
9
|
+
{"id": "g", "priority": 0.1, "prerequisites": ["i", "m"]},
|
10
|
+
{"id": "h", "priority": 0.5, "prerequisites": []},
|
11
|
+
{"id": "i", "priority": 0.45, "prerequisites": ["j"]},
|
12
|
+
{"id": "j", "priority": 0.3, "prerequisites": []},
|
13
|
+
{"id": "k", "priority": 0.8, "prerequisites": ["l", "m", "d"]},
|
14
|
+
{"id": "l", "priority": 0.6, "prerequisites": ["o"]},
|
15
|
+
{"id": "m", "priority": 0.9, "prerequisites": []},
|
16
|
+
{"id": "n", "priority": 0.4, "prerequisites": ["o"]},
|
17
|
+
{"id": "o", "priority": 0.2, "prerequisites": []},
|
18
|
+
{"id": "p", "priority": 0.5, "prerequisites": []},
|
19
|
+
]
|
20
|
+
|
21
|
+
|
22
|
+
def dep_sort(task_list: list[dict]) -> list[dict]:
|
23
|
+
"""
|
24
|
+
Sort list of dictionaries such that
|
25
|
+
1) dependency constraints are satisfied and
|
26
|
+
2) priority ordering is satisfied subject to (1)
|
27
|
+
"""
|
28
|
+
# sort on priority to preserve priority order in the output
|
29
|
+
task_list.sort(key=lambda t: t["priority"], reverse=True)
|
30
|
+
task_ids = [t["id"] for t in task_list]
|
31
|
+
|
32
|
+
# important to make changes in reverse priority order so that insertion preserves priority order
|
33
|
+
dep_dict = {t["id"]: t["prerequisites"] for t in reversed(task_list) if t["prerequisites"]}
|
34
|
+
print(dep_dict)
|
35
|
+
|
36
|
+
def place_after(to_move: str, deps: list[str], id_list: list) -> bool:
|
37
|
+
if not deps:
|
38
|
+
return False
|
39
|
+
idx1 = id_list.index(to_move)
|
40
|
+
idx2 = max(map(id_list.index, deps))
|
41
|
+
if idx1 > idx2:
|
42
|
+
return False
|
43
|
+
id_list.pop(idx1)
|
44
|
+
id_list.insert(idx2, to_move)
|
45
|
+
return True
|
46
|
+
|
47
|
+
print(task_ids)
|
48
|
+
count = 0
|
49
|
+
maxcount = sum(range(len(task_ids) + 1))
|
50
|
+
while count < maxcount:
|
51
|
+
change_tracker = []
|
52
|
+
for task_id, task_deps in dep_dict.items():
|
53
|
+
changed = place_after(task_id, task_deps, task_ids)
|
54
|
+
change_tracker.append(changed)
|
55
|
+
unchanged = not any(change_tracker)
|
56
|
+
print(task_ids)
|
57
|
+
if unchanged:
|
58
|
+
return sorted(task_list, key=lambda t: task_ids.index(t["id"]))
|
59
|
+
count += 1
|
60
|
+
|
61
|
+
print("ERROR ---------------------------------------------------")
|
62
|
+
str(task_ids)
|
63
|
+
for task_id, task_deps in dep_dict.items():
|
64
|
+
changed = place_after(task_id, task_deps, task_ids)
|
65
|
+
if changed:
|
66
|
+
after = str(task_ids)
|
67
|
+
print(after)
|
68
|
+
|
69
|
+
raise ValueError("Graph contains a cycle.")
|
70
|
+
|
71
|
+
# levels = {t: 0 for t in task_ids}
|
72
|
+
# roots = list(filter(lambda t: t["depencies"] == [], task_list))
|
73
|
+
# depended_on = {t: set() for t in task_ids}
|
74
|
+
# for t in task_list:
|
75
|
+
# for d in t["prerequisites"]:
|
76
|
+
# depended_on[d].add(t)
|
77
|
+
# dict_rep = json.dumps(levels)
|
78
|
+
# new_rep = ""
|
79
|
+
# while new_rep != dict_rep:
|
80
|
+
# dict_rep = json.dumps(levels)
|
81
|
+
# for t in task_list:
|
82
|
+
# levels[t["id"]] = max([levels[d] for d in t["prerequisites"]]) + 1
|
83
|
+
# new_rep = json.dumps(levels)
|
84
|
+
# return sorted(task_list, key=lambda t: levels[t["id"]])
|
adiumentum/display.py
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
import re
|
2
|
+
from collections import defaultdict
|
3
|
+
from collections.abc import Callable
|
4
|
+
|
5
|
+
|
6
|
+
def display_counts(key: str, depth: int, dl: list[str]) -> None:
|
7
|
+
print(f"\n=== {key} ===")
|
8
|
+
for d in dl:
|
9
|
+
if key not in d:
|
10
|
+
print(d["name"])
|
11
|
+
cats = [d[key] for d in dl]
|
12
|
+
cats = [".".join(re.split(r"\.|, ", c)[:depth]) for c in cats]
|
13
|
+
counts = sorted([(cats.count(c), c) for c in sorted(set(cats))], reverse=True)
|
14
|
+
for count, item in counts:
|
15
|
+
print(f"{count:>4} {item}")
|
16
|
+
|
17
|
+
|
18
|
+
def print_tree(strings):
|
19
|
+
# Nested dictionary to hold tree structure
|
20
|
+
def tree() -> defaultdict:
|
21
|
+
return defaultdict(tree)
|
22
|
+
|
23
|
+
root = tree()
|
24
|
+
|
25
|
+
# Build the tree
|
26
|
+
for _string in strings:
|
27
|
+
parts = _string.split(".")
|
28
|
+
current_level = root
|
29
|
+
for part in parts:
|
30
|
+
current_level = current_level[part]
|
31
|
+
|
32
|
+
# Function to print the tree recursively
|
33
|
+
def print_subtree(node, prefix=""):
|
34
|
+
children = list(node.keys())
|
35
|
+
for i, child in enumerate(children):
|
36
|
+
is_last = i == len(children) - 1
|
37
|
+
if is_last:
|
38
|
+
print(prefix + "└─ " + child)
|
39
|
+
new_prefix = prefix + " "
|
40
|
+
else:
|
41
|
+
print(prefix + "├─ " + child)
|
42
|
+
new_prefix = prefix + "│ "
|
43
|
+
print_subtree(node[child], new_prefix)
|
44
|
+
|
45
|
+
# Print the root
|
46
|
+
print_subtree(root)
|
47
|
+
|
48
|
+
|
49
|
+
def wrap_line(line: str, length: int, formatter: Callable) -> str: ...
|
adiumentum/functional.py
CHANGED
@@ -1,12 +1,39 @@
|
|
1
|
-
from collections.abc import Callable, Iterable
|
1
|
+
from collections.abc import Callable, Hashable, Iterable
|
2
2
|
from functools import reduce
|
3
|
-
from typing import TypeVar
|
3
|
+
from typing import TypeVar, overload
|
4
4
|
|
5
5
|
T = TypeVar("T")
|
6
6
|
TPost = TypeVar("TPost")
|
7
7
|
TPre = TypeVar("TPre")
|
8
|
-
K = TypeVar("K")
|
8
|
+
K = TypeVar("K", bound=Hashable)
|
9
9
|
V = TypeVar("V")
|
10
|
+
type Filterer[T] = Callable[[T], bool]
|
11
|
+
|
12
|
+
|
13
|
+
@overload
|
14
|
+
def endomap(callable_: Callable[[TPre], TPost], sequence: list[TPre]) -> list[TPost]: ...
|
15
|
+
@overload
|
16
|
+
def endomap(callable_: Callable[[TPre], TPost], sequence: set[TPre]) -> set[TPost]: ...
|
17
|
+
@overload
|
18
|
+
def endomap(
|
19
|
+
callable_: Callable[[TPre], TPost], sequence: tuple[TPre, ...]
|
20
|
+
) -> tuple[TPost, ...]: ...
|
21
|
+
|
22
|
+
|
23
|
+
def endomap(callable_, sequence):
|
24
|
+
return type(sequence)(map(callable_, sequence))
|
25
|
+
|
26
|
+
|
27
|
+
@overload
|
28
|
+
def endofilter(callable_: Callable[[T], bool], sequence: list[T]) -> list[T]: ...
|
29
|
+
@overload
|
30
|
+
def endofilter(callable_: Callable[[T], bool], sequence: set[T]) -> set[T]: ...
|
31
|
+
@overload
|
32
|
+
def endofilter(callable_: Callable[[T], bool], sequence: tuple[T, ...]) -> tuple[T, ...]: ...
|
33
|
+
|
34
|
+
|
35
|
+
def endofilter(callable_, sequence):
|
36
|
+
return type(sequence)(filter(callable_, sequence))
|
10
37
|
|
11
38
|
|
12
39
|
def lmap(callable_: Callable[[TPre], TPost], iterable: Iterable[TPre]) -> list[TPost]:
|
@@ -33,12 +60,36 @@ def dmap(callable_: Callable[[TPre], TPost], dictionary: dict[TPre, TPre]) -> di
|
|
33
60
|
return {callable_(k): callable_(v) for k, v in dictionary.items()}
|
34
61
|
|
35
62
|
|
63
|
+
def lfilter(filterer: Filterer[T], iterable: Iterable[T]) -> list[T]:
|
64
|
+
return list(filter(filterer, iterable))
|
65
|
+
|
66
|
+
|
67
|
+
def sfilter(filterer: Filterer[T], iterable: Iterable[T]) -> set[T]:
|
68
|
+
return set(filter(filterer, iterable))
|
69
|
+
|
70
|
+
|
71
|
+
def tfilter(filterer: Filterer[T], iterable: Iterable[T]) -> tuple[T, ...]:
|
72
|
+
return tuple(filter(filterer, iterable))
|
73
|
+
|
74
|
+
|
75
|
+
def vfilter(filterer: Filterer[V], dictionary: dict[K, V]) -> dict[K, V]:
|
76
|
+
return {k: v for k, v in dictionary.items() if filterer(v)}
|
77
|
+
|
78
|
+
|
79
|
+
def kfilter(filterer: Filterer[K], dictionary: dict[K, V]) -> dict[K, V]:
|
80
|
+
return {k: v for k, v in dictionary.items() if filterer(k)}
|
81
|
+
|
82
|
+
|
83
|
+
def dfilter(filterer: Filterer[T], dictionary: dict[T, T]) -> dict[T, T]:
|
84
|
+
return {k: v for k, v in dictionary.items() if filterer(k) and filterer(v)}
|
85
|
+
|
86
|
+
|
36
87
|
def identity[T](x: T) -> T:
|
37
88
|
return x
|
38
89
|
|
39
90
|
|
40
|
-
def fold_dictionaries[K,
|
41
|
-
def _or(dict1: dict[K,
|
91
|
+
def fold_dictionaries[K, V](dicts: Iterable[dict[K, V]]) -> dict[K, V]:
|
92
|
+
def _or(dict1: dict[K, V], dict2: dict[K, V]) -> dict[K, V]:
|
42
93
|
return dict1 | dict2
|
43
94
|
|
44
95
|
return reduce(_or, dicts)
|
adiumentum/io_utils.py
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
import json
|
2
|
+
import os
|
3
|
+
import shutil
|
4
|
+
from pathlib import Path
|
5
|
+
from typing import Literal
|
6
|
+
|
7
|
+
from .timestamping import make_timestamp
|
8
|
+
from .typing_utils import JSONDict, JSONList
|
9
|
+
|
10
|
+
|
11
|
+
def list_full(directory: str | Path, ending: str = "") -> list[Path]:
|
12
|
+
directory = Path(directory)
|
13
|
+
return sorted([directory / file for file in os.listdir(directory) if file.endswith(ending)])
|
14
|
+
|
15
|
+
|
16
|
+
def read_raw(json_path: Path) -> str:
|
17
|
+
with open(json_path, encoding="utf-8") as f:
|
18
|
+
return f.read()
|
19
|
+
|
20
|
+
|
21
|
+
def back_up_json(original_path: Path, backup_dir: Path | None) -> None:
|
22
|
+
if not backup_dir:
|
23
|
+
return
|
24
|
+
backup_path = backup_dir / original_path.name.replace(".json", f"__{make_timestamp()}.json")
|
25
|
+
if original_path.exists():
|
26
|
+
shutil.copy(original_path, backup_path)
|
27
|
+
|
28
|
+
|
29
|
+
def read_json(json_path: Path, backup_dir: Path | None = None) -> JSONDict | JSONList:
|
30
|
+
back_up_json(json_path, backup_dir)
|
31
|
+
with open(json_path, encoding="utf-8") as f:
|
32
|
+
return json.load(f)
|
33
|
+
|
34
|
+
|
35
|
+
def write_json(
|
36
|
+
python_obj: JSONDict | JSONList | bytes,
|
37
|
+
json_path: Path,
|
38
|
+
backup_dir: Path | None = None,
|
39
|
+
mode: Literal["w", "a"] = "w",
|
40
|
+
) -> None:
|
41
|
+
if mode not in {"w", "a"}:
|
42
|
+
raise ValueError(f"Invalid mode: '{mode}'")
|
43
|
+
back_up_json(json_path, backup_dir)
|
44
|
+
if mode == "a":
|
45
|
+
existing = read_json(json_path)
|
46
|
+
if isinstance(existing, list) and isinstance(existing, list):
|
47
|
+
python_obj = existing + python_obj
|
48
|
+
elif isinstance(existing, list) and isinstance(existing, list):
|
49
|
+
python_obj = existing | python_obj
|
50
|
+
else:
|
51
|
+
raise ValueError("Data types do not match.")
|
52
|
+
with open(json_path, "w", encoding="utf-8") as f:
|
53
|
+
json.dump(python_obj, f, ensure_ascii=False, indent=4)
|
54
|
+
|
55
|
+
|
56
|
+
def write_raw(text: str, file_path: Path) -> None:
|
57
|
+
with open(file_path, "w", encoding="utf-8") as f:
|
58
|
+
f.write(text)
|
59
|
+
|
60
|
+
|
61
|
+
def write_raw_bytes(text: bytes, json_path: Path) -> None:
|
62
|
+
with open(json_path, "wb") as f:
|
63
|
+
f.write(text)
|
adiumentum/markers.py
CHANGED
@@ -1,117 +1,100 @@
|
|
1
1
|
from collections.abc import Callable
|
2
2
|
from functools import wraps
|
3
|
+
from typing import TypeVar, cast
|
3
4
|
|
4
|
-
from .functional import identity
|
5
|
+
# from .functional import identity
|
5
6
|
|
6
7
|
|
7
|
-
|
8
|
+
T = TypeVar("T")
|
9
|
+
In = TypeVar("In")
|
10
|
+
Out = TypeVar("Out")
|
11
|
+
|
12
|
+
|
13
|
+
def trivial_decorator[In, Out](func: Callable[[In], Out]) -> Callable[[In], Out]:
|
14
|
+
return func
|
15
|
+
|
16
|
+
|
17
|
+
def impure[In, Out](
|
18
|
+
callable_or_none: Callable[[In], Out] | None = None,
|
19
|
+
message: str = "",
|
20
|
+
) -> Callable[[In], Out] | Callable[[Callable[[In], Out]], Callable[[In], Out]]:
|
8
21
|
if callable_or_none is None:
|
9
|
-
return
|
22
|
+
return trivial_decorator
|
10
23
|
else:
|
11
24
|
return callable_or_none
|
12
25
|
|
13
26
|
|
14
|
-
def pure
|
27
|
+
def pure[In, Out](
|
28
|
+
callable_or_none: Callable[[In], Out] | None = None,
|
29
|
+
message: str = "",
|
30
|
+
) -> Callable[[In], Out] | Callable[[Callable[[In], Out]], Callable[[In], Out]]:
|
15
31
|
if callable_or_none is None:
|
16
|
-
return
|
32
|
+
return trivial_decorator
|
17
33
|
else:
|
18
34
|
return callable_or_none
|
19
35
|
|
20
36
|
|
21
|
-
def
|
37
|
+
def endo(
|
38
|
+
callable_or_none: Callable[[T], T] | None = None,
|
39
|
+
message: str = "",
|
40
|
+
) -> Callable[[T], T] | Callable[[Callable[[In], Out]], Callable[[In], Out]]:
|
22
41
|
if callable_or_none is None:
|
23
|
-
return
|
42
|
+
return trivial_decorator
|
24
43
|
else:
|
25
44
|
return callable_or_none
|
26
45
|
|
27
46
|
|
28
|
-
def
|
47
|
+
def decorate_with_message[In, Out](func: Callable[[In], Out], message: str) -> Callable[[In], Out]:
|
29
48
|
@wraps(func)
|
30
|
-
def wrapper(*fargs, **fkwargs):
|
31
|
-
|
49
|
+
def wrapper(*fargs, **fkwargs) -> Out: # type: ignore
|
50
|
+
print(message)
|
32
51
|
|
33
|
-
|
52
|
+
return func(*fargs, **fkwargs) # type: ignore
|
34
53
|
|
54
|
+
return cast(Callable[[In], Out], wrapper)
|
35
55
|
|
36
|
-
def step_transition(func: Callable) -> Callable:
|
37
|
-
@wraps(func)
|
38
|
-
def wrapper(*fargs, **fkwargs):
|
39
|
-
return func(*fargs, **fkwargs)
|
40
56
|
|
41
|
-
|
57
|
+
def mutates_instance[In, Out](func: Callable[[In], Out], *mutated: str) -> Callable[[In], Out]:
|
58
|
+
message = (
|
59
|
+
f"\u001b[31mMutating in place\u001b[0m: "
|
60
|
+
f" via \u001b[33m{func.__name__:<25}\u001b[0m"
|
61
|
+
f" in \u001b[34m{func.__module__}\u001b[0m"
|
62
|
+
)
|
42
63
|
|
64
|
+
return decorate_with_message(func, message)
|
43
65
|
|
44
|
-
def validator(func: Callable) -> Callable:
|
45
|
-
@wraps(func)
|
46
|
-
def wrapper(*fargs, **fkwargs):
|
47
|
-
return func(*fargs, **fkwargs)
|
48
66
|
|
49
|
-
|
67
|
+
def mutates_and_returns_instance[In, Out](
|
68
|
+
func: Callable[[In], Out],
|
69
|
+
*mutated: str,
|
70
|
+
) -> Callable[[In], Out]:
|
71
|
+
message = (
|
72
|
+
f"\u001b[31mMutating\u001b[0m"
|
73
|
+
f"{(' ' * bool(mutated)) + ', '.join(mutated)}"
|
74
|
+
f" \u001b[31mand returning instance of\u001b[0m: {func.__class__.__name__}"
|
75
|
+
f" via \u001b[33m{func.__name__:<25}\u001b[0m"
|
76
|
+
f" in \u001b[34m{func.__module__}\u001b[0m"
|
77
|
+
)
|
50
78
|
|
79
|
+
return decorate_with_message(func, message)
|
51
80
|
|
52
|
-
def mutates_instance(func: Callable) -> Callable:
|
53
|
-
@wraps(func)
|
54
|
-
def wrapper(*fargs, **fkwargs):
|
55
|
-
# print(
|
56
|
-
# (
|
57
|
-
# f"\u001b[31mMutating in place\u001b[0m: "
|
58
|
-
# f"\u001b[32m{type(fargs[0]).__name__:<25}\u001b[0m"
|
59
|
-
# f" via \u001b[33m{func.__name__:<25}\u001b[0m"
|
60
|
-
# f" in \u001b[34m{func.__module__}\u001b[0m"
|
61
|
-
# )
|
62
|
-
# )
|
63
|
-
return func(*fargs, **fkwargs)
|
64
81
|
|
65
|
-
|
82
|
+
def mutates[In, Out](func: Callable[[In], Out], *mutated: str) -> Callable[[In], Out]:
|
83
|
+
message = (
|
84
|
+
f"Mutating \u001b[36m{', '.join(mutated):<50}\u001b[0m"
|
85
|
+
f" via \u001b[33m{func.__name__:<25}\u001b[0m"
|
86
|
+
f" in \u001b[34m{func.__module__:<40}\u001b[0m"
|
87
|
+
)
|
66
88
|
|
89
|
+
return decorate_with_message(func, message)
|
67
90
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
# )
|
79
|
-
return func(*fargs, **fkwargs)
|
80
|
-
|
81
|
-
return wrapper
|
82
|
-
|
83
|
-
|
84
|
-
def mutates(*args, **kwargs) -> Callable:
|
85
|
-
def decorator(func: Callable) -> Callable:
|
86
|
-
@wraps(func)
|
87
|
-
def wrapper(*fargs, **fkwargs):
|
88
|
-
print(
|
89
|
-
f"Mutating attributes \u001b[36m{', '.join(args):<50}\u001b[0m"
|
90
|
-
f" of \u001b[32m{type(fargs[0]).__name__:<25}\u001b[0m"
|
91
|
-
f" via \u001b[33m{func.__name__:<25}\u001b[0m"
|
92
|
-
f" in \u001b[34m{func.__module__.replace('consilium.', '.'):<40}\u001b[0m"
|
93
|
-
)
|
94
|
-
|
95
|
-
return func(*fargs, **fkwargs)
|
96
|
-
|
97
|
-
return wrapper
|
98
|
-
|
99
|
-
return decorator
|
100
|
-
|
101
|
-
|
102
|
-
def refactor(*args) -> Callable:
|
103
|
-
def decorator(func: Callable) -> Callable:
|
104
|
-
@wraps(func)
|
105
|
-
def wrapper(*fargs, **fkwargs):
|
106
|
-
print(
|
107
|
-
f"\u001b[36mREFACTOR\u001b[0m"
|
108
|
-
f" \u001b[33m{func.__name__}\u001b[0m"
|
109
|
-
f" in \u001b[34m{func.__module__.replace('consilium.', '.')}\u001b[0m."
|
110
|
-
f" Notes: \u001b[32m{', '.join(args)}\u001b[0m"
|
111
|
-
)
|
112
|
-
|
113
|
-
return func(*fargs, **fkwargs)
|
114
|
-
|
115
|
-
return wrapper
|
116
|
-
|
117
|
-
return decorator
|
91
|
+
|
92
|
+
def refactor[In, Out](func: Callable[[In], Out], message: str = "") -> Callable[[In], Out]:
|
93
|
+
message = (
|
94
|
+
f"\u001b[36mREFACTOR\u001b[0m"
|
95
|
+
f" \u001b[33m{func.__name__}\u001b[0m"
|
96
|
+
f" in \u001b[34m{func.__module__.replace('consilium.', '.')}\u001b[0m."
|
97
|
+
f"\n {message}"
|
98
|
+
)
|
99
|
+
|
100
|
+
return decorate_with_message(func, message)
|