88
99import importlib
1010from types import ModuleType
11- from typing import Callable , Optional , Union
11+ from typing import Callable , Optional , TypeVar , Union
1212
1313
1414def load_module (path : str ) -> Union [ModuleType , Optional [Callable [..., object ]]]:
1515 """
1616 Loads and returns the module/module attr represented by the ``path``: ``full.module.path:optional_attr``
1717
18- ::
19-
20-
2118 1. ``load_module("this.is.a_module:fn")`` -> equivalent to ``this.is.a_module.fn``
2219 1. ``load_module("this.is.a_module")`` -> equivalent to ``this.is.a_module``
2320 """
@@ -33,3 +30,36 @@ def load_module(path: str) -> Union[ModuleType, Optional[Callable[..., object]]]
3330 return getattr (module , method ) if method else module
3431 except Exception :
3532 return None
33+
34+
35+ T = TypeVar ("T" )
36+
37+
38+ def import_attr (name : str , attr : str , default : T ) -> T :
39+ """
40+ Imports ``name.attr`` and returns it if the module is found.
41+ Otherwise, returns the specified ``default``.
42+ Useful when getting an attribute from an optional dependency.
43+
44+ Note that the ``default`` parameter is intentionally not an optional
45+ since this function is intended to be used with modules that may not be
46+ installed as a dependency. Therefore the caller must ALWAYS provide a
47+ sensible default.
48+
49+ Usage:
50+
51+ .. code-block:: python
52+
53+ aws_resources = import_attr("torchx.specs.named_resources_aws", "NAMED_RESOURCES", default={})
54+ all_resources.update(aws_resources)
55+
56+ Raises:
57+ AttributeError: If the module exists (e.g. can be imported)
58+ but does not have an attribute with name ``attr``.
59+ """
60+ try :
61+ mod = importlib .import_module (name )
62+ except ModuleNotFoundError :
63+ return default
64+ else :
65+ return getattr (mod , attr )
0 commit comments