skylos 1.0.7__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.
Potentially problematic release.
This version of skylos might be problematic. Click here for more details.
- skylos/__init__.py +8 -0
- skylos/analyzer.py +180 -0
- skylos/cli.py +332 -0
- skylos/visitor.py +181 -0
- skylos-1.0.7.dist-info/METADATA +8 -0
- skylos-1.0.7.dist-info/RECORD +21 -0
- skylos-1.0.7.dist-info/WHEEL +5 -0
- skylos-1.0.7.dist-info/entry_points.txt +2 -0
- skylos-1.0.7.dist-info/top_level.txt +2 -0
- test/__init__.py +0 -0
- test/compare_tools.py +604 -0
- test/diagnostics.py +364 -0
- test/sample_repo/__init__.py +0 -0
- test/sample_repo/app.py +13 -0
- test/sample_repo/sample_repo/__init__.py +0 -0
- test/sample_repo/sample_repo/commands.py +81 -0
- test/sample_repo/sample_repo/models.py +122 -0
- test/sample_repo/sample_repo/routes.py +89 -0
- test/sample_repo/sample_repo/utils.py +36 -0
- test/test_skylos.py +456 -0
- test/test_visitor.py +220 -0
skylos/visitor.py
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import ast,re
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
PYTHON_BUILTINS={"print","len","str","int","float","list","dict","set","tuple","range","open","super","object","type","enumerate","zip","map","filter","sorted","reversed","sum","min","max","all","any","next","iter","repr","chr","ord","bytes","bytearray","memoryview","format","round","abs","pow","divmod","complex","hash","id","bool","callable","getattr","setattr","delattr","hasattr","isinstance","issubclass","globals","locals","vars","dir","property","classmethod","staticmethod"}
|
|
6
|
+
DYNAMIC_PATTERNS={"getattr","globals","eval","exec"}
|
|
7
|
+
|
|
8
|
+
class Definition:
|
|
9
|
+
__slots__ = ('name', 'type', 'filename', 'line', 'simple_name', 'confidence', 'references', 'is_exported', 'in_init')
|
|
10
|
+
|
|
11
|
+
def __init__(self, n, t, f, l):
|
|
12
|
+
self.name = n
|
|
13
|
+
self.type = t
|
|
14
|
+
self.filename = f
|
|
15
|
+
self.line = l
|
|
16
|
+
self.simple_name = n.split('.')[-1]
|
|
17
|
+
self.confidence = 100
|
|
18
|
+
self.references = 0
|
|
19
|
+
self.is_exported = False
|
|
20
|
+
self.in_init = "__init__.py" in str(f)
|
|
21
|
+
|
|
22
|
+
def to_dict(self):
|
|
23
|
+
if self.type == "method" and "." in self.name:
|
|
24
|
+
parts = self.name.split(".")
|
|
25
|
+
if len(parts) >= 3:
|
|
26
|
+
output_name = ".".join(parts[-2:])
|
|
27
|
+
else:
|
|
28
|
+
output_name = self.name
|
|
29
|
+
else:
|
|
30
|
+
output_name = self.simple_name
|
|
31
|
+
|
|
32
|
+
return{
|
|
33
|
+
"name": output_name,
|
|
34
|
+
"full_name": self.name,
|
|
35
|
+
"simple_name": self.simple_name,
|
|
36
|
+
"type": self.type,
|
|
37
|
+
"file": str(self.filename),
|
|
38
|
+
"basename": Path(self.filename).name,
|
|
39
|
+
"line": self.line,
|
|
40
|
+
"confidence": self.confidence,
|
|
41
|
+
"references": self.references
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
class Visitor(ast.NodeVisitor):
|
|
45
|
+
def __init__(self,mod,file):
|
|
46
|
+
self.mod=mod
|
|
47
|
+
self.file=file
|
|
48
|
+
self.defs=[]
|
|
49
|
+
self.refs=[]
|
|
50
|
+
self.cls=None
|
|
51
|
+
self.alias={}
|
|
52
|
+
self.dyn=set()
|
|
53
|
+
self.exports=set()
|
|
54
|
+
self.current_function_scope = []
|
|
55
|
+
|
|
56
|
+
def add_def(self,n,t,l):
|
|
57
|
+
if n not in{d.name for d in self.defs}:self.defs.append(Definition(n,t,self.file,l))
|
|
58
|
+
|
|
59
|
+
def add_ref(self,n):self.refs.append((n,self.file))
|
|
60
|
+
|
|
61
|
+
def qual(self,n):
|
|
62
|
+
if n in self.alias:return self.alias[n]
|
|
63
|
+
if n in PYTHON_BUILTINS:return n
|
|
64
|
+
return f"{self.mod}.{n}"if self.mod else n
|
|
65
|
+
|
|
66
|
+
def visit_Import(self,node):
|
|
67
|
+
for a in node.names:
|
|
68
|
+
full=a.name
|
|
69
|
+
self.alias[a.asname or a.name.split(".")[-1]]=full
|
|
70
|
+
self.add_def(full,"import",node.lineno)
|
|
71
|
+
|
|
72
|
+
def visit_ImportFrom(self,node):
|
|
73
|
+
if node.module is None:return
|
|
74
|
+
for a in node.names:
|
|
75
|
+
if a.name=="*":continue
|
|
76
|
+
base=node.module
|
|
77
|
+
if node.level:
|
|
78
|
+
parts=self.mod.split(".")
|
|
79
|
+
base=".".join(parts[:-node.level])+(f".{node.module}"if node.module else"")
|
|
80
|
+
full=f"{base}.{a.name}"
|
|
81
|
+
self.alias[a.asname or a.name]=full
|
|
82
|
+
self.add_def(full,"import",node.lineno)
|
|
83
|
+
|
|
84
|
+
def visit_FunctionDef(self,node):
|
|
85
|
+
outer_scope_prefix = '.'.join(self.current_function_scope) + '.' if self.current_function_scope else ''
|
|
86
|
+
|
|
87
|
+
if self.cls:
|
|
88
|
+
name_parts = [self.mod, self.cls, outer_scope_prefix + node.name]
|
|
89
|
+
else:
|
|
90
|
+
name_parts = [self.mod, outer_scope_prefix + node.name]
|
|
91
|
+
|
|
92
|
+
qualified_name = ".".join(filter(None, name_parts))
|
|
93
|
+
|
|
94
|
+
self.add_def(qualified_name,"method"if self.cls else"function",node.lineno)
|
|
95
|
+
|
|
96
|
+
self.current_function_scope.append(node.name)
|
|
97
|
+
for d_node in node.decorator_list:
|
|
98
|
+
self.visit(d_node)
|
|
99
|
+
for stmt in node.body:
|
|
100
|
+
self.visit(stmt)
|
|
101
|
+
self.current_function_scope.pop()
|
|
102
|
+
|
|
103
|
+
visit_AsyncFunctionDef=visit_FunctionDef
|
|
104
|
+
|
|
105
|
+
def visit_ClassDef(self,node):
|
|
106
|
+
cname=f"{self.mod}.{node.name}"
|
|
107
|
+
self.add_def(cname,"class",node.lineno)
|
|
108
|
+
prev=self.cls;self.cls=node.name
|
|
109
|
+
for b in node.body:self.visit(b)
|
|
110
|
+
self.cls=prev
|
|
111
|
+
|
|
112
|
+
def visit_Assign(self, node):
|
|
113
|
+
for target in node.targets:
|
|
114
|
+
if isinstance(target, ast.Name) and target.id == "__all__":
|
|
115
|
+
if isinstance(node.value, (ast.List, ast.Tuple)):
|
|
116
|
+
for elt in node.value.elts:
|
|
117
|
+
value = None
|
|
118
|
+
if isinstance(elt, ast.Constant) and isinstance(elt.value, str):
|
|
119
|
+
value = elt.value
|
|
120
|
+
elif hasattr(elt, 's') and isinstance(elt.s, str):
|
|
121
|
+
value = elt.s
|
|
122
|
+
|
|
123
|
+
if value is not None:
|
|
124
|
+
full_name = f"{self.mod}.{value}"
|
|
125
|
+
self.add_ref(full_name)
|
|
126
|
+
self.add_ref(value)
|
|
127
|
+
self.generic_visit(node)
|
|
128
|
+
|
|
129
|
+
def visit_Call(self, node):
|
|
130
|
+
self.generic_visit(node)
|
|
131
|
+
|
|
132
|
+
if isinstance(node.func, ast.Name) and node.func.id in ("getattr", "hasattr") and len(node.args) >= 2:
|
|
133
|
+
if isinstance(node.args[1], ast.Constant) and isinstance(node.args[1].value, str):
|
|
134
|
+
attr_name = node.args[1].value
|
|
135
|
+
self.add_ref(attr_name)
|
|
136
|
+
|
|
137
|
+
if isinstance(node.args[0], ast.Name):
|
|
138
|
+
module_name = node.args[0].id
|
|
139
|
+
if module_name != "self":
|
|
140
|
+
qualified_name = f"{self.qual(module_name)}.{attr_name}"
|
|
141
|
+
self.add_ref(qualified_name)
|
|
142
|
+
|
|
143
|
+
elif isinstance(node.func, ast.Name) and node.func.id == "globals":
|
|
144
|
+
parent = getattr(node, 'parent', None)
|
|
145
|
+
if (isinstance(parent, ast.Subscript) and
|
|
146
|
+
isinstance(parent.slice, ast.Constant) and
|
|
147
|
+
isinstance(parent.slice.value, str)):
|
|
148
|
+
func_name = parent.slice.value
|
|
149
|
+
self.add_ref(func_name)
|
|
150
|
+
self.add_ref(f"{self.mod}.{func_name}")
|
|
151
|
+
|
|
152
|
+
elif (isinstance(node.func, ast.Attribute) and
|
|
153
|
+
node.func.attr == "format" and
|
|
154
|
+
isinstance(node.func.value, ast.Constant) and
|
|
155
|
+
isinstance(node.func.value.value, str)):
|
|
156
|
+
fmt = node.func.value.value
|
|
157
|
+
if any(isinstance(k.arg, str) and k.arg is None for k in node.keywords):
|
|
158
|
+
for _, n, _, _ in re.findall(r'\{([^}:!]+)', fmt):
|
|
159
|
+
if n:
|
|
160
|
+
self.add_ref(self.qual(n))
|
|
161
|
+
|
|
162
|
+
def visit_Name(self,node):
|
|
163
|
+
if isinstance(node.ctx,ast.Load):
|
|
164
|
+
self.add_ref(self.qual(node.id))
|
|
165
|
+
if node.id in DYNAMIC_PATTERNS:self.dyn.add(self.mod.split(".")[0])
|
|
166
|
+
|
|
167
|
+
def visit_Attribute(self,node):
|
|
168
|
+
self.generic_visit(node)
|
|
169
|
+
if isinstance(node.ctx,ast.Load)and isinstance(node.value,ast.Name):
|
|
170
|
+
self.add_ref(f"{self.qual(node.value.id)}.{node.attr}")
|
|
171
|
+
|
|
172
|
+
def generic_visit(self, node):
|
|
173
|
+
for field, value in ast.iter_fields(node):
|
|
174
|
+
if isinstance(value, list):
|
|
175
|
+
for item in value:
|
|
176
|
+
if isinstance(item, ast.AST):
|
|
177
|
+
item.parent = node
|
|
178
|
+
self.visit(item)
|
|
179
|
+
elif isinstance(value, ast.AST):
|
|
180
|
+
value.parent = node
|
|
181
|
+
self.visit(value)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
skylos/__init__.py,sha256=QzhqFsdZqpu52fXFnvDem4P56R3SOC0dKOHfekwFuEQ,151
|
|
2
|
+
skylos/analyzer.py,sha256=EtL0IIw1NYzdGpXiOFQk4SXTSuTayVGpmGi0mZXL_Hk,7347
|
|
3
|
+
skylos/cli.py,sha256=Pq71Gtc3d8E7JVYnEORK_LoLclG96A6b76SIGZh-C6s,13821
|
|
4
|
+
skylos/visitor.py,sha256=DvRLXI-N2B6XG7VAB7cXEooD52oG1YNkl86iJbXe2-w,7336
|
|
5
|
+
test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
test/compare_tools.py,sha256=0g9PDeJlbst-7hOaQzrL4MiJFQKpqM8q8VeBGzpPczg,22738
|
|
7
|
+
test/diagnostics.py,sha256=ExuFOCVpc9BDwNYapU96vj9RXLqxji32Sv6wVF4nJYU,13802
|
|
8
|
+
test/test_skylos.py,sha256=kz77STrS4k3Eez5RDYwGxOg2WH3e7zNZPUYEaTLbGTs,15608
|
|
9
|
+
test/test_visitor.py,sha256=bxUY_Zn_gLadZlz_n3Mu6rhVcExqElISwwVBo4eqVAY,7337
|
|
10
|
+
test/sample_repo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
test/sample_repo/app.py,sha256=M5XgoAn-LPz50mKAj_ZacRKf-Pg7I4HbjWP7Z9jE4a0,226
|
|
12
|
+
test/sample_repo/sample_repo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
+
test/sample_repo/sample_repo/commands.py,sha256=b6gQ9YDabt2yyfqGbOpLo0osF7wya8O4Lm7m8gtCr3g,2575
|
|
14
|
+
test/sample_repo/sample_repo/models.py,sha256=xXIg3pToEZwKuUCmKX2vTlCF_VeFA0yZlvlBVPIy5Qw,3320
|
|
15
|
+
test/sample_repo/sample_repo/routes.py,sha256=8yITrt55BwS01G7nWdESdx8LuxmReqop1zrGUKPeLi8,2475
|
|
16
|
+
test/sample_repo/sample_repo/utils.py,sha256=S56hEYh8wkzwsD260MvQcmUFOkw2EjFU27nMLFE6G2k,1103
|
|
17
|
+
skylos-1.0.7.dist-info/METADATA,sha256=Aqcg8zkHwx-l7ZGIXVVUPmXNQn9DZzZACiGOqM-YIks,224
|
|
18
|
+
skylos-1.0.7.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
|
|
19
|
+
skylos-1.0.7.dist-info/entry_points.txt,sha256=zzRpN2ByznlQoLeuLolS_TFNYSQxUGBL1EXQsAd6bIA,43
|
|
20
|
+
skylos-1.0.7.dist-info/top_level.txt,sha256=f8GA_7KwfaEopPMP8-EXDQXaqd4IbsOQPakZy01LkdQ,12
|
|
21
|
+
skylos-1.0.7.dist-info/RECORD,,
|
test/__init__.py
ADDED
|
File without changes
|