2
2
from importlib import import_module
3
3
4
4
5
- def import_string (dotted_path ):
5
+ def import_string (dotted_path , dotted_attributes = None ):
6
6
"""
7
7
Import a dotted module path and return the attribute/class designated by the
8
- last name in the path. Raise ImportError if the import failed.
8
+ last name in the path. When a dotted attribute path is also provided, the
9
+ dotted attribute path would be applied to the attribute/class retrieved from
10
+ the first step, and return the corresponding value designated by the
11
+ attribute path. Raise ImportError if the import failed.
9
12
"""
10
13
try :
11
14
module_path , class_name = dotted_path .rsplit ('.' , 1 )
@@ -15,12 +18,27 @@ def import_string(dotted_path):
15
18
module = import_module (module_path )
16
19
17
20
try :
18
- return getattr (module , class_name )
21
+ result = getattr (module , class_name )
19
22
except AttributeError :
20
23
raise ImportError ('Module "%s" does not define a "%s" attribute/class' % (
21
24
module_path , class_name )
22
25
)
23
26
27
+ if not dotted_attributes :
28
+ return result
29
+ else :
30
+ attributes = dotted_attributes .split ('.' )
31
+ traveled_attributes = []
32
+ try :
33
+ for attribute in attributes :
34
+ traveled_attributes .append (attribute )
35
+ result = getattr (result , attribute )
36
+ return result
37
+ except AttributeError :
38
+ raise ImportError ('Module "%s" does not define a "%s" attribute inside attribute/class "%s"' % (
39
+ module_path , '.' .join (traveled_attributes ), class_name
40
+ ))
24
41
25
- def lazy_import (dotted_path ):
26
- return partial (import_string , dotted_path )
42
+
43
+ def lazy_import (dotted_path , dotted_attributes = None ):
44
+ return partial (import_string , dotted_path , dotted_attributes )
0 commit comments