gyomu-python-analysis 0.2.0__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.
- gyomu_python_analysis/__init__.py +0 -0
- gyomu_python_analysis/analysis/__init__.py +0 -0
- gyomu_python_analysis/analysis/analyzers/__init__.py +0 -0
- gyomu_python_analysis/analysis/analyzers/cls.py +343 -0
- gyomu_python_analysis/analysis/analyzers/context.py +66 -0
- gyomu_python_analysis/analysis/analyzers/decorator.py +88 -0
- gyomu_python_analysis/analysis/analyzers/dependency.py +134 -0
- gyomu_python_analysis/analysis/analyzers/docstring.py +288 -0
- gyomu_python_analysis/analysis/analyzers/expression/__init__.py +0 -0
- gyomu_python_analysis/analysis/analyzers/expression/expr.py +400 -0
- gyomu_python_analysis/analysis/analyzers/expression/name.py +38 -0
- gyomu_python_analysis/analysis/analyzers/functions.py +53 -0
- gyomu_python_analysis/analysis/analyzers/imports.py +21 -0
- gyomu_python_analysis/analysis/analyzers/internal/__init__.py +0 -0
- gyomu_python_analysis/analysis/analyzers/internal/common.py +87 -0
- gyomu_python_analysis/analysis/analyzers/internal/location.py +68 -0
- gyomu_python_analysis/analysis/analyzers/internal/visibility.py +27 -0
- gyomu_python_analysis/analysis/analyzers/pydantic.py +62 -0
- gyomu_python_analysis/analysis/analyzers/type_alias.py +24 -0
- gyomu_python_analysis/analysis/analyzers/types.py +29 -0
- gyomu_python_analysis/analysis/analyzers/variables.py +22 -0
- gyomu_python_analysis/analysis/extract/__init__.py +0 -0
- gyomu_python_analysis/analysis/extract/symbols.py +118 -0
- gyomu_python_analysis/analysis/file/__init__.py +0 -0
- gyomu_python_analysis/analysis/file/source_file_context.py +10 -0
- gyomu_python_analysis/analysis/get_module.py +55 -0
- gyomu_python_analysis/analysis/load.py +42 -0
- gyomu_python_analysis/analysis/load_file_context.py +27 -0
- gyomu_python_analysis/analysis/load_module.py +65 -0
- gyomu_python_analysis/analysis/metadata.py +49 -0
- gyomu_python_analysis/analysis/parser/__init__.py +0 -0
- gyomu_python_analysis/analysis/parser/docstring.py +7 -0
- gyomu_python_analysis/error/__init__.py +0 -0
- gyomu_python_analysis/error/analysis.py +36 -0
- gyomu_python_analysis/error/update.py +32 -0
- gyomu_python_analysis/path/__init__.py +0 -0
- gyomu_python_analysis/path/conversion.py +25 -0
- gyomu_python_analysis/project/__init__.py +0 -0
- gyomu_python_analysis/project/context.py +18 -0
- gyomu_python_analysis-0.2.0.dist-info/METADATA +15 -0
- gyomu_python_analysis-0.2.0.dist-info/RECORD +42 -0
- gyomu_python_analysis-0.2.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import re
|
|
3
|
+
from textwrap import dedent
|
|
4
|
+
|
|
5
|
+
from griffe import (
|
|
6
|
+
Docstring,
|
|
7
|
+
DocstringNamedElement,
|
|
8
|
+
DocstringSectionAdmonition,
|
|
9
|
+
DocstringSectionKind,
|
|
10
|
+
)
|
|
11
|
+
from griffe import DocstringSection as GriffeSection
|
|
12
|
+
from griffe import DocstringSectionExamples as GriffeSectionExamples
|
|
13
|
+
from griffe import DocstringSectionParameters as GriffeSectionParameters
|
|
14
|
+
from griffe import DocstringSectionRaises as GriffeSectionRaises
|
|
15
|
+
from griffe import DocstringSectionReturns as GriffeSectionReturns
|
|
16
|
+
from griffe import (
|
|
17
|
+
DocstringSectionText as GriffeSectionText,
|
|
18
|
+
)
|
|
19
|
+
from gyomu_infra.logger import logger
|
|
20
|
+
from gyomu_schema.schemas.python.docstring import (
|
|
21
|
+
DocstringAnalysis,
|
|
22
|
+
DocstringCommon,
|
|
23
|
+
DocstringCustomSection,
|
|
24
|
+
DocstringExamplesSection,
|
|
25
|
+
DocstringExamplesSectionItem,
|
|
26
|
+
DocstringGyomuContextSection,
|
|
27
|
+
DocstringNotesSection,
|
|
28
|
+
DocstringParametersSection,
|
|
29
|
+
DocstringParametersSectionItem,
|
|
30
|
+
DocstringRaisesSection,
|
|
31
|
+
DocstringRaisesSectionItem,
|
|
32
|
+
DocstringReturnsSection,
|
|
33
|
+
DocstringReturnsSectionItem,
|
|
34
|
+
DocstringSection,
|
|
35
|
+
DocstringStyle,
|
|
36
|
+
DocstringTextSection,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
from gyomu_python_analysis.analysis.analyzers.context import SymbolContext
|
|
40
|
+
from gyomu_python_analysis.analysis.analyzers.types import analyze_type
|
|
41
|
+
|
|
42
|
+
_CUSTOM_SECTION_PATTERN = re.compile(r"^(?P<name>[A-Za-z][A-Za-z0-9 _-]*):\s*$")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _analyze_parameters(
|
|
46
|
+
section: GriffeSectionParameters, context: SymbolContext
|
|
47
|
+
) -> DocstringParametersSection:
|
|
48
|
+
parameters: list[DocstringParametersSectionItem] = []
|
|
49
|
+
for parameter in section.value:
|
|
50
|
+
param_type = analyze_type(parameter.annotation, context)
|
|
51
|
+
parameters.append(
|
|
52
|
+
DocstringParametersSectionItem(
|
|
53
|
+
name=parameter.name,
|
|
54
|
+
description=parameter.description,
|
|
55
|
+
type=param_type if param_type is None else param_type.text,
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
return DocstringParametersSection(items=tuple(parameters))
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _analyze_raises(
|
|
62
|
+
section: GriffeSectionRaises, context: SymbolContext
|
|
63
|
+
) -> DocstringRaisesSection:
|
|
64
|
+
raises: list[DocstringRaisesSectionItem] = []
|
|
65
|
+
for raiseItem in section.value:
|
|
66
|
+
raise_type = analyze_type(raiseItem.annotation, context)
|
|
67
|
+
raises.append(
|
|
68
|
+
DocstringRaisesSectionItem(
|
|
69
|
+
description=raiseItem.description,
|
|
70
|
+
type="" if raise_type is None else raise_type.text,
|
|
71
|
+
)
|
|
72
|
+
)
|
|
73
|
+
return DocstringRaisesSection(items=tuple(raises))
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _analyze_examples(section: GriffeSectionExamples) -> DocstringExamplesSection:
|
|
77
|
+
examples: list[DocstringExamplesSectionItem] = []
|
|
78
|
+
for exampleItem in section.value:
|
|
79
|
+
for item in exampleItem:
|
|
80
|
+
item_text: str
|
|
81
|
+
if (
|
|
82
|
+
isinstance(item, str)
|
|
83
|
+
and item is not DocstringSectionKind.text
|
|
84
|
+
and item is not DocstringSectionKind.examples
|
|
85
|
+
):
|
|
86
|
+
item_text = item
|
|
87
|
+
examples.append(DocstringExamplesSectionItem(value=item_text))
|
|
88
|
+
return DocstringExamplesSection(items=tuple(examples))
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _analyze_returns(
|
|
92
|
+
section: GriffeSectionReturns, context: SymbolContext
|
|
93
|
+
) -> DocstringReturnsSection:
|
|
94
|
+
# Gyomu models a Returns section as a single item.
|
|
95
|
+
# Google-style docstrings are expected to contain at most one return item.
|
|
96
|
+
if len(section.value) > 1:
|
|
97
|
+
logger.warning(
|
|
98
|
+
"Multiple return items are not supported; using the first item: %r",
|
|
99
|
+
section.value,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
return_item = section.value[0]
|
|
103
|
+
print(return_item.as_dict())
|
|
104
|
+
return_type = analyze_type(return_item.annotation, context)
|
|
105
|
+
|
|
106
|
+
return DocstringReturnsSection(
|
|
107
|
+
item=DocstringReturnsSectionItem(
|
|
108
|
+
description=_extract_return_description(return_item.description),
|
|
109
|
+
type=return_type.text if return_type is not None else None,
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _extract_return_description(description: str) -> str:
|
|
115
|
+
description = description.strip()
|
|
116
|
+
|
|
117
|
+
if ":" not in description:
|
|
118
|
+
return description
|
|
119
|
+
|
|
120
|
+
type_text, return_description = description.split(":", 1)
|
|
121
|
+
type_text = type_text.strip()
|
|
122
|
+
return_description = return_description.strip()
|
|
123
|
+
|
|
124
|
+
if not _looks_like_type(type_text):
|
|
125
|
+
return description
|
|
126
|
+
|
|
127
|
+
return return_description
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _looks_like_type(text: str) -> bool:
|
|
131
|
+
try:
|
|
132
|
+
ast.parse(text, mode="eval")
|
|
133
|
+
return True
|
|
134
|
+
except SyntaxError:
|
|
135
|
+
return False
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _analyze_admonition(section: DocstringSectionAdmonition) -> DocstringSection:
|
|
139
|
+
return _parse_custom_section(title=section.title, value=section.value.description)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _parse_custom_section(title: str | None, value: str) -> DocstringSection:
|
|
143
|
+
if title == "Notes":
|
|
144
|
+
return DocstringNotesSection(value=value)
|
|
145
|
+
elif title == "Gyomu Context":
|
|
146
|
+
return DocstringGyomuContextSection(value=value)
|
|
147
|
+
else:
|
|
148
|
+
return DocstringCustomSection(
|
|
149
|
+
title=title if title is not None else "",
|
|
150
|
+
value=value,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _is_indented(line: str) -> bool:
|
|
155
|
+
return bool(line) and line[0].isspace()
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def parse_text_section(
|
|
159
|
+
text: str,
|
|
160
|
+
) -> tuple[str, str, list[DocstringSection]]:
|
|
161
|
+
lines = text.splitlines()
|
|
162
|
+
|
|
163
|
+
while lines and not lines[0].strip():
|
|
164
|
+
lines.pop(0)
|
|
165
|
+
|
|
166
|
+
while lines and not lines[-1].strip():
|
|
167
|
+
lines.pop()
|
|
168
|
+
|
|
169
|
+
if not lines:
|
|
170
|
+
return "", "", []
|
|
171
|
+
|
|
172
|
+
# First paragraph = summary.
|
|
173
|
+
summary_lines: list[str] = []
|
|
174
|
+
|
|
175
|
+
index = 0
|
|
176
|
+
while index < len(lines) and lines[index].strip():
|
|
177
|
+
summary_lines.append(lines[index])
|
|
178
|
+
index += 1
|
|
179
|
+
|
|
180
|
+
summary = "\n".join(summary_lines).strip()
|
|
181
|
+
|
|
182
|
+
# Skip blank lines between summary and the rest.
|
|
183
|
+
while index < len(lines) and not lines[index].strip():
|
|
184
|
+
index += 1
|
|
185
|
+
|
|
186
|
+
description_lines: list[str] = []
|
|
187
|
+
custom_sections: list[DocstringSection] = []
|
|
188
|
+
|
|
189
|
+
while index < len(lines):
|
|
190
|
+
match = _CUSTOM_SECTION_PATTERN.match(lines[index])
|
|
191
|
+
|
|
192
|
+
if match and index + 1 < len(lines) and _is_indented(lines[index + 1]):
|
|
193
|
+
name = match.group("name")
|
|
194
|
+
index += 1
|
|
195
|
+
|
|
196
|
+
section_lines: list[str] = []
|
|
197
|
+
|
|
198
|
+
while index < len(lines):
|
|
199
|
+
if (
|
|
200
|
+
_CUSTOM_SECTION_PATTERN.match(lines[index])
|
|
201
|
+
and index + 1 < len(lines)
|
|
202
|
+
and _is_indented(lines[index + 1])
|
|
203
|
+
):
|
|
204
|
+
break
|
|
205
|
+
|
|
206
|
+
section_lines.append(lines[index])
|
|
207
|
+
index += 1
|
|
208
|
+
|
|
209
|
+
section_text = "\n".join(section_lines)
|
|
210
|
+
section_text = dedent(section_text).strip()
|
|
211
|
+
|
|
212
|
+
custom_sections.append(
|
|
213
|
+
_parse_custom_section(
|
|
214
|
+
title=name,
|
|
215
|
+
value=section_text,
|
|
216
|
+
)
|
|
217
|
+
)
|
|
218
|
+
continue
|
|
219
|
+
|
|
220
|
+
description_lines.append(lines[index])
|
|
221
|
+
index += 1
|
|
222
|
+
|
|
223
|
+
description = "\n".join(description_lines).strip()
|
|
224
|
+
|
|
225
|
+
return summary, description, (custom_sections)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def analyze_docstring(
|
|
229
|
+
doc: Docstring | None,
|
|
230
|
+
doc_common: DocstringCommon,
|
|
231
|
+
context: SymbolContext,
|
|
232
|
+
) -> DocstringAnalysis | None:
|
|
233
|
+
if doc is None:
|
|
234
|
+
return None
|
|
235
|
+
|
|
236
|
+
# print(doc.source)
|
|
237
|
+
sections = doc.parse(parser="auto")
|
|
238
|
+
|
|
239
|
+
text_section: DocstringTextSection | None = None
|
|
240
|
+
parsed_sections: list[DocstringSection] = []
|
|
241
|
+
for section in sections:
|
|
242
|
+
if isinstance(section, GriffeSectionText):
|
|
243
|
+
text_section = DocstringTextSection(value=section.value)
|
|
244
|
+
elif isinstance(section, GriffeSectionParameters):
|
|
245
|
+
parsed_sections.append(_analyze_parameters(section, context))
|
|
246
|
+
elif isinstance(section, GriffeSectionRaises):
|
|
247
|
+
parsed_sections.append(_analyze_raises(section, context))
|
|
248
|
+
elif isinstance(section, GriffeSectionExamples):
|
|
249
|
+
parsed_sections.append(_analyze_examples(section))
|
|
250
|
+
elif isinstance(section, GriffeSectionReturns):
|
|
251
|
+
parsed_sections.append(_analyze_returns(section, context))
|
|
252
|
+
elif isinstance(section, DocstringSectionAdmonition):
|
|
253
|
+
parsed_sections.append(_analyze_admonition(section))
|
|
254
|
+
else:
|
|
255
|
+
print("Unknown Section in Docstring")
|
|
256
|
+
value = section.value
|
|
257
|
+
if isinstance(value, str):
|
|
258
|
+
print(section.as_dict())
|
|
259
|
+
elif isinstance(value, list):
|
|
260
|
+
for item in value:
|
|
261
|
+
print(f"Kind: {section.kind}, item is list")
|
|
262
|
+
if isinstance(
|
|
263
|
+
item,
|
|
264
|
+
(GriffeSection, DocstringNamedElement, GriffeSectionRaises),
|
|
265
|
+
):
|
|
266
|
+
print(item.as_dict())
|
|
267
|
+
else:
|
|
268
|
+
print(item)
|
|
269
|
+
else:
|
|
270
|
+
print(section.as_dict())
|
|
271
|
+
if text_section:
|
|
272
|
+
summary, description, sections2 = parse_text_section(text_section.value)
|
|
273
|
+
return DocstringAnalysis(
|
|
274
|
+
**doc_common,
|
|
275
|
+
raw=doc.source,
|
|
276
|
+
summary=None if summary == "" else summary,
|
|
277
|
+
description=None if description == "" else description,
|
|
278
|
+
sections=tuple(parsed_sections if len(parsed_sections) > 0 else sections2),
|
|
279
|
+
style=DocstringStyle.GOOGLE,
|
|
280
|
+
)
|
|
281
|
+
return DocstringAnalysis(
|
|
282
|
+
**doc_common,
|
|
283
|
+
raw=doc.source,
|
|
284
|
+
summary=None,
|
|
285
|
+
description=None,
|
|
286
|
+
sections=tuple(parsed_sections),
|
|
287
|
+
style=DocstringStyle.GOOGLE,
|
|
288
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
|
|
3
|
+
from griffe import (
|
|
4
|
+
Expr,
|
|
5
|
+
ExprAttribute,
|
|
6
|
+
ExprBinOp,
|
|
7
|
+
ExprCall,
|
|
8
|
+
ExprConstant,
|
|
9
|
+
ExprDict,
|
|
10
|
+
ExprKeyword,
|
|
11
|
+
ExprList,
|
|
12
|
+
ExprName,
|
|
13
|
+
ExprSet,
|
|
14
|
+
ExprSubscript,
|
|
15
|
+
ExprTuple,
|
|
16
|
+
)
|
|
17
|
+
from gyomu_schema.schemas.python.type.structure import (
|
|
18
|
+
EllipsisStructureAnalysis,
|
|
19
|
+
LiteralValue,
|
|
20
|
+
NameStructureAnalysis,
|
|
21
|
+
NoneStructureAnalysis,
|
|
22
|
+
TypeStructureKind,
|
|
23
|
+
UnknownStructureAnalysis,
|
|
24
|
+
)
|
|
25
|
+
from gyomu_schema.schemas.python.type.type_analysis import (
|
|
26
|
+
ArrayStructureAnalysis,
|
|
27
|
+
AttributeStructureAnalysis,
|
|
28
|
+
CallableStructureAnalysis,
|
|
29
|
+
CallStructureAnalysis,
|
|
30
|
+
DictionaryStructureAnalysis,
|
|
31
|
+
ExpressionAnalysis,
|
|
32
|
+
GenericsStructureAnalysis,
|
|
33
|
+
KeywordStructureAnalysis,
|
|
34
|
+
LiteralStructureAnalysis,
|
|
35
|
+
SetStructureAnalysis,
|
|
36
|
+
TupleStructureAnalysis,
|
|
37
|
+
TypeExpression,
|
|
38
|
+
UnionStructureAnalysis,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
from gyomu_python_analysis.analysis.analyzers.context import SymbolContext
|
|
42
|
+
from gyomu_python_analysis.analysis.analyzers.dependency import register_dependency
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def analyze_expression(
|
|
46
|
+
expression: Expr, context: SymbolContext, need_registration_dependency: bool = True
|
|
47
|
+
) -> ExpressionAnalysis:
|
|
48
|
+
if isinstance(expression, ExprName):
|
|
49
|
+
return analyze_expression_name(
|
|
50
|
+
expression, context, need_registration_dependency
|
|
51
|
+
)
|
|
52
|
+
if isinstance(expression, ExprBinOp):
|
|
53
|
+
return _analyze_expression_binary_operation(expression, context)
|
|
54
|
+
if isinstance(expression, ExprSubscript):
|
|
55
|
+
return analyze_subscript(expression, context)
|
|
56
|
+
if isinstance(expression, ExprAttribute):
|
|
57
|
+
return _analyze_expression_attribute(expression, context)
|
|
58
|
+
if isinstance(expression, ExprTuple):
|
|
59
|
+
return analyze_tuple(expression, context)
|
|
60
|
+
if isinstance(expression, ExprList):
|
|
61
|
+
return analyze_array(expression, context)
|
|
62
|
+
if isinstance(expression, ExprDict):
|
|
63
|
+
return analyze_dictionary(expression, context)
|
|
64
|
+
if isinstance(expression, ExprSet):
|
|
65
|
+
return analyze_set(expression, context)
|
|
66
|
+
if isinstance(expression, ExprKeyword):
|
|
67
|
+
return _analyze_keyword(expression, context)
|
|
68
|
+
if isinstance(expression, ExprCall):
|
|
69
|
+
return _analyze_call(expression, context)
|
|
70
|
+
|
|
71
|
+
else:
|
|
72
|
+
print(f"Unsupported expression type: {type(expression)}")
|
|
73
|
+
print(expression.as_dict())
|
|
74
|
+
return UnknownStructureAnalysis()
|
|
75
|
+
# elif isinstance(expression, ExprSubscript):
|
|
76
|
+
# return analyze_expression_subscript(expression)
|
|
77
|
+
# else:
|
|
78
|
+
# raise ValueError(f"Unsupported expression type: {type(expression)}")
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def analyze_expression_constant(
|
|
82
|
+
expression: ExprConstant, context: SymbolContext
|
|
83
|
+
) -> TypeExpression:
|
|
84
|
+
return analyze_type_expression(expression.value, context)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _analyze_expression_attribute(
|
|
88
|
+
expression: ExprAttribute, context: SymbolContext
|
|
89
|
+
) -> AttributeStructureAnalysis:
|
|
90
|
+
# print(
|
|
91
|
+
# dict(
|
|
92
|
+
# canonical_path=expression.canonical_path,
|
|
93
|
+
# values=expression.values,
|
|
94
|
+
# path=expression.path,
|
|
95
|
+
# canonical_name=expression.canonical_name,
|
|
96
|
+
# is_classvar=expression.is_classvar,
|
|
97
|
+
# is_generator=expression.is_generator,
|
|
98
|
+
# is_iterator=expression.is_iterator,
|
|
99
|
+
# is_tuple=expression.is_tuple,
|
|
100
|
+
# )
|
|
101
|
+
# )
|
|
102
|
+
return AttributeStructureAnalysis(
|
|
103
|
+
values=tuple(
|
|
104
|
+
analyzed
|
|
105
|
+
for index, value in enumerate(expression.values)
|
|
106
|
+
if (
|
|
107
|
+
analyzed := analyze_type_expression(
|
|
108
|
+
value,
|
|
109
|
+
context,
|
|
110
|
+
need_registration_dependency=index == 0,
|
|
111
|
+
)
|
|
112
|
+
)
|
|
113
|
+
is not None
|
|
114
|
+
)
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _analyze_expression_binary_operation(
|
|
119
|
+
expression: ExprBinOp, context: SymbolContext
|
|
120
|
+
) -> UnionStructureAnalysis | UnknownStructureAnalysis:
|
|
121
|
+
# print(
|
|
122
|
+
# dict(
|
|
123
|
+
# name=expression.name,
|
|
124
|
+
# member=expression.member,
|
|
125
|
+
# path=expression.path,
|
|
126
|
+
# canonical_name=expression.canonical_name,
|
|
127
|
+
# is_enum_class=expression.is_enum_class,
|
|
128
|
+
# is_enum_instance=expression.is_enum_instance,
|
|
129
|
+
# is_enum_value=expression.is_enum_value,
|
|
130
|
+
# is_type_parameter=expression.is_type_parameter,
|
|
131
|
+
# )
|
|
132
|
+
# )
|
|
133
|
+
match expression.operator:
|
|
134
|
+
case "|":
|
|
135
|
+
return _analyze_union(expression, context)
|
|
136
|
+
case _:
|
|
137
|
+
print(f"Unsupported operation: {type(expression)}")
|
|
138
|
+
print(expression.as_dict())
|
|
139
|
+
return UnknownStructureAnalysis()
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _analyze_union(
|
|
143
|
+
expression: ExprBinOp, context: SymbolContext
|
|
144
|
+
) -> UnionStructureAnalysis:
|
|
145
|
+
types: list[TypeExpression] = []
|
|
146
|
+
|
|
147
|
+
def append_union_types(value: str | Expr) -> None:
|
|
148
|
+
if isinstance(value, ExprBinOp) and value.operator == "|":
|
|
149
|
+
append_union_types(value.left)
|
|
150
|
+
append_union_types(value.right)
|
|
151
|
+
return
|
|
152
|
+
|
|
153
|
+
analyzed = analyze_type_expression(value, context)
|
|
154
|
+
types.append(analyzed)
|
|
155
|
+
|
|
156
|
+
append_union_types(expression)
|
|
157
|
+
|
|
158
|
+
return UnionStructureAnalysis(
|
|
159
|
+
types=tuple(types),
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
# def _analyze_type_internal(annotation: str | Expr) -> TypeAnalysis:
|
|
164
|
+
# if isinstance(annotation, str):
|
|
165
|
+
# print(annotation)
|
|
166
|
+
# if annotation == "None":
|
|
167
|
+
# return TypeAnalysis(text=annotation, structure=NoneStructureAnalysis())
|
|
168
|
+
# return TypeAnalysis(text=annotation)
|
|
169
|
+
# if isinstance(annotation, Expr):
|
|
170
|
+
# text = str(annotation)
|
|
171
|
+
# print(annotation.as_dict())
|
|
172
|
+
# return TypeAnalysis(text=text, structure=analyze_expression(annotation))
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def analyze_subscript(
|
|
176
|
+
expression: ExprSubscript, context: SymbolContext
|
|
177
|
+
) -> ExpressionAnalysis:
|
|
178
|
+
# print(expression.as_dict())
|
|
179
|
+
# print(f"canonical_name : {expression.canonical_name}")
|
|
180
|
+
# print(f"canonical_path : {expression.canonical_path}")
|
|
181
|
+
# print(f"classname : {expression.classname}")
|
|
182
|
+
# print(f"is_classvar: {expression.is_classvar}")
|
|
183
|
+
# print(f"is_generator: {expression.is_generator}")
|
|
184
|
+
# print(f"is_iterator: {expression.is_iterator}")
|
|
185
|
+
# print(f"is_tuple : {expression.is_tuple}")
|
|
186
|
+
|
|
187
|
+
left = expression.left
|
|
188
|
+
slice = expression.slice
|
|
189
|
+
if isinstance(left, ExprName):
|
|
190
|
+
if left.name == "Literal":
|
|
191
|
+
return analyze_literal(slice, context)
|
|
192
|
+
elif left.name == "list":
|
|
193
|
+
return _analyze_array_from_subscript(slice, context)
|
|
194
|
+
elif left.name == "dict":
|
|
195
|
+
return _analyze_dictionary_from_subscript(slice, context)
|
|
196
|
+
elif left.name == "Callable":
|
|
197
|
+
return _analyze_callable_from_subscript(slice, context)
|
|
198
|
+
elif left.name == "tuple":
|
|
199
|
+
return _analyze_tuple_from_subscript(slice, context)
|
|
200
|
+
elif left.name == "set":
|
|
201
|
+
return _analyze_set_from_subscript(slice, context)
|
|
202
|
+
|
|
203
|
+
param = analyze_type_expression(slice, context)
|
|
204
|
+
if not isinstance(param, LiteralValue):
|
|
205
|
+
parameters: list[TypeExpression] = []
|
|
206
|
+
if isinstance(param, TupleStructureAnalysis):
|
|
207
|
+
parameters = list(param.elements)
|
|
208
|
+
elif isinstance(param, NameStructureAnalysis):
|
|
209
|
+
parameters.append(param)
|
|
210
|
+
else:
|
|
211
|
+
parameters.append(param)
|
|
212
|
+
return GenericsStructureAnalysis(
|
|
213
|
+
base=analyze_type_expression(left, context), parameters=tuple(parameters)
|
|
214
|
+
)
|
|
215
|
+
print(f"Unsupported expression type in subscript: {type(expression)}")
|
|
216
|
+
print(expression.as_dict())
|
|
217
|
+
return UnknownStructureAnalysis()
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def analyze_dictionary(
|
|
221
|
+
expression: ExprDict, context: SymbolContext
|
|
222
|
+
) -> DictionaryStructureAnalysis:
|
|
223
|
+
assert len(expression.keys) == 1
|
|
224
|
+
assert expression.keys[0]
|
|
225
|
+
assert len(expression.values) == 1
|
|
226
|
+
return DictionaryStructureAnalysis(
|
|
227
|
+
keys=analyze_type_expression(expression.keys[0], context),
|
|
228
|
+
values=analyze_type_expression(expression.values[0], context),
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def _analyze_dictionary_from_subscript(
|
|
233
|
+
slice: str | Expr, context: SymbolContext
|
|
234
|
+
) -> DictionaryStructureAnalysis:
|
|
235
|
+
assert isinstance(slice, ExprTuple)
|
|
236
|
+
assert len(slice.elements) == 2
|
|
237
|
+
return DictionaryStructureAnalysis(
|
|
238
|
+
keys=analyze_type_expression(slice.elements[0], context),
|
|
239
|
+
values=analyze_type_expression(slice.elements[1], context),
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def analyze_array(
|
|
244
|
+
expression: ExprList, context: SymbolContext
|
|
245
|
+
) -> ArrayStructureAnalysis:
|
|
246
|
+
return ArrayStructureAnalysis(
|
|
247
|
+
element=analyze_type_expression(expression.elements[0], context)
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def _analyze_callable_from_subscript(
|
|
252
|
+
slice: str | Expr, context: SymbolContext
|
|
253
|
+
) -> CallableStructureAnalysis:
|
|
254
|
+
assert isinstance(slice, ExprTuple)
|
|
255
|
+
assert len(slice.elements) == 2
|
|
256
|
+
parameters_expression = slice.elements[0]
|
|
257
|
+
if isinstance(parameters_expression, ExprList):
|
|
258
|
+
parameters: list[TypeExpression] = []
|
|
259
|
+
for expression in parameters_expression.elements:
|
|
260
|
+
analyzed = analyze_type_expression(expression, context)
|
|
261
|
+
parameters.append(analyzed)
|
|
262
|
+
|
|
263
|
+
return CallableStructureAnalysis(
|
|
264
|
+
parameters=tuple(parameters),
|
|
265
|
+
return_type=analyze_type_expression(slice.elements[1], context),
|
|
266
|
+
)
|
|
267
|
+
assert isinstance(parameters_expression, str)
|
|
268
|
+
assert parameters_expression == "..."
|
|
269
|
+
return CallableStructureAnalysis(
|
|
270
|
+
parameters=None,
|
|
271
|
+
return_type=analyze_type_expression(slice.elements[1], context),
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _analyze_array_from_subscript(
|
|
276
|
+
slice: str | Expr, context: SymbolContext
|
|
277
|
+
) -> ArrayStructureAnalysis:
|
|
278
|
+
return ArrayStructureAnalysis(element=analyze_type_expression(slice, context))
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def analyze_literal(
|
|
282
|
+
slice: str | Expr, context: SymbolContext
|
|
283
|
+
) -> LiteralStructureAnalysis:
|
|
284
|
+
|
|
285
|
+
return LiteralStructureAnalysis(value=analyze_type_expression(slice, context))
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def analyze_type_expression(
|
|
289
|
+
value: str | Expr, context: SymbolContext, need_registration_dependency: bool = True
|
|
290
|
+
) -> TypeExpression:
|
|
291
|
+
if isinstance(value, str):
|
|
292
|
+
parsed = ast.literal_eval(value)
|
|
293
|
+
if parsed is None:
|
|
294
|
+
return NoneStructureAnalysis()
|
|
295
|
+
if parsed is Ellipsis:
|
|
296
|
+
return EllipsisStructureAnalysis()
|
|
297
|
+
return LiteralValue(value=parse_literal_value(value))
|
|
298
|
+
return analyze_expression(value, context, need_registration_dependency)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def parse_literal_value(value: str) -> str | int | bool:
|
|
302
|
+
parsed = ast.literal_eval(value)
|
|
303
|
+
if isinstance(parsed, bool):
|
|
304
|
+
return parsed
|
|
305
|
+
|
|
306
|
+
if isinstance(parsed, int):
|
|
307
|
+
return parsed
|
|
308
|
+
|
|
309
|
+
if isinstance(parsed, str):
|
|
310
|
+
return parsed
|
|
311
|
+
|
|
312
|
+
raise ValueError(f"Unsupported literal value: {value}")
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def _analyze_tuple_from_subscript(
|
|
316
|
+
slice: str | Expr, context: SymbolContext
|
|
317
|
+
) -> TupleStructureAnalysis:
|
|
318
|
+
assert isinstance(slice, ExprTuple)
|
|
319
|
+
|
|
320
|
+
return analyze_tuple(slice, context)
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def analyze_tuple(
|
|
324
|
+
expression: ExprTuple, context: SymbolContext
|
|
325
|
+
) -> TupleStructureAnalysis:
|
|
326
|
+
variable_length = False
|
|
327
|
+
elements: list[TypeExpression] = []
|
|
328
|
+
|
|
329
|
+
for value in expression.elements:
|
|
330
|
+
if value == "...":
|
|
331
|
+
variable_length = True
|
|
332
|
+
continue
|
|
333
|
+
|
|
334
|
+
analyzed = analyze_type_expression(value, context)
|
|
335
|
+
if analyzed is not None:
|
|
336
|
+
elements.append(analyzed)
|
|
337
|
+
|
|
338
|
+
return TupleStructureAnalysis(
|
|
339
|
+
elements=tuple(elements),
|
|
340
|
+
variable_length=variable_length,
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def _analyze_set_from_subscript(
|
|
345
|
+
slice: str | Expr, context: SymbolContext
|
|
346
|
+
) -> SetStructureAnalysis:
|
|
347
|
+
return SetStructureAnalysis(element_type=analyze_type_expression(slice, context))
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def analyze_set(expression: ExprSet, context: SymbolContext) -> SetStructureAnalysis:
|
|
351
|
+
return SetStructureAnalysis(
|
|
352
|
+
element_type=analyze_type_expression(expression.elements[0], context)
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def analyze_expression_name(
|
|
357
|
+
expression: ExprName, context: SymbolContext, need_registration_dependency: bool
|
|
358
|
+
) -> NameStructureAnalysis | NoneStructureAnalysis:
|
|
359
|
+
# print(
|
|
360
|
+
# dict(
|
|
361
|
+
# name=expression.name,
|
|
362
|
+
# member=expression.member,
|
|
363
|
+
# path=expression.path,
|
|
364
|
+
# canonical_name=expression.canonical_name,
|
|
365
|
+
# is_enum_class=expression.is_enum_class,
|
|
366
|
+
# is_enum_instance=expression.is_enum_instance,
|
|
367
|
+
# is_enum_value=expression.is_enum_value,
|
|
368
|
+
# is_type_parameter=expression.is_type_parameter,
|
|
369
|
+
# )
|
|
370
|
+
# )
|
|
371
|
+
if expression.name == "None":
|
|
372
|
+
return NoneStructureAnalysis(
|
|
373
|
+
kind=TypeStructureKind.NONE,
|
|
374
|
+
)
|
|
375
|
+
if need_registration_dependency:
|
|
376
|
+
register_dependency(context.declaration, expression.name, context)
|
|
377
|
+
return NameStructureAnalysis(
|
|
378
|
+
name=expression.name,
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def _analyze_keyword(
|
|
383
|
+
expression: ExprKeyword, context: SymbolContext
|
|
384
|
+
) -> KeywordStructureAnalysis:
|
|
385
|
+
name = expression.name
|
|
386
|
+
value = analyze_type_expression(expression.value, context)
|
|
387
|
+
return KeywordStructureAnalysis(name=name, value=value)
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def _analyze_call(
|
|
391
|
+
expression: ExprCall, context: SymbolContext
|
|
392
|
+
) -> CallStructureAnalysis:
|
|
393
|
+
func = analyze_expression(expression.function, context)
|
|
394
|
+
arguments: list[TypeExpression] = []
|
|
395
|
+
|
|
396
|
+
for value in expression.arguments:
|
|
397
|
+
analyzed = analyze_type_expression(value, context)
|
|
398
|
+
arguments.append(analyzed)
|
|
399
|
+
|
|
400
|
+
return CallStructureAnalysis(function=func, arguments=tuple(arguments))
|