fabricatio 0.3.14.dev4__cp313-cp313-manylinux_2_34_x86_64.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.
- fabricatio/__init__.py +29 -0
- fabricatio/actions/__init__.py +1 -0
- fabricatio/actions/article.py +319 -0
- fabricatio/actions/article_rag.py +416 -0
- fabricatio/actions/fs.py +25 -0
- fabricatio/actions/output.py +248 -0
- fabricatio/actions/rag.py +96 -0
- fabricatio/actions/rules.py +83 -0
- fabricatio/capabilities/__init__.py +1 -0
- fabricatio/capabilities/advanced_judge.py +20 -0
- fabricatio/capabilities/advanced_rag.py +61 -0
- fabricatio/capabilities/censor.py +105 -0
- fabricatio/capabilities/check.py +212 -0
- fabricatio/capabilities/correct.py +228 -0
- fabricatio/capabilities/extract.py +74 -0
- fabricatio/capabilities/persist.py +103 -0
- fabricatio/capabilities/propose.py +65 -0
- fabricatio/capabilities/rag.py +263 -0
- fabricatio/capabilities/rating.py +404 -0
- fabricatio/capabilities/review.py +114 -0
- fabricatio/capabilities/task.py +113 -0
- fabricatio/decorators.py +251 -0
- fabricatio/emitter.py +177 -0
- fabricatio/fs/__init__.py +35 -0
- fabricatio/fs/curd.py +153 -0
- fabricatio/fs/readers.py +61 -0
- fabricatio/journal.py +12 -0
- fabricatio/models/action.py +263 -0
- fabricatio/models/adv_kwargs_types.py +63 -0
- fabricatio/models/extra/__init__.py +1 -0
- fabricatio/models/extra/advanced_judge.py +32 -0
- fabricatio/models/extra/aricle_rag.py +284 -0
- fabricatio/models/extra/article_base.py +422 -0
- fabricatio/models/extra/article_essence.py +101 -0
- fabricatio/models/extra/article_main.py +285 -0
- fabricatio/models/extra/article_outline.py +46 -0
- fabricatio/models/extra/article_proposal.py +52 -0
- fabricatio/models/extra/patches.py +20 -0
- fabricatio/models/extra/problem.py +165 -0
- fabricatio/models/extra/rag.py +98 -0
- fabricatio/models/extra/rule.py +52 -0
- fabricatio/models/generic.py +812 -0
- fabricatio/models/kwargs_types.py +121 -0
- fabricatio/models/role.py +95 -0
- fabricatio/models/task.py +310 -0
- fabricatio/models/tool.py +328 -0
- fabricatio/models/usages.py +791 -0
- fabricatio/parser.py +114 -0
- fabricatio/py.typed +0 -0
- fabricatio/rust.cpython-313-x86_64-linux-gnu.so +0 -0
- fabricatio/rust.pyi +846 -0
- fabricatio/toolboxes/__init__.py +15 -0
- fabricatio/toolboxes/arithmetic.py +62 -0
- fabricatio/toolboxes/fs.py +31 -0
- fabricatio/utils.py +156 -0
- fabricatio/workflows/__init__.py +1 -0
- fabricatio/workflows/articles.py +24 -0
- fabricatio/workflows/rag.py +11 -0
- fabricatio-0.3.14.dev4.data/scripts/tdown +0 -0
- fabricatio-0.3.14.dev4.data/scripts/ttm +0 -0
- fabricatio-0.3.14.dev4.dist-info/METADATA +188 -0
- fabricatio-0.3.14.dev4.dist-info/RECORD +64 -0
- fabricatio-0.3.14.dev4.dist-info/WHEEL +4 -0
- fabricatio-0.3.14.dev4.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,114 @@
|
|
1
|
+
"""A module that provides functionality to rate tasks based on a rating manual and score range."""
|
2
|
+
|
3
|
+
from abc import ABC
|
4
|
+
from typing import Dict, Optional, Set, Unpack
|
5
|
+
|
6
|
+
from fabricatio.capabilities.propose import Propose
|
7
|
+
from fabricatio.capabilities.rating import Rating
|
8
|
+
from fabricatio.models.extra.problem import Improvement
|
9
|
+
from fabricatio.models.generic import Display, WithBriefing
|
10
|
+
from fabricatio.models.kwargs_types import ReviewKwargs, ValidateKwargs
|
11
|
+
from fabricatio.models.task import Task
|
12
|
+
from fabricatio.rust import CONFIG, TEMPLATE_MANAGER
|
13
|
+
from fabricatio.utils import ok
|
14
|
+
|
15
|
+
|
16
|
+
class Review(Rating, Propose, ABC):
|
17
|
+
"""Class that provides functionality to review tasks and strings using a language model.
|
18
|
+
|
19
|
+
This class extends GiveRating and Propose capabilities to analyze content,
|
20
|
+
identify problems, and suggest solutions based on specified criteria.
|
21
|
+
|
22
|
+
The review process can be applied to Task objects or plain strings with
|
23
|
+
appropriate topic and criteria.
|
24
|
+
"""
|
25
|
+
|
26
|
+
async def review_task[T](self, task: Task[T], **kwargs: Unpack[ReviewKwargs]) -> Optional[Improvement]:
|
27
|
+
"""Review a task using specified review criteria.
|
28
|
+
|
29
|
+
This method analyzes a task object to identify problems and propose solutions
|
30
|
+
based on the criteria provided in kwargs.
|
31
|
+
|
32
|
+
Args:
|
33
|
+
task (Task[T]): The task object to be reviewed.
|
34
|
+
**kwargs (Unpack[ReviewKwargs]): Additional keyword arguments for the review process,
|
35
|
+
including topic and optional criteria.
|
36
|
+
|
37
|
+
Returns:
|
38
|
+
Improvement[Task[T]]: A review result containing identified problems and proposed solutions,
|
39
|
+
with a reference to the original task.
|
40
|
+
"""
|
41
|
+
return await self.review_obj(task, **kwargs)
|
42
|
+
|
43
|
+
async def review_string(
|
44
|
+
self,
|
45
|
+
input_text: str,
|
46
|
+
topic: str,
|
47
|
+
criteria: Optional[Set[str]] = None,
|
48
|
+
rating_manual: Optional[Dict[str, str]] = None,
|
49
|
+
**kwargs: Unpack[ValidateKwargs[Improvement]],
|
50
|
+
) -> Optional[Improvement]:
|
51
|
+
"""Review a string based on specified topic and criteria.
|
52
|
+
|
53
|
+
This method analyzes a text string to identify problems and propose solutions
|
54
|
+
based on the given topic and criteria.
|
55
|
+
|
56
|
+
Args:
|
57
|
+
input_text (str): The text content to be reviewed.
|
58
|
+
topic (str): The subject topic for the review criteria.
|
59
|
+
criteria (Optional[Set[str]], optional): A set of criteria for the review.
|
60
|
+
If not provided, criteria will be drafted automatically. Defaults to None.
|
61
|
+
rating_manual (Optional[Dict[str,str]], optional): A dictionary of rating criteria and their corresponding scores.
|
62
|
+
**kwargs (Unpack[ValidateKwargs]): Additional keyword arguments for the LLM usage.
|
63
|
+
|
64
|
+
Returns:
|
65
|
+
Improvement: A review result containing identified problems and proposed solutions,
|
66
|
+
with a reference to the original text.
|
67
|
+
"""
|
68
|
+
default = None
|
69
|
+
if "default" in kwargs:
|
70
|
+
# this `default` is the default for the `propose` method
|
71
|
+
default = kwargs.pop("default")
|
72
|
+
|
73
|
+
criteria = ok(criteria or (await self.draft_rating_criteria(topic, **kwargs)), " No criteria could be use.")
|
74
|
+
manual = rating_manual or await self.draft_rating_manual(topic, criteria, **kwargs)
|
75
|
+
|
76
|
+
if default is not None:
|
77
|
+
kwargs["default"] = default
|
78
|
+
return await self.propose(
|
79
|
+
Improvement,
|
80
|
+
TEMPLATE_MANAGER.render_template(
|
81
|
+
CONFIG.templates.review_string_template,
|
82
|
+
{"text": input_text, "topic": topic, "criteria_manual": manual},
|
83
|
+
),
|
84
|
+
**kwargs,
|
85
|
+
)
|
86
|
+
|
87
|
+
async def review_obj[M: (Display, WithBriefing)](
|
88
|
+
self, obj: M, **kwargs: Unpack[ReviewKwargs[Improvement]]
|
89
|
+
) -> Optional[Improvement]:
|
90
|
+
"""Review an object that implements Display or WithBriefing interface.
|
91
|
+
|
92
|
+
This method extracts displayable text from the object and performs a review
|
93
|
+
based on the criteria provided in kwargs.
|
94
|
+
|
95
|
+
Args:
|
96
|
+
obj (M): The object to be reviewed, which must implement either Display or WithBriefing.
|
97
|
+
**kwargs (Unpack[ReviewKwargs]): Additional keyword arguments for the review process,
|
98
|
+
including topic and optional criteria.
|
99
|
+
|
100
|
+
Raises:
|
101
|
+
TypeError: If the object does not implement Display or WithBriefing.
|
102
|
+
|
103
|
+
Returns:
|
104
|
+
Improvement: A review result containing identified problems and proposed solutions,
|
105
|
+
with a reference to the original object.
|
106
|
+
"""
|
107
|
+
if isinstance(obj, Display):
|
108
|
+
text_to_review = obj.display()
|
109
|
+
elif isinstance(obj, WithBriefing):
|
110
|
+
text_to_review = obj.briefing
|
111
|
+
else:
|
112
|
+
raise TypeError(f"Unsupported type for review: {type(obj)}")
|
113
|
+
|
114
|
+
return await self.review_string(text_to_review, **kwargs)
|
@@ -0,0 +1,113 @@
|
|
1
|
+
"""A module for the task capabilities of the Fabricatio library."""
|
2
|
+
|
3
|
+
from abc import ABC
|
4
|
+
from types import CodeType
|
5
|
+
from typing import Any, Dict, List, Optional, Tuple, Unpack
|
6
|
+
|
7
|
+
import ujson
|
8
|
+
|
9
|
+
from fabricatio.capabilities.propose import Propose
|
10
|
+
from fabricatio.journal import logger
|
11
|
+
from fabricatio.models.kwargs_types import ChooseKwargs, ValidateKwargs
|
12
|
+
from fabricatio.models.task import Task
|
13
|
+
from fabricatio.models.tool import Tool, ToolExecutor
|
14
|
+
from fabricatio.models.usages import ToolBoxUsage
|
15
|
+
from fabricatio.parser import JsonCapture, PythonCapture
|
16
|
+
from fabricatio.rust import CONFIG, TEMPLATE_MANAGER
|
17
|
+
|
18
|
+
|
19
|
+
class ProposeTask(Propose, ABC):
|
20
|
+
"""A class that proposes a task based on a prompt."""
|
21
|
+
|
22
|
+
async def propose_task[T](
|
23
|
+
self,
|
24
|
+
prompt: str,
|
25
|
+
**kwargs: Unpack[ValidateKwargs[Task[T]]],
|
26
|
+
) -> Optional[Task[T]]:
|
27
|
+
"""Asynchronously proposes a task based on a given prompt and parameters.
|
28
|
+
|
29
|
+
Parameters:
|
30
|
+
prompt: The prompt text for proposing a task, which is a string that must be provided.
|
31
|
+
**kwargs: The keyword arguments for the LLM (Large Language Model) usage.
|
32
|
+
|
33
|
+
Returns:
|
34
|
+
A Task object based on the proposal result.
|
35
|
+
"""
|
36
|
+
if not prompt:
|
37
|
+
logger.error(err := "Prompt must be provided.")
|
38
|
+
raise ValueError(err)
|
39
|
+
|
40
|
+
return await self.propose(Task, prompt, **kwargs)
|
41
|
+
|
42
|
+
|
43
|
+
class HandleTask(ToolBoxUsage,ABC):
|
44
|
+
"""A class that handles a task based on a task object."""
|
45
|
+
|
46
|
+
async def draft_tool_usage_code(
|
47
|
+
self,
|
48
|
+
task: Task,
|
49
|
+
tools: List[Tool],
|
50
|
+
data: Dict[str, Any],
|
51
|
+
**kwargs: Unpack[ValidateKwargs],
|
52
|
+
) -> Optional[Tuple[CodeType, List[str]]]:
|
53
|
+
"""Asynchronously drafts the tool usage code for a task based on a given task object and tools."""
|
54
|
+
logger.info(f"Drafting tool usage code for task: {task.briefing}")
|
55
|
+
|
56
|
+
if not tools:
|
57
|
+
err = "Tools must be provided to draft the tool usage code."
|
58
|
+
logger.error(err)
|
59
|
+
raise ValueError(err)
|
60
|
+
|
61
|
+
def _validator(response: str) -> Tuple[CodeType, List[str]] | None:
|
62
|
+
if (source := PythonCapture.convert_with(response, lambda resp: compile(resp, "<string>", "exec"))) and (
|
63
|
+
to_extract := JsonCapture.convert_with(response, ujson.loads)
|
64
|
+
):
|
65
|
+
return source, to_extract
|
66
|
+
|
67
|
+
return None
|
68
|
+
|
69
|
+
q = TEMPLATE_MANAGER.render_template(
|
70
|
+
CONFIG.templates.draft_tool_usage_code_template,
|
71
|
+
{
|
72
|
+
"data_module_name": CONFIG.toolbox.data_module_name,
|
73
|
+
"tool_module_name": CONFIG.toolbox.tool_module_name,
|
74
|
+
"task": task.briefing,
|
75
|
+
"deps": task.dependencies_prompt,
|
76
|
+
"tools": [{"name": t.name, "briefing": t.briefing} for t in tools],
|
77
|
+
"data": data,
|
78
|
+
},
|
79
|
+
)
|
80
|
+
logger.debug(f"Code Drafting Question: \n{q}")
|
81
|
+
return await self.aask_validate(
|
82
|
+
question=q,
|
83
|
+
validator=_validator,
|
84
|
+
**kwargs,
|
85
|
+
)
|
86
|
+
|
87
|
+
async def handle_fine_grind(
|
88
|
+
self,
|
89
|
+
task: Task,
|
90
|
+
data: Dict[str, Any],
|
91
|
+
box_choose_kwargs: Optional[ChooseKwargs] = None,
|
92
|
+
tool_choose_kwargs: Optional[ChooseKwargs] = None,
|
93
|
+
**kwargs: Unpack[ValidateKwargs],
|
94
|
+
) -> Optional[Tuple]:
|
95
|
+
"""Asynchronously handles a task based on a given task object and parameters."""
|
96
|
+
logger.info(f"Handling task: \n{task.briefing}")
|
97
|
+
|
98
|
+
tools = await self.gather_tools_fine_grind(task, box_choose_kwargs, tool_choose_kwargs)
|
99
|
+
logger.info(f"Gathered {[t.name for t in tools]}")
|
100
|
+
|
101
|
+
if tools and (pack := await self.draft_tool_usage_code(task, tools, data, **kwargs)):
|
102
|
+
executor = ToolExecutor(candidates=tools, data=data)
|
103
|
+
|
104
|
+
code, to_extract = pack
|
105
|
+
cxt = executor.execute(code)
|
106
|
+
if to_extract:
|
107
|
+
return tuple(cxt.get(k) for k in to_extract)
|
108
|
+
|
109
|
+
return None
|
110
|
+
|
111
|
+
async def handle(self, task: Task, data: Dict[str, Any], **kwargs: Unpack[ValidateKwargs]) -> Optional[Tuple]:
|
112
|
+
"""Asynchronously handles a task based on a given task object and parameters."""
|
113
|
+
return await self.handle_fine_grind(task, data, **kwargs)
|
fabricatio/decorators.py
ADDED
@@ -0,0 +1,251 @@
|
|
1
|
+
"""Decorators for Fabricatio."""
|
2
|
+
|
3
|
+
from asyncio import iscoroutinefunction
|
4
|
+
from functools import wraps
|
5
|
+
from importlib.util import find_spec
|
6
|
+
from inspect import signature
|
7
|
+
from shutil import which
|
8
|
+
from types import ModuleType
|
9
|
+
from typing import Callable, Coroutine, List, Optional
|
10
|
+
|
11
|
+
from fabricatio.journal import logger
|
12
|
+
from fabricatio.rust import CONFIG
|
13
|
+
|
14
|
+
|
15
|
+
def precheck_package[**P, R](package_name: str, msg: str) -> Callable[[Callable[P, R]], Callable[P, R]]:
|
16
|
+
"""Check if a package exists in the current environment.
|
17
|
+
|
18
|
+
Args:
|
19
|
+
package_name (str): The name of the package to check.
|
20
|
+
msg (str): The message to display if the package is not found.
|
21
|
+
|
22
|
+
Returns:
|
23
|
+
bool: True if the package exists, False otherwise.
|
24
|
+
"""
|
25
|
+
|
26
|
+
def _wrapper(
|
27
|
+
func: Callable[P, R] | Callable[P, Coroutine[None, None, R]],
|
28
|
+
) -> Callable[P, R] | Callable[P, Coroutine[None, None, R]]:
|
29
|
+
if iscoroutinefunction(func):
|
30
|
+
|
31
|
+
@wraps(func)
|
32
|
+
async def _async_inner(*args: P.args, **kwargs: P.kwargs) -> R:
|
33
|
+
if find_spec(package_name):
|
34
|
+
return await func(*args, **kwargs)
|
35
|
+
raise RuntimeError(msg)
|
36
|
+
|
37
|
+
return _async_inner
|
38
|
+
|
39
|
+
@wraps(func)
|
40
|
+
def _inner(*args: P.args, **kwargs: P.kwargs) -> R:
|
41
|
+
if find_spec(package_name):
|
42
|
+
return func(*args, **kwargs)
|
43
|
+
raise RuntimeError(msg)
|
44
|
+
|
45
|
+
return _inner
|
46
|
+
|
47
|
+
return _wrapper
|
48
|
+
|
49
|
+
|
50
|
+
def depend_on_external_cmd[**P, R](
|
51
|
+
bin_name: str, install_tip: Optional[str], homepage: Optional[str] = None
|
52
|
+
) -> Callable[[Callable[P, R]], Callable[P, R]]:
|
53
|
+
"""Decorator to check for the presence of an external command.
|
54
|
+
|
55
|
+
Args:
|
56
|
+
bin_name (str): The name of the required binary.
|
57
|
+
install_tip (Optional[str]): Installation instructions for the required binary.
|
58
|
+
homepage (Optional[str]): The homepage of the required binary.
|
59
|
+
|
60
|
+
Returns:
|
61
|
+
Callable[[Callable[P, R]], Callable[P, R]]: A decorator that wraps the function to check for the binary.
|
62
|
+
|
63
|
+
Raises:
|
64
|
+
RuntimeError: If the required binary is not found.
|
65
|
+
"""
|
66
|
+
|
67
|
+
def _decorator(func: Callable[P, R]) -> Callable[P, R]:
|
68
|
+
@wraps(func)
|
69
|
+
def _wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
|
70
|
+
if which(bin_name) is None:
|
71
|
+
err = f"`{bin_name}` is required to run {func.__name__}{signature(func)}, please install it the to `PATH` first."
|
72
|
+
if install_tip is not None:
|
73
|
+
err += f"\nInstall tip: {install_tip}"
|
74
|
+
if homepage is not None:
|
75
|
+
err += f"\nHomepage: {homepage}"
|
76
|
+
logger.error(err)
|
77
|
+
raise RuntimeError(err)
|
78
|
+
return func(*args, **kwargs)
|
79
|
+
|
80
|
+
return _wrapper
|
81
|
+
|
82
|
+
return _decorator
|
83
|
+
|
84
|
+
|
85
|
+
def logging_execution_info[**P, R](func: Callable[P, R]) -> Callable[P, R]:
|
86
|
+
"""Decorator to log the execution of a function.
|
87
|
+
|
88
|
+
Args:
|
89
|
+
func (Callable): The function to be executed
|
90
|
+
|
91
|
+
Returns:
|
92
|
+
Callable: A decorator that wraps the function to log the execution.
|
93
|
+
"""
|
94
|
+
|
95
|
+
@wraps(func)
|
96
|
+
def _wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
|
97
|
+
logger.info(f"Executing function: {func.__name__}{signature(func)}")
|
98
|
+
logger.debug(f"{func.__name__}{signature(func)}\nArgs: {args}\nKwargs: {kwargs}")
|
99
|
+
return func(*args, **kwargs)
|
100
|
+
|
101
|
+
return _wrapper
|
102
|
+
|
103
|
+
|
104
|
+
@precheck_package(
|
105
|
+
"questionary", "'questionary' is required to run this function. Have you installed `fabricatio[qa]`?."
|
106
|
+
)
|
107
|
+
def confirm_to_execute[**P, R](func: Callable[P, R]) -> Callable[P, Optional[R]] | Callable[P, R]:
|
108
|
+
"""Decorator to confirm before executing a function.
|
109
|
+
|
110
|
+
Args:
|
111
|
+
func (Callable): The function to be executed
|
112
|
+
|
113
|
+
Returns:
|
114
|
+
Callable: A decorator that wraps the function to confirm before execution.
|
115
|
+
"""
|
116
|
+
if not CONFIG.general.confirm_on_ops:
|
117
|
+
# Skip confirmation if the configuration is set to False
|
118
|
+
return func
|
119
|
+
from questionary import confirm
|
120
|
+
|
121
|
+
if iscoroutinefunction(func):
|
122
|
+
|
123
|
+
@wraps(func)
|
124
|
+
async def _wrapper(*args: P.args, **kwargs: P.kwargs) -> Optional[R]:
|
125
|
+
if await confirm(
|
126
|
+
f"Are you sure to execute function: {func.__name__}{signature(func)} \n📦 Args:{args}\n🔑 Kwargs:{kwargs}\n",
|
127
|
+
instruction="Please input [Yes/No] to proceed (default: Yes):",
|
128
|
+
).ask_async():
|
129
|
+
return await func(*args, **kwargs)
|
130
|
+
logger.warning(f"Function: {func.__name__}{signature(func)} canceled by user.")
|
131
|
+
return None
|
132
|
+
|
133
|
+
else:
|
134
|
+
|
135
|
+
@wraps(func)
|
136
|
+
def _wrapper(*args: P.args, **kwargs: P.kwargs) -> Optional[R]:
|
137
|
+
if confirm(
|
138
|
+
f"Are you sure to execute function: {func.__name__}{signature(func)} \n📦 Args:{args}\n��� Kwargs:{kwargs}\n",
|
139
|
+
instruction="Please input [Yes/No] to proceed (default: Yes):",
|
140
|
+
).ask():
|
141
|
+
return func(*args, **kwargs)
|
142
|
+
logger.warning(f"Function: {func.__name__}{signature(func)} canceled by user.")
|
143
|
+
return None
|
144
|
+
|
145
|
+
return _wrapper
|
146
|
+
|
147
|
+
|
148
|
+
def use_temp_module[**P, R](modules: ModuleType | List[ModuleType]) -> Callable[[Callable[P, R]], Callable[P, R]]:
|
149
|
+
"""Temporarily inject modules into sys.modules during function execution.
|
150
|
+
|
151
|
+
This decorator allows you to temporarily inject one or more modules into sys.modules
|
152
|
+
while the decorated function executes. After execution, it restores the original
|
153
|
+
state of sys.modules.
|
154
|
+
|
155
|
+
Args:
|
156
|
+
modules (ModuleType | List[ModuleType]): A single module or list of modules to
|
157
|
+
temporarily inject into sys.modules.
|
158
|
+
|
159
|
+
Returns:
|
160
|
+
Callable[[Callable[P, R]], Callable[P, R]]: A decorator that handles temporary
|
161
|
+
module injection.
|
162
|
+
|
163
|
+
Examples:
|
164
|
+
```python
|
165
|
+
from types import ModuleSpec, ModuleType, module_from_spec
|
166
|
+
|
167
|
+
# Create a temporary module
|
168
|
+
temp_module = module_from_spec(ModuleSpec("temp_math", None))
|
169
|
+
temp_module.pi = 3.14
|
170
|
+
|
171
|
+
# Use the decorator to temporarily inject the module
|
172
|
+
@use_temp_module(temp_module)
|
173
|
+
def calculate_area(radius: float) -> float:
|
174
|
+
from temp_math import pi
|
175
|
+
return pi * radius ** 2
|
176
|
+
|
177
|
+
# The temp_module is only available inside the function
|
178
|
+
result = calculate_area(5.0) # Uses temp_module.pi
|
179
|
+
```
|
180
|
+
|
181
|
+
Multiple modules can also be injected:
|
182
|
+
```python
|
183
|
+
module1 = module_from_spec(ModuleSpec("mod1", None))
|
184
|
+
module2 = module_from_spec(ModuleSpec("mod2", None))
|
185
|
+
|
186
|
+
@use_temp_module([module1, module2])
|
187
|
+
def process_data():
|
188
|
+
import mod1, mod2
|
189
|
+
# Work with temporary modules
|
190
|
+
...
|
191
|
+
```
|
192
|
+
"""
|
193
|
+
module_list = [modules] if isinstance(modules, ModuleType) else modules
|
194
|
+
|
195
|
+
def _decorator(func: Callable[P, R]) -> Callable[P, R]:
|
196
|
+
@wraps(func)
|
197
|
+
def _wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
|
198
|
+
import sys
|
199
|
+
|
200
|
+
# Store original modules if they exist
|
201
|
+
for module in module_list:
|
202
|
+
if module.__name__ in sys.modules:
|
203
|
+
raise RuntimeError(
|
204
|
+
f"Module '{module.__name__}' is already present in sys.modules and cannot be overridden."
|
205
|
+
)
|
206
|
+
sys.modules[module.__name__] = module
|
207
|
+
|
208
|
+
try:
|
209
|
+
return func(*args, **kwargs)
|
210
|
+
finally:
|
211
|
+
# Restore original state
|
212
|
+
for module in module_list:
|
213
|
+
del sys.modules[module.__name__]
|
214
|
+
|
215
|
+
return _wrapper
|
216
|
+
|
217
|
+
return _decorator
|
218
|
+
|
219
|
+
|
220
|
+
def logging_exec_time[**P, R](
|
221
|
+
func: Callable[P, R] | Callable[P, Coroutine[None, None, R]],
|
222
|
+
) -> Callable[P, R] | Callable[P, Coroutine[None, None, R]]:
|
223
|
+
"""Decorator to log the execution time of a function.
|
224
|
+
|
225
|
+
Args:
|
226
|
+
func (Callable): The function to be executed
|
227
|
+
|
228
|
+
Returns:
|
229
|
+
Callable: A decorator that wraps the function to log the execution time.
|
230
|
+
"""
|
231
|
+
from time import time
|
232
|
+
|
233
|
+
if iscoroutinefunction(func):
|
234
|
+
|
235
|
+
@wraps(func)
|
236
|
+
async def _async_wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
|
237
|
+
start_time = time()
|
238
|
+
result = await func(*args, **kwargs)
|
239
|
+
logger.debug(f"Execution time of `{func.__name__}`: {time() - start_time:.2f} s")
|
240
|
+
return result
|
241
|
+
|
242
|
+
return _async_wrapper
|
243
|
+
|
244
|
+
@wraps(func)
|
245
|
+
def _wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
|
246
|
+
start_time = time()
|
247
|
+
result = func(*args, **kwargs)
|
248
|
+
logger.debug(f"Execution time of {func.__name__}: {(time() - start_time) * 1000:.2f} ms")
|
249
|
+
return result
|
250
|
+
|
251
|
+
return _wrapper
|
fabricatio/emitter.py
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
"""Core module that contains the Env class for managing event handling."""
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import Callable, ClassVar, Optional, Self, overload
|
4
|
+
|
5
|
+
from pymitter import EventEmitter
|
6
|
+
|
7
|
+
from fabricatio.rust import CONFIG, Event
|
8
|
+
|
9
|
+
|
10
|
+
@dataclass
|
11
|
+
class Env:
|
12
|
+
"""Environment class that manages event handling using EventEmitter."""
|
13
|
+
|
14
|
+
ee: ClassVar[EventEmitter] = EventEmitter(
|
15
|
+
delimiter=CONFIG.pymitter.delimiter,
|
16
|
+
new_listener=CONFIG.pymitter.new_listener_event,
|
17
|
+
max_listeners=CONFIG.pymitter.max_listeners,
|
18
|
+
wildcard=True,
|
19
|
+
)
|
20
|
+
|
21
|
+
@overload
|
22
|
+
def on(self, event: str | Event, /, ttl: int = -1) -> Self:
|
23
|
+
"""
|
24
|
+
Registers an event listener that listens indefinitely or for a specified number of times.
|
25
|
+
|
26
|
+
Args:
|
27
|
+
event (str | Event): The event to listen for.
|
28
|
+
ttl (int): Time-to-live for the listener. If -1, the listener will listen indefinitely.
|
29
|
+
|
30
|
+
Returns:
|
31
|
+
Self: The current instance of Env.
|
32
|
+
"""
|
33
|
+
...
|
34
|
+
|
35
|
+
@overload
|
36
|
+
def on[**P, R](
|
37
|
+
self,
|
38
|
+
event: str | Event,
|
39
|
+
func: Optional[Callable[P, R]] = None,
|
40
|
+
/,
|
41
|
+
ttl: int = -1,
|
42
|
+
) -> Callable[[Callable[P, R]], Callable[P, R]]:
|
43
|
+
"""
|
44
|
+
Registers an event listener with a specific function that listens indefinitely or for a specified number of times.
|
45
|
+
|
46
|
+
Args:
|
47
|
+
event (str | Event): The event to listen for.
|
48
|
+
func (Callable[P, R]): The function to be called when the event is emitted.
|
49
|
+
ttl (int): Time-to-live for the listener. If -1, the listener will listen indefinitely.
|
50
|
+
|
51
|
+
Returns:
|
52
|
+
Callable[[Callable[P, R]], Callable[P, R]]: A decorator that registers the function as an event listener.
|
53
|
+
"""
|
54
|
+
...
|
55
|
+
|
56
|
+
def on[**P, R](
|
57
|
+
self,
|
58
|
+
event: str | Event,
|
59
|
+
func: Optional[Callable[P, R]] = None,
|
60
|
+
/,
|
61
|
+
ttl=-1,
|
62
|
+
) -> Callable[[Callable[P, R]], Callable[P, R]] | Self:
|
63
|
+
"""Registers an event listener with a specific function that listens indefinitely or for a specified number of times.
|
64
|
+
|
65
|
+
Args:
|
66
|
+
event (str | Event): The event to listen for.
|
67
|
+
func (Callable[P, R]): The function to be called when the event is emitted.
|
68
|
+
ttl (int): Time-to-live for the listener. If -1, the listener will listen indefinitely.
|
69
|
+
|
70
|
+
Returns:
|
71
|
+
Callable[[Callable[P, R]], Callable[P, R]] | Self: A decorator that registers the function as an event listener or the current instance of Env.
|
72
|
+
"""
|
73
|
+
if isinstance(event, Event):
|
74
|
+
event = event.collapse()
|
75
|
+
if func is None:
|
76
|
+
return self.ee.on(event, ttl=ttl)
|
77
|
+
self.ee.on(event, func, ttl=ttl)
|
78
|
+
return self
|
79
|
+
|
80
|
+
@overload
|
81
|
+
def once[**P, R](
|
82
|
+
self,
|
83
|
+
event: str | Event,
|
84
|
+
) -> Callable[[Callable[P, R]], Callable[P, R]]:
|
85
|
+
"""
|
86
|
+
Registers an event listener that listens only once.
|
87
|
+
|
88
|
+
Args:
|
89
|
+
event (str | Event): The event to listen for.
|
90
|
+
|
91
|
+
Returns:
|
92
|
+
Callable[[Callable[P, R]], Callable[P, R]]: A decorator that registers the function as an event listener.
|
93
|
+
"""
|
94
|
+
...
|
95
|
+
|
96
|
+
@overload
|
97
|
+
def once[**P, R](
|
98
|
+
self,
|
99
|
+
event: str | Event,
|
100
|
+
func: Callable[[Callable[P, R]], Callable[P, R]],
|
101
|
+
) -> Self:
|
102
|
+
"""
|
103
|
+
Registers an event listener with a specific function that listens only once.
|
104
|
+
|
105
|
+
Args:
|
106
|
+
event (str | Event): The event to listen for.
|
107
|
+
func (Callable[P, R]): The function to be called when the event is emitted.
|
108
|
+
|
109
|
+
Returns:
|
110
|
+
Self: The current instance of Env.
|
111
|
+
"""
|
112
|
+
...
|
113
|
+
|
114
|
+
def once[**P, R](
|
115
|
+
self,
|
116
|
+
event: str | Event,
|
117
|
+
func: Optional[Callable[P, R]] = None,
|
118
|
+
) -> Callable[[Callable[P, R]], Callable[P, R]] | Self:
|
119
|
+
"""Registers an event listener with a specific function that listens only once.
|
120
|
+
|
121
|
+
Args:
|
122
|
+
event (str | Event): The event to listen for.
|
123
|
+
func (Callable[P, R]): The function to be called when the event is emitted.
|
124
|
+
|
125
|
+
Returns:
|
126
|
+
Callable[[Callable[P, R]], Callable[P, R]] | Self: A decorator that registers the function as an event listener or the current instance
|
127
|
+
"""
|
128
|
+
if isinstance(event, Event):
|
129
|
+
event = event.collapse()
|
130
|
+
if func is None:
|
131
|
+
return self.ee.once(event)
|
132
|
+
|
133
|
+
self.ee.once(event, func)
|
134
|
+
return self
|
135
|
+
|
136
|
+
def emit[**P](self, event: str | Event, *args: P.args, **kwargs: P.kwargs) -> None:
|
137
|
+
"""Emits an event to all registered listeners.
|
138
|
+
|
139
|
+
Args:
|
140
|
+
event (str | Event): The event to emit.
|
141
|
+
*args: Positional arguments to pass to the listeners.
|
142
|
+
**kwargs: Keyword arguments to pass to the listeners.
|
143
|
+
"""
|
144
|
+
if isinstance(event, Event):
|
145
|
+
event = event.collapse()
|
146
|
+
|
147
|
+
self.ee.emit(event, *args, **kwargs)
|
148
|
+
|
149
|
+
async def emit_async[**P](self, event: str | Event, *args: P.args, **kwargs: P.kwargs) -> None:
|
150
|
+
"""Asynchronously emits an event to all registered listeners.
|
151
|
+
|
152
|
+
Args:
|
153
|
+
event (str | Event): The event to emit.
|
154
|
+
*args: Positional arguments to pass to the listeners.
|
155
|
+
**kwargs: Keyword arguments to pass to the listeners.
|
156
|
+
"""
|
157
|
+
if isinstance(event, Event):
|
158
|
+
event = event.collapse()
|
159
|
+
return await self.ee.emit_async(event, *args, **kwargs)
|
160
|
+
|
161
|
+
def emit_future[**P](self, event: str | Event, *args: P.args, **kwargs: P.kwargs) -> None:
|
162
|
+
"""Emits an event to all registered listeners and returns a future object.
|
163
|
+
|
164
|
+
Args:
|
165
|
+
event (str | Event): The event to emit.
|
166
|
+
*args: Positional arguments to pass to the listeners.
|
167
|
+
**kwargs: Keyword arguments to pass to the listeners.
|
168
|
+
|
169
|
+
Returns:
|
170
|
+
None: The future object.
|
171
|
+
"""
|
172
|
+
if isinstance(event, Event):
|
173
|
+
event = event.collapse()
|
174
|
+
return self.ee.emit_future(event, *args, **kwargs)
|
175
|
+
|
176
|
+
|
177
|
+
env = Env()
|
@@ -0,0 +1,35 @@
|
|
1
|
+
"""FileSystem manipulation module for Fabricatio."""
|
2
|
+
from importlib.util import find_spec
|
3
|
+
|
4
|
+
from fabricatio.fs.curd import (
|
5
|
+
absolute_path,
|
6
|
+
copy_file,
|
7
|
+
create_directory,
|
8
|
+
delete_directory,
|
9
|
+
delete_file,
|
10
|
+
dump_text,
|
11
|
+
gather_files,
|
12
|
+
move_file,
|
13
|
+
tree,
|
14
|
+
)
|
15
|
+
from fabricatio.fs.readers import safe_json_read, safe_text_read
|
16
|
+
|
17
|
+
__all__ = [
|
18
|
+
"absolute_path",
|
19
|
+
"copy_file",
|
20
|
+
"create_directory",
|
21
|
+
"delete_directory",
|
22
|
+
"delete_file",
|
23
|
+
"dump_text",
|
24
|
+
"gather_files",
|
25
|
+
"move_file",
|
26
|
+
"safe_json_read",
|
27
|
+
"safe_text_read",
|
28
|
+
"tree",
|
29
|
+
]
|
30
|
+
|
31
|
+
if find_spec("magika"):
|
32
|
+
from magika import Magika
|
33
|
+
|
34
|
+
MAGIKA = Magika()
|
35
|
+
__all__ += ["MAGIKA"]
|