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
|
|
|
|