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.
Files changed (30) hide show
  1. gyomu_docstring-0.2.0/.gitignore +19 -0
  2. gyomu_docstring-0.2.0/PKG-INFO +14 -0
  3. gyomu_docstring-0.2.0/README.md +3 -0
  4. gyomu_docstring-0.2.0/docstring_test_support/__init__.py +0 -0
  5. gyomu_docstring-0.2.0/docstring_test_support/helpers.py +372 -0
  6. gyomu_docstring-0.2.0/pyproject.toml +25 -0
  7. gyomu_docstring-0.2.0/src/gyomu_docstring/__init__.py +0 -0
  8. gyomu_docstring-0.2.0/src/gyomu_docstring/update/__init__.py +0 -0
  9. gyomu_docstring-0.2.0/src/gyomu_docstring/update/apply_file_update.py +21 -0
  10. gyomu_docstring-0.2.0/src/gyomu_docstring/update/apply_merge.py +407 -0
  11. gyomu_docstring-0.2.0/src/gyomu_docstring/update/build_file_update.py +286 -0
  12. gyomu_docstring-0.2.0/src/gyomu_docstring/update/docstring/__init__.py +0 -0
  13. gyomu_docstring-0.2.0/src/gyomu_docstring/update/docstring/file_update_plan.py +16 -0
  14. gyomu_docstring-0.2.0/src/gyomu_docstring/update/docstring/line.py +22 -0
  15. gyomu_docstring-0.2.0/src/gyomu_docstring/update/docstring/merge_plan.py +109 -0
  16. gyomu_docstring-0.2.0/src/gyomu_docstring/update/docstring/rendered_symbol.py +11 -0
  17. gyomu_docstring-0.2.0/src/gyomu_docstring/update/docstring/updated_docstring.py +10 -0
  18. gyomu_docstring-0.2.0/src/gyomu_docstring/update/internal/__init__.py +0 -0
  19. gyomu_docstring-0.2.0/src/gyomu_docstring/update/internal/render_line.py +159 -0
  20. gyomu_docstring-0.2.0/src/gyomu_docstring/update/internal/render_string.py +63 -0
  21. gyomu_docstring-0.2.0/src/gyomu_docstring/update/render_docstring.py +40 -0
  22. gyomu_docstring-0.2.0/tests/unit/__init__.py +0 -0
  23. gyomu_docstring-0.2.0/tests/unit/update/__init__.py +0 -0
  24. gyomu_docstring-0.2.0/tests/unit/update/internal/__init__.py +0 -0
  25. gyomu_docstring-0.2.0/tests/unit/update/internal/test_render_line.py +381 -0
  26. gyomu_docstring-0.2.0/tests/unit/update/internal/test_render_string.py +180 -0
  27. gyomu_docstring-0.2.0/tests/unit/update/test_apply_file_update.py +100 -0
  28. gyomu_docstring-0.2.0/tests/unit/update/test_apply_merge.py +982 -0
  29. gyomu_docstring-0.2.0/tests/unit/update/test_build_file_update.py +822 -0
  30. gyomu_docstring-0.2.0/tests/unit/update/test_render_docstring.py +146 -0
@@ -0,0 +1,19 @@
1
+ venv/
2
+ .idea/
3
+
4
+ /build/
5
+ /dist/
6
+ /src/gyomu.egg-info/
7
+ /.coverage
8
+ /htmlcov/
9
+ __pycache__/
10
+ *.py[cod]
11
+
12
+ .env
13
+ .env.*
14
+ !.env.example
15
+
16
+ .mypy_cache/
17
+ .ruff_cache/
18
+ htmlcov/
19
+ *.egg-info/
@@ -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.
@@ -0,0 +1,3 @@
1
+ # gyomu-docstring
2
+
3
+ docstring components for Gyomu Python.
@@ -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
@@ -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