"""Routines to support optional packages."""importimportlibtry:importpytestexceptImportError:have_pytest=Falseelse:have_pytest=TrueclassTripWireError(AttributeError):"""Exception if trying to use TripWire object."""defis_tripwire(obj):"""Return True if `obj` appears to be a TripWire object. Examples -------- >>> is_tripwire(object()) False >>> is_tripwire(TripWire('some message')) True """try:obj.any_attributeexceptTripWireError:returnTrueexceptException:passreturnFalseclassTripWire(object):"""Class raising error if used. Standard use is to proxy modules that we could not import Examples -------- >>> try: ... import silly_module_name ... except ImportError: ... silly_module_name = TripWire('We do not have silly_module_name') >>> silly_module_name.do_silly_thing('with silly string') #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... TripWireError: We do not have silly_module_name """def__init__(self,msg):self._msg=msgdef__getattr__(self,attr_name):"""Raise informative error accessing attributes."""raiseTripWireError(self._msg)def__call__(self,*args,**kwargs):"""Raise informative error while calling."""raiseTripWireError(self._msg)
[docs]defoptional_package(name,trip_msg=None):"""Return package-like thing and module setup for package `name`. Parameters ---------- name : str package name trip_msg : None or str message to give when someone tries to use the return package, but we could not import it, and have returned a TripWire object instead. Default message if None. Returns ------- pkg_like : module or ``TripWire`` instance If we can import the package, return it. Otherwise return an object raising an error when accessed have_pkg : bool True if import for package was successful, false otherwise module_setup : function callable usually set as ``setup_module`` in calling namespace, to allow skipping tests. Examples -------- Typical use would be something like this at the top of a module using an optional package: >>> from fury.optpkg import optional_package >>> pkg, have_pkg, setup_module = optional_package('not_a_package') Of course in this case the package doesn't exist, and so, in the module: >>> have_pkg False and >>> pkg.some_function() #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... TripWireError: We need package not_a_package for these functions, but ``import not_a_package`` raised an ImportError If the module does exist - we get the module >>> pkg, _, _ = optional_package('os') >>> hasattr(pkg, 'path') True Or a submodule if that's what we asked for >>> subpkg, _, _ = optional_package('os.path') >>> hasattr(subpkg, 'dirname') True """try:pkg=importlib.import_module(name)exceptImportError:passelse:# import worked# top level modulereturnpkg,True,lambda:Noneiftrip_msgisNone:trip_msg=('We need package %s for these functions, but ''``import %s`` raised an ImportError'%(name,name))pkg=TripWire(trip_msg)defsetup_module():ifhave_pytest:pytest.mark.skip('No {0} for these tests'.format(name))returnpkg,False,setup_module