|
| 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 | + |
0 commit comments