injectify.utils   A
last analyzed

Complexity

Total Complexity 16

Size/Duplication

Total Lines 62
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 46
dl 0
loc 62
rs 10
c 0
b 0
f 0
wmc 16

4 Functions

Rating   Name   Duplication   Size   Complexity  
C get_defining_class() 0 23 9
A tryattrs() 0 9 3
A caninject() 0 6 1
A parse_object() 0 8 3
1
"""This module contains the utility functions that power Injectify."""
2
3
import ast
4
import inspect
5
from textwrap import dedent
6
7
from .inspect_mate import getsource
8
9
10
def parse_object(obj):
11
    """Parse the source into an AST node."""
12
    source = getsource(obj)
13
    for _ in range(5):
14
        try:
15
            return ast.parse(source)
16
        except IndentationError:
17
            source = dedent(source)
18
19
20
def get_defining_class(obj):
21
    """Return the class that defines the given object or ``None`` if there is
22
    no class."""
23
    if inspect.ismethod(obj):
24
        for cls in inspect.getmro(obj.__self__.__class__):
25
            if cls.__dict__.get(obj.__name__) is obj:
26
                return cls
27
        obj = obj.__func__  # fallback to __qualname__ parsing
28
    if inspect.isfunction(obj):
29
        class_name = obj.__qualname__.split('.<locals>', 1)[0].rsplit('.', 1)[0]
30
        try:
31
            cls = getattr(inspect.getmodule(obj), class_name)
32
        except AttributeError:
33
            cls = obj.__globals__.get(class_name)
34
        if isinstance(cls, type):
35
            return cls
36
    if inspect.isclass(obj):
37
        class_path = obj.__qualname__.split('.<locals>', 1)[0].rsplit('.', 1)
38
        parent = inspect.getmodule(obj)
39
        for p in class_path[:-1]:
40
            parent = getattr(parent, p)
41
        return parent
42
    return getattr(obj, '__objclass__', None)  # handle special descriptor objects
43
44
45
def tryattrs(obj, *attrs):
46
    """Return the first value of the named attributes found of the given object."""
47
    for attr in attrs:
48
        try:
49
            return getattr(obj, attr)
50
        except AttributeError:
51
            pass
52
    obj_name = obj.__name__
53
    raise AttributeError("'{}' object has no attribute in {}", obj_name, attrs)
54
55
56
def caninject(obj):
57
    """Check whether the given object can be injected with code."""
58
    return not (inspect.ismodule(obj)
59
                or inspect.isclass(obj)
60
                or inspect.ismethod(obj)
61
                or inspect.isfunction(obj))
62