gyomu-docstring 0.2.0__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.
- gyomu_docstring-0.2.0/.gitignore +19 -0
- gyomu_docstring-0.2.0/PKG-INFO +14 -0
- gyomu_docstring-0.2.0/README.md +3 -0
- gyomu_docstring-0.2.0/docstring_test_support/__init__.py +0 -0
- gyomu_docstring-0.2.0/docstring_test_support/helpers.py +372 -0
- gyomu_docstring-0.2.0/pyproject.toml +25 -0
- gyomu_docstring-0.2.0/src/gyomu_docstring/__init__.py +0 -0
- gyomu_docstring-0.2.0/src/gyomu_docstring/update/__init__.py +0 -0
- gyomu_docstring-0.2.0/src/gyomu_docstring/update/apply_file_update.py +21 -0
- gyomu_docstring-0.2.0/src/gyomu_docstring/update/apply_merge.py +407 -0
- gyomu_docstring-0.2.0/src/gyomu_docstring/update/build_file_update.py +286 -0
- gyomu_docstring-0.2.0/src/gyomu_docstring/update/docstring/__init__.py +0 -0
- gyomu_docstring-0.2.0/src/gyomu_docstring/update/docstring/file_update_plan.py +16 -0
- gyomu_docstring-0.2.0/src/gyomu_docstring/update/docstring/line.py +22 -0
- gyomu_docstring-0.2.0/src/gyomu_docstring/update/docstring/merge_plan.py +109 -0
- gyomu_docstring-0.2.0/src/gyomu_docstring/update/docstring/rendered_symbol.py +11 -0
- gyomu_docstring-0.2.0/src/gyomu_docstring/update/docstring/updated_docstring.py +10 -0
- gyomu_docstring-0.2.0/src/gyomu_docstring/update/internal/__init__.py +0 -0
- gyomu_docstring-0.2.0/src/gyomu_docstring/update/internal/render_line.py +159 -0
- gyomu_docstring-0.2.0/src/gyomu_docstring/update/internal/render_string.py +63 -0
- gyomu_docstring-0.2.0/src/gyomu_docstring/update/render_docstring.py +40 -0
- gyomu_docstring-0.2.0/tests/unit/__init__.py +0 -0
- gyomu_docstring-0.2.0/tests/unit/update/__init__.py +0 -0
- gyomu_docstring-0.2.0/tests/unit/update/internal/__init__.py +0 -0
- gyomu_docstring-0.2.0/tests/unit/update/internal/test_render_line.py +381 -0
- gyomu_docstring-0.2.0/tests/unit/update/internal/test_render_string.py +180 -0
- gyomu_docstring-0.2.0/tests/unit/update/test_apply_file_update.py +100 -0
- gyomu_docstring-0.2.0/tests/unit/update/test_apply_merge.py +982 -0
- gyomu_docstring-0.2.0/tests/unit/update/test_build_file_update.py +822 -0
- gyomu_docstring-0.2.0/tests/unit/update/test_render_docstring.py +146 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: gyomu-docstring
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Gyomu docstring
|
|
5
|
+
Requires-Python: >=3.13
|
|
6
|
+
Requires-Dist: gyomu-infra
|
|
7
|
+
Requires-Dist: gyomu-python-analysis
|
|
8
|
+
Requires-Dist: gyomu-schema
|
|
9
|
+
Requires-Dist: returns>=0.29.0
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# gyomu-docstring
|
|
13
|
+
|
|
14
|
+
docstring components for Gyomu Python.
|
|
File without changes
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from griffe import Attribute, Class, Function, TypeAlias
|
|
5
|
+
from gyomu_docstring.update.docstring.file_update_plan import FileUpdatePlanEntry
|
|
6
|
+
from gyomu_docstring.update.docstring.rendered_symbol import (
|
|
7
|
+
RenderedSymbolDocstring,
|
|
8
|
+
)
|
|
9
|
+
from gyomu_infra.logger import logger
|
|
10
|
+
from gyomu_python_analysis.analysis.analyzers.cls import analyze_class
|
|
11
|
+
from gyomu_python_analysis.analysis.analyzers.context import initialize_symbol_context
|
|
12
|
+
from gyomu_python_analysis.analysis.analyzers.functions import analyze_function
|
|
13
|
+
from gyomu_python_analysis.analysis.analyzers.type_alias import analyze_type_alias
|
|
14
|
+
from gyomu_python_analysis.analysis.analyzers.variables import analyze_variable
|
|
15
|
+
from gyomu_python_analysis.analysis.file.source_file_context import SourceFileContext
|
|
16
|
+
from gyomu_python_analysis.analysis.load import load_module
|
|
17
|
+
from gyomu_python_analysis.analysis.load_module import load_module_analysis
|
|
18
|
+
from gyomu_python_analysis.project.context import ProjectContext
|
|
19
|
+
from gyomu_schema.schemas.python.class_analysis import ClassAnalysis
|
|
20
|
+
from gyomu_schema.schemas.python.file_analysis import (
|
|
21
|
+
FileAnalysisContext,
|
|
22
|
+
FileAnalysisMetadata,
|
|
23
|
+
)
|
|
24
|
+
from gyomu_schema.schemas.python.function_analysis import FunctionAnalysis
|
|
25
|
+
from gyomu_schema.schemas.python.location import SourceLocation
|
|
26
|
+
from gyomu_schema.schemas.python.method_analysis import MethodAnalysis
|
|
27
|
+
from gyomu_schema.schemas.python.module import ModuleAnalysis
|
|
28
|
+
from gyomu_schema.schemas.python.symbol import MemberAnalysis, SymbolAnalysis
|
|
29
|
+
from gyomu_schema.schemas.python.symbol_base import DeclarationKind
|
|
30
|
+
from gyomu_schema.schemas.python.type_alias import TypeAliasAnalysis
|
|
31
|
+
from gyomu_schema.schemas.python.types import (
|
|
32
|
+
DeclarationId,
|
|
33
|
+
DeclarationIdentity,
|
|
34
|
+
ProjectRelativePath,
|
|
35
|
+
PythonPath,
|
|
36
|
+
SourceRelativePath,
|
|
37
|
+
SymbolId,
|
|
38
|
+
)
|
|
39
|
+
from gyomu_schema.schemas.python.variable import VariableAnalysis
|
|
40
|
+
from gyomu_schema.schemas.python.visibility import Visibility
|
|
41
|
+
from gyomu_schema.schemas.types import FullPath
|
|
42
|
+
from returns.result import Failure
|
|
43
|
+
|
|
44
|
+
FIXTURES_ROOT = FullPath(Path(__file__).parent / "fixtures")
|
|
45
|
+
|
|
46
|
+
print("LOADED DOCSTRING TESTS.HELPERS")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _create_context() -> ProjectContext:
|
|
50
|
+
return ProjectContext(
|
|
51
|
+
project_root=FIXTURES_ROOT,
|
|
52
|
+
source_root=ProjectRelativePath(Path("src")),
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def create_declaration_identity(id: str) -> DeclarationIdentity:
|
|
57
|
+
return DeclarationIdentity(
|
|
58
|
+
symbol_id=SymbolId(id),
|
|
59
|
+
declaration_id=DeclarationId("."),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def create_location(
|
|
64
|
+
start_offset: int = 0,
|
|
65
|
+
end_offset: int = 0,
|
|
66
|
+
start_line: int = 1,
|
|
67
|
+
start_column: int = 0,
|
|
68
|
+
end_line: int = 1,
|
|
69
|
+
end_column: int = 0,
|
|
70
|
+
) -> SourceLocation:
|
|
71
|
+
return SourceLocation(
|
|
72
|
+
start_line=start_line,
|
|
73
|
+
start_offset=start_offset,
|
|
74
|
+
end_line=end_line,
|
|
75
|
+
end_offset=end_offset,
|
|
76
|
+
start_column=start_column,
|
|
77
|
+
end_column=end_column,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
_default_identity = DeclarationIdentity(
|
|
82
|
+
symbol_id=SymbolId("test.User"),
|
|
83
|
+
declaration_id=DeclarationId("."),
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def create_entry(
|
|
88
|
+
start_offset: int = 0,
|
|
89
|
+
end_offset: int = 0,
|
|
90
|
+
identity: DeclarationIdentity = _default_identity,
|
|
91
|
+
new_text: str = "",
|
|
92
|
+
) -> FileUpdatePlanEntry:
|
|
93
|
+
|
|
94
|
+
return FileUpdatePlanEntry(
|
|
95
|
+
identity=identity,
|
|
96
|
+
location=create_location(start_offset=start_offset, end_offset=end_offset),
|
|
97
|
+
new_text=new_text,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def create_rendered_docstring(
|
|
102
|
+
docstring: str | None,
|
|
103
|
+
start_offset: int = 0,
|
|
104
|
+
end_offset: int = 0,
|
|
105
|
+
identity: DeclarationIdentity = _default_identity,
|
|
106
|
+
) -> RenderedSymbolDocstring:
|
|
107
|
+
return RenderedSymbolDocstring(
|
|
108
|
+
identity=identity,
|
|
109
|
+
docstring=docstring,
|
|
110
|
+
location=create_location(start_offset=start_offset, end_offset=end_offset),
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def create_type_alias_analysis(
|
|
115
|
+
indent: int,
|
|
116
|
+
location: SourceLocation,
|
|
117
|
+
name: str = "test_func",
|
|
118
|
+
identity: DeclarationIdentity | None = None,
|
|
119
|
+
) -> TypeAliasAnalysis:
|
|
120
|
+
if identity is None:
|
|
121
|
+
identity = create_declaration_identity(name)
|
|
122
|
+
return TypeAliasAnalysis(
|
|
123
|
+
name=name,
|
|
124
|
+
docstring=None,
|
|
125
|
+
identity=identity,
|
|
126
|
+
decorators=tuple(),
|
|
127
|
+
dependencies=tuple(),
|
|
128
|
+
indent=indent,
|
|
129
|
+
kind=DeclarationKind.TYPEALIAS,
|
|
130
|
+
visibility=Visibility.PUBLIC,
|
|
131
|
+
location=location,
|
|
132
|
+
alias_type=None,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def create_variable_analysis(
|
|
137
|
+
indent: int,
|
|
138
|
+
location: SourceLocation,
|
|
139
|
+
name: str = "test_func",
|
|
140
|
+
identity: DeclarationIdentity | None = None,
|
|
141
|
+
) -> VariableAnalysis:
|
|
142
|
+
if identity is None:
|
|
143
|
+
identity = create_declaration_identity(name)
|
|
144
|
+
return VariableAnalysis(
|
|
145
|
+
name=name,
|
|
146
|
+
docstring=None,
|
|
147
|
+
identity=identity,
|
|
148
|
+
decorators=tuple(),
|
|
149
|
+
dependencies=tuple(),
|
|
150
|
+
indent=indent,
|
|
151
|
+
kind=DeclarationKind.VARIABLE,
|
|
152
|
+
visibility=Visibility.PUBLIC,
|
|
153
|
+
location=location,
|
|
154
|
+
type=None,
|
|
155
|
+
value_source=None,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def create_function_analysis(
|
|
160
|
+
indent: int,
|
|
161
|
+
location: SourceLocation,
|
|
162
|
+
name: str = "test_func",
|
|
163
|
+
identity: DeclarationIdentity | None = None,
|
|
164
|
+
) -> FunctionAnalysis:
|
|
165
|
+
if identity is None:
|
|
166
|
+
identity = create_declaration_identity(name)
|
|
167
|
+
return FunctionAnalysis(
|
|
168
|
+
name=name,
|
|
169
|
+
docstring=None,
|
|
170
|
+
identity=identity,
|
|
171
|
+
decorators=tuple(),
|
|
172
|
+
dependencies=tuple(),
|
|
173
|
+
indent=indent,
|
|
174
|
+
is_async=False,
|
|
175
|
+
kind=DeclarationKind.FUNCTION,
|
|
176
|
+
parameters=tuple(),
|
|
177
|
+
return_type=None,
|
|
178
|
+
visibility=Visibility.PUBLIC,
|
|
179
|
+
location=location,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def create_method_analysis(
|
|
184
|
+
indent: int | None,
|
|
185
|
+
location: SourceLocation | None,
|
|
186
|
+
name: str = "test_func",
|
|
187
|
+
identity: DeclarationIdentity | None = None,
|
|
188
|
+
) -> MethodAnalysis:
|
|
189
|
+
if identity is None:
|
|
190
|
+
identity = create_declaration_identity(name)
|
|
191
|
+
return MethodAnalysis(
|
|
192
|
+
name=name,
|
|
193
|
+
docstring=None,
|
|
194
|
+
identity=identity,
|
|
195
|
+
decorators=tuple(),
|
|
196
|
+
indent=indent,
|
|
197
|
+
is_async=False,
|
|
198
|
+
kind=DeclarationKind.METHOD,
|
|
199
|
+
parameters=tuple(),
|
|
200
|
+
return_type=None,
|
|
201
|
+
visibility=Visibility.PUBLIC,
|
|
202
|
+
location=location,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def create_class_analysis(
|
|
207
|
+
indent: int,
|
|
208
|
+
location: SourceLocation,
|
|
209
|
+
name: str = "test_class",
|
|
210
|
+
identity: DeclarationIdentity | None = None,
|
|
211
|
+
) -> ClassAnalysis:
|
|
212
|
+
if identity is None:
|
|
213
|
+
identity = create_declaration_identity(name)
|
|
214
|
+
return ClassAnalysis(
|
|
215
|
+
name=name,
|
|
216
|
+
docstring=None,
|
|
217
|
+
identity=identity,
|
|
218
|
+
decorators=tuple(),
|
|
219
|
+
dependencies=tuple(),
|
|
220
|
+
indent=indent,
|
|
221
|
+
kind=DeclarationKind.CLASS,
|
|
222
|
+
bases=tuple(),
|
|
223
|
+
visibility=Visibility.PUBLIC,
|
|
224
|
+
location=location,
|
|
225
|
+
methods=tuple(),
|
|
226
|
+
variables=tuple(),
|
|
227
|
+
type_aliases=tuple(),
|
|
228
|
+
inner_classes=tuple(),
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def create_file_analysis_context(
|
|
233
|
+
symbol: SymbolAnalysis | MemberAnalysis | None = None,
|
|
234
|
+
symbol2: SymbolAnalysis | MemberAnalysis | None = None,
|
|
235
|
+
symbol3: SymbolAnalysis | MemberAnalysis | None = None,
|
|
236
|
+
) -> FileAnalysisContext:
|
|
237
|
+
symbols: dict[DeclarationIdentity, SymbolAnalysis | MemberAnalysis]
|
|
238
|
+
symbols = dict([(symbol.identity, symbol)]) if symbol else dict()
|
|
239
|
+
|
|
240
|
+
if symbol2:
|
|
241
|
+
symbols[symbol2.identity] = symbol2
|
|
242
|
+
if symbol3:
|
|
243
|
+
symbols[symbol3.identity] = symbol3
|
|
244
|
+
return FileAnalysisContext(
|
|
245
|
+
metadata=FileAnalysisMetadata(parsed_docstring=dict(), symbols=symbols),
|
|
246
|
+
analysis=ModuleAnalysis(
|
|
247
|
+
path=SourceRelativePath(Path(".")),
|
|
248
|
+
name="test",
|
|
249
|
+
module_name=PythonPath(""),
|
|
250
|
+
docstring=None,
|
|
251
|
+
imports=tuple(),
|
|
252
|
+
symbols=(),
|
|
253
|
+
),
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
@dataclass
|
|
258
|
+
class BaseContext:
|
|
259
|
+
project: ProjectContext
|
|
260
|
+
source: SourceFileContext
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
class AnalysisTestBase:
|
|
264
|
+
def _analyze_module_base(self, file_name: PythonPath) -> ModuleAnalysis:
|
|
265
|
+
context = self._read_module_fixture(file_name)
|
|
266
|
+
result = load_module_analysis(context.project, file_name)
|
|
267
|
+
if isinstance(result, Failure):
|
|
268
|
+
raise result.failure()
|
|
269
|
+
|
|
270
|
+
return result.unwrap()
|
|
271
|
+
|
|
272
|
+
def _analyze_typealias_bases(
|
|
273
|
+
self, file_name: PythonPath, symbol_name: str, dump_required: bool = False
|
|
274
|
+
) -> TypeAliasAnalysis:
|
|
275
|
+
symbol, source_lines = self._analyze_symbol(
|
|
276
|
+
file_name, symbol_name, dump_required
|
|
277
|
+
)
|
|
278
|
+
assert isinstance(symbol, TypeAlias)
|
|
279
|
+
return analyze_type_alias(
|
|
280
|
+
alias=symbol,
|
|
281
|
+
name=symbol_name,
|
|
282
|
+
context=initialize_symbol_context(
|
|
283
|
+
module_name=file_name, name=symbol_name, source_lines=source_lines
|
|
284
|
+
),
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
def _analyze_function_base(
|
|
288
|
+
self, file_name: PythonPath, symbol_name: str, dump_required: bool = False
|
|
289
|
+
) -> FunctionAnalysis:
|
|
290
|
+
symbol, source_lines = self._analyze_symbol(
|
|
291
|
+
file_name, symbol_name, dump_required
|
|
292
|
+
)
|
|
293
|
+
assert isinstance(symbol, Function)
|
|
294
|
+
return analyze_function(
|
|
295
|
+
func=symbol,
|
|
296
|
+
name=symbol_name,
|
|
297
|
+
context=initialize_symbol_context(
|
|
298
|
+
module_name=file_name, name=symbol_name, source_lines=source_lines
|
|
299
|
+
),
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
def _analyze_variable_base(
|
|
303
|
+
self, file_name: PythonPath, symbol_name: str, dump_required: bool = False
|
|
304
|
+
) -> VariableAnalysis:
|
|
305
|
+
symbol, source_lines = self._analyze_symbol(
|
|
306
|
+
file_name, symbol_name, dump_required
|
|
307
|
+
)
|
|
308
|
+
assert isinstance(symbol, Attribute)
|
|
309
|
+
return analyze_variable(
|
|
310
|
+
variable=symbol,
|
|
311
|
+
name=symbol_name,
|
|
312
|
+
context=initialize_symbol_context(
|
|
313
|
+
module_name=file_name, name=symbol_name, source_lines=source_lines
|
|
314
|
+
),
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
def _analyze_class_base(
|
|
318
|
+
self, file_name: PythonPath, symbol_name: str, dump_required: bool = False
|
|
319
|
+
) -> ClassAnalysis:
|
|
320
|
+
symbol, source_lines = self._analyze_symbol(
|
|
321
|
+
file_name, symbol_name, dump_required
|
|
322
|
+
)
|
|
323
|
+
assert isinstance(symbol, Class)
|
|
324
|
+
return analyze_class(
|
|
325
|
+
cls=symbol,
|
|
326
|
+
name=symbol_name,
|
|
327
|
+
context=initialize_symbol_context(
|
|
328
|
+
module_name=file_name, name=symbol_name, source_lines=source_lines
|
|
329
|
+
),
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
def _analyze_symbol(
|
|
333
|
+
self, file_name: PythonPath, symbol_name: str, dump_required: bool = False
|
|
334
|
+
) -> tuple[SymbolAnalysis, list[str]]:
|
|
335
|
+
module_name = file_name
|
|
336
|
+
context = self._read_module_fixture(module_name)
|
|
337
|
+
module = context.source.module
|
|
338
|
+
symbol = module[symbol_name]
|
|
339
|
+
|
|
340
|
+
if dump_required:
|
|
341
|
+
logger.info(symbol.as_dict())
|
|
342
|
+
|
|
343
|
+
source_full_path = (
|
|
344
|
+
context.project.project_root
|
|
345
|
+
/ context.project.source_root
|
|
346
|
+
/ context.source.path
|
|
347
|
+
)
|
|
348
|
+
source_lines = source_full_path.read_text(
|
|
349
|
+
encoding="utf-8",
|
|
350
|
+
).splitlines(keepends=True)
|
|
351
|
+
|
|
352
|
+
return symbol, source_lines
|
|
353
|
+
|
|
354
|
+
def _read_module_fixture(self, module_path: PythonPath) -> BaseContext:
|
|
355
|
+
context = _create_context()
|
|
356
|
+
# search_path = context.project_root / context.source_root
|
|
357
|
+
# print("search:", search_path)
|
|
358
|
+
# print("modulePath:", modulePath)
|
|
359
|
+
|
|
360
|
+
# loader = GriffeLoader(
|
|
361
|
+
# search_paths=[search_path],
|
|
362
|
+
# )
|
|
363
|
+
# module = loader.load(modulePath)
|
|
364
|
+
# if isinstance(module, Module):
|
|
365
|
+
# print("location:", module.filepath)
|
|
366
|
+
# return module
|
|
367
|
+
# raise ValueError("Invalid:")
|
|
368
|
+
result = load_module(context, module_path)
|
|
369
|
+
|
|
370
|
+
source_file_context = result.unwrap()
|
|
371
|
+
|
|
372
|
+
return BaseContext(project=context, source=source_file_context)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "gyomu-docstring"
|
|
3
|
+
dynamic = ["version"]
|
|
4
|
+
description = "Gyomu docstring"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"gyomu-schema",
|
|
9
|
+
"gyomu-infra",
|
|
10
|
+
"gyomu-python-analysis",
|
|
11
|
+
"returns>=0.29.0",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[build-system]
|
|
15
|
+
requires = ["hatchling"]
|
|
16
|
+
build-backend = "hatchling.build"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
[tool.uv.sources]
|
|
20
|
+
gyomu-schema = { workspace = true }
|
|
21
|
+
gyomu-infra = { workspace = true }
|
|
22
|
+
gyomu-python-analysis = { workspace = true }
|
|
23
|
+
|
|
24
|
+
[tool.hatch.version]
|
|
25
|
+
path = "../../version/__about__.py"
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from gyomu_docstring.update.docstring.file_update_plan import FileUpdatePlan
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def apply_file_update_plan(
|
|
5
|
+
source: str,
|
|
6
|
+
plan: FileUpdatePlan,
|
|
7
|
+
) -> str:
|
|
8
|
+
result = source
|
|
9
|
+
|
|
10
|
+
for item in sorted(
|
|
11
|
+
plan.items,
|
|
12
|
+
key=lambda item: item.location.start_offset,
|
|
13
|
+
reverse=True,
|
|
14
|
+
):
|
|
15
|
+
result = (
|
|
16
|
+
result[: item.location.start_offset]
|
|
17
|
+
+ item.new_text
|
|
18
|
+
+ result[item.location.end_offset :]
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
return result
|