OLD | NEW |
1 # pylint: disable=W0611 | 1 # pylint: disable=W0611 |
2 # | 2 # |
3 # Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). | 3 # Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). |
4 # http://www.logilab.fr/ -- mailto:contact@logilab.fr | 4 # http://www.logilab.fr/ -- mailto:contact@logilab.fr |
5 # | 5 # |
6 # This program is free software; you can redistribute it and/or modify it under | 6 # This program is free software; you can redistribute it and/or modify it under |
7 # the terms of the GNU General Public License as published by the Free Software | 7 # the terms of the GNU General Public License as published by the Free Software |
8 # Foundation; either version 2 of the License, or (at your option) any later | 8 # Foundation; either version 2 of the License, or (at your option) any later |
9 # version. | 9 # version. |
10 # | 10 # |
(...skipping 16 matching lines...) Expand all Loading... |
27 from logilab.common.compat import builtins | 27 from logilab.common.compat import builtins |
28 | 28 |
29 BUILTINS_NAME = builtins.__name__ | 29 BUILTINS_NAME = builtins.__name__ |
30 COMP_NODE_TYPES = astroid.ListComp, astroid.SetComp, astroid.DictComp, astroid.G
enExpr | 30 COMP_NODE_TYPES = astroid.ListComp, astroid.SetComp, astroid.DictComp, astroid.G
enExpr |
31 PY3K = sys.version_info[0] == 3 | 31 PY3K = sys.version_info[0] == 3 |
32 | 32 |
33 if not PY3K: | 33 if not PY3K: |
34 EXCEPTIONS_MODULE = "exceptions" | 34 EXCEPTIONS_MODULE = "exceptions" |
35 else: | 35 else: |
36 EXCEPTIONS_MODULE = "builtins" | 36 EXCEPTIONS_MODULE = "builtins" |
| 37 ABC_METHODS = set(('abc.abstractproperty', 'abc.abstractmethod', |
| 38 'abc.abstractclassmethod', 'abc.abstractstaticmethod')) |
37 | 39 |
38 | 40 |
39 class NoSuchArgumentError(Exception): | 41 class NoSuchArgumentError(Exception): |
40 pass | 42 pass |
41 | 43 |
42 def is_inside_except(node): | 44 def is_inside_except(node): |
43 """Returns true if node is inside the name of an except handler.""" | 45 """Returns true if node is inside the name of an except handler.""" |
44 current = node | 46 current = node |
45 while current and not isinstance(current.parent, astroid.ExceptHandler): | 47 while current and not isinstance(current.parent, astroid.ExceptHandler): |
46 current = current.parent | 48 current = current.parent |
(...skipping 445 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
492 if isinstance(infered, astroid.Class): | 494 if isinstance(infered, astroid.Class): |
493 if (infered.root().name == BUILTINS_NAME and | 495 if (infered.root().name == BUILTINS_NAME and |
494 infered.name == 'property'): | 496 infered.name == 'property'): |
495 return True | 497 return True |
496 for ancestor in infered.ancestors(): | 498 for ancestor in infered.ancestors(): |
497 if (ancestor.name == 'property' and | 499 if (ancestor.name == 'property' and |
498 ancestor.root().name == BUILTINS_NAME): | 500 ancestor.root().name == BUILTINS_NAME): |
499 return True | 501 return True |
500 except astroid.InferenceError: | 502 except astroid.InferenceError: |
501 pass | 503 pass |
| 504 |
| 505 |
| 506 def decorated_with_abc(func): |
| 507 """Determine if the `func` node is decorated with `abc` decorators.""" |
| 508 if func.decorators: |
| 509 for node in func.decorators.nodes: |
| 510 try: |
| 511 infered = next(node.infer()) |
| 512 except astroid.InferenceError: |
| 513 continue |
| 514 if infered and infered.qname() in ABC_METHODS: |
| 515 return True |
| 516 |
| 517 |
| 518 def unimplemented_abstract_methods(node, is_abstract_cb=decorated_with_abc): |
| 519 """ |
| 520 Get the unimplemented abstract methods for the given *node*. |
| 521 |
| 522 A method can be considered abstract if the callback *is_abstract_cb* |
| 523 returns a ``True`` value. The check defaults to verifying that |
| 524 a method is decorated with abstract methods. |
| 525 The function will work only for new-style classes. For old-style |
| 526 classes, it will simply return an empty dictionary. |
| 527 For the rest of them, it will return a dictionary of abstract method |
| 528 names and their inferred objects. |
| 529 """ |
| 530 visited = {} |
| 531 try: |
| 532 mro = reversed(node.mro()) |
| 533 except NotImplementedError: |
| 534 # Old style class, it will not have a mro. |
| 535 return {} |
| 536 except astroid.ResolveError: |
| 537 # Probably inconsistent hierarchy, don'try |
| 538 # to figure this out here. |
| 539 return {} |
| 540 for ancestor in mro: |
| 541 for obj in ancestor.values(): |
| 542 infered = obj |
| 543 if isinstance(obj, astroid.AssName): |
| 544 infered = safe_infer(obj) |
| 545 if not infered: |
| 546 continue |
| 547 if not isinstance(infered, astroid.Function): |
| 548 if obj.name in visited: |
| 549 del visited[obj.name] |
| 550 if isinstance(infered, astroid.Function): |
| 551 # It's critical to use the original name, |
| 552 # since after inferring, an object can be something |
| 553 # else than expected, as in the case of the |
| 554 # following assignment. |
| 555 # |
| 556 # class A: |
| 557 # def keys(self): pass |
| 558 # __iter__ = keys |
| 559 abstract = is_abstract_cb(infered) |
| 560 if abstract: |
| 561 visited[obj.name] = infered |
| 562 elif not abstract and obj.name in visited: |
| 563 del visited[obj.name] |
| 564 return visited |
OLD | NEW |