Commit 7cb76722 authored by Joseph Weston's avatar Joseph Weston

fix discretizer packaging and optional testing

Previously the testing/importing 'continuum' would fail if sympy was not
installed. Now we do the following:

    * add sympy as an optional dependency in 'extras_require'
    * force pytest to ignore tests in packages that have uninstalled
      dependencies by defining a hook in 'conftest.py'
    * use the 'class as a module' hack when importing 'continuum'.
      When sympy is not installed the continuum module will be replaced
      with an instance of 'ExtensionUnavailable' that will raise a runtime
      error on attribute access.
    * no warning is raised if sympy is not installed (it is an
      optional dependency).
parent b5c22bf4
Pipeline #3034 passed with stages
in 25 minutes and 10 seconds
# Copyright 2011-2017 Kwant authors.
#
# This file is part of Kwant. It is subject to the license terms in the file
# LICENSE.rst found in the top-level directory of this distribution and at
# http://kwant-project.org/license. A list of Kwant authors can be found in
# the file AUTHORS.rst at the top-level directory of this distribution and at
# http://kwant-project.org/authors.
"""Pytest plugin to ignore packages that have uninstalled dependencies.
This ignores packages on test collection, which is required when the
tests reside in a package that itself requires the dependency to be
installed.
"""
import importlib
# map from subpackage to sequence of dependency module names
subpackage_dependencies = {
'kwant/continuum': ['sympy']
}
# map from subpackage to sequence of dependency modules that are not installed
dependencies_not_installed = {}
for package, dependencies in subpackage_dependencies.items():
not_installed = []
for dep in dependencies:
try:
importlib.import_module(dep)
except ImportError:
not_installed.append(dep)
if len(not_installed) != 0:
dependencies_not_installed[package] = not_installed
def pytest_ignore_collect(path, config):
for subpackage, not_installed in dependencies_not_installed.items():
if subpackage in path.strpath:
print('ignoring {} because the following dependencies are not '
'installed: {}'.format(subpackage, ', '.join(not_installed)))
return True
......@@ -31,7 +31,7 @@ __all__.extend(['KwantDeprecationWarning', 'UserCodeError'])
from ._common import version as __version__
for module in ['system', 'builder', 'lattice', 'solvers', 'digest', 'rmt',
'operator', 'kpm', 'wraparound']:
'operator', 'kpm', 'wraparound', 'continuum']:
exec('from . import {0}'.format(module))
__all__.append(module)
......@@ -63,12 +63,3 @@ def test(verbose=True):
"-s"] + (['-v'] if verbose else []))
test.__test__ = False
# Exposing discretizer
try:
from .continuum import discretize
__all__.extend(['discretize'])
except ImportError:
warnings.warn('Discretizer module not available. Is sympy installed?',
RuntimeWarning)
......@@ -107,6 +107,30 @@ class UserCodeError(Exception):
pass
class ExtensionUnavailable:
"""Class that replaces unavailable extension modules in the Kwant namespace.
Some extensions for Kwant (e.g. 'kwant.continuum') require additional
dependencies that are not required for core functionality. When the
additional dependencies are not installed an instance of this class will
be inserted into Kwant's root namespace to simulate the presence of the
extension and inform users that they need to install additional
dependencies.
See https://mail.python.org/pipermail/python-ideas/2012-May/014969.html
for more details.
"""
def __init__(self, name, dependencies):
self.name = name
self.dependencies = ', '.join(dependencies)
def __getattr__(self, _):
msg = ("'{}' is not available because one or more of the following "
"dependencies are not installed: {}")
raise RuntimeError(msg.format(self.name, self.dependencies))
def ensure_isinstance(obj, typ, msg=None):
if isinstance(obj, typ):
return
......
......@@ -6,10 +6,18 @@
# the file AUTHORS.rst at the top-level directory of this distribution and at
# http://kwant-project.org/authors.
from .discretizer import discretize, discretize_symbolic, build_discretized
from ._common import momentum_operators, position_operators
from ._common import sympify, lambdify, make_commutative
import sys
from .._common import ExtensionUnavailable
try:
from .discretizer import discretize, discretize_symbolic, build_discretized
from ._common import momentum_operators, position_operators
from ._common import sympify, lambdify, make_commutative
except ImportError:
sys.modules[__name__] = ExtensionUnavailable(__name__, ('sympy',))
del sys, ExtensionUnavailable
__all__ = ['discretize', 'discretize_symbolic', 'build_discretized',
'momentum_operators', 'position_operators', 'sympify',
......
......@@ -646,7 +646,11 @@ def main():
'test': test},
ext_modules=exts,
install_requires=['numpy > 1.6.1', 'scipy >= 0.11.0', 'tinyarray'],
extras_require={'plotting': 'matplotlib >= 1.2'},
extras_require={
'plotting': 'matplotlib >= 1.2',
# Ubuntu 16.04 is the oldest supported distro with python3-sympy
'continuum': 'sympy >= 0.7.6',
},
classifiers=[c.strip() for c in classifiers.split('\n')])
if __name__ == '__main__':
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment