Skip to content

Commit 4d7fec5

Browse files
committed
NF - add routines for optional packages
From dipy.
1 parent 64720a3 commit 4d7fec5

File tree

2 files changed

+134
-0
lines changed

2 files changed

+134
-0
lines changed

nibabel/optpkg.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
""" Routines to support optional packages """
2+
3+
try:
4+
import nose
5+
except ImportError:
6+
have_nose = False
7+
else:
8+
have_nose = True
9+
10+
from .tripwire import TripWire, is_tripwire
11+
12+
def optional_package(name, trip_msg=None):
13+
""" Return package-like thing and module setup for package `name`
14+
15+
Parameters
16+
----------
17+
name : str
18+
package name
19+
trip_msg : None or str
20+
message to give when someone tries to use the return package, but we
21+
could not import it, and have returned a TripWire object instead.
22+
Default message if None.
23+
24+
Returns
25+
-------
26+
pkg_like : module or ``TripWire`` instance
27+
If we can import the package, return it. Otherwise return an object
28+
raising an error when accessed
29+
have_pkg : bool
30+
True if import for package was successful, false otherwise
31+
module_setup : function
32+
callable usually set as ``setup_module`` in calling namespace, to allow
33+
skipping tests.
34+
35+
Example
36+
-------
37+
Typical use would be something like this at the top of a module using an
38+
optional package:
39+
40+
>>> from nipy.utils.optpkg import optional_package
41+
>>> pkg, have_pkg, setup_module = optional_package('not_a_package')
42+
43+
Of course in this case the package doesn't exist, and so, in the module:
44+
45+
>>> have_pkg
46+
False
47+
48+
and
49+
50+
>>> pkg.some_function()
51+
Traceback (most recent call last):
52+
...
53+
TripWireError: We need package not_a_package for these functions, but ``import not_a_package`` raised an ImportError
54+
55+
If the module does exist - we get the module
56+
57+
>>> pkg, _, _ = optional_package('os')
58+
>>> hasattr(pkg, 'path')
59+
True
60+
61+
Or a submodule if that's what we asked for
62+
63+
>>> subpkg, _, _ = optional_package('os.path')
64+
>>> hasattr(subpkg, 'dirname')
65+
True
66+
"""
67+
# fromlist=[''] results in submodule being returned, rather than the top
68+
# level module. See help(__import__)
69+
try:
70+
pkg = __import__(name, fromlist=[''])
71+
except ImportError:
72+
pass
73+
else: # import worked
74+
# top level module
75+
return pkg, True, lambda : None
76+
if trip_msg is None:
77+
trip_msg = ('We need package %s for these functions, but '
78+
'``import %s`` raised an ImportError'
79+
% (name, name))
80+
pkg = TripWire(trip_msg)
81+
def setup_module():
82+
if have_nose:
83+
raise nose.plugins.skip.SkipTest('No %s for these tests'
84+
% name)
85+
return pkg, False, setup_module
86+

nibabel/tripwire.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
""" Class to raise error for missing modules or other misfortunes
2+
"""
3+
4+
class TripWireError(Exception):
5+
""" Exception if trying to use TripWire object """
6+
7+
8+
def is_tripwire(obj):
9+
""" Returns True if `obj` appears to be a TripWire object
10+
11+
Examples
12+
--------
13+
>>> is_tripwire(object())
14+
False
15+
>>> is_tripwire(TripWire('some message'))
16+
True
17+
"""
18+
try:
19+
obj.any_attribute
20+
except TripWireError:
21+
return True
22+
except:
23+
pass
24+
return False
25+
26+
27+
class TripWire(object):
28+
""" Class raising error if used
29+
30+
Standard use is to proxy modules that we could not import
31+
32+
Examples
33+
--------
34+
>>> try:
35+
... import silly_module_name
36+
... except ImportError:
37+
... silly_module_name = TripWire('We do not have silly_module_name')
38+
>>> silly_module_name.do_silly_thing('with silly string')
39+
Traceback (most recent call last):
40+
...
41+
TripWireError: We do not have silly_module_name
42+
"""
43+
def __init__(self, msg):
44+
self._msg = msg
45+
46+
def __getattr__(self, attr_name):
47+
''' Raise informative error accessing attributes '''
48+
raise TripWireError(self._msg)

0 commit comments

Comments
 (0)