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
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
from griffe import Attribute, Class, Function, TypeAlias
|
|
2
|
+
from gyomu_infra.logger import logger
|
|
3
|
+
from gyomu_schema.schemas.python.class_analysis import (
|
|
4
|
+
ClassAnalysis,
|
|
5
|
+
ClassCommon,
|
|
6
|
+
ClassTypeAliasAnalysis,
|
|
7
|
+
ClassVariableAnalysis,
|
|
8
|
+
InnerClassAnalysis,
|
|
9
|
+
)
|
|
10
|
+
from gyomu_schema.schemas.python.location import SourceLocation
|
|
11
|
+
from gyomu_schema.schemas.python.method_analysis import MethodAnalysis
|
|
12
|
+
from gyomu_schema.schemas.python.parameter import ParameterAnalysis
|
|
13
|
+
from gyomu_schema.schemas.python.pydantic import PydanticFieldAnalysis
|
|
14
|
+
from gyomu_schema.schemas.python.type.structure import NameStructureAnalysis
|
|
15
|
+
from gyomu_schema.schemas.python.type.type_analysis import TypeAnalysis
|
|
16
|
+
|
|
17
|
+
from gyomu_python_analysis.analysis.analyzers.context import (
|
|
18
|
+
MemberPath,
|
|
19
|
+
SymbolContext,
|
|
20
|
+
build_declaration_identity,
|
|
21
|
+
)
|
|
22
|
+
from gyomu_python_analysis.analysis.analyzers.expression.expr import (
|
|
23
|
+
analyze_type_expression,
|
|
24
|
+
)
|
|
25
|
+
from gyomu_python_analysis.analysis.analyzers.functions import (
|
|
26
|
+
_get_function_parameter_kind,
|
|
27
|
+
)
|
|
28
|
+
from gyomu_python_analysis.analysis.analyzers.internal.common import (
|
|
29
|
+
build_member_common,
|
|
30
|
+
build_symbol_common,
|
|
31
|
+
)
|
|
32
|
+
from gyomu_python_analysis.analysis.analyzers.pydantic import analyze_pydantic
|
|
33
|
+
from gyomu_python_analysis.analysis.analyzers.types import analyze_type
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _retrieve_constructor_location(
|
|
37
|
+
cls: Class,
|
|
38
|
+
context: SymbolContext,
|
|
39
|
+
) -> SourceLocation | None:
|
|
40
|
+
constructor_location: SourceLocation | None = None
|
|
41
|
+
if "__init__" in cls.members:
|
|
42
|
+
init_member = cls.members["__init__"]
|
|
43
|
+
if isinstance(init_member, Function):
|
|
44
|
+
constructor_common = build_member_common(
|
|
45
|
+
symbol=init_member,
|
|
46
|
+
name="__init__",
|
|
47
|
+
parent_location=None,
|
|
48
|
+
context=context,
|
|
49
|
+
)
|
|
50
|
+
constructor_location = constructor_common["location"]
|
|
51
|
+
return constructor_location
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _build_class_type_aliases(
|
|
55
|
+
cls: Class,
|
|
56
|
+
parent_location: SourceLocation | None,
|
|
57
|
+
context: SymbolContext,
|
|
58
|
+
member_path: MemberPath,
|
|
59
|
+
) -> list[ClassTypeAliasAnalysis]:
|
|
60
|
+
aliases: list[ClassTypeAliasAnalysis] = []
|
|
61
|
+
for member_name, member in cls.members.items():
|
|
62
|
+
if isinstance(member, TypeAlias):
|
|
63
|
+
aliases.append(
|
|
64
|
+
_build_class_type_alias_analysis(
|
|
65
|
+
member=member,
|
|
66
|
+
name=member_name,
|
|
67
|
+
parent_location=parent_location,
|
|
68
|
+
context=context,
|
|
69
|
+
member_path=member_path,
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
return aliases
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _build_class_type_alias_analysis(
|
|
76
|
+
member: TypeAlias,
|
|
77
|
+
name: str,
|
|
78
|
+
parent_location: SourceLocation | None,
|
|
79
|
+
context: SymbolContext,
|
|
80
|
+
member_path: MemberPath,
|
|
81
|
+
) -> ClassTypeAliasAnalysis:
|
|
82
|
+
new_member_path = (*member_path, name)
|
|
83
|
+
alias_common = build_member_common(
|
|
84
|
+
symbol=member,
|
|
85
|
+
name=name,
|
|
86
|
+
parent_location=parent_location,
|
|
87
|
+
context=context,
|
|
88
|
+
)
|
|
89
|
+
return ClassTypeAliasAnalysis(
|
|
90
|
+
**alias_common,
|
|
91
|
+
alias_type=analyze_type(member.value, context),
|
|
92
|
+
identity=build_declaration_identity(
|
|
93
|
+
context=context, member_path=new_member_path
|
|
94
|
+
),
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _build_class_variables(
|
|
99
|
+
cls: Class,
|
|
100
|
+
parent_location: SourceLocation | None,
|
|
101
|
+
context: SymbolContext,
|
|
102
|
+
member_path: MemberPath,
|
|
103
|
+
is_pydantic_base_class: bool,
|
|
104
|
+
) -> list[ClassVariableAnalysis]:
|
|
105
|
+
variables: list[ClassVariableAnalysis] = []
|
|
106
|
+
for member_name, member in cls.members.items():
|
|
107
|
+
if isinstance(member, Attribute):
|
|
108
|
+
variables.append(
|
|
109
|
+
_build_class_variable_analysis(
|
|
110
|
+
member=member,
|
|
111
|
+
name=member_name,
|
|
112
|
+
parent_location=parent_location,
|
|
113
|
+
context=context,
|
|
114
|
+
member_path=member_path,
|
|
115
|
+
is_pydantic_base_class=is_pydantic_base_class,
|
|
116
|
+
)
|
|
117
|
+
)
|
|
118
|
+
return variables
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _build_class_variable_analysis(
|
|
122
|
+
member: Attribute,
|
|
123
|
+
name: str,
|
|
124
|
+
parent_location: SourceLocation | None,
|
|
125
|
+
context: SymbolContext,
|
|
126
|
+
member_path: MemberPath,
|
|
127
|
+
is_pydantic_base_class: bool,
|
|
128
|
+
) -> ClassVariableAnalysis:
|
|
129
|
+
new_member_path = (*member_path, name)
|
|
130
|
+
variable_common = build_member_common(
|
|
131
|
+
symbol=member,
|
|
132
|
+
name=name,
|
|
133
|
+
parent_location=parent_location,
|
|
134
|
+
context=context,
|
|
135
|
+
)
|
|
136
|
+
variable_type = analyze_type(member.annotation, context)
|
|
137
|
+
value_expression = (
|
|
138
|
+
analyze_type_expression(member.value, context)
|
|
139
|
+
if member.value is not None
|
|
140
|
+
else None
|
|
141
|
+
)
|
|
142
|
+
pydantic: PydanticFieldAnalysis | None = None
|
|
143
|
+
logger.info(f"pydantic_base:{is_pydantic_base_class}")
|
|
144
|
+
if (
|
|
145
|
+
value_expression
|
|
146
|
+
and variable_type
|
|
147
|
+
and variable_type.structure
|
|
148
|
+
and is_pydantic_base_class
|
|
149
|
+
):
|
|
150
|
+
print(repr(variable_type.structure))
|
|
151
|
+
print(repr(value_expression))
|
|
152
|
+
pydantic = analyze_pydantic(variable_type.structure, value_expression)
|
|
153
|
+
|
|
154
|
+
return ClassVariableAnalysis(
|
|
155
|
+
**variable_common,
|
|
156
|
+
type=variable_type,
|
|
157
|
+
value_source=str(member.value) if member.value is not None else None,
|
|
158
|
+
value_expression=analyze_type_expression(member.value, context)
|
|
159
|
+
if member.value is not None
|
|
160
|
+
else None,
|
|
161
|
+
pydantic=pydantic,
|
|
162
|
+
identity=build_declaration_identity(
|
|
163
|
+
context=context, member_path=new_member_path
|
|
164
|
+
),
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _build_class_method_analysis(
|
|
169
|
+
member: Function,
|
|
170
|
+
name: str,
|
|
171
|
+
parent_location: SourceLocation | None,
|
|
172
|
+
context: SymbolContext,
|
|
173
|
+
member_path: MemberPath,
|
|
174
|
+
) -> MethodAnalysis:
|
|
175
|
+
new_member_path = (*member_path, name)
|
|
176
|
+
method_parameters: list[ParameterAnalysis] = []
|
|
177
|
+
for param in member.parameters:
|
|
178
|
+
method_parameters.append(
|
|
179
|
+
ParameterAnalysis(
|
|
180
|
+
name=param.name,
|
|
181
|
+
kind=_get_function_parameter_kind(param.kind),
|
|
182
|
+
type=analyze_type(param.annotation, context),
|
|
183
|
+
default=None,
|
|
184
|
+
)
|
|
185
|
+
)
|
|
186
|
+
method_common = build_member_common(
|
|
187
|
+
symbol=member,
|
|
188
|
+
name=name,
|
|
189
|
+
parent_location=parent_location,
|
|
190
|
+
context=context,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
return MethodAnalysis(
|
|
194
|
+
**method_common,
|
|
195
|
+
parameters=tuple(method_parameters),
|
|
196
|
+
return_type=analyze_type(member.returns, context),
|
|
197
|
+
is_async="async" in member.labels,
|
|
198
|
+
identity=build_declaration_identity(
|
|
199
|
+
context=context, member_path=new_member_path
|
|
200
|
+
),
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _build_class_methods(
|
|
205
|
+
cls: Class,
|
|
206
|
+
parent_location: SourceLocation | None,
|
|
207
|
+
context: SymbolContext,
|
|
208
|
+
member_path: MemberPath,
|
|
209
|
+
) -> list[MethodAnalysis]:
|
|
210
|
+
methods: list[MethodAnalysis] = []
|
|
211
|
+
for member_name, member in cls.members.items():
|
|
212
|
+
if isinstance(member, Function):
|
|
213
|
+
methods.append(
|
|
214
|
+
_build_class_method_analysis(
|
|
215
|
+
member=member,
|
|
216
|
+
name=member_name,
|
|
217
|
+
parent_location=parent_location,
|
|
218
|
+
context=context,
|
|
219
|
+
member_path=member_path,
|
|
220
|
+
)
|
|
221
|
+
)
|
|
222
|
+
return methods
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def _build_inner_classes(
|
|
226
|
+
cls: Class, context: SymbolContext, member_path: MemberPath
|
|
227
|
+
) -> list[InnerClassAnalysis]:
|
|
228
|
+
inner_classes: list[InnerClassAnalysis] = []
|
|
229
|
+
for member_name, member in cls.members.items():
|
|
230
|
+
if isinstance(member, Class):
|
|
231
|
+
inner_classes.append(
|
|
232
|
+
_analyze_inner_class(
|
|
233
|
+
cls=member,
|
|
234
|
+
name=member_name,
|
|
235
|
+
context=context,
|
|
236
|
+
member_path=member_path,
|
|
237
|
+
)
|
|
238
|
+
)
|
|
239
|
+
return inner_classes
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def _is_pydantic_base_class(bases: list[TypeAnalysis]) -> bool:
|
|
243
|
+
for base in bases:
|
|
244
|
+
if (
|
|
245
|
+
isinstance(base.structure, NameStructureAnalysis)
|
|
246
|
+
and base.structure.name == "BaseModel"
|
|
247
|
+
):
|
|
248
|
+
return True
|
|
249
|
+
|
|
250
|
+
return False
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _analyze_class_common(
|
|
254
|
+
cls: Class,
|
|
255
|
+
name: str,
|
|
256
|
+
context: SymbolContext,
|
|
257
|
+
member_path: MemberPath,
|
|
258
|
+
) -> ClassCommon:
|
|
259
|
+
bases: list[TypeAnalysis] = [
|
|
260
|
+
analyzed
|
|
261
|
+
for base in cls.bases
|
|
262
|
+
if (analyzed := analyze_type(base, context)) is not None
|
|
263
|
+
]
|
|
264
|
+
|
|
265
|
+
is_pydantic_base_class = _is_pydantic_base_class(bases)
|
|
266
|
+
|
|
267
|
+
constructor_location: SourceLocation | None = _retrieve_constructor_location(
|
|
268
|
+
cls, context
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
parameters: list[ClassVariableAnalysis] = _build_class_variables(
|
|
272
|
+
cls=cls,
|
|
273
|
+
parent_location=constructor_location,
|
|
274
|
+
context=context,
|
|
275
|
+
member_path=member_path,
|
|
276
|
+
is_pydantic_base_class=is_pydantic_base_class,
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
methods: list[MethodAnalysis] = _build_class_methods(
|
|
280
|
+
cls=cls,
|
|
281
|
+
parent_location=constructor_location,
|
|
282
|
+
context=context,
|
|
283
|
+
member_path=member_path,
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
type_aliases: list[ClassTypeAliasAnalysis] = _build_class_type_aliases(
|
|
287
|
+
cls=cls,
|
|
288
|
+
parent_location=constructor_location,
|
|
289
|
+
context=context,
|
|
290
|
+
member_path=member_path,
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
inner_classes: list[InnerClassAnalysis] = _build_inner_classes(
|
|
294
|
+
cls=cls, context=context, member_path=member_path
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
return {
|
|
298
|
+
"bases": tuple(bases),
|
|
299
|
+
"inner_classes": tuple(inner_classes),
|
|
300
|
+
"methods": tuple(methods),
|
|
301
|
+
"variables": tuple(parameters),
|
|
302
|
+
"type_aliases": tuple(type_aliases),
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def _analyze_inner_class(
|
|
307
|
+
cls: Class,
|
|
308
|
+
name: str,
|
|
309
|
+
context: SymbolContext,
|
|
310
|
+
member_path: MemberPath,
|
|
311
|
+
) -> InnerClassAnalysis:
|
|
312
|
+
new_member_path = (*member_path, name)
|
|
313
|
+
class_common = _analyze_class_common(
|
|
314
|
+
cls, name, context, member_path=new_member_path
|
|
315
|
+
)
|
|
316
|
+
# pprint(cls.as_dict())
|
|
317
|
+
base_common = build_member_common(
|
|
318
|
+
symbol=cls,
|
|
319
|
+
name=name,
|
|
320
|
+
parent_location=None,
|
|
321
|
+
context=context,
|
|
322
|
+
)
|
|
323
|
+
return InnerClassAnalysis(
|
|
324
|
+
**base_common,
|
|
325
|
+
**class_common,
|
|
326
|
+
identity=build_declaration_identity(
|
|
327
|
+
context=context, member_path=new_member_path
|
|
328
|
+
),
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def analyze_class(cls: Class, name: str, context: SymbolContext) -> ClassAnalysis:
|
|
333
|
+
member_path: MemberPath = ()
|
|
334
|
+
class_common = _analyze_class_common(cls, name, context, member_path)
|
|
335
|
+
# pprint(cls.as_dict())
|
|
336
|
+
base_common = build_symbol_common(symbol=cls, name=name, context=context)
|
|
337
|
+
|
|
338
|
+
return ClassAnalysis(
|
|
339
|
+
**base_common,
|
|
340
|
+
**class_common,
|
|
341
|
+
dependencies=tuple(),
|
|
342
|
+
identity=context.declaration,
|
|
343
|
+
)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from gyomu_schema.schemas.python.types import (
|
|
4
|
+
DeclarationId,
|
|
5
|
+
DeclarationIdentity,
|
|
6
|
+
PythonPath,
|
|
7
|
+
SymbolId,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class DependencyInformation:
|
|
13
|
+
source: DeclarationIdentity
|
|
14
|
+
target_name: str
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class SymbolContext:
|
|
19
|
+
dependencies: list[DependencyInformation]
|
|
20
|
+
declaration: DeclarationIdentity
|
|
21
|
+
source_lines: list[str]
|
|
22
|
+
line_start_offsets: list[int]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
type MemberPath = tuple[str, ...]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def initialize_symbol_context(
|
|
29
|
+
module_name: PythonPath, name: str, source_lines: list[str]
|
|
30
|
+
) -> SymbolContext:
|
|
31
|
+
symbol_id = build_symbol_id(module_name=module_name, name=name)
|
|
32
|
+
line_start_offsets = [0]
|
|
33
|
+
for line in source_lines[:-1]:
|
|
34
|
+
line_start_offsets.append(
|
|
35
|
+
line_start_offsets[-1] + len(line),
|
|
36
|
+
)
|
|
37
|
+
return SymbolContext(
|
|
38
|
+
dependencies=[],
|
|
39
|
+
declaration=DeclarationIdentity(
|
|
40
|
+
symbol_id=symbol_id,
|
|
41
|
+
declaration_id=_build_declaration_id(tuple()),
|
|
42
|
+
),
|
|
43
|
+
source_lines=source_lines,
|
|
44
|
+
line_start_offsets=line_start_offsets,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def build_symbol_id(module_name: PythonPath, name: str) -> SymbolId:
|
|
49
|
+
if name == "":
|
|
50
|
+
return SymbolId(f"{module_name}")
|
|
51
|
+
return SymbolId(f"{module_name}::{name}")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _build_declaration_id(member_path: MemberPath) -> DeclarationId:
|
|
55
|
+
if not member_path:
|
|
56
|
+
return DeclarationId(".")
|
|
57
|
+
return DeclarationId(".::" + "::".join(member_path))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def build_declaration_identity(
|
|
61
|
+
context: SymbolContext, member_path: MemberPath
|
|
62
|
+
) -> DeclarationIdentity:
|
|
63
|
+
return DeclarationIdentity(
|
|
64
|
+
symbol_id=context.declaration.symbol_id,
|
|
65
|
+
declaration_id=_build_declaration_id(member_path),
|
|
66
|
+
)
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from griffe import Decorator
|
|
2
|
+
from gyomu_infra.logger import logger
|
|
3
|
+
from gyomu_schema.schemas.python.decorator import DecoratorAnalysis, DecoratorArgument
|
|
4
|
+
from gyomu_schema.schemas.python.type.structure import (
|
|
5
|
+
LiteralValue,
|
|
6
|
+
NameStructureAnalysis,
|
|
7
|
+
)
|
|
8
|
+
from gyomu_schema.schemas.python.type.type_analysis import (
|
|
9
|
+
AttributeStructureAnalysis,
|
|
10
|
+
CallStructureAnalysis,
|
|
11
|
+
ExpressionAnalysis,
|
|
12
|
+
KeywordStructureAnalysis,
|
|
13
|
+
TypeExpression,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from gyomu_python_analysis.analysis.analyzers.context import SymbolContext
|
|
17
|
+
from gyomu_python_analysis.analysis.analyzers.expression.expr import (
|
|
18
|
+
analyze_type_expression,
|
|
19
|
+
)
|
|
20
|
+
from gyomu_python_analysis.analysis.analyzers.internal.location import (
|
|
21
|
+
calculate_decorator_location,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def analyze_decorators(
|
|
26
|
+
decorators: list[Decorator], context: SymbolContext
|
|
27
|
+
) -> list[DecoratorAnalysis]:
|
|
28
|
+
returns: list[DecoratorAnalysis] = []
|
|
29
|
+
for dec in decorators:
|
|
30
|
+
returns.append(analyze_decorator(dec, context))
|
|
31
|
+
|
|
32
|
+
return returns
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def analyze_decorator(
|
|
36
|
+
decorator: Decorator, context: SymbolContext
|
|
37
|
+
) -> DecoratorAnalysis:
|
|
38
|
+
value = analyze_type_expression(decorator.value, context)
|
|
39
|
+
name: str
|
|
40
|
+
arguments: list[DecoratorArgument] = []
|
|
41
|
+
if isinstance(value, LiteralValue):
|
|
42
|
+
name = str(value.value)
|
|
43
|
+
|
|
44
|
+
else:
|
|
45
|
+
name, arguments = _retrieve_expression_name(value)
|
|
46
|
+
|
|
47
|
+
return DecoratorAnalysis(
|
|
48
|
+
location=calculate_decorator_location(decorator=decorator, context=context),
|
|
49
|
+
name=name,
|
|
50
|
+
arguments=tuple(arguments),
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _retrieve_expression_name(
|
|
55
|
+
expression: ExpressionAnalysis,
|
|
56
|
+
) -> tuple[str, list[DecoratorArgument]]:
|
|
57
|
+
if isinstance(expression, NameStructureAnalysis):
|
|
58
|
+
return expression.name, []
|
|
59
|
+
elif isinstance(expression, AttributeStructureAnalysis):
|
|
60
|
+
return ".".join(_retrieve_attribute_names(expression)), []
|
|
61
|
+
elif isinstance(expression, CallStructureAnalysis):
|
|
62
|
+
name, _ = _retrieve_expression_name(expression.function)
|
|
63
|
+
arguments: list[DecoratorArgument] = []
|
|
64
|
+
for arg in expression.arguments:
|
|
65
|
+
arguments.append(_retrieve_expression_argument(arg))
|
|
66
|
+
return name, arguments
|
|
67
|
+
else:
|
|
68
|
+
logger.error(f"Unsupported Expression: {expression.kind}")
|
|
69
|
+
return "", []
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _retrieve_expression_argument(expression: TypeExpression) -> DecoratorArgument:
|
|
73
|
+
if isinstance(expression, KeywordStructureAnalysis):
|
|
74
|
+
return DecoratorArgument(expression=expression.value, name=expression.name)
|
|
75
|
+
return DecoratorArgument(expression=expression)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _retrieve_attribute_names(attribute: AttributeStructureAnalysis) -> list[str]:
|
|
79
|
+
strings: list[str] = []
|
|
80
|
+
for value in attribute.values:
|
|
81
|
+
if isinstance(value, LiteralValue):
|
|
82
|
+
strings.append(str(value.value))
|
|
83
|
+
elif isinstance(value, NameStructureAnalysis):
|
|
84
|
+
strings.append(value.name)
|
|
85
|
+
elif isinstance(value, AttributeStructureAnalysis):
|
|
86
|
+
for child in _retrieve_attribute_names(value):
|
|
87
|
+
strings.append(child)
|
|
88
|
+
return strings
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
from gyomu_schema.schemas.python.dependency import (
|
|
2
|
+
DependencyAnalysis,
|
|
3
|
+
ImportedSymbolDependency,
|
|
4
|
+
LocalFileDependency,
|
|
5
|
+
)
|
|
6
|
+
from gyomu_schema.schemas.python.import_analysis import ImportAnalysis, ImportKind
|
|
7
|
+
from gyomu_schema.schemas.python.symbol import SymbolAnalysis
|
|
8
|
+
from gyomu_schema.schemas.python.types import DeclarationIdentity, SymbolId
|
|
9
|
+
|
|
10
|
+
from gyomu_python_analysis.analysis.analyzers.context import (
|
|
11
|
+
DependencyInformation,
|
|
12
|
+
SymbolContext,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
PYTHON_RESERVED_TYPE_NAMES: frozenset[str] = frozenset(
|
|
16
|
+
{
|
|
17
|
+
# Built-in scalar types
|
|
18
|
+
"bool",
|
|
19
|
+
"int",
|
|
20
|
+
"float",
|
|
21
|
+
"complex",
|
|
22
|
+
"str",
|
|
23
|
+
"bytes",
|
|
24
|
+
"bytearray",
|
|
25
|
+
"memoryview",
|
|
26
|
+
# Built-in container types
|
|
27
|
+
"list",
|
|
28
|
+
"tuple",
|
|
29
|
+
"dict",
|
|
30
|
+
"set",
|
|
31
|
+
"frozenset",
|
|
32
|
+
# Built-in utility types
|
|
33
|
+
"range",
|
|
34
|
+
"object",
|
|
35
|
+
"type",
|
|
36
|
+
# Typing primitives
|
|
37
|
+
"Any",
|
|
38
|
+
"Never",
|
|
39
|
+
"NoReturn",
|
|
40
|
+
"Literal",
|
|
41
|
+
"Union",
|
|
42
|
+
"Optional",
|
|
43
|
+
"Annotated",
|
|
44
|
+
"Final",
|
|
45
|
+
"ClassVar",
|
|
46
|
+
"Type",
|
|
47
|
+
"Callable",
|
|
48
|
+
"TypeVar",
|
|
49
|
+
"Generic",
|
|
50
|
+
"Protocol",
|
|
51
|
+
"Self",
|
|
52
|
+
}
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def register_dependency(
|
|
57
|
+
identity: DeclarationIdentity, name: str, context: SymbolContext
|
|
58
|
+
) -> None:
|
|
59
|
+
if name in PYTHON_RESERVED_TYPE_NAMES:
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
context.dependencies.append(
|
|
63
|
+
DependencyInformation(source=identity, target_name=name)
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _find_imported(
|
|
68
|
+
name: str,
|
|
69
|
+
imported: list[ImportAnalysis],
|
|
70
|
+
) -> ImportAnalysis | None:
|
|
71
|
+
for item in imported:
|
|
72
|
+
if item.local_name == name:
|
|
73
|
+
return item
|
|
74
|
+
return None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _retrieve_imported_symbol_id(imported_item: ImportAnalysis) -> SymbolId:
|
|
78
|
+
if imported_item.kind == ImportKind.MODULE:
|
|
79
|
+
return SymbolId(imported_item.imported_name)
|
|
80
|
+
module_name, symbol_name = imported_item.imported_name.rsplit(".", 1)
|
|
81
|
+
return SymbolId(f"{module_name}::{symbol_name}")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _find_symbol(
|
|
85
|
+
name: str,
|
|
86
|
+
symbols: list[SymbolAnalysis],
|
|
87
|
+
) -> SymbolAnalysis | None:
|
|
88
|
+
for symbol in symbols:
|
|
89
|
+
if symbol.name == name:
|
|
90
|
+
return symbol
|
|
91
|
+
|
|
92
|
+
return None
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def analyze_dependency(
|
|
96
|
+
record: DependencyInformation,
|
|
97
|
+
imported: list[ImportAnalysis],
|
|
98
|
+
symbols: list[SymbolAnalysis],
|
|
99
|
+
) -> DependencyAnalysis | None:
|
|
100
|
+
symbol_id: SymbolId
|
|
101
|
+
if imported_item := _find_imported(record.target_name, imported):
|
|
102
|
+
symbol_id = _retrieve_imported_symbol_id(imported_item)
|
|
103
|
+
return DependencyAnalysis(
|
|
104
|
+
source=record.source, target=ImportedSymbolDependency(symbol_id=symbol_id)
|
|
105
|
+
)
|
|
106
|
+
else:
|
|
107
|
+
result = _find_symbol(record.target_name, symbols)
|
|
108
|
+
if result is None:
|
|
109
|
+
return None
|
|
110
|
+
symbol_id = result.identity.symbol_id
|
|
111
|
+
return DependencyAnalysis(
|
|
112
|
+
source=record.source, target=LocalFileDependency(symbol_id=symbol_id)
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def resolve_dependencies(
|
|
117
|
+
dependencies: list[DependencyInformation],
|
|
118
|
+
imported: list[ImportAnalysis],
|
|
119
|
+
symbols: list[SymbolAnalysis],
|
|
120
|
+
) -> dict[DeclarationIdentity, tuple[DependencyAnalysis, ...]]:
|
|
121
|
+
result: dict[
|
|
122
|
+
DeclarationIdentity,
|
|
123
|
+
list[DependencyAnalysis],
|
|
124
|
+
] = {}
|
|
125
|
+
|
|
126
|
+
for dependency in dependencies:
|
|
127
|
+
parsed = analyze_dependency(dependency, imported, symbols)
|
|
128
|
+
|
|
129
|
+
if parsed is None:
|
|
130
|
+
continue
|
|
131
|
+
|
|
132
|
+
result.setdefault(parsed.source, []).append(parsed)
|
|
133
|
+
|
|
134
|
+
return {source: tuple(items) for source, items in result.items()}
|