diff --git a/MANIFEST.in b/MANIFEST.in index 944ebbada4661ba0768179e055121b922de73dfa..400dae6498e2413550e5e5b3b5252d1dc8bcd939 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -5,6 +5,10 @@ include version recursive-include spams_wrap *.h recursive-include spams_wrap *.cpp +include spams/version + +recursive-include spams/tests *.py +recursive-include spams/data *.png recursive-include tests *.py recursive-include data *.png recursive-include doc * diff --git a/README.md b/README.md index a04c1b805b1ab3754a58d529373f82a5d891d3d6..3f49b0905465c347617cf8ac86e5090e90401ddd 100644 --- a/README.md +++ b/README.md @@ -49,9 +49,26 @@ Manipulated objects are imported from numpy and scipy. Matrices should be stored ### Testing the interface +- From the command line (to be called from the project root directory): ```bash -python tests/test_spams.py -h # to get help -python tests/test_spams.py # will run all the tests +python tests/test_spams.py -h # print the man page +python tests/test_spams.py # run all the tests +``` + +- From Python (assuming `spams` package is installed): +```python +from spams.tests import test_spams + +test_spams('-h') # print the man page +test_spams() # run all tests +test_spams(['sort', 'calcAAt']) # run specific tests +test_spams(python_exec='python3') # specify the python exec +``` + +- From the command line (assuming `spams` package is installed): +```bash +# c.f. previous point for the different options +python -c "from spams.tests import test_spams; test_spams()" ``` --- diff --git a/setup.py b/setup.py index 8344486396d8c6243987687711ec08ee09e10b89..561409a5300176b0af867c4037e925cf7e6b01ee 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ import os # import platform import sys -from setuptools import setup, Extension, find_packages +from setuptools import setup, Extension from setuptools.command.build_ext import build_ext from distutils.sysconfig import get_python_inc @@ -193,18 +193,12 @@ opts = dict( python_requires='>=3', install_requires=['Cython>=0.29', 'numpy>=1.12', 'Pillow>=6.0', 'scipy>=1.0', 'six>=1.12'], - packages=find_packages(), + packages=['myscipy_rand', 'spams_wrap', 'spams', 'spams.tests'], cmdclass={'build_ext': CustomBuildExtCommand}, ext_modules=get_extension(), - data_files=[('tests', ['tests/test_spams.py', - 'tests/test_decomp.py', - 'tests/test_dictLearn.py', - 'tests/test_linalg.py', - 'tests/test_prox.py', - 'tests/test_utils.py']), - ('doc', ['doc/doc_spams.pdf']), - ('tests', ['data/boat.png', 'data/lena.png'])], - include_package_data=True, + package_data={ + "spams": ["data/*.png", "version"] + }, zip_safe=True ) diff --git a/spams/data b/spams/data new file mode 120000 index 0000000000000000000000000000000000000000..4909e06efb479a01e44e67265074c726796f4959 --- /dev/null +++ b/spams/data @@ -0,0 +1 @@ +../data \ No newline at end of file diff --git a/spams/tests b/spams/tests new file mode 120000 index 0000000000000000000000000000000000000000..6dd24e02b51335a68a3fcb605ac7754f9435ec66 --- /dev/null +++ b/spams/tests @@ -0,0 +1 @@ +../tests \ No newline at end of file diff --git a/spams/version b/spams/version new file mode 120000 index 0000000000000000000000000000000000000000..1748d5b2e856f6c5a64d3bbda9a7cf0754d3f96a --- /dev/null +++ b/spams/version @@ -0,0 +1 @@ +../version \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..23aa21946bed4f5b6c7ee5fc3251f2f247d968ee --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +from .run import test_spams diff --git a/tests/run.py b/tests/run.py new file mode 100644 index 0000000000000000000000000000000000000000..1838ac7d4b5202d95ed74b983ea8aeb603221234 --- /dev/null +++ b/tests/run.py @@ -0,0 +1,59 @@ +import subprocess +import os + +def test_spams(args=None, python_exec = 'python'): + """Run spams-python tests from python + + To run the tests, from the command line, please check the README.md file + + Input: + args (string|string list): argument to pass to the the main test script, + use `args='-h'` for more details. If None (default), all tests are + run. + python_exec (string): python executable. By default, it is `'python'`. + Python 3+ is required. On some system, you may have to set + `python_exec = 'python3'`. + """ + + # check spams availability + try: + import spams + except: + raise ModuleNotFoundError("No module named 'spams'") + + # main test file + test_file = os.path.join("tests", "test_spams.py") + if os.path.isfile(test_file): + test_file = os.path.abspath(test_file) + else: + test_file = os.path.join( + os.path.dirname(os.path.abspath(spams.__file__)), test_file + ) + + # test dir + test_dir = os.path.dirname(test_file) + + # check python executable + try: + python_version = subprocess.run([python_exec, "-V"], capture_output=True) + except: + raise SystemError(f"{python_exec} is not a valid python executable") + # check python version + if not "Python 3." in python_version.stdout.decode('UTF-8'): + raise SystemError("Python 3+ is required, try using python_exec = 'python3'") + + # check args + if args is None: + args = [] + elif not (isinstance(args, str) or \ + (isinstance(args, list) and \ + all(isinstance(arg, str) for arg in args))): + raise TypeError("'args' input should be a string or a list of strings.") + + if isinstance(args, str): + args = [args] + + # run + subprocess.run([python_exec, 'test_spams.py'] + args, cwd = test_dir) + + diff --git a/tests/test_dictLearn.py b/tests/test_dictLearn.py index 88714a0a7bf624a321e7f109bafb60e1a25e6a48..c6ea44a460f9646ad4da8d02c427e64cd8744374 100644 --- a/tests/test_dictLearn.py +++ b/tests/test_dictLearn.py @@ -1,6 +1,7 @@ from __future__ import absolute_import, division, print_function import sys +import os import numpy as np import scipy import scipy.sparse as ssp @@ -15,6 +16,33 @@ if not ('rand' in ssp.__dict__): ssprand = myscipy_rand.rand else: ssprand = ssp.rand + + +def get_img_file_path(img): + """Return path to an image file + + Arguments: + img (string): image filename without path among 'boat.png' or + 'lena.png'. + + Output: + img_file (string): normalized path to image input filename. + + """ + # check input + if not img in ["boat.png", "lena.png"]: + raise ValueError("bad input, `img` should be 'boat.png' or 'lena.png'") + # try local file + img_file = os.path.join("data", img) + if os.path.isfile(img_file): + img_file = os.path.abspath(img_file) + else: + # file from install + img_file = os.path.join( + os.path.dirname(os.path.abspath(spams.__file__)), img_file + ) + # output + return img_file def _extract_lasso_param(f_param): @@ -46,8 +74,8 @@ def _objective(X, D, param, imgname=None): def test_trainDL(): - img_file = 'data/boat.png' try: + img_file = get_img_file_path("boat.png") img = Image.open(img_file) except: print("Cannot load image %s : skipping test" % img_file) @@ -140,8 +168,8 @@ def test_trainDL(): def test_trainDL_Memory(): - img_file = 'data/lena.png' try: + img_file = get_img_file_path("lena.png") img = Image.open(img_file) except: print("Cannot load image %s : skipping test" % img_file) @@ -202,8 +230,8 @@ def test_trainDL_Memory(): def test_structTrainDL(): - img_file = 'data/lena.png' try: + img_file = get_img_file_path("lena.png") img = Image.open(img_file) except Exception as e: print("Cannot load image %s (%s) : skipping test" % (img_file, e)) @@ -376,8 +404,8 @@ def test_structTrainDL(): def test_nmf(): - img_file = 'data/boat.png' try: + img_file = get_img_file_path("boat.png") img = Image.open(img_file) except: print("Cannot load image %s : skipping test" % img_file) @@ -413,8 +441,8 @@ def test_nmf(): # Archetypal Analysis, run first steps with FISTA and run last steps with activeSet, def test_archetypalAnalysis(): - img_file = 'data/lena.png' try: + img_file = get_img_file_path("lena.png") img = Image.open(img_file) except Exception as e: print("Cannot load image %s (%s) : skipping test" % (img_file, e)) diff --git a/version b/version index 0ce6c9df5cb1a0caeb7671af7d5fa924298be93c..dacc32b008abee77516e2844a77cf0549fa23780 100644 --- a/version +++ b/version @@ -1 +1 @@ -2.6.5.1 +2.6.5.2