fabricatio 0.2.4.dev3__cp312-cp312-win_amd64.whl → 0.2.5__cp312-cp312-win_amd64.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.
Files changed (39) hide show
  1. fabricatio/__init__.py +14 -5
  2. fabricatio/_rust.cp312-win_amd64.pyd +0 -0
  3. fabricatio/_rust.pyi +65 -16
  4. fabricatio/_rust_instances.py +2 -0
  5. fabricatio/actions/article.py +46 -14
  6. fabricatio/actions/output.py +21 -0
  7. fabricatio/actions/rag.py +1 -1
  8. fabricatio/capabilities/propose.py +14 -20
  9. fabricatio/capabilities/rag.py +57 -22
  10. fabricatio/capabilities/rating.py +59 -51
  11. fabricatio/capabilities/review.py +241 -0
  12. fabricatio/capabilities/task.py +7 -8
  13. fabricatio/config.py +33 -4
  14. fabricatio/fs/__init__.py +13 -1
  15. fabricatio/fs/curd.py +27 -8
  16. fabricatio/fs/readers.py +6 -3
  17. fabricatio/journal.py +1 -1
  18. fabricatio/models/action.py +6 -8
  19. fabricatio/models/events.py +6 -4
  20. fabricatio/models/extra.py +100 -25
  21. fabricatio/models/generic.py +56 -4
  22. fabricatio/models/kwargs_types.py +123 -35
  23. fabricatio/models/role.py +3 -3
  24. fabricatio/models/task.py +0 -14
  25. fabricatio/models/tool.py +7 -6
  26. fabricatio/models/usages.py +144 -101
  27. fabricatio/parser.py +26 -5
  28. fabricatio/toolboxes/__init__.py +1 -3
  29. fabricatio/toolboxes/fs.py +17 -1
  30. fabricatio/workflows/articles.py +10 -6
  31. fabricatio/workflows/rag.py +11 -0
  32. fabricatio-0.2.5.data/scripts/tdown.exe +0 -0
  33. {fabricatio-0.2.4.dev3.dist-info → fabricatio-0.2.5.dist-info}/METADATA +2 -1
  34. fabricatio-0.2.5.dist-info/RECORD +41 -0
  35. fabricatio/toolboxes/task.py +0 -6
  36. fabricatio-0.2.4.dev3.data/scripts/tdown.exe +0 -0
  37. fabricatio-0.2.4.dev3.dist-info/RECORD +0 -39
  38. {fabricatio-0.2.4.dev3.dist-info → fabricatio-0.2.5.dist-info}/WHEEL +0 -0
  39. {fabricatio-0.2.4.dev3.dist-info → fabricatio-0.2.5.dist-info}/licenses/LICENSE +0 -0
fabricatio/fs/curd.py CHANGED
@@ -2,14 +2,14 @@
2
2
 
3
3
  import shutil
4
4
  import subprocess
5
+ from os import PathLike
5
6
  from pathlib import Path
6
7
  from typing import Union
7
8
 
8
- from fabricatio.decorators import depend_on_external_cmd, logging_execution_info
9
+ from fabricatio.decorators import depend_on_external_cmd
9
10
  from fabricatio.journal import logger
10
11
 
11
12
 
12
- @logging_execution_info
13
13
  def dump_text(path: Union[str, Path], text: str) -> None:
14
14
  """Dump text to a file. you need to make sure the file's parent directory exists.
15
15
 
@@ -23,7 +23,6 @@ def dump_text(path: Union[str, Path], text: str) -> None:
23
23
  Path(path).write_text(text, encoding="utf-8", errors="ignore")
24
24
 
25
25
 
26
- @logging_execution_info
27
26
  def copy_file(src: Union[str, Path], dst: Union[str, Path]) -> None:
28
27
  """Copy a file from source to destination.
29
28
 
@@ -43,7 +42,6 @@ def copy_file(src: Union[str, Path], dst: Union[str, Path]) -> None:
43
42
  raise
44
43
 
45
44
 
46
- @logging_execution_info
47
45
  def move_file(src: Union[str, Path], dst: Union[str, Path]) -> None:
48
46
  """Move a file from source to destination.
49
47
 
@@ -63,7 +61,6 @@ def move_file(src: Union[str, Path], dst: Union[str, Path]) -> None:
63
61
  raise
64
62
 
65
63
 
66
- @logging_execution_info
67
64
  def delete_file(file_path: Union[str, Path]) -> None:
68
65
  """Delete a file.
69
66
 
@@ -82,7 +79,6 @@ def delete_file(file_path: Union[str, Path]) -> None:
82
79
  raise
83
80
 
84
81
 
85
- @logging_execution_info
86
82
  def create_directory(dir_path: Union[str, Path], parents: bool = True, exist_ok: bool = True) -> None:
87
83
  """Create a directory.
88
84
 
@@ -99,7 +95,6 @@ def create_directory(dir_path: Union[str, Path], parents: bool = True, exist_ok:
99
95
  raise
100
96
 
101
97
 
102
- @logging_execution_info
103
98
  @depend_on_external_cmd(
104
99
  "erd",
105
100
  "Please install `erd` using `cargo install erdtree` or `scoop install erdtree`.",
@@ -111,7 +106,6 @@ def tree(dir_path: Union[str, Path]) -> str:
111
106
  return subprocess.check_output(("erd", dir_path.as_posix()), encoding="utf-8") # noqa: S603
112
107
 
113
108
 
114
- @logging_execution_info
115
109
  def delete_directory(dir_path: Union[str, Path]) -> None:
116
110
  """Delete a directory and its contents.
117
111
 
@@ -128,3 +122,28 @@ def delete_directory(dir_path: Union[str, Path]) -> None:
128
122
  except OSError as e:
129
123
  logger.error(f"Failed to delete directory {dir_path}: {e!s}")
130
124
  raise
125
+
126
+
127
+ def absolute_path(path: str | Path | PathLike) -> str:
128
+ """Get the absolute path of a file or directory.
129
+
130
+ Args:
131
+ path (str, Path, PathLike): The path to the file or directory.
132
+
133
+ Returns:
134
+ str: The absolute path of the file or directory.
135
+ """
136
+ return Path(path).expanduser().resolve().as_posix()
137
+
138
+
139
+ def gather_files(directory: str | Path | PathLike, extension: str) -> list[str]:
140
+ """Gather all files with a specific extension in a directory.
141
+
142
+ Args:
143
+ directory (str, Path, PathLike): The directory to search in.
144
+ extension (str): The file extension to look for.
145
+
146
+ Returns:
147
+ list[str]: A list of file paths with the specified extension.
148
+ """
149
+ return [file.as_posix() for file in Path(directory).rglob(f"*.{extension}")]
fabricatio/fs/readers.py CHANGED
@@ -3,10 +3,11 @@
3
3
  from pathlib import Path
4
4
  from typing import Dict
5
5
 
6
+ import orjson
6
7
  from magika import Magika
7
- from orjson import orjson
8
8
 
9
9
  from fabricatio.config import configs
10
+ from fabricatio.journal import logger
10
11
 
11
12
  magika = Magika(model_dir=configs.magika.model_dir)
12
13
 
@@ -23,7 +24,8 @@ def safe_text_read(path: Path | str) -> str:
23
24
  path = Path(path)
24
25
  try:
25
26
  return path.read_text(encoding="utf-8")
26
- except (UnicodeDecodeError, IsADirectoryError, FileNotFoundError):
27
+ except (UnicodeDecodeError, IsADirectoryError, FileNotFoundError) as e:
28
+ logger.error(f"Failed to read file {path}: {e!s}")
27
29
  return ""
28
30
 
29
31
 
@@ -39,5 +41,6 @@ def safe_json_read(path: Path | str) -> Dict:
39
41
  path = Path(path)
40
42
  try:
41
43
  return orjson.loads(path.read_text(encoding="utf-8"))
42
- except (orjson.JSONDecodeError, IsADirectoryError, FileNotFoundError):
44
+ except (orjson.JSONDecodeError, IsADirectoryError, FileNotFoundError) as e:
45
+ logger.error(f"Failed to read file {path}: {e!s}")
43
46
  return {}
fabricatio/journal.py CHANGED
@@ -18,7 +18,7 @@ logger.add(
18
18
  )
19
19
  logger.add(sys.stderr, level=configs.debug.log_level)
20
20
 
21
-
21
+ __all__ = ["logger"]
22
22
  if __name__ == "__main__":
23
23
  logger.debug("This is a trace message.")
24
24
  logger.info("This is an information message.")
@@ -5,7 +5,7 @@ from abc import abstractmethod
5
5
  from asyncio import Queue, create_task
6
6
  from typing import Any, Dict, Self, Tuple, Type, Union, Unpack, final
7
7
 
8
- from fabricatio.capabilities.rating import GiveRating
8
+ from fabricatio.capabilities.review import Review
9
9
  from fabricatio.capabilities.task import HandleTask, ProposeTask
10
10
  from fabricatio.journal import logger
11
11
  from fabricatio.models.generic import WithBriefing
@@ -14,7 +14,7 @@ from fabricatio.models.usages import ToolBoxUsage
14
14
  from pydantic import Field, PrivateAttr
15
15
 
16
16
 
17
- class Action(HandleTask, ProposeTask, GiveRating):
17
+ class Action(HandleTask, ProposeTask, Review):
18
18
  """Class that represents an action to be executed in a workflow."""
19
19
 
20
20
  name: str = Field(default="")
@@ -34,7 +34,6 @@ class Action(HandleTask, ProposeTask, GiveRating):
34
34
  __context: The context to be used for initialization.
35
35
  """
36
36
  self.name = self.name or self.__class__.__name__
37
-
38
37
  self.description = self.description or self.__class__.__doc__ or ""
39
38
 
40
39
  @abstractmethod
@@ -76,7 +75,7 @@ class WorkFlow(WithBriefing, ToolBoxUsage):
76
75
  _context: Queue[Dict[str, Any]] = PrivateAttr(default_factory=lambda: Queue(maxsize=1))
77
76
  """ The context dictionary to be used for workflow execution."""
78
77
 
79
- _instances: Tuple[Action, ...] = PrivateAttr(...)
78
+ _instances: Tuple[Action, ...] = PrivateAttr(default_factory=tuple)
80
79
  """ The instances of the workflow steps."""
81
80
 
82
81
  steps: Tuple[Union[Type[Action], Action], ...] = Field(...)
@@ -123,17 +122,16 @@ class WorkFlow(WithBriefing, ToolBoxUsage):
123
122
  current_action = None
124
123
  try:
125
124
  for step in self._instances:
126
- logger.debug(f"Executing step: {step.name}")
125
+ logger.debug(f"Executing step: {(current_action := step.name)}")
127
126
  act_task = create_task(step.act(await self._context.get()))
128
127
  if task.is_cancelled():
129
128
  act_task.cancel(f"Cancelled by task: {task.name}")
130
129
  break
131
130
  modified_ctx = await act_task
132
131
  await self._context.put(modified_ctx)
133
- current_action = step.name
134
132
  logger.info(f"Finished executing workflow: {self.name}")
135
- final_ctx = await self._context.get()
136
- if self.task_output_key not in final_ctx:
133
+
134
+ if self.task_output_key not in (final_ctx := await self._context.get()):
137
135
  logger.warning(
138
136
  f"Task output key: {self.task_output_key} not found in the context, None will be returned. You can check if `Action.output_key` is set the same as `WorkFlow.task_output_key`."
139
137
  )
@@ -18,7 +18,7 @@ class Event(BaseModel):
18
18
  """ The segments of the namespaces."""
19
19
 
20
20
  @classmethod
21
- def instantiate_from(cls, event: EventLike) -> Self:
21
+ def instantiate_from(cls, event: EventLike) -> "Event":
22
22
  """Create an Event instance from a string or list of strings or an Event instance.
23
23
 
24
24
  Args:
@@ -35,7 +35,7 @@ class Event(BaseModel):
35
35
  return cls(segments=event)
36
36
 
37
37
  @classmethod
38
- def quick_instantiate(cls, event: EventLike) -> Self:
38
+ def quick_instantiate(cls, event: EventLike) -> "Event":
39
39
  """Create an Event instance from a string or list of strings or an Event instance and push a wildcard and pending segment.
40
40
 
41
41
  Args:
@@ -59,7 +59,7 @@ class Event(BaseModel):
59
59
 
60
60
  def clone(self) -> Self:
61
61
  """Clone the event."""
62
- return Event(segments=list(self.segments))
62
+ return self.__class__(segments=list(self.segments))
63
63
 
64
64
  def push(self, segment: str) -> Self:
65
65
  """Push a segment to the event."""
@@ -113,6 +113,8 @@ class Event(BaseModel):
113
113
  """Return the hash of the event, using the collapsed string."""
114
114
  return hash(self.collapse())
115
115
 
116
- def __eq__(self, other: str | List[str] | Self) -> bool:
116
+ def __eq__(self, other: object) -> bool:
117
117
  """Check if the event is equal to another event or a string."""
118
+ if not isinstance(other, (str , list , Event)):
119
+ return False
118
120
  return self.collapse() == Event.instantiate_from(other).collapse()
@@ -2,43 +2,39 @@
2
2
 
3
3
  from typing import List
4
4
 
5
- from fabricatio.models.generic import Display, PrepareVectorization, ProposedAble
6
- from pydantic import BaseModel, ConfigDict, Field
5
+ from fabricatio.models.generic import Base, Display, FinalizedDumpAble, PrepareVectorization, ProposedAble
6
+ from pydantic import Field
7
7
 
8
8
 
9
- class Equation(BaseModel):
9
+ class Equation(Base):
10
10
  """Structured representation of mathematical equations (including their physical or conceptual meanings)."""
11
11
 
12
- model_config = ConfigDict(use_attribute_docstrings=True)
13
-
14
- description: str = Field(...)
12
+ description: str
15
13
  """A concise explanation of the equation's meaning, purpose, and relevance in the context of the research."""
16
14
 
17
- latex_code: str = Field(...)
15
+ latex_code: str
18
16
  """The LaTeX code used to represent the equation in a publication-ready format."""
19
17
 
20
18
 
21
- class Figure(BaseModel):
19
+ class Figure(Base):
22
20
  """Structured representation of figures (including their academic significance and explanatory captions)."""
23
21
 
24
- model_config = ConfigDict(use_attribute_docstrings=True)
25
-
26
- description: str = Field(...)
22
+ description: str
27
23
  """A detailed explanation of the figure's content and its role in conveying key insights."""
28
24
 
29
- figure_caption: str = Field(...)
25
+ figure_caption: str
30
26
  """The caption accompanying the figure, summarizing its main points and academic value."""
31
27
 
32
- figure_path: str = Field(...)
28
+ figure_path: str
33
29
  """The file path to the figure"""
34
30
 
35
31
 
36
- class Highlightings(BaseModel):
32
+ class Highlightings(Base):
37
33
  """Structured representation of highlighted elements in an academic paper (including equations, algorithms, figures, and tables)."""
38
34
 
39
35
  # Academic Achievements Showcase
40
36
  highlighted_equations: List[Equation] = Field(default_factory=list)
41
- """Core mathematical equations that represent breakthroughs in the field, accompanied by explanations of their physical or conceptual significance."""
37
+ """Core mathematical equations that represent breakthroughs in the field, accompanied by explanations of their physical or conceptual significance,Should always be in LaTeX format wrapped in $ or $$ signs."""
42
38
 
43
39
  highlighted_algorithms: List[str] = Field(default_factory=list)
44
40
  """Pseudocode for key algorithms, annotated to highlight innovative components."""
@@ -57,40 +53,119 @@ class ArticleEssence(ProposedAble, Display, PrepareVectorization):
57
53
  title: str = Field(...)
58
54
  """The full title of the paper, including any subtitles if applicable."""
59
55
 
60
- authors: List[str] = Field(default_factory=list)
56
+ authors: List[str]
61
57
  """A list of the paper's authors, typically in the order of contribution."""
62
58
 
63
- keywords: List[str] = Field(default_factory=list)
59
+ keywords: List[str]
64
60
  """A list of keywords that summarize the paper's focus and facilitate indexing."""
65
61
 
66
- publication_year: int = Field(None)
62
+ publication_year: int
67
63
  """The year in which the paper was published."""
68
64
 
69
65
  # Core Content Elements
70
- domain: List[str] = Field(default_factory=list)
66
+ highlightings: Highlightings = Field(default_factory=Highlightings)
67
+ """A collection of highlighted elements in the paper, including equations, algorithms, figures, and tables."""
68
+
69
+ domain: List[str]
71
70
  """The research domains or fields addressed by the paper (e.g., ['Natural Language Processing', 'Computer Vision'])."""
72
71
 
73
72
  abstract: str = Field(...)
74
73
  """A structured abstract that outlines the research problem, methodology, and conclusions in three distinct sections."""
75
74
 
76
- core_contributions: List[str] = Field(default_factory=list)
75
+ core_contributions: List[str]
77
76
  """Key academic contributions that distinguish the paper from prior work in the field."""
78
77
 
79
- technical_novelty: List[str] = Field(default_factory=list)
78
+ technical_novelty: List[str]
80
79
  """Specific technical innovations introduced by the research, listed as individual points."""
81
80
 
82
81
  # Academic Discussion Dimensions
83
- research_problem: str = Field("")
82
+ research_problems: List[str]
84
83
  """A clearly defined research question or problem addressed by the study."""
85
84
 
86
- limitations: List[str] = Field(default_factory=list)
85
+ limitations: List[str]
87
86
  """An analysis of the methodological or experimental limitations of the research."""
88
87
 
89
- future_work: List[str] = Field(default_factory=list)
88
+ future_work: List[str]
90
89
  """Suggestions for potential directions or topics for follow-up studies."""
91
90
 
92
- impact_analysis: str = Field("")
91
+ impact_analysis: List[str]
93
92
  """An assessment of the paper's potential influence on the development of the field."""
94
93
 
95
94
  def _prepare_vectorization_inner(self) -> str:
96
95
  return self.model_dump_json()
96
+
97
+
98
+ class ArticleProposal(ProposedAble, Display):
99
+ """Structured representation of the proposal for an academic paper."""
100
+
101
+ title: str = Field(...)
102
+ """The proposed title of the paper."""
103
+
104
+ focused_problem: List[str] = Field(default_factory=list)
105
+ """The specific research problem or question that the paper aims to address."""
106
+ research_aim: List[str] = Field(default_factory=list)
107
+ """The main objective or goal of the research, outlining what the study aims to achieve."""
108
+ research_methods: List[str] = Field(default_factory=list)
109
+ """The methods used in the research, including the approach, techniques, and tools employed."""
110
+
111
+
112
+ class ArticleSubsectionOutline(Base):
113
+ """Structured representation of the subsections of an academic paper."""
114
+
115
+ title: str = Field(...)
116
+ """The title of the subsection."""
117
+
118
+ description: str = Field(...)
119
+ """A brief description of the subsection's content should be, how it fits into the overall structure of the paper, and its significance in the context of the research."""
120
+
121
+
122
+ class ArticleSectionOutline(Base):
123
+ """Structured representation of the sections of an academic paper."""
124
+
125
+ title: str = Field(...)
126
+ """The title of the section."""
127
+ description: str = Field(...)
128
+ """A brief description of the section's content should be, how it fits into the overall structure of the paper, and its significance in the context of the research."""
129
+ subsections: List[ArticleSubsectionOutline] = Field(default_factory=list)
130
+ """The subsections of the section, outlining their content and significance."""
131
+
132
+
133
+ class ArticleChapterOutline(Base):
134
+ """Structured representation of the chapters of an academic paper."""
135
+
136
+ title: str = Field(...)
137
+ """The title of the chapter."""
138
+ description: str = Field(...)
139
+ """A brief description of the chapter's content should be, how it fits into the overall structure of the paper, and its significance in the context of the research."""
140
+ sections: List[ArticleSectionOutline] = Field(default_factory=list)
141
+ """The sections of the chapter, outlining their content and significance."""
142
+
143
+
144
+ class ArticleOutline(ProposedAble, Display, FinalizedDumpAble):
145
+ """Structured representation of the outline for an academic paper."""
146
+
147
+ title: str = Field(...)
148
+ """The proposed title of the paper."""
149
+
150
+ prospect: str = Field(...)
151
+ """A brief description of the research problem or question that the paper aims to address manipulating methods or techniques"""
152
+
153
+ chapters: List[ArticleChapterOutline] = Field(default_factory=list)
154
+ """The chapters of the paper, outlining their content and significance."""
155
+
156
+ def finalized_dump(self) -> str:
157
+ """Finalized dump of the article outline.
158
+
159
+ Returns:
160
+ str: The finalized dump of the article outline.
161
+ """
162
+ lines: List[str] = []
163
+
164
+ for chapter in self.chapters:
165
+ lines.append(f"= {chapter.title}")
166
+ for section in chapter.sections:
167
+ lines.append(f"== {section.title}")
168
+ for subsection in section.subsections:
169
+ lines.append(f"=== {subsection.title}")
170
+
171
+ return "\n\n".join(lines)
@@ -2,7 +2,7 @@
2
2
 
3
3
  from abc import abstractmethod
4
4
  from pathlib import Path
5
- from typing import Callable, Iterable, List, Optional, Self, Union, final
5
+ from typing import Any, Callable, Dict, Iterable, List, Optional, Self, Union, final, overload
6
6
 
7
7
  import orjson
8
8
  from fabricatio._rust import blake3_hash
@@ -67,6 +67,18 @@ class WithBriefing(Named, Described):
67
67
  """
68
68
  return f"{self.name}: {self.description}" if self.description else self.name
69
69
 
70
+ def prepend[D: Dict[str, Any]](self, kwargs: D) -> D:
71
+ """Prepend the briefing to the system message in the kwargs.
72
+
73
+ Args:
74
+ kwargs (Dict[str, Any]): The keyword arguments to modify.
75
+
76
+ Returns:
77
+ Dict[str, Any]: The modified keyword arguments.
78
+ """
79
+ kwargs["system_message"] = f"# your personal briefing: \n{self.briefing}\n" + kwargs.get("system_message", "")
80
+ return kwargs
81
+
70
82
 
71
83
  class WithFormatedJsonSchema(Base):
72
84
  """Class that provides a formatted JSON schema of the model."""
@@ -87,8 +99,15 @@ class WithFormatedJsonSchema(Base):
87
99
  class CreateJsonObjPrompt(WithFormatedJsonSchema):
88
100
  """Class that provides a prompt for creating a JSON object."""
89
101
 
102
+
90
103
  @classmethod
91
- def create_json_prompt(cls, requirement: str) -> str:
104
+ @overload
105
+ def create_json_prompt(cls, requirement: List[str]) -> List[str]:...
106
+ @classmethod
107
+ @overload
108
+ def create_json_prompt(cls, requirement: str) -> str:...
109
+ @classmethod
110
+ def create_json_prompt(cls, requirement: str|List[str]) -> str|List[str]:
92
111
  """Create the prompt for creating a JSON object with given requirement.
93
112
 
94
113
  Args:
@@ -97,10 +116,18 @@ class CreateJsonObjPrompt(WithFormatedJsonSchema):
97
116
  Returns:
98
117
  str: The prompt for creating a JSON object with given requirement.
99
118
  """
100
- return template_manager.render_template(
119
+ if isinstance(requirement, str):
120
+ return template_manager.render_template(
101
121
  configs.templates.create_json_obj_template,
102
122
  {"requirement": requirement, "json_schema": cls.formated_json_schema()},
103
123
  )
124
+ return [
125
+ template_manager.render_template(
126
+ configs.templates.create_json_obj_template,
127
+ {"requirement": r, "json_schema": cls.formated_json_schema()},
128
+ )
129
+ for r in requirement
130
+ ]
104
131
 
105
132
 
106
133
  class InstantiateFromString(Base):
@@ -120,11 +147,35 @@ class InstantiateFromString(Base):
120
147
 
121
148
 
122
149
  class ProposedAble(CreateJsonObjPrompt, InstantiateFromString):
123
- """Class that provides methods for proposing a task."""
150
+ """Class that provides a method to propose a JSON object based on the requirement."""
124
151
 
125
152
  pass
126
153
 
127
154
 
155
+ class FinalizedDumpAble(Base):
156
+ """Class that provides a method to finalize the dump of the object."""
157
+
158
+ @abstractmethod
159
+ def finalized_dump(self) -> str:
160
+ """Finalize the dump of the object.
161
+
162
+ Returns:
163
+ str: The finalized dump of the object.
164
+ """
165
+
166
+ def finalized_dump_to(self, path: str | Path) -> Self:
167
+ """Finalize the dump of the object to a file.
168
+
169
+ Args:
170
+ path (str | Path): The path to save the finalized dump.
171
+
172
+ Returns:
173
+ Self: The current instance of the object.
174
+ """
175
+ Path(path).write_text(self.finalized_dump(), encoding="utf-8")
176
+ return self
177
+
178
+
128
179
  class WithDependency(Base):
129
180
  """Class that manages file dependencies."""
130
181
 
@@ -337,3 +388,4 @@ class ScopedConfig(Base):
337
388
  for attr_name in ScopedConfig.model_fields:
338
389
  if (attr := getattr(self, attr_name)) is not None and getattr(other, attr_name) is None:
339
390
  setattr(other, attr_name, attr)
391
+ return self