1 """Pylint plugin for cubicweb""" |
1 """Pylint plugin to analyse cubicweb cubes |
2 |
2 |
3 from astroid import MANAGER, InferenceError, nodes, scoped_nodes, ClassDef, FunctionDef |
3 Done: |
|
4 * turn functions decorated by @objectify_predicate into classes |
|
5 * add yams base types to yams.buildobjs module |
|
6 * add data() function to uiprops module's namespace |
|
7 * avoid 'abstract method not implemented' for `cell_call`, `entity_call`, `render_body` |
|
8 |
|
9 TODO: |
|
10 * avoid invalid class name for predicates and predicates |
|
11 * W:188, 0: Method '__lt__' is abstract in class 'Entity' but is not overridden (abstract-method) |
|
12 * generate entity attributes from the schema? |
|
13 """ |
|
14 |
|
15 from astroid import MANAGER, InferenceError, nodes, ClassDef, FunctionDef |
4 from astroid.builder import AstroidBuilder |
16 from astroid.builder import AstroidBuilder |
|
17 |
5 from pylint.checkers.utils import unimplemented_abstract_methods, class_is_abstract |
18 from pylint.checkers.utils import unimplemented_abstract_methods, class_is_abstract |
6 |
19 |
7 |
20 |
8 def turn_function_to_class(node): |
21 def turn_function_to_class(node): |
9 """turn a Function node into a Class node (in-place)""" |
22 """turn a Function node into a Class node (in-place)""" |
10 node.__class__ = ClassDef |
23 node.__class__ = ClassDef |
11 node.bases = () |
24 node.bases = () |
|
25 # mark class as a new style class |
|
26 node._newstyle = True |
12 # remove return nodes so that we don't get warned about 'return outside |
27 # remove return nodes so that we don't get warned about 'return outside |
13 # function' by pylint |
28 # function' by pylint |
14 for rnode in node.nodes_of_class(nodes.Return): |
29 for rnode in node.nodes_of_class(nodes.Return): |
15 rnode.parent.body.remove(rnode) |
30 rnode.parent.body.remove(rnode) |
|
31 # add __init__ method to avoid no-init |
|
32 |
16 # that seems to be enough :) |
33 # that seems to be enough :) |
17 |
34 |
18 |
35 |
19 def cubicweb_transform(module): |
36 def cubicweb_transform(module): |
20 # handle objectify_predicate decorator (and its former name until bw compat |
37 # handle objectify_predicate decorator (and its former name until bw compat |
36 # add yams base types into 'yams.buildobjs', astng doesn't grasp globals() |
53 # add yams base types into 'yams.buildobjs', astng doesn't grasp globals() |
37 # magic in there |
54 # magic in there |
38 if module.name == 'yams.buildobjs': |
55 if module.name == 'yams.buildobjs': |
39 from yams import BASE_TYPES |
56 from yams import BASE_TYPES |
40 for etype in BASE_TYPES: |
57 for etype in BASE_TYPES: |
41 module.locals[etype] = [scoped_nodes.Class(etype, None)] |
58 module.locals[etype] = [ClassDef(etype, None)] |
42 # add data() to uiprops module |
59 # add data() to uiprops module |
43 if module.name.split('.')[-1] == 'uiprops': |
60 elif module.name.split('.')[-1] == 'uiprops': |
44 fake = AstroidBuilder(MANAGER).string_build(''' |
61 fake = AstroidBuilder(MANAGER).string_build(''' |
45 def data(string): |
62 def data(string): |
46 return u'' |
63 return u'' |
47 ''') |
64 ''') |
48 module.locals['data'] = fake.locals['data'] |
65 module.locals['data'] = fake.locals['data'] |
58 methods = sorted( |
75 methods = sorted( |
59 unimplemented_abstract_methods(classdef, is_abstract).items(), |
76 unimplemented_abstract_methods(classdef, is_abstract).items(), |
60 key=lambda item: item[0], |
77 key=lambda item: item[0], |
61 ) |
78 ) |
62 |
79 |
63 def dummy_method(): |
80 dummy_method = AstroidBuilder(MANAGER).string_build(''' |
64 """""" |
81 def dummy_method(self): |
|
82 """""" |
|
83 ''') |
|
84 |
65 for name, method in methods: |
85 for name, method in methods: |
66 owner = method.parent.frame() |
86 owner = method.parent.frame() |
67 if owner is classdef: |
87 if owner is classdef: |
68 continue |
88 continue |
69 if name not in classdef.locals: |
89 if name not in classdef.locals: |
70 if name in ('entity_call', 'render_body'): |
90 if name in ('cell_call', 'entity_call', 'render_body'): |
71 classdef.set_local(name, dummy_method) |
91 classdef.set_local(name, dummy_method) |
72 |
92 |
73 |
93 |
74 def register(linter): |
94 def register(linter): |
75 """called when loaded by pylint --load-plugins, nothing to do here""" |
95 """called when loaded by pylint --load-plugins, nothing to do here""" |
76 MANAGER.register_transform(nodes.Module, cubicweb_transform) |
96 MANAGER.register_transform(nodes.Module, cubicweb_transform) |
77 MANAGER.register_transform(nodes.ClassDef, cubicweb_abstractmethods_transform) |
97 MANAGER.register_transform(ClassDef, cubicweb_abstractmethods_transform) |