Skip to content

Commit f018f89

Browse files
authored
Merge pull request #1 from xnuinside/dev
fix case with nested class in parser
2 parents 58707ee + 7eff80f commit f018f89

File tree

5 files changed

+58
-57
lines changed

5 files changed

+58
-57
lines changed

README.rst

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,21 @@ Pass '-o' flag if you want only print dependencies in console and don't want gra
2828

2929
cg /path/to/your_python_code -o
3030

31+
If you want to change view and play with graph output - you can check 'vizualyzer.py'
32+
and play with matplotlib and networkX settings.
3133

32-
Visualisation samples
33-
*********************
34+
In default view - red line show dependencies between entities in different modules. Green - entities in module.
35+
36+
.. image:: codegraph/docs/img/graph_visualisation.png
37+
:width: 250
38+
:alt: Graph visualisation
3439

3540
.. image:: codegraph/docs/img/code_with_trash_module.png
36-
:width: 300
41+
:width: 250
3742
:alt: Code with not used module
3843

3944
.. image:: codegraph/docs/img/normal_code.png
40-
:width: 300
45+
:width: 250
4146
:alt: Code there all modules linked together
4247

4348
TODO:
527 KB
Loading

codegraph/parser.py

Lines changed: 14 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# 'cut of' code from pythons pyclrb
1+
# 'cut of' code from pythons pyclrb with some additionals
22
import io
33
import tokenize
44
from token import NAME, DEDENT, NL
@@ -19,6 +19,7 @@ def __init__(self, name: Text, file: Text, lineno: int, parent: object):
1919

2020
def _addchild(self, name, obj):
2121
self.children[name] = obj
22+
obj.main = self
2223

2324
def __repr__(self):
2425
return f"{self.name} <{self.__class__.__name__}: Parent {self.parent}>"
@@ -78,7 +79,7 @@ def _nest_function(ob, func_name, lineno, async_f=False):
7879

7980
def _nest_class(ob, class_name, lineno, super=None):
8081
"Return a Class after nesting within ob."
81-
newclass = Class(ob.module, class_name, super, ob.file, lineno, ob)
82+
newclass = Class(class_name, super, ob.file, lineno, ob)
8283
ob._addchild(class_name, newclass)
8384
return newclass
8485

@@ -94,13 +95,17 @@ def create_objects_array(fname, source):
9495
try:
9596
new_lines = 0
9697
imports = None
98+
cur_func = None
9799
for tokentype, token, start, _end, _line in g:
98100
if tokentype == DEDENT:
99101
lineno, thisindent = start
100102
# Close previous nested classes and defs.
101103
while stack and stack[-1][1] >= thisindent:
102104
if isinstance(stack[-1][0], _Object):
103-
stack[-1][0].endno = lineno -1 - new_lines
105+
if getattr(stack[-1][0], 'main', None):
106+
stack[-1][0].endno = lineno - 1 - new_lines
107+
else:
108+
stack[-1][0].endno = lineno -1 - new_lines
104109
del stack[-1]
105110
else:
106111
if tree:
@@ -152,24 +157,27 @@ def create_objects_array(fname, source):
152157
tokentype, func_name, start = next(g)[0:3]
153158
if tokentype != NAME:
154159
continue # Skip def with syntax error.
155-
cur_func = None
156160
if stack:
157161
cur_obj = stack[-1][0]
158162
cur_func = _nest_function(cur_obj, func_name, lineno)
159163
cur_obj.endno = lineno - new_lines
160164
else:
161-
tree.append(Function(func_name, fname, lineno))
162-
stack.append((cur_func, thisindent))
165+
cur_func = Function(func_name, fname, lineno)
166+
tree.append(cur_func)
167+
if cur_func:
168+
stack.append((cur_func, thisindent))
163169
elif token == 'class':
164170
new_lines = 0
165171
lineno, thisindent = start
166172
# Close previous nested classes and defs.
167173
while stack and stack[-1][1] >= thisindent:
168174
del stack[-1]
169175
tokentype, class_name, start = next(g)[0:3]
176+
170177
if tokentype != NAME:
171178
continue
172179
inherit = None
180+
173181
if stack:
174182
cur_obj = stack[-1][0]
175183
cur_class = _nest_class(
@@ -189,41 +197,3 @@ def create_objects_array(fname, source):
189197
tree.append(imports)
190198
return tree
191199

192-
193-
def _getnamelist(g):
194-
"""Return list of (dotted-name, as-name or None) tuples for token source g.
195-
An as-name is the name that follows 'as' in an as clause.
196-
"""
197-
names = []
198-
while True:
199-
name, token = _getname(g)
200-
if not name:
201-
break
202-
if token == 'as':
203-
name2, token = _getname(g)
204-
else:
205-
name2 = None
206-
names.append((name, name2))
207-
while token != "," and "\n" not in token:
208-
token = next(g)[1]
209-
if token != ",":
210-
break
211-
return names
212-
213-
214-
def _getname(g):
215-
"Return (dotted-name or None, next-token) tuple for token source g."
216-
parts = []
217-
tokentype, token = next(g)[0:2]
218-
if tokentype != NAME and token != '*':
219-
return (None, token)
220-
parts.append(token)
221-
while True:
222-
tokentype, token = next(g)[0:2]
223-
if token != '.':
224-
break
225-
tokentype, token = next(g)[0:2]
226-
if tokentype != NAME:
227-
break
228-
parts.append(token)
229-
return (".".join(parts), token)

codegraph/vizualyzer.py

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,42 @@
55

66

77
def draw_graph(modules_entities: Dict) -> None:
8-
gr = nx.Graph()
9-
module_edges = []
10-
sub_edges = []
8+
G = nx.DiGraph()
9+
module_edges_all = []
10+
sub_edges_all = []
1111
for module in modules_entities:
12+
_module = os.path.basename(module)
13+
module_edges = []
1214
for entity in modules_entities[module]:
13-
module_edges.append((os.path.basename(module), entity))
15+
sub_edges = []
16+
module_edges.append((_module, entity))
1417
for dep in modules_entities[module][entity]:
1518
if '.' in dep:
1619
dep = dep.split('.')[1].replace('.', '.py')
1720
sub_edges.append((entity, dep))
18-
gr.add_edges_from(module_edges)
19-
gr.add_edges_from(sub_edges)
20-
pos = nx.spring_layout(gr)
21-
nx.draw(gr, pos, font_size=12, with_labels=False)
21+
G.add_edges_from(sub_edges)
22+
sub_edges_all += sub_edges
23+
if not modules_entities[module]:
24+
G.add_node(_module)
25+
G.add_edges_from(module_edges)
26+
module_edges_all += module_edges
27+
pos = nx.spring_layout(G)
28+
module_list = [os.path.basename(module) for module in modules_entities]
29+
module_list_labels = {module_name: module_name for module_name in module_list}
30+
31+
entities_labels = {edge[1]: edge[1] for edge in module_edges_all}
32+
nx.draw_networkx_nodes(G, pos, nodelist=module_list,
33+
node_color='#009c2c', node_size=800, node_shape="s", alpha=0.8)
34+
nx.draw(G, pos, node_color='#009c2c', arrows=False, edge_color='#ffffff', node_shape="o", alpha=0.8)
35+
36+
nx.draw_networkx_labels(G, pos, labels=module_list_labels, font_weight='bold', font_size=11)
37+
nx.draw_networkx_labels(G, pos, labels=entities_labels, font_weight='bold', font_family='Arial', font_size=10)
38+
nx.draw_networkx_edges(G, pos, edgelist=module_edges_all,
39+
edge_color='#009c2c', width=2,
40+
arrows=False, style="dashed", node_size=50)
41+
nx.draw_networkx_edges(G, pos, edgelist=sub_edges_all,
42+
edge_color='r', width=2,
43+
arrows=False, style="dashed")
2244
for p in pos: # raise text positions
2345
pos[p][1] += 0.07
24-
nx.draw_networkx_labels(gr, pos)
2546
plt.show()

pyproject.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ pytest = "^3.0"
2020
[tool.poetry.scripts]
2121
cg = 'codegraph.main:cli'
2222

23+
[tool.poetry-dynamic-versioning]
24+
enable = true
25+
vcs = "git"
26+
style = "pep440"
27+
2328
[build-system]
2429
requires = ["poetry>=0.12"]
2530
build-backend = "poetry.masonry.api"

0 commit comments

Comments
 (0)