diff --git a/.coveragerc b/.coveragerc index 57747ec0d8..bcf28e09c2 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,7 +1,7 @@ [run] branch = True -source = nibabel, nisext -include = */nibabel/*, */nisext/* +source = nibabel +include = */nibabel/* omit = */externals/* */benchmarks/* diff --git a/Makefile b/Makefile index 7d4c6666ae..689ad6a75f 100644 --- a/Makefile +++ b/Makefile @@ -233,25 +233,6 @@ bdist_rpm: bdist_mpkg: $(PYTHON) tools/mpkg_wrapper.py setup.py install -# Check for files not installed -check-files: - $(PYTHON) -c 'from nisext.testers import check_files; check_files("nibabel")' - -# Print out info for possible install methods -check-version-info: - $(PYTHON) -c 'from nisext.testers import info_from_here; info_from_here("nibabel")' - -# Run tests from installed code -installed-tests: - $(PYTHON) -c 'from nisext.testers import tests_installed; tests_installed("nibabel")' - -# Run tests from packaged distributions -sdist-tests: - $(PYTHON) -c 'from nisext.testers import sdist_tests; sdist_tests("nibabel", doctests=False)' - -bdist-egg-tests: - $(PYTHON) -c 'from nisext.testers import bdist_egg_tests; bdist_egg_tests("nibabel", doctests=False, label="not script_test")' - sdist-venv: clean rm -rf dist venv unset PYTHONPATH && $(PYTHON) setup.py sdist --formats=zip @@ -260,7 +241,7 @@ sdist-venv: clean mkdir venv/tmp cd venv/tmp && unzip ../../dist/*.zip . venv/bin/activate && cd venv/tmp/nibabel* && python setup.py install - unset PYTHONPATH && . venv/bin/activate && cd venv && nosetests --with-doctest nibabel nisext + unset PYTHONPATH && . venv/bin/activate && cd venv && pytest --doctest-modules --doctest-plus --pyargs nibabel source-release: distclean $(PYTHON) -m compileall . diff --git a/nisext/__init__.py b/nisext/__init__.py deleted file mode 100644 index 6b19d7eb8e..0000000000 --- a/nisext/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# init for sext package -"""Setuptools extensions - -nibabel uses these routines, and houses them, and installs them. nipy-proper -and dipy use them. -""" - -import warnings - -warnings.warn( - """The nisext package is deprecated as of NiBabel 5.0 and will be fully -removed in NiBabel 6.0""" -) diff --git a/nisext/py3builder.py b/nisext/py3builder.py deleted file mode 100644 index 24bd298364..0000000000 --- a/nisext/py3builder.py +++ /dev/null @@ -1,38 +0,0 @@ -"""distutils utilities for porting to python 3 within 2-compatible tree""" - - -try: - from distutils.command.build_py import build_py_2to3 -except ImportError: - # 2.x - no parsing of code - from distutils.command.build_py import build_py -else: # Python 3 - # Command to also apply 2to3 to doctests - from distutils import log - - class build_py(build_py_2to3): - def run_2to3(self, files): - # Add doctest parsing; this stuff copied from distutils.utils in - # python 3.2 source - if not files: - return - fixer_names, options, explicit = (self.fixer_names, self.options, self.explicit) - # Make this class local, to delay import of 2to3 - from lib2to3.refactor import RefactoringTool, get_fixers_from_package - - class DistutilsRefactoringTool(RefactoringTool): - def log_error(self, msg, *args, **kw): - log.error(msg, *args) - - def log_message(self, msg, *args): - log.info(msg, *args) - - def log_debug(self, msg, *args): - log.debug(msg, *args) - - if fixer_names is None: - fixer_names = get_fixers_from_package('lib2to3.fixes') - r = DistutilsRefactoringTool(fixer_names, options=options) - r.refactor(files, write=True) - # Then doctests - r.refactor(files, write=True, doctests_only=True) diff --git a/nisext/sexts.py b/nisext/sexts.py deleted file mode 100644 index b206588dec..0000000000 --- a/nisext/sexts.py +++ /dev/null @@ -1,285 +0,0 @@ -"""Distutils / setuptools helpers""" - -import os -from configparser import ConfigParser -from distutils import log -from distutils.command.build_py import build_py -from distutils.command.install_scripts import install_scripts -from distutils.version import LooseVersion -from os.path import join as pjoin -from os.path import split as psplit -from os.path import splitext - - -def get_comrec_build(pkg_dir, build_cmd=build_py): - """Return extended build command class for recording commit - - The extended command tries to run git to find the current commit, getting - the empty string if it fails. It then writes the commit hash into a file - in the `pkg_dir` path, named ``COMMIT_INFO.txt``. - - In due course this information can be used by the package after it is - installed, to tell you what commit it was installed from if known. - - To make use of this system, you need a package with a COMMIT_INFO.txt file - - e.g. ``myproject/COMMIT_INFO.txt`` - that might well look like this:: - - # This is an ini file that may contain information about the code state - [commit hash] - # The line below may contain a valid hash if it has been substituted during 'git archive' - archive_subst_hash=$Format:%h$ - # This line may be modified by the install process - install_hash= - - The COMMIT_INFO file above is also designed to be used with git substitution - - so you probably also want a ``.gitattributes`` file in the root directory - of your working tree that contains something like this:: - - myproject/COMMIT_INFO.txt export-subst - - That will cause the ``COMMIT_INFO.txt`` file to get filled in by ``git - archive`` - useful in case someone makes such an archive - for example with - via the github 'download source' button. - - Although all the above will work as is, you might consider having something - like a ``get_info()`` function in your package to display the commit - information at the terminal. See the ``pkg_info.py`` module in the nipy - package for an example. - """ - - class MyBuildPy(build_cmd): - """Subclass to write commit data into installation tree""" - - def run(self): - build_cmd.run(self) - import subprocess - - proc = subprocess.Popen( - 'git rev-parse --short HEAD', - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - shell=True, - ) - repo_commit, _ = proc.communicate() - # Fix for python 3 - repo_commit = str(repo_commit) - # We write the installation commit even if it's empty - cfg_parser = ConfigParser() - cfg_parser.read(pjoin(pkg_dir, 'COMMIT_INFO.txt')) - cfg_parser.set('commit hash', 'install_hash', repo_commit) - out_pth = pjoin(self.build_lib, pkg_dir, 'COMMIT_INFO.txt') - cfg_parser.write(open(out_pth, 'wt')) - - return MyBuildPy - - -def _add_append_key(in_dict, key, value): - """Helper for appending dependencies to setuptools args""" - # If in_dict[key] does not exist, create it - # If in_dict[key] is a string, make it len 1 list of strings - # Append value to in_dict[key] list - if key not in in_dict: - in_dict[key] = [] - elif isinstance(in_dict[key], str): - in_dict[key] = [in_dict[key]] - in_dict[key].append(value) - - -# Dependency checks -def package_check( - pkg_name, - version=None, - optional=False, - checker=LooseVersion, - version_getter=None, - messages=None, - setuptools_args=None, -): - """Check if package `pkg_name` is present and has good enough version - - Has two modes of operation. If `setuptools_args` is None (the default), - raise an error for missing non-optional dependencies and log warnings for - missing optional dependencies. If `setuptools_args` is a dict, then fill - ``install_requires`` key value with any missing non-optional dependencies, - and the ``extras_requires`` key value with optional dependencies. - - This allows us to work with and without setuptools. It also means we can - check for packages that have not been installed with setuptools to avoid - installing them again. - - Parameters - ---------- - pkg_name : str - name of package as imported into python - version : {None, str}, optional - minimum version of the package that we require. If None, we don't - check the version. Default is None - optional : bool or str, optional - If ``bool(optional)`` is False, raise error for absent package or wrong - version; otherwise warn. If ``setuptools_args`` is not None, and - ``bool(optional)`` is not False, then `optional` should be a string - giving the feature name for the ``extras_require`` argument to setup. - checker : callable, optional - callable with which to return comparable thing from version - string. Default is ``distutils.version.LooseVersion`` - version_getter : {None, callable}: - Callable that takes `pkg_name` as argument, and returns the - package version string - as in:: - - ``version = version_getter(pkg_name)`` - - If None, equivalent to:: - - mod = __import__(pkg_name); version = mod.__version__`` - messages : None or dict, optional - dictionary giving output messages - setuptools_args : None or dict - If None, raise errors / warnings for missing non-optional / optional - dependencies. If dict fill key values ``install_requires`` and - ``extras_require`` for non-optional and optional dependencies. - """ - setuptools_mode = not setuptools_args is None - optional_tf = bool(optional) - if version_getter is None: - - def version_getter(pkg_name): - mod = __import__(pkg_name) - return mod.__version__ - - if messages is None: - messages = {} - msgs = { - 'missing': 'Cannot import package "%s" - is it installed?', - 'missing opt': 'Missing optional package "%s"', - 'opt suffix': '; you may get run-time errors', - 'version too old': 'You have version %s of package "%s" but we need version >= %s', - } - msgs.update(messages) - status, have_version = _package_status(pkg_name, version, version_getter, checker) - if status == 'satisfied': - return - if not setuptools_mode: - if status == 'missing': - if not optional_tf: - raise RuntimeError(msgs['missing'] % pkg_name) - log.warn(msgs['missing opt'] % pkg_name + msgs['opt suffix']) - return - elif status == 'no-version': - raise RuntimeError(f'Cannot find version for {pkg_name}') - assert status == 'low-version' - if not optional_tf: - raise RuntimeError(msgs['version too old'] % (have_version, pkg_name, version)) - log.warn(msgs['version too old'] % (have_version, pkg_name, version) + msgs['opt suffix']) - return - # setuptools mode - if optional_tf and not isinstance(optional, str): - raise RuntimeError('Not-False optional arg should be string') - dependency = pkg_name - if version: - dependency += '>=' + version - if optional_tf: - if not 'extras_require' in setuptools_args: - setuptools_args['extras_require'] = {} - _add_append_key(setuptools_args['extras_require'], optional, dependency) - else: - _add_append_key(setuptools_args, 'install_requires', dependency) - - -def _package_status(pkg_name, version, version_getter, checker): - try: - __import__(pkg_name) - except ImportError: - return 'missing', None - if not version: - return 'satisfied', None - try: - have_version = version_getter(pkg_name) - except AttributeError: - return 'no-version', None - if checker(have_version) < checker(version): - return 'low-version', have_version - return 'satisfied', have_version - - -BAT_TEMPLATE = r"""@echo off -REM wrapper to use shebang first line of {FNAME} -set mypath=%~dp0 -set pyscript="%mypath%{FNAME}" -set /p line1=<%pyscript% -if "%line1:~0,2%" == "#!" (goto :goodstart) -echo First line of %pyscript% does not start with "#!" -exit /b 1 -:goodstart -set py_exe=%line1:~2% -call "%py_exe%" %pyscript% %* -""" - - -class install_scripts_bat(install_scripts): - """Make scripts executable on Windows - - Scripts are bare file names without extension on Unix, fitting (for example) - Debian rules. They identify as python scripts with the usual ``#!`` first - line. Unix recognizes and uses this first "shebang" line, but Windows does - not. So, on Windows only we add a ``.bat`` wrapper of name - ``bare_script_name.bat`` to call ``bare_script_name`` using the python - interpreter from the #! first line of the script. - - Notes - ----- - See discussion at - https://matthew-brett.github.io/pydagogue/installing_scripts.html and - example at git://github.com/matthew-brett/myscripter.git for more - background. - """ - - def run(self): - install_scripts.run(self) - if not os.name == 'nt': - return - for filepath in self.get_outputs(): - # If we can find an executable name in the #! top line of the script - # file, make .bat wrapper for script. - with open(filepath, 'rt') as fobj: - first_line = fobj.readline() - if not (first_line.startswith('#!') and 'python' in first_line.lower()): - log.info('No #!python executable found, skipping .bat wrapper') - continue - pth, fname = psplit(filepath) - froot, ext = splitext(fname) - bat_file = pjoin(pth, froot + '.bat') - bat_contents = BAT_TEMPLATE.replace('{FNAME}', fname) - log.info(f'Making {bat_file} wrapper for {filepath}') - if self.dry_run: - continue - with open(bat_file, 'wt') as fobj: - fobj.write(bat_contents) - - -class Bunch: - def __init__(self, vars): - for key, name in vars.items(): - if key.startswith('__'): - continue - self.__dict__[key] = name - - -def read_vars_from(ver_file): - """Read variables from Python text file - - Parameters - ---------- - ver_file : str - Filename of file to read - - Returns - ------- - info_vars : Bunch instance - Bunch object where variables read from `ver_file` appear as - attributes - """ - # Use exec for compabibility with Python 3 - ns = {} - with open(ver_file, 'rt') as fobj: - exec(fobj.read(), ns) - return Bunch(ns) diff --git a/nisext/testers.py b/nisext/testers.py deleted file mode 100644 index 07f71af696..0000000000 --- a/nisext/testers.py +++ /dev/null @@ -1,523 +0,0 @@ -"""Test package information in various install settings - -The routines here install the package from source directories, zips or eggs, and -check these installations by running tests, checking version information, -looking for files that were not copied over. - -The typical use for this module is as a Makefile target. For example, here are -the Makefile targets from nibabel:: - - # Check for files not installed - check-files: - $(PYTHON) -c 'from nisext.testers import check_files; check_files("nibabel")' - - # Print out info for possible install methods - check-version-info: - $(PYTHON) -c 'from nisext.testers import info_from_here; info_from_here("nibabel")' - - # Run tests from installed code - installed-tests: - $(PYTHON) -c 'from nisext.testers import tests_installed; tests_installed("nibabel")' - - # Run tests from installed code - sdist-tests: - $(PYTHON) -c 'from nisext.testers import sdist_tests; sdist_tests("nibabel")' - - # Run tests from binary egg - bdist-egg-tests: - $(PYTHON) -c 'from nisext.testers import bdist_egg_tests; bdist_egg_tests("nibabel")' -""" - - -import os -import re -import shutil -import sys -import tempfile -import zipfile -from glob import glob -from os.path import abspath -from os.path import join as pjoin -from subprocess import PIPE, Popen - -NEEDS_SHELL = os.name != 'nt' -PYTHON = sys.executable -HAVE_PUTENV = hasattr(os, 'putenv') - -PY_LIB_SDIR = 'pylib' - - -def back_tick(cmd, ret_err=False, as_str=True): - """Run command `cmd`, return stdout, or stdout, stderr if `ret_err` - - Roughly equivalent to ``check_output`` in Python 2.7 - - Parameters - ---------- - cmd : str - command to execute - ret_err : bool, optional - If True, return stderr in addition to stdout. If False, just return - stdout - as_str : bool, optional - Whether to decode outputs to unicode string on exit. - - Returns - ------- - out : str or tuple - If `ret_err` is False, return stripped string containing stdout from - `cmd`. If `ret_err` is True, return tuple of (stdout, stderr) where - ``stdout`` is the stripped stdout, and ``stderr`` is the stripped - stderr. - - Raises - ------ - RuntimeError - if command returns non-zero exit code. - """ - proc = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=NEEDS_SHELL) - out, err = proc.communicate() - retcode = proc.returncode - if retcode is None: - proc.terminate() - raise RuntimeError(cmd + ' process did not terminate') - if retcode != 0: - raise RuntimeError(cmd + ' process returned code %d' % retcode) - out = out.strip() - if as_str: - out = out.decode('latin-1') - if not ret_err: - return out - err = err.strip() - if as_str: - err = err.decode('latin-1') - return out, err - - -def run_mod_cmd(mod_name, pkg_path, cmd, script_dir=None, print_location=True): - """Run command in own process in anonymous path - - Parameters - ---------- - mod_name : str - Name of module to import - e.g. 'nibabel' - pkg_path : str - directory containing `mod_name` package. Typically that will be the - directory containing the e.g. 'nibabel' directory. - cmd : str - Python command to execute - script_dir : None or str, optional - script directory to prepend to PATH - print_location : bool, optional - Whether to print the location of the imported `mod_name` - - Returns - ------- - stdout : str - stdout as str - stderr : str - stderr as str - """ - if script_dir is None: - paths_add = '' - else: - if not HAVE_PUTENV: - raise RuntimeError('We cannot set environment variables') - # Need to add the python path for the scripts to pick up our package in - # their environment, because the scripts will get called via the shell - # (via `cmd`). Consider that PYTHONPATH may not be set. Because the - # command might run scripts via the shell, prepend script_dir to the - # system path also. - paths_add = r""" -os.environ['PATH'] = r'"{script_dir}"' + os.path.pathsep + os.environ['PATH'] -PYTHONPATH = os.environ.get('PYTHONPATH') -if PYTHONPATH is None: - os.environ['PYTHONPATH'] = r'"{pkg_path}"' -else: - os.environ['PYTHONPATH'] = r'"{pkg_path}"' + os.path.pathsep + PYTHONPATH -""".format( - **locals() - ) - if print_location: - p_loc = f'print({mod_name}.__file__);' - else: - p_loc = '' - cwd = os.getcwd() - tmpdir = tempfile.mkdtemp() - try: - os.chdir(tmpdir) - with open('script.py', 'wt') as fobj: - fobj.write( - r""" -import os -import sys -sys.path.insert(0, r"{pkg_path}") -{paths_add} -import {mod_name} -{p_loc} -{cmd}""".format( - **locals() - ) - ) - res = back_tick(f'{PYTHON} script.py', ret_err=True) - finally: - os.chdir(cwd) - shutil.rmtree(tmpdir) - return res - - -def zip_extract_all(fname, path=None): - """Extract all members from zipfile - - Deals with situation where the directory is stored in the zipfile as a name, - as well as files that have to go into this directory. - """ - zf = zipfile.ZipFile(fname) - members = zf.namelist() - # Remove members that are just bare directories - members = [m for m in members if not m.endswith('/')] - for zipinfo in members: - zf.extract(zipinfo, path, None) - - -def install_from_to(from_dir, to_dir, py_lib_sdir=PY_LIB_SDIR, bin_sdir='bin'): - """Install package in `from_dir` to standard location in `to_dir` - - Parameters - ---------- - from_dir : str - path containing files to install with ``python setup.py ...`` - to_dir : str - prefix path to which files will be installed, as in ``python setup.py - install --prefix=to_dir`` - py_lib_sdir : str, optional - subdirectory within `to_dir` to which library code will be installed - bin_sdir : str, optional - subdirectory within `to_dir` to which scripts will be installed - """ - site_pkgs_path = os.path.join(to_dir, py_lib_sdir) - py_lib_locs = f' --install-purelib={site_pkgs_path} ' f'--install-platlib={site_pkgs_path}' - pwd = os.path.abspath(os.getcwd()) - cmd = f'{PYTHON} setup.py --quiet install --prefix={to_dir} {py_lib_locs}' - try: - os.chdir(from_dir) - back_tick(cmd) - finally: - os.chdir(pwd) - - -def install_from_zip( - zip_fname, install_path, pkg_finder=None, py_lib_sdir=PY_LIB_SDIR, script_sdir='bin' -): - """Install package from zip file `zip_fname` - - Parameters - ---------- - zip_fname : str - filename of zip file containing package code - install_path : str - output prefix at which to install package - pkg_finder : None or callable, optional - If None, assume zip contains ``setup.py`` at the top level. Otherwise, - find directory containing ``setup.py`` with ``pth = - pkg_finder(unzip_path)`` where ``unzip_path`` is the path to which we - have unzipped the zip file contents. - py_lib_sdir : str, optional - subdirectory to which to write the library code from the package. Thus - if package called ``nibabel``, the written code will be in - ``//nibabel - script_sdir : str, optional - subdirectory to which we write the installed scripts. Thus scripts will - be written to ``/ - """ - unzip_path = tempfile.mkdtemp() - try: - # Zip may unpack module into current directory - zip_extract_all(zip_fname, unzip_path) - if pkg_finder is None: - from_path = unzip_path - else: - from_path = pkg_finder(unzip_path) - install_from_to(from_path, install_path, py_lib_sdir, script_sdir) - finally: - shutil.rmtree(unzip_path) - - -def contexts_print_info(mod_name, repo_path, install_path): - """Print result of get_info from different installation routes - - Runs installation from: - - * git archive zip file - * with setup.py install from repository directory - * just running code from repository directory - - and prints out result of get_info in each case. There will be many files - written into `install_path` that you may want to clean up somehow. - - Parameters - ---------- - mod_name : str - package name that will be installed, and tested - repo_path : str - path to location of git repository - install_path : str - path into which to install temporary installations - """ - site_pkgs_path = os.path.join(install_path, PY_LIB_SDIR) - # first test archive - pwd = os.path.abspath(os.getcwd()) - out_fname = pjoin(install_path, 'test.zip') - try: - os.chdir(repo_path) - back_tick(f'git archive --format zip -o {out_fname} HEAD') - finally: - os.chdir(pwd) - install_from_zip(out_fname, install_path, None) - cmd_str = f'print({mod_name}.get_info())' - print(run_mod_cmd(mod_name, site_pkgs_path, cmd_str)[0]) - # now test install into a directory from the repository - install_from_to(repo_path, install_path, PY_LIB_SDIR) - print(run_mod_cmd(mod_name, site_pkgs_path, cmd_str)[0]) - # test from development tree - print(run_mod_cmd(mod_name, repo_path, cmd_str)[0]) - - -def info_from_here(mod_name): - """Run info context checks starting in working directory - - Runs checks from current working directory, installing temporary - installations into a new temporary directory - - Parameters - ---------- - mod_name : str - package name that will be installed, and tested - """ - repo_path = os.path.abspath(os.getcwd()) - install_path = tempfile.mkdtemp() - try: - contexts_print_info(mod_name, repo_path, install_path) - finally: - shutil.rmtree(install_path) - - -def tests_installed(mod_name, source_path=None): - """Install from `source_path` into temporary directory; run tests - - Parameters - ---------- - mod_name : str - name of module - e.g. 'nibabel' - source_path : None or str - Path from which to install. If None, defaults to working directory - """ - if source_path is None: - source_path = os.path.abspath(os.getcwd()) - install_path = tempfile.mkdtemp() - site_pkgs_path = pjoin(install_path, PY_LIB_SDIR) - scripts_path = pjoin(install_path, 'bin') - try: - install_from_to(source_path, install_path, PY_LIB_SDIR, 'bin') - stdout, stderr = run_mod_cmd(mod_name, site_pkgs_path, mod_name + '.test()', scripts_path) - finally: - shutil.rmtree(install_path) - print(stdout) - print(stderr) - - -# Tell nose this is not a test -tests_installed.__test__ = False - - -def check_installed_files(repo_mod_path, install_mod_path): - """Check files in `repo_mod_path` are installed at `install_mod_path` - - At the moment, all this does is check that all the ``*.py`` files in - `repo_mod_path` are installed at `install_mod_path`. - - Parameters - ---------- - repo_mod_path : str - repository path containing package files, e.g. /nibabel> - install_mod_path : str - path at which package has been installed. This is the path where the - root package ``__init__.py`` lives. - - Return - ------ - uninstalled : list - list of files that should have been installed, but have not been - installed - """ - return missing_from(repo_mod_path, install_mod_path, filter=r'\.py$') - - -def missing_from(path0, path1, filter=None): - """Return filenames present in `path0` but not in `path1` - - Parameters - ---------- - path0 : str - path which contains all files of interest - path1 : str - path which should contain all files of interest - filter : None or str or regexp, optional - A successful result from ``filter.search(fname)`` means the file is of - interest. None means all files are of interest - - Returns - ------- - path1_missing : list - list of all files missing from `path1` that are in `path0` at the same - relative path. - """ - if not filter is None: - filter = re.compile(filter) - uninstalled = [] - # Walk directory tree to get py files - for dirpath, dirnames, filenames in os.walk(path0): - out_dirpath = dirpath.replace(path0, path1) - for fname in filenames: - if not filter is None and filter.search(fname) is None: - continue - equiv_fname = os.path.join(out_dirpath, fname) - if not os.path.isfile(equiv_fname): - uninstalled.append(pjoin(dirpath, fname)) - return uninstalled - - -def check_files(mod_name, repo_path=None, scripts_sdir='bin'): - """Print library and script files not picked up during install""" - if repo_path is None: - repo_path = abspath(os.getcwd()) - install_path = tempfile.mkdtemp() - repo_mod_path = pjoin(repo_path, mod_name) - installed_mod_path = pjoin(install_path, PY_LIB_SDIR, mod_name) - repo_bin = pjoin(repo_path, 'bin') - installed_bin = pjoin(install_path, 'bin') - try: - zip_fname = make_dist(repo_path, install_path, 'sdist --formats=zip', '*.zip') - pf = get_sdist_finder(mod_name) - install_from_zip(zip_fname, install_path, pf, PY_LIB_SDIR, scripts_sdir) - lib_misses = missing_from(repo_mod_path, installed_mod_path, r'\.py$') - script_misses = missing_from(repo_bin, installed_bin) - finally: - shutil.rmtree(install_path) - if lib_misses: - print('Missed library files: ', ', '.join(lib_misses)) - else: - print('You got all the library files') - if script_misses: - print('Missed script files: ', ', '.join(script_misses)) - else: - print('You got all the script files') - return len(lib_misses) > 0 or len(script_misses) > 0 - - -def get_sdist_finder(mod_name): - """Return function finding sdist source directory for `mod_name`""" - - def pf(pth): - pkg_dirs = glob(pjoin(pth, mod_name + '-*')) - if len(pkg_dirs) != 1: - raise OSError('There must be one and only one package dir') - return pkg_dirs[0] - - return pf - - -def sdist_tests(mod_name, repo_path=None, label='fast', doctests=True): - """Make sdist zip, install from it, and run tests""" - if repo_path is None: - repo_path = abspath(os.getcwd()) - install_path = tempfile.mkdtemp() - try: - zip_fname = make_dist(repo_path, install_path, 'sdist --formats=zip', '*.zip') - pf = get_sdist_finder(mod_name) - install_from_zip(zip_fname, install_path, pf, PY_LIB_SDIR, 'bin') - site_pkgs_path = pjoin(install_path, PY_LIB_SDIR) - script_path = pjoin(install_path, 'bin') - cmd = f"{mod_name}.test(label='{label}', doctests={doctests})" - stdout, stderr = run_mod_cmd(mod_name, site_pkgs_path, cmd, script_path) - finally: - shutil.rmtree(install_path) - print(stdout) - print(stderr) - - -sdist_tests.__test__ = False - - -def bdist_egg_tests(mod_name, repo_path=None, label='fast', doctests=True): - """Make bdist_egg, unzip it, and run tests from result - - We've got a problem here, because the egg does not contain the scripts, and - so, if we are testing the scripts with ``mod.test()``, we won't pick up the - scripts from the repository we are testing. - - So, you might need to add a label to the script tests, and use the `label` - parameter to indicate these should be skipped. As in: - - bdist_egg_tests('nibabel', None, label='not script_test') - """ - if repo_path is None: - repo_path = abspath(os.getcwd()) - install_path = tempfile.mkdtemp() - scripts_path = pjoin(install_path, 'bin') - try: - zip_fname = make_dist(repo_path, install_path, 'bdist_egg', '*.egg') - zip_extract_all(zip_fname, install_path) - cmd = f"{mod_name}.test(label='{label}', doctests={doctests})" - stdout, stderr = run_mod_cmd(mod_name, install_path, cmd, scripts_path) - finally: - shutil.rmtree(install_path) - print(stdout) - print(stderr) - - -bdist_egg_tests.__test__ = False - - -def make_dist(repo_path, out_dir, setup_params, zipglob): - """Create distutils distribution file - - Parameters - ---------- - repo_path : str - path to repository containing code and ``setup.py`` - out_dir : str - path to which to write new distribution file - setup_params: str - parameters to pass to ``setup.py`` to create distribution. - zipglob : str - glob identifying expected output file. - - Returns - ------- - out_fname : str - filename of generated distribution file - - Examples - -------- - Make, return a zipped sdist:: - - make_dist('/path/to/repo', '/tmp/path', 'sdist --formats=zip', '*.zip') - - Make, return a binary egg:: - - make_dist('/path/to/repo', '/tmp/path', 'bdist_egg', '*.egg') - """ - pwd = os.path.abspath(os.getcwd()) - try: - os.chdir(repo_path) - back_tick(f'{PYTHON} setup.py {setup_params} --dist-dir={out_dir}') - zips = glob(pjoin(out_dir, zipglob)) - if len(zips) != 1: - raise OSError( - f'There must be one and only one {zipglob} ' - f"file, but I found \"{': '.join(zips)}\"" - ) - finally: - os.chdir(pwd) - return zips[0] diff --git a/nisext/tests/__init__.py b/nisext/tests/__init__.py deleted file mode 100644 index af7d1d1dd2..0000000000 --- a/nisext/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# Tests for nisext package diff --git a/nisext/tests/test_sexts.py b/nisext/tests/test_sexts.py deleted file mode 100644 index f262ec5685..0000000000 --- a/nisext/tests/test_sexts.py +++ /dev/null @@ -1,106 +0,0 @@ -"""Tests for nisexts.sexts module -""" - -import sys -import types - -import pytest - -from ..sexts import package_check - -FAKE_NAME = 'nisext_improbable' -assert FAKE_NAME not in sys.modules -FAKE_MODULE = types.ModuleType('nisext_fake') - - -def test_package_check(): - # Try to use a required package - raise error - with pytest.raises(RuntimeError): - package_check(FAKE_NAME) - # Optional, log.warn - package_check(FAKE_NAME, optional=True) - # Can also pass a string - package_check(FAKE_NAME, optional='some-package') - try: - # Make a package - sys.modules[FAKE_NAME] = FAKE_MODULE - # Now it passes if we don't check the version - package_check(FAKE_NAME) - # A fake version - FAKE_MODULE.__version__ = '0.2' - package_check(FAKE_NAME, version='0.2') - # fails when version not good enough - with pytest.raises(RuntimeError): - package_check(FAKE_NAME, '0.3') - # Unless optional in which case log.warns - package_check(FAKE_NAME, version='0.3', optional=True) - # Might do custom version check - package_check(FAKE_NAME, version='0.2', version_getter=lambda x: '0.2') - finally: - del sys.modules[FAKE_NAME] - - -def test_package_check_setuptools(): - # If setuptools arg not None, missing package just adds it to arg - with pytest.raises(RuntimeError): - package_check(FAKE_NAME, setuptools_args=None) - - def pkg_chk_sta(*args, **kwargs): - st_args = {} - package_check(*args, setuptools_args=st_args, **kwargs) - return st_args - - assert pkg_chk_sta(FAKE_NAME) == {'install_requires': ['nisext_improbable']} - # Check that this gets appended to existing value - old_sta = {'install_requires': ['something']} - package_check(FAKE_NAME, setuptools_args=old_sta) - assert old_sta == {'install_requires': ['something', 'nisext_improbable']} - # That existing value as string gets converted to a list - old_sta = {'install_requires': 'something'} - package_check(FAKE_NAME, setuptools_args=old_sta) - assert old_sta == {'install_requires': ['something', 'nisext_improbable']} - # Optional, add to extras_require - assert pkg_chk_sta(FAKE_NAME, optional='something') == { - 'extras_require': {'something': ['nisext_improbable']} - } - # Check that this gets appended to existing value - old_sta = {'extras_require': {'something': ['amodule']}} - package_check(FAKE_NAME, optional='something', setuptools_args=old_sta) - assert old_sta == {'extras_require': {'something': ['amodule', 'nisext_improbable']}} - # That string gets converted to a list here too - old_sta = {'extras_require': {'something': 'amodule'}} - package_check(FAKE_NAME, optional='something', setuptools_args=old_sta) - assert old_sta == {'extras_require': {'something': ['amodule', 'nisext_improbable']}} - # But optional has to be a string if not empty and setuptools_args defined - with pytest.raises(RuntimeError): - package_check(FAKE_NAME, optional=True, setuptools_args={}) - try: - # Make a package - sys.modules[FAKE_NAME] = FAKE_MODULE - # No install_requires because we already have it - assert pkg_chk_sta(FAKE_NAME) == {} - # A fake version still works - FAKE_MODULE.__version__ = '0.2' - assert pkg_chk_sta(FAKE_NAME, version='0.2') == {} - # goes into install requires when version not good enough - exp_spec = [FAKE_NAME + '>=0.3'] - assert pkg_chk_sta(FAKE_NAME, version='0.3') == {'install_requires': exp_spec} - # Unless optional in which case goes into extras_require - package_check(FAKE_NAME, version='0.2', version_getter=lambda x: '0.2') - assert pkg_chk_sta(FAKE_NAME, version='0.3', optional='afeature') == { - 'extras_require': {'afeature': exp_spec} - } - # Might do custom version check - assert pkg_chk_sta(FAKE_NAME, version='0.2', version_getter=lambda x: '0.2') == {} - # If the version check fails, put into requires - bad_getter = lambda x: x.not_an_attribute - exp_spec = [FAKE_NAME + '>=0.2'] - assert pkg_chk_sta(FAKE_NAME, version='0.2', version_getter=bad_getter) == { - 'install_requires': exp_spec - } - # Likewise for optional dependency - assert pkg_chk_sta( - FAKE_NAME, version='0.2', optional='afeature', version_getter=bad_getter - ) == {'extras_require': {'afeature': [FAKE_NAME + '>=0.2']}} - finally: - del sys.modules[FAKE_NAME] diff --git a/nisext/tests/test_testers.py b/nisext/tests/test_testers.py deleted file mode 100644 index f81a40f1df..0000000000 --- a/nisext/tests/test_testers.py +++ /dev/null @@ -1,35 +0,0 @@ -"""Tests for testers -""" - -import os -from os.path import dirname, pathsep - -import pytest - -from ..testers import PYTHON, back_tick, run_mod_cmd - - -def test_back_tick(): - cmd = f'{PYTHON} -c "print(\'Hello\')"' - assert back_tick(cmd) == 'Hello' - assert back_tick(cmd, ret_err=True) == ('Hello', '') - assert back_tick(cmd, True, False) == (b'Hello', b'') - cmd = f'{PYTHON} -c "raise ValueError()"' - with pytest.raises(RuntimeError): - back_tick(cmd) - - -def test_run_mod_cmd(): - mod = 'os' - mod_dir = dirname(os.__file__) - assert run_mod_cmd(mod, mod_dir, "print('Hello')", None, False) == ('Hello', '') - sout, serr = run_mod_cmd(mod, mod_dir, "print('Hello again')") - assert serr == '' - mod_file, out_str = [s.strip() for s in sout.split('\n')] - assert mod_file.startswith(mod_dir) - assert out_str == 'Hello again' - sout, serr = run_mod_cmd(mod, mod_dir, "print(os.environ['PATH'])", None, False) - assert serr == '' - sout2, serr = run_mod_cmd(mod, mod_dir, "print(os.environ['PATH'])", 'pth2', False) - assert serr == '' - assert sout2 == '"pth2"' + pathsep + sout diff --git a/pyproject.toml b/pyproject.toml index 14095b8f22..e92c465e0d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -87,7 +87,7 @@ exclude = [ ] [tool.hatch.build.targets.wheel] -packages = ["nibabel", "nisext"] +packages = ["nibabel"] exclude = [ # 56MB test file does not need to be installed everywhere "nibabel/nicom/tests/data/4d_multiframe_test.dcm",