Skip to content

Commit d594a0e

Browse files
authored
Merge pull request #442 from blakegong/features/add-dotted-arguments-for-lazy-import
Improve lazy_import() to accept dotted_attributes
2 parents d4d8a76 + 3822d65 commit d594a0e

File tree

2 files changed

+51
-5
lines changed

2 files changed

+51
-5
lines changed

graphene/utils/module_loading.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22
from importlib import import_module
33

44

5-
def import_string(dotted_path):
5+
def import_string(dotted_path, dotted_attributes=None):
66
"""
77
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.
912
"""
1013
try:
1114
module_path, class_name = dotted_path.rsplit('.', 1)
@@ -15,12 +18,27 @@ def import_string(dotted_path):
1518
module = import_module(module_path)
1619

1720
try:
18-
return getattr(module, class_name)
21+
result = getattr(module, class_name)
1922
except AttributeError:
2023
raise ImportError('Module "%s" does not define a "%s" attribute/class' % (
2124
module_path, class_name)
2225
)
2326

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+
))
2441

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)

graphene/utils/tests/test_module_loading.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
from pytest import raises
22

33
from graphene import String
4+
from graphene.types.objecttype import ObjectTypeMeta
45
from ..module_loading import lazy_import, import_string
56

67

78
def test_import_string():
89
MyString = import_string('graphene.String')
910
assert MyString == String
1011

12+
MyObjectTypeMeta = import_string('graphene.ObjectType', '__class__')
13+
assert MyObjectTypeMeta == ObjectTypeMeta
14+
1115

1216
def test_import_string_module():
1317
with raises(Exception) as exc_info:
@@ -23,7 +27,31 @@ def test_import_string_class():
2327
assert str(exc_info.value) == 'Module "graphene" does not define a "Stringa" attribute/class'
2428

2529

30+
def test_import_string_attributes():
31+
with raises(Exception) as exc_info:
32+
import_string('graphene.String', 'length')
33+
34+
assert str(exc_info.value) == 'Module "graphene" does not define a "length" attribute inside attribute/class ' \
35+
'"String"'
36+
37+
with raises(Exception) as exc_info:
38+
import_string('graphene.ObjectType', '__class__.length')
39+
40+
assert str(exc_info.value) == 'Module "graphene" does not define a "__class__.length" attribute inside ' \
41+
'attribute/class "ObjectType"'
42+
43+
with raises(Exception) as exc_info:
44+
import_string('graphene.ObjectType', '__classa__.__base__')
45+
46+
assert str(exc_info.value) == 'Module "graphene" does not define a "__classa__" attribute inside attribute/class ' \
47+
'"ObjectType"'
48+
49+
2650
def test_lazy_import():
2751
f = lazy_import('graphene.String')
2852
MyString = f()
2953
assert MyString == String
54+
55+
f = lazy_import('graphene.ObjectType', '__class__')
56+
MyObjectTypeMeta = f()
57+
assert MyObjectTypeMeta == ObjectTypeMeta

0 commit comments

Comments
 (0)