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