diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml new file mode 100644 index 0000000..a302903 --- /dev/null +++ b/.github/workflows/ci_tests.yml @@ -0,0 +1,86 @@ +# GitHub Actions workflow for testing and continuous integration. +# +# This file performs testing using tox and tox.ini to define and configure the test environments. + +name: CI Tests + +on: + push: + branches: + - main # GitHub now defaults to 'main' as the name of the primary branch. Change this as needed. + # tags: # run CI if specific tags are pushed + pull_request: + # branches: # only build on PRs against 'main' if you need to further limit when CI is run. + # - main + +jobs: + # Github Actions supports ubuntu, windows, and macos virtual environments: + # https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners + ci_tests: + name: ${{ matrix.name }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - name: Code style checks + os: ubuntu-latest + python: 3.x + toxenv: codestyle + + - name: Python 3.7 with minimal dependencies + os: ubuntu-latest + python: 3.7 + toxenv: py37-test + + - name: Python 3.8 with all optional dependencies and coverage checking + os: ubuntu-latest + python: 3.8 + toxenv: py38-test-alldeps-cov + + - name: OS X - Python 3.8 with all optional dependencies + os: macos-latest + python: 3.8 + toxenv: py38-test-alldeps + + - name: Windows - Python 3.8 with all optional dependencies + os: windows-latest + python: 3.8 + toxenv: py38-test-alldeps + + # - name: Python 3.7 with oldest supported version of all dependencies + # os: ubuntu-16.04 + # python: 3.7 + # toxenv: py37-test-oldestdeps + + # - name: Python 3.8 with latest dev versions of key dependencies + # os: ubuntu-latest + # python: 3.8 + # toxenv: py38-test-devdeps + + # - name: Test building of Sphinx docs + # os: ubuntu-latest + # python: 3.x + # toxenv: build_docs + + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set up python ${{ matrix.python }} on ${{ matrix.os }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + - name: Install base dependencies + run: | + python -m pip install --upgrade pip + python -m pip install tox codecov + - name: Test with tox + run: | + tox -e ${{ matrix.toxenv }} + # This is an example of how to upload coverage to codecov + # - name: Upload coverage to codecov + # if: "contains(matrix.toxenv, '-cov')" + # uses: codecov/codecov-action@v1 + # with: + # file: ./coverage.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..658bff7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +gaussfit_catalog/version.py diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 6a20fa6..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "astropy_helpers"] - path = astropy_helpers - url = https://github.com/astropy/astropy-helpers.git diff --git a/MANIFEST.in b/MANIFEST.in index c3bf88f..5326189 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,8 +1,8 @@ +include pyproject.toml + include README.rst include CHANGES.rst -include ez_setup.py -include ah_bootstrap.py include setup.cfg recursive-include packagename *.pyx *.c *.pxd @@ -15,26 +15,4 @@ prune build prune docs/_build prune docs/api - -# the next few stanzas are for astropy_helpers. It's derived from the -# astropy_helpers/MANIFEST.in, but requires additional includes for the actual -# package directory and egg-info. - -include astropy_helpers/README.rst -include astropy_helpers/CHANGES.rst -include astropy_helpers/LICENSE.rst -recursive-include astropy_helpers/licenses * - -include astropy_helpers/ez_setup.py -include astropy_helpers/ah_bootstrap.py - -recursive-include astropy_helpers/astropy_helpers *.py *.pyx *.c *.h -recursive-include astropy_helpers/astropy_helpers.egg-info * -# include the sphinx stuff with "*" because there are css/html/rst/etc. -recursive-include astropy_helpers/astropy_helpers/sphinx * - -prune astropy_helpers/build -prune astropy_helpers/astropy_helpers/tests - - global-exclude *.pyc *.o diff --git a/ah_bootstrap.py b/ah_bootstrap.py deleted file mode 100644 index 0dc5007..0000000 --- a/ah_bootstrap.py +++ /dev/null @@ -1,987 +0,0 @@ -""" -This bootstrap module contains code for ensuring that the astropy_helpers -package will be importable by the time the setup.py script runs. It also -includes some workarounds to ensure that a recent-enough version of setuptools -is being used for the installation. - -This module should be the first thing imported in the setup.py of distributions -that make use of the utilities in astropy_helpers. If the distribution ships -with its own copy of astropy_helpers, this module will first attempt to import -from the shipped copy. However, it will also check PyPI to see if there are -any bug-fix releases on top of the current version that may be useful to get -past platform-specific bugs that have been fixed. When running setup.py, use -the ``--offline`` command-line option to disable the auto-upgrade checks. - -When this module is imported or otherwise executed it automatically calls a -main function that attempts to read the project's setup.cfg file, which it -checks for a configuration section called ``[ah_bootstrap]`` the presences of -that section, and options therein, determine the next step taken: If it -contains an option called ``auto_use`` with a value of ``True``, it will -automatically call the main function of this module called -`use_astropy_helpers` (see that function's docstring for full details). -Otherwise no further action is taken (however, -``ah_bootstrap.use_astropy_helpers`` may be called manually from within the -setup.py script). - -Additional options in the ``[ah_boostrap]`` section of setup.cfg have the same -names as the arguments to `use_astropy_helpers`, and can be used to configure -the bootstrap script when ``auto_use = True``. - -See https://github.com/astropy/astropy-helpers for more details, and for the -latest version of this module. -""" - -import contextlib -import errno -import imp -import io -import locale -import os -import re -import subprocess as sp -import sys - -try: - from ConfigParser import ConfigParser, RawConfigParser -except ImportError: - from configparser import ConfigParser, RawConfigParser - - -if sys.version_info[0] < 3: - _str_types = (str, unicode) - _text_type = unicode - PY3 = False -else: - _str_types = (str, bytes) - _text_type = str - PY3 = True - - -# What follows are several import statements meant to deal with install-time -# issues with either missing or misbehaving pacakges (including making sure -# setuptools itself is installed): - - -# Some pre-setuptools checks to ensure that either distribute or setuptools >= -# 0.7 is used (over pre-distribute setuptools) if it is available on the path; -# otherwise the latest setuptools will be downloaded and bootstrapped with -# ``ez_setup.py``. This used to be included in a separate file called -# setuptools_bootstrap.py; but it was combined into ah_bootstrap.py -try: - import pkg_resources - _setuptools_req = pkg_resources.Requirement.parse('setuptools>=0.7') - # This may raise a DistributionNotFound in which case no version of - # setuptools or distribute is properly installed - _setuptools = pkg_resources.get_distribution('setuptools') - if _setuptools not in _setuptools_req: - # Older version of setuptools; check if we have distribute; again if - # this results in DistributionNotFound we want to give up - _distribute = pkg_resources.get_distribution('distribute') - if _setuptools != _distribute: - # It's possible on some pathological systems to have an old version - # of setuptools and distribute on sys.path simultaneously; make - # sure distribute is the one that's used - sys.path.insert(1, _distribute.location) - _distribute.activate() - imp.reload(pkg_resources) -except: - # There are several types of exceptions that can occur here; if all else - # fails bootstrap and use the bootstrapped version - from ez_setup import use_setuptools - use_setuptools() - - -# Note: The following import is required as a workaround to -# https://github.com/astropy/astropy-helpers/issues/89; if we don't import this -# module now, it will get cleaned up after `run_setup` is called, but that will -# later cause the TemporaryDirectory class defined in it to stop working when -# used later on by setuptools -try: - import setuptools.py31compat -except ImportError: - pass - - -# matplotlib can cause problems if it is imported from within a call of -# run_setup(), because in some circumstances it will try to write to the user's -# home directory, resulting in a SandboxViolation. See -# https://github.com/matplotlib/matplotlib/pull/4165 -# Making sure matplotlib, if it is available, is imported early in the setup -# process can mitigate this (note importing matplotlib.pyplot has the same -# issue) -try: - import matplotlib - matplotlib.use('Agg') - import matplotlib.pyplot -except: - # Ignore if this fails for *any* reason* - pass - - -# End compatibility imports... - - -# In case it didn't successfully import before the ez_setup checks -import pkg_resources - -from setuptools import Distribution -from setuptools.package_index import PackageIndex -from setuptools.sandbox import run_setup - -from distutils import log -from distutils.debug import DEBUG - - -# TODO: Maybe enable checking for a specific version of astropy_helpers? -DIST_NAME = 'astropy-helpers' -PACKAGE_NAME = 'astropy_helpers' - -# Defaults for other options -DOWNLOAD_IF_NEEDED = True -INDEX_URL = 'https://pypi.python.org/simple' -USE_GIT = True -OFFLINE = False -AUTO_UPGRADE = True - -# A list of all the configuration options and their required types -CFG_OPTIONS = [ - ('auto_use', bool), ('path', str), ('download_if_needed', bool), - ('index_url', str), ('use_git', bool), ('offline', bool), - ('auto_upgrade', bool) -] - - -class _Bootstrapper(object): - """ - Bootstrapper implementation. See ``use_astropy_helpers`` for parameter - documentation. - """ - - def __init__(self, path=None, index_url=None, use_git=None, offline=None, - download_if_needed=None, auto_upgrade=None): - - if path is None: - path = PACKAGE_NAME - - if not (isinstance(path, _str_types) or path is False): - raise TypeError('path must be a string or False') - - if PY3 and not isinstance(path, _text_type): - fs_encoding = sys.getfilesystemencoding() - path = path.decode(fs_encoding) # path to unicode - - self.path = path - - # Set other option attributes, using defaults where necessary - self.index_url = index_url if index_url is not None else INDEX_URL - self.offline = offline if offline is not None else OFFLINE - - # If offline=True, override download and auto-upgrade - if self.offline: - download_if_needed = False - auto_upgrade = False - - self.download = (download_if_needed - if download_if_needed is not None - else DOWNLOAD_IF_NEEDED) - self.auto_upgrade = (auto_upgrade - if auto_upgrade is not None else AUTO_UPGRADE) - - # If this is a release then the .git directory will not exist so we - # should not use git. - git_dir_exists = os.path.exists(os.path.join(os.path.dirname(__file__), '.git')) - if use_git is None and not git_dir_exists: - use_git = False - - self.use_git = use_git if use_git is not None else USE_GIT - # Declared as False by default--later we check if astropy-helpers can be - # upgraded from PyPI, but only if not using a source distribution (as in - # the case of import from a git submodule) - self.is_submodule = False - - @classmethod - def main(cls, argv=None): - if argv is None: - argv = sys.argv - - config = cls.parse_config() - config.update(cls.parse_command_line(argv)) - - auto_use = config.pop('auto_use', False) - bootstrapper = cls(**config) - - if auto_use: - # Run the bootstrapper, otherwise the setup.py is using the old - # use_astropy_helpers() interface, in which case it will run the - # bootstrapper manually after reconfiguring it. - bootstrapper.run() - - return bootstrapper - - @classmethod - def parse_config(cls): - if not os.path.exists('setup.cfg'): - return {} - - cfg = ConfigParser() - - try: - cfg.read('setup.cfg') - except Exception as e: - if DEBUG: - raise - - log.error( - "Error reading setup.cfg: {0!r}\n{1} will not be " - "automatically bootstrapped and package installation may fail." - "\n{2}".format(e, PACKAGE_NAME, _err_help_msg)) - return {} - - if not cfg.has_section('ah_bootstrap'): - return {} - - config = {} - - for option, type_ in CFG_OPTIONS: - if not cfg.has_option('ah_bootstrap', option): - continue - - if type_ is bool: - value = cfg.getboolean('ah_bootstrap', option) - else: - value = cfg.get('ah_bootstrap', option) - - config[option] = value - - return config - - @classmethod - def parse_command_line(cls, argv=None): - if argv is None: - argv = sys.argv - - config = {} - - # For now we just pop recognized ah_bootstrap options out of the - # arg list. This is imperfect; in the unlikely case that a setup.py - # custom command or even custom Distribution class defines an argument - # of the same name then we will break that. However there's a catch22 - # here that we can't just do full argument parsing right here, because - # we don't yet know *how* to parse all possible command-line arguments. - if '--no-git' in argv: - config['use_git'] = False - argv.remove('--no-git') - - if '--offline' in argv: - config['offline'] = True - argv.remove('--offline') - - return config - - def run(self): - strategies = ['local_directory', 'local_file', 'index'] - dist = None - - # First, remove any previously imported versions of astropy_helpers; - # this is necessary for nested installs where one package's installer - # is installing another package via setuptools.sandbox.run_setup, as in - # the case of setup_requires - for key in list(sys.modules): - try: - if key == PACKAGE_NAME or key.startswith(PACKAGE_NAME + '.'): - del sys.modules[key] - except AttributeError: - # Sometimes mysterious non-string things can turn up in - # sys.modules - continue - - # Check to see if the path is a submodule - self.is_submodule = self._check_submodule() - - for strategy in strategies: - method = getattr(self, 'get_{0}_dist'.format(strategy)) - dist = method() - if dist is not None: - break - else: - raise _AHBootstrapSystemExit( - "No source found for the {0!r} package; {0} must be " - "available and importable as a prerequisite to building " - "or installing this package.".format(PACKAGE_NAME)) - - # This is a bit hacky, but if astropy_helpers was loaded from a - # directory/submodule its Distribution object gets a "precedence" of - # "DEVELOP_DIST". However, in other cases it gets a precedence of - # "EGG_DIST". However, when activing the distribution it will only be - # placed early on sys.path if it is treated as an EGG_DIST, so always - # do that - dist = dist.clone(precedence=pkg_resources.EGG_DIST) - - # Otherwise we found a version of astropy-helpers, so we're done - # Just active the found distribution on sys.path--if we did a - # download this usually happens automatically but it doesn't hurt to - # do it again - # Note: Adding the dist to the global working set also activates it - # (makes it importable on sys.path) by default. - - try: - pkg_resources.working_set.add(dist, replace=True) - except TypeError: - # Some (much) older versions of setuptools do not have the - # replace=True option here. These versions are old enough that all - # bets may be off anyways, but it's easy enough to work around just - # in case... - if dist.key in pkg_resources.working_set.by_key: - del pkg_resources.working_set.by_key[dist.key] - pkg_resources.working_set.add(dist) - - @property - def config(self): - """ - A `dict` containing the options this `_Bootstrapper` was configured - with. - """ - - return dict((optname, getattr(self, optname)) - for optname, _ in CFG_OPTIONS if hasattr(self, optname)) - - def get_local_directory_dist(self): - """ - Handle importing a vendored package from a subdirectory of the source - distribution. - """ - - if not os.path.isdir(self.path): - return - - log.info('Attempting to import astropy_helpers from {0} {1!r}'.format( - 'submodule' if self.is_submodule else 'directory', - self.path)) - - dist = self._directory_import() - - if dist is None: - log.warn( - 'The requested path {0!r} for importing {1} does not ' - 'exist, or does not contain a copy of the {1} ' - 'package.'.format(self.path, PACKAGE_NAME)) - elif self.auto_upgrade and not self.is_submodule: - # A version of astropy-helpers was found on the available path, but - # check to see if a bugfix release is available on PyPI - upgrade = self._do_upgrade(dist) - if upgrade is not None: - dist = upgrade - - return dist - - def get_local_file_dist(self): - """ - Handle importing from a source archive; this also uses setup_requires - but points easy_install directly to the source archive. - """ - - if not os.path.isfile(self.path): - return - - log.info('Attempting to unpack and import astropy_helpers from ' - '{0!r}'.format(self.path)) - - try: - dist = self._do_download(find_links=[self.path]) - except Exception as e: - if DEBUG: - raise - - log.warn( - 'Failed to import {0} from the specified archive {1!r}: ' - '{2}'.format(PACKAGE_NAME, self.path, str(e))) - dist = None - - if dist is not None and self.auto_upgrade: - # A version of astropy-helpers was found on the available path, but - # check to see if a bugfix release is available on PyPI - upgrade = self._do_upgrade(dist) - if upgrade is not None: - dist = upgrade - - return dist - - def get_index_dist(self): - if not self.download: - log.warn('Downloading {0!r} disabled.'.format(DIST_NAME)) - return None - - log.warn( - "Downloading {0!r}; run setup.py with the --offline option to " - "force offline installation.".format(DIST_NAME)) - - try: - dist = self._do_download() - except Exception as e: - if DEBUG: - raise - log.warn( - 'Failed to download and/or install {0!r} from {1!r}:\n' - '{2}'.format(DIST_NAME, self.index_url, str(e))) - dist = None - - # No need to run auto-upgrade here since we've already presumably - # gotten the most up-to-date version from the package index - return dist - - def _directory_import(self): - """ - Import astropy_helpers from the given path, which will be added to - sys.path. - - Must return True if the import succeeded, and False otherwise. - """ - - # Return True on success, False on failure but download is allowed, and - # otherwise raise SystemExit - path = os.path.abspath(self.path) - - # Use an empty WorkingSet rather than the man - # pkg_resources.working_set, since on older versions of setuptools this - # will invoke a VersionConflict when trying to install an upgrade - ws = pkg_resources.WorkingSet([]) - ws.add_entry(path) - dist = ws.by_key.get(DIST_NAME) - - if dist is None: - # We didn't find an egg-info/dist-info in the given path, but if a - # setup.py exists we can generate it - setup_py = os.path.join(path, 'setup.py') - if os.path.isfile(setup_py): - with _silence(): - run_setup(os.path.join(path, 'setup.py'), - ['egg_info']) - - for dist in pkg_resources.find_distributions(path, True): - # There should be only one... - return dist - - return dist - - def _do_download(self, version='', find_links=None): - if find_links: - allow_hosts = '' - index_url = None - else: - allow_hosts = None - index_url = self.index_url - - # Annoyingly, setuptools will not handle other arguments to - # Distribution (such as options) before handling setup_requires, so it - # is not straightforward to programmatically augment the arguments which - # are passed to easy_install - class _Distribution(Distribution): - def get_option_dict(self, command_name): - opts = Distribution.get_option_dict(self, command_name) - if command_name == 'easy_install': - if find_links is not None: - opts['find_links'] = ('setup script', find_links) - if index_url is not None: - opts['index_url'] = ('setup script', index_url) - if allow_hosts is not None: - opts['allow_hosts'] = ('setup script', allow_hosts) - return opts - - if version: - req = '{0}=={1}'.format(DIST_NAME, version) - else: - req = DIST_NAME - - attrs = {'setup_requires': [req]} - - try: - if DEBUG: - _Distribution(attrs=attrs) - else: - with _silence(): - _Distribution(attrs=attrs) - - # If the setup_requires succeeded it will have added the new dist to - # the main working_set - return pkg_resources.working_set.by_key.get(DIST_NAME) - except Exception as e: - if DEBUG: - raise - - msg = 'Error retrieving {0} from {1}:\n{2}' - if find_links: - source = find_links[0] - elif index_url != INDEX_URL: - source = index_url - else: - source = 'PyPI' - - raise Exception(msg.format(DIST_NAME, source, repr(e))) - - def _do_upgrade(self, dist): - # Build up a requirement for a higher bugfix release but a lower minor - # release (so API compatibility is guaranteed) - next_version = _next_version(dist.parsed_version) - - req = pkg_resources.Requirement.parse( - '{0}>{1},<{2}'.format(DIST_NAME, dist.version, next_version)) - - package_index = PackageIndex(index_url=self.index_url) - - upgrade = package_index.obtain(req) - - if upgrade is not None: - return self._do_download(version=upgrade.version) - - def _check_submodule(self): - """ - Check if the given path is a git submodule. - - See the docstrings for ``_check_submodule_using_git`` and - ``_check_submodule_no_git`` for further details. - """ - - if (self.path is None or - (os.path.exists(self.path) and not os.path.isdir(self.path))): - return False - - if self.use_git: - return self._check_submodule_using_git() - else: - return self._check_submodule_no_git() - - def _check_submodule_using_git(self): - """ - Check if the given path is a git submodule. If so, attempt to initialize - and/or update the submodule if needed. - - This function makes calls to the ``git`` command in subprocesses. The - ``_check_submodule_no_git`` option uses pure Python to check if the given - path looks like a git submodule, but it cannot perform updates. - """ - - cmd = ['git', 'submodule', 'status', '--', self.path] - - try: - log.info('Running `{0}`; use the --no-git option to disable git ' - 'commands'.format(' '.join(cmd))) - returncode, stdout, stderr = run_cmd(cmd) - except _CommandNotFound: - # The git command simply wasn't found; this is most likely the - # case on user systems that don't have git and are simply - # trying to install the package from PyPI or a source - # distribution. Silently ignore this case and simply don't try - # to use submodules - return False - - stderr = stderr.strip() - - if returncode != 0 and stderr: - # Unfortunately the return code alone cannot be relied on, as - # earlier versions of git returned 0 even if the requested submodule - # does not exist - - # This is a warning that occurs in perl (from running git submodule) - # which only occurs with a malformatted locale setting which can - # happen sometimes on OSX. See again - # https://github.com/astropy/astropy/issues/2749 - perl_warning = ('perl: warning: Falling back to the standard locale ' - '("C").') - if not stderr.strip().endswith(perl_warning): - # Some other unknown error condition occurred - log.warn('git submodule command failed ' - 'unexpectedly:\n{0}'.format(stderr)) - return False - - # Output of `git submodule status` is as follows: - # - # 1: Status indicator: '-' for submodule is uninitialized, '+' if - # submodule is initialized but is not at the commit currently indicated - # in .gitmodules (and thus needs to be updated), or 'U' if the - # submodule is in an unstable state (i.e. has merge conflicts) - # - # 2. SHA-1 hash of the current commit of the submodule (we don't really - # need this information but it's useful for checking that the output is - # correct) - # - # 3. The output of `git describe` for the submodule's current commit - # hash (this includes for example what branches the commit is on) but - # only if the submodule is initialized. We ignore this information for - # now - _git_submodule_status_re = re.compile( - '^(?P[+-U ])(?P[0-9a-f]{40}) ' - '(?P\S+)( .*)?$') - - # The stdout should only contain one line--the status of the - # requested submodule - m = _git_submodule_status_re.match(stdout) - if m: - # Yes, the path *is* a git submodule - self._update_submodule(m.group('submodule'), m.group('status')) - return True - else: - log.warn( - 'Unexpected output from `git submodule status`:\n{0}\n' - 'Will attempt import from {1!r} regardless.'.format( - stdout, self.path)) - return False - - def _check_submodule_no_git(self): - """ - Like ``_check_submodule_using_git``, but simply parses the .gitmodules file - to determine if the supplied path is a git submodule, and does not exec any - subprocesses. - - This can only determine if a path is a submodule--it does not perform - updates, etc. This function may need to be updated if the format of the - .gitmodules file is changed between git versions. - """ - - gitmodules_path = os.path.abspath('.gitmodules') - - if not os.path.isfile(gitmodules_path): - return False - - # This is a minimal reader for gitconfig-style files. It handles a few of - # the quirks that make gitconfig files incompatible with ConfigParser-style - # files, but does not support the full gitconfig syntax (just enough - # needed to read a .gitmodules file). - gitmodules_fileobj = io.StringIO() - - # Must use io.open for cross-Python-compatible behavior wrt unicode - with io.open(gitmodules_path) as f: - for line in f: - # gitconfig files are more flexible with leading whitespace; just - # go ahead and remove it - line = line.lstrip() - - # comments can start with either # or ; - if line and line[0] in (':', ';'): - continue - - gitmodules_fileobj.write(line) - - gitmodules_fileobj.seek(0) - - cfg = RawConfigParser() - - try: - cfg.readfp(gitmodules_fileobj) - except Exception as exc: - log.warn('Malformatted .gitmodules file: {0}\n' - '{1} cannot be assumed to be a git submodule.'.format( - exc, self.path)) - return False - - for section in cfg.sections(): - if not cfg.has_option(section, 'path'): - continue - - submodule_path = cfg.get(section, 'path').rstrip(os.sep) - - if submodule_path == self.path.rstrip(os.sep): - return True - - return False - - def _update_submodule(self, submodule, status): - if status == ' ': - # The submodule is up to date; no action necessary - return - elif status == '-': - if self.offline: - raise _AHBootstrapSystemExit( - "Cannot initialize the {0} submodule in --offline mode; " - "this requires being able to clone the submodule from an " - "online repository.".format(submodule)) - cmd = ['update', '--init'] - action = 'Initializing' - elif status == '+': - cmd = ['update'] - action = 'Updating' - if self.offline: - cmd.append('--no-fetch') - elif status == 'U': - raise _AHBoostrapSystemExit( - 'Error: Submodule {0} contains unresolved merge conflicts. ' - 'Please complete or abandon any changes in the submodule so that ' - 'it is in a usable state, then try again.'.format(submodule)) - else: - log.warn('Unknown status {0!r} for git submodule {1!r}. Will ' - 'attempt to use the submodule as-is, but try to ensure ' - 'that the submodule is in a clean state and contains no ' - 'conflicts or errors.\n{2}'.format(status, submodule, - _err_help_msg)) - return - - err_msg = None - cmd = ['git', 'submodule'] + cmd + ['--', submodule] - log.warn('{0} {1} submodule with: `{2}`'.format( - action, submodule, ' '.join(cmd))) - - try: - log.info('Running `{0}`; use the --no-git option to disable git ' - 'commands'.format(' '.join(cmd))) - returncode, stdout, stderr = run_cmd(cmd) - except OSError as e: - err_msg = str(e) - else: - if returncode != 0: - err_msg = stderr - - if err_msg is not None: - log.warn('An unexpected error occurred updating the git submodule ' - '{0!r}:\n{1}\n{2}'.format(submodule, err_msg, - _err_help_msg)) - -class _CommandNotFound(OSError): - """ - An exception raised when a command run with run_cmd is not found on the - system. - """ - - -def run_cmd(cmd): - """ - Run a command in a subprocess, given as a list of command-line - arguments. - - Returns a ``(returncode, stdout, stderr)`` tuple. - """ - - try: - p = sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.PIPE) - # XXX: May block if either stdout or stderr fill their buffers; - # however for the commands this is currently used for that is - # unlikely (they should have very brief output) - stdout, stderr = p.communicate() - except OSError as e: - if DEBUG: - raise - - if e.errno == errno.ENOENT: - msg = 'Command not found: `{0}`'.format(' '.join(cmd)) - raise _CommandNotFound(msg, cmd) - else: - raise _AHBoostrapSystemExit( - 'An unexpected error occurred when running the ' - '`{0}` command:\n{1}'.format(' '.join(cmd), str(e))) - - - # Can fail of the default locale is not configured properly. See - # https://github.com/astropy/astropy/issues/2749. For the purposes under - # consideration 'latin1' is an acceptable fallback. - try: - stdio_encoding = locale.getdefaultlocale()[1] or 'latin1' - except ValueError: - # Due to an OSX oddity locale.getdefaultlocale() can also crash - # depending on the user's locale/language settings. See: - # http://bugs.python.org/issue18378 - stdio_encoding = 'latin1' - - # Unlikely to fail at this point but even then let's be flexible - if not isinstance(stdout, _text_type): - stdout = stdout.decode(stdio_encoding, 'replace') - if not isinstance(stderr, _text_type): - stderr = stderr.decode(stdio_encoding, 'replace') - - return (p.returncode, stdout, stderr) - - -def _next_version(version): - """ - Given a parsed version from pkg_resources.parse_version, returns a new - version string with the next minor version. - - Examples - ======== - >>> _next_version(pkg_resources.parse_version('1.2.3')) - '1.3.0' - """ - - if hasattr(version, 'base_version'): - # New version parsing from setuptools >= 8.0 - if version.base_version: - parts = version.base_version.split('.') - else: - parts = [] - else: - parts = [] - for part in version: - if part.startswith('*'): - break - parts.append(part) - - parts = [int(p) for p in parts] - - if len(parts) < 3: - parts += [0] * (3 - len(parts)) - - major, minor, micro = parts[:3] - - return '{0}.{1}.{2}'.format(major, minor + 1, 0) - - -class _DummyFile(object): - """A noop writeable object.""" - - errors = '' # Required for Python 3.x - encoding = 'utf-8' - - def write(self, s): - pass - - def flush(self): - pass - - -@contextlib.contextmanager -def _silence(): - """A context manager that silences sys.stdout and sys.stderr.""" - - old_stdout = sys.stdout - old_stderr = sys.stderr - sys.stdout = _DummyFile() - sys.stderr = _DummyFile() - exception_occurred = False - try: - yield - except: - exception_occurred = True - # Go ahead and clean up so that exception handling can work normally - sys.stdout = old_stdout - sys.stderr = old_stderr - raise - - if not exception_occurred: - sys.stdout = old_stdout - sys.stderr = old_stderr - - -_err_help_msg = """ -If the problem persists consider installing astropy_helpers manually using pip -(`pip install astropy_helpers`) or by manually downloading the source archive, -extracting it, and installing by running `python setup.py install` from the -root of the extracted source code. -""" - - -class _AHBootstrapSystemExit(SystemExit): - def __init__(self, *args): - if not args: - msg = 'An unknown problem occurred bootstrapping astropy_helpers.' - else: - msg = args[0] - - msg += '\n' + _err_help_msg - - super(_AHBootstrapSystemExit, self).__init__(msg, *args[1:]) - - -if sys.version_info[:2] < (2, 7): - # In Python 2.6 the distutils log does not log warnings, errors, etc. to - # stderr so we have to wrap it to ensure consistency at least in this - # module - import distutils - - class log(object): - def __getattr__(self, attr): - return getattr(distutils.log, attr) - - def warn(self, msg, *args): - self._log_to_stderr(distutils.log.WARN, msg, *args) - - def error(self, msg): - self._log_to_stderr(distutils.log.ERROR, msg, *args) - - def fatal(self, msg): - self._log_to_stderr(distutils.log.FATAL, msg, *args) - - def log(self, level, msg, *args): - if level in (distutils.log.WARN, distutils.log.ERROR, - distutils.log.FATAL): - self._log_to_stderr(level, msg, *args) - else: - distutils.log.log(level, msg, *args) - - def _log_to_stderr(self, level, msg, *args): - # This is the only truly 'public' way to get the current threshold - # of the log - current_threshold = distutils.log.set_threshold(distutils.log.WARN) - distutils.log.set_threshold(current_threshold) - if level >= current_threshold: - if args: - msg = msg % args - sys.stderr.write('%s\n' % msg) - sys.stderr.flush() - - log = log() - - -BOOTSTRAPPER = _Bootstrapper.main() - - -def use_astropy_helpers(**kwargs): - """ - Ensure that the `astropy_helpers` module is available and is importable. - This supports automatic submodule initialization if astropy_helpers is - included in a project as a git submodule, or will download it from PyPI if - necessary. - - Parameters - ---------- - - path : str or None, optional - A filesystem path relative to the root of the project's source code - that should be added to `sys.path` so that `astropy_helpers` can be - imported from that path. - - If the path is a git submodule it will automatically be initialized - and/or updated. - - The path may also be to a ``.tar.gz`` archive of the astropy_helpers - source distribution. In this case the archive is automatically - unpacked and made temporarily available on `sys.path` as a ``.egg`` - archive. - - If `None` skip straight to downloading. - - download_if_needed : bool, optional - If the provided filesystem path is not found an attempt will be made to - download astropy_helpers from PyPI. It will then be made temporarily - available on `sys.path` as a ``.egg`` archive (using the - ``setup_requires`` feature of setuptools. If the ``--offline`` option - is given at the command line the value of this argument is overridden - to `False`. - - index_url : str, optional - If provided, use a different URL for the Python package index than the - main PyPI server. - - use_git : bool, optional - If `False` no git commands will be used--this effectively disables - support for git submodules. If the ``--no-git`` option is given at the - command line the value of this argument is overridden to `False`. - - auto_upgrade : bool, optional - By default, when installing a package from a non-development source - distribution ah_boostrap will try to automatically check for patch - releases to astropy-helpers on PyPI and use the patched version over - any bundled versions. Setting this to `False` will disable that - functionality. If the ``--offline`` option is given at the command line - the value of this argument is overridden to `False`. - - offline : bool, optional - If `False` disable all actions that require an internet connection, - including downloading packages from the package index and fetching - updates to any git submodule. Defaults to `True`. - """ - - global BOOTSTRAPPER - - config = BOOTSTRAPPER.config - config.update(**kwargs) - - # Create a new bootstrapper with the updated configuration and run it - BOOTSTRAPPER = _Bootstrapper(**config) - BOOTSTRAPPER.run() diff --git a/astropy_helpers b/astropy_helpers deleted file mode 160000 index c470966..0000000 --- a/astropy_helpers +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c47096618922181df712e42699fcbeeb313580fb diff --git a/docs/conf.py b/docs/conf.py index 502e4f0..a3203a8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -30,22 +30,13 @@ import sys try: - import astropy_helpers + from sphinx_astropy.conf.v1 import * # noqa except ImportError: - # Building from inside the docs/ directory? - if os.path.basename(os.getcwd()) == 'docs': - a_h_path = os.path.abspath(os.path.join('..', 'astropy_helpers')) - if os.path.isdir(a_h_path): - sys.path.insert(1, a_h_path) - -# Load all of the global Astropy configuration -from astropy_helpers.sphinx.conf import * + print('ERROR: the documentation requires the sphinx-astropy package to be installed') + sys.exit(1) # Get configuration information from setup.cfg -try: - from ConfigParser import ConfigParser -except ImportError: - from configparser import ConfigParser +from configparser import ConfigParser conf = ConfigParser() conf.read([os.path.join(os.path.dirname(__file__), '..', 'setup.cfg')]) @@ -75,7 +66,7 @@ # -- Project information ------------------------------------------------------ # This does not *have* to match the package name, but typically does -project = setup_cfg['package_name'] +project = setup_cfg['name'] author = setup_cfg['author'] copyright = '{0}, {1}'.format( datetime.datetime.now().year, setup_cfg['author']) @@ -84,8 +75,8 @@ # |version| and |release|, also used in various other places throughout the # built documents. -__import__(setup_cfg['package_name']) -package = sys.modules[setup_cfg['package_name']] +__import__(setup_cfg['name']) +package = sys.modules[setup_cfg['name']] # The short X.Y version. version = package.__version__.split('-', 1)[0] @@ -164,7 +155,7 @@ if eval(setup_cfg.get('edit_on_github')): extensions += ['astropy_helpers.sphinx.ext.edit_on_github'] - versionmod = __import__(setup_cfg['package_name'] + '.version') + versionmod = __import__(setup_cfg['name'] + '.version') edit_on_github_project = setup_cfg['github_project'] if versionmod.version.release: edit_on_github_branch = "v" + versionmod.version.version diff --git a/gaussfit_catalog/_astropy_init.py b/gaussfit_catalog/_astropy_init.py index 73a52fb..e98ea45 100644 --- a/gaussfit_catalog/_astropy_init.py +++ b/gaussfit_catalog/_astropy_init.py @@ -1,6 +1,6 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst -__all__ = ['__version__', '__githash__', 'test'] +__all__ = ['__version__', 'test'] # this indicates whether or not we are in the package's setup.py try: @@ -17,10 +17,6 @@ from .version import version as __version__ except ImportError: __version__ = '' -try: - from .version import githash as __githash__ -except ImportError: - __githash__ = '' # set up the test command diff --git a/gaussfit_catalog/conftest.py b/gaussfit_catalog/conftest.py index 0ab65db..e86b969 100644 --- a/gaussfit_catalog/conftest.py +++ b/gaussfit_catalog/conftest.py @@ -1,38 +1,59 @@ -# this contains imports plugins that configure py.test for astropy tests. -# by importing them here in conftest.py they are discoverable by py.test -# no matter how it is invoked within the source tree. +"""Configure Test Suite. -from astropy.tests.pytest_plugins import * +This file is used to configure the behavior of pytest when using the Astropy +test infrastructure. It needs to live inside the package in order for it to +get picked up when running the tests inside an interpreter using +packagename.test -## Uncomment the following line to treat all DeprecationWarnings as -## exceptions -# enable_deprecations_as_exceptions() +""" + +import os + +from astropy.version import version as astropy_version + +# For Astropy 3.0 and later, we can use the standalone pytest plugin +if astropy_version < '3.0': + from astropy.tests.pytest_plugins import * # noqa + del pytest_report_header + ASTROPY_HEADER = True +else: + try: + from pytest_astropy_header.display import PYTEST_HEADER_MODULES, TESTED_VERSIONS + ASTROPY_HEADER = True + except ImportError: + ASTROPY_HEADER = False + + +def pytest_configure(config): + """Configure Pytest with Astropy. -## Uncomment and customize the following lines to add/remove entries from -## the list of packages for which version numbers are displayed when running -## the tests. Making it pass for KeyError is essential in some cases when -## the package uses other astropy affiliated packages. -# try: -# PYTEST_HEADER_MODULES['Astropy'] = 'astropy' -# PYTEST_HEADER_MODULES['scikit-image'] = 'skimage' -# del PYTEST_HEADER_MODULES['h5py'] -# except (NameError, KeyError): # NameError is needed to support Astropy < 1.0 -# pass - -## Uncomment the following lines to display the version number of the -## package rather than the version number of Astropy in the top line when -## running the tests. -# import os -# -## This is to figure out the affiliated package version, rather than -## using Astropy's -# try: -# from .version import version -# except ImportError: -# version = 'dev' -# -# try: -# packagename = os.path.basename(os.path.dirname(__file__)) -# TESTED_VERSIONS[packagename] = version -# except NameError: # Needed to support Astropy <= 1.0.0 -# pass + Parameters + ---------- + config : pytest configuration + + """ + if ASTROPY_HEADER: + + config.option.astropy_header = True + + # Customize the following lines to add/remove entries from the list of + # packages for which version numbers are displayed when running the tests. + PYTEST_HEADER_MODULES.pop('Pandas', None) + PYTEST_HEADER_MODULES['scikit-image'] = 'skimage' + + from . import __version__ + packagename = os.path.basename(os.path.dirname(__file__)) + TESTED_VERSIONS[packagename] = __version__ + +# Uncomment the last two lines in this block to treat all DeprecationWarnings as +# exceptions. For Astropy v2.0 or later, there are 2 additional keywords, +# as follow (although default should work for most cases). +# To ignore some packages that produce deprecation warnings on import +# (in addition to 'compiler', 'scipy', 'pygments', 'ipykernel', and +# 'setuptools'), add: +# modules_to_ignore_on_import=['module_1', 'module_2'] +# To ignore some specific deprecation warning messages for Python version +# MAJOR.MINOR or later, add: +# warnings_to_ignore_by_pyver={(MAJOR, MINOR): ['Message to ignore']} +# from astropy.tests.helper import enable_deprecations_as_exceptions # noqa +# enable_deprecations_as_exceptions() diff --git a/gaussfit_catalog/tests/coveragerc b/gaussfit_catalog/tests/coveragerc deleted file mode 100644 index bec7c29..0000000 --- a/gaussfit_catalog/tests/coveragerc +++ /dev/null @@ -1,31 +0,0 @@ -[run] -source = {packagename} -omit = - {packagename}/_astropy_init* - {packagename}/conftest* - {packagename}/cython_version* - {packagename}/setup_package* - {packagename}/*/setup_package* - {packagename}/*/*/setup_package* - {packagename}/tests/* - {packagename}/*/tests/* - {packagename}/*/*/tests/* - {packagename}/version* - -[report] -exclude_lines = - # Have to re-enable the standard pragma - pragma: no cover - - # Don't complain about packages we have installed - except ImportError - - # Don't complain if tests don't hit assertions - raise AssertionError - raise NotImplementedError - - # Don't complain about script hooks - def main\(.*\): - - # Ignore branches that don't pertain to this version of Python - pragma: py{ignore_python_version} \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..7e7daea --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,7 @@ +[build-system] + +requires = ["setuptools", + "setuptools_scm", + "wheel"] + +build-backend = 'setuptools.build_meta' diff --git a/setup.cfg b/setup.cfg index 09d3a82..c50de1e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,52 +1,76 @@ -[build_sphinx] -source-dir = docs -build-dir = docs/_build -all_files = 1 - -[build_docs] -source-dir = docs -build-dir = docs/_build -all_files = 1 - -[upload_docs] -upload-dir = docs/_build/html -show-response = 1 - -[pytest] -minversion = 2.2 -norecursedirs = build docs/_build -doctest_plus = enabled - -[ah_bootstrap] -auto_use = True - -[pep8] -# E101 - mix of tabs and spaces -# W191 - use of tabs -# W291 - trailing whitespace -# W292 - no newline at end of file -# W293 - trailing whitespace -# W391 - blank line at end of file -# E111 - 4 spaces per indentation level -# E112 - 4 spaces per indentation level -# E113 - 4 spaces per indentation level -# E901 - SyntaxError or IndentationError -# E902 - IOError -select = E101,W191,W291,W292,W293,W391,E111,E112,E113,E901,E902 -exclude = extern,sphinx,*parsetab.py - [metadata] +name = gaussfit_catalog package_name = gaussfit_catalog description = Fit Gaussians to each entry in a catalog -long_description = Gaussian fits for everyone! -author = Adam Ginsburg +author = Adam Ginsburg & radio-astr-tools developers author_email = adam.g.ginsburg@gmail.com -license = BSD -url = http://astropy.org/ -edit_on_github = False +license = BSD 3-Clause +license_file = licenses/LICENSE.rst +url = https://github.com/radio-astro-tools/gaussfit_catalog +long_description = file: README.rst +long_description_content_type = text/x-rst +edit_on_github = True github_project = radio-astro-tools/gaussfit_catalog install_requires = astropy # version should be PEP440 compatible (http://www.python.org/dev/peps/pep-0440) version = 0.1.dev -[entry_points] +[options] +zip_safe = False +packages = find: +python_requires = >=3.7 +setup_requires = setuptools_scm +install_requires = + astropy + spectral-cube + radio-beam + + +[options.extras_require] +test = + pytest-astropy +docs = + sphinx-astropy + +[options.package_data] +gaussfit_catalog = data/* + +[tool:pytest] +testpaths = "gaussfit_catalog" "docs" +astropy_header = true +doctest_plus = enabled +text_file_format = rst +addopts = --doctest-rst + +[coverage:run] +omit = + gaussfit_catalog/_astropy_init* + gaussfit_catalog/conftest.py + gaussfit_catalog/*setup_package* + gaussfit_catalog/tests/* + gaussfit_catalog/*/tests/* + gaussfit_catalog/extern/* + gaussfit_catalog/version* + */gaussfit_catalog/_astropy_init* + */gaussfit_catalog/conftest.py + */gaussfit_catalog/*setup_package* + */gaussfit_catalog/tests/* + */gaussfit_catalog/*/tests/* + */gaussfit_catalog/extern/* + */gaussfit_catalog/version* + +[coverage:report] +exclude_lines = + # Have to re-enable the standard pragma + pragma: no cover + # Don't complain about packages we have installed + except ImportError + # Don't complain if tests don't hit assertions + raise AssertionError + raise NotImplementedError + # Don't complain about script hooks + def main\(.*\): + # Ignore branches that don't pertain to this version of Python + pragma: py{ignore_python_version} + # Don't complain about IPython completion helper + def _ipython_key_completions_ diff --git a/setup.py b/setup.py index 500e83c..158ff12 100755 --- a/setup.py +++ b/setup.py @@ -1,139 +1,78 @@ #!/usr/bin/env python # Licensed under a 3-clause BSD style license - see LICENSE.rst -import glob +# NOTE: The configuration for the package, including the name, version, and +# other information are set in the setup.cfg file. + import os import sys -import ah_bootstrap from setuptools import setup -# A dirty hack to get around some early import/configurations ambiguities -if sys.version_info[0] >= 3: - import builtins -else: - import __builtin__ as builtins -builtins._ASTROPY_SETUP_ = True -from astropy_helpers.setup_helpers import (register_commands, get_debug_option, - get_package_info) -from astropy_helpers.git_helpers import get_git_devstr -from astropy_helpers.version_helpers import generate_version_py +# First provide helpful messages if contributors try and run legacy commands +# for tests or docs. + +TEST_HELP = """ +Note: running tests is no longer done using 'python setup.py test'. Instead +you will need to run: + + tox -e test + +If you don't already have tox installed, you can install it with: + + pip install tox + +If you only want to run part of the test suite, you can also use pytest +directly with:: + + pip install -e .[test] + pytest + +For more information, see: + + http://docs.astropy.org/en/latest/development/testguide.html#running-tests +""" + +if 'test' in sys.argv: + print(TEST_HELP) + sys.exit(1) + +DOCS_HELP = """ +Note: building the documentation is no longer done using +'python setup.py build_docs'. Instead you will need to run: + + tox -e build_docs + +If you don't already have tox installed, you can install it with: + + pip install tox + +You can also build the documentation with Sphinx directly using:: + + pip install -e .[docs] + cd docs + make html + +For more information, see: + + http://docs.astropy.org/en/latest/install.html#builddocs +""" + +if 'build_docs' in sys.argv or 'build_sphinx' in sys.argv: + print(DOCS_HELP) + sys.exit(1) -# Get some values from the setup.cfg +VERSION_TEMPLATE = """ +# Note that we need to fall back to the hard-coded version if either +# setuptools_scm can't be imported or setuptools_scm can't determine the +# version, so we catch the generic 'Exception'. try: - from ConfigParser import ConfigParser -except ImportError: - from configparser import ConfigParser - -conf = ConfigParser() -conf.read(['setup.cfg']) -metadata = dict(conf.items('metadata')) - -PACKAGENAME = metadata.get('package_name', 'packagename') -DESCRIPTION = metadata.get('description', 'Astropy affiliated package') -AUTHOR = metadata.get('author', '') -AUTHOR_EMAIL = metadata.get('author_email', '') -LICENSE = metadata.get('license', 'unknown') -URL = metadata.get('url', 'http://astropy.org') - -# order of priority for long_description: -# (1) set in setup.cfg, -# (2) load LONG_DESCRIPTION.rst, -# (3) load README.rst, -# (4) package docstring -readme_glob = 'README*' -_cfg_long_description = metadata.get('long_description', '') -if _cfg_long_description: - LONG_DESCRIPTION = _cfg_long_description - -elif os.path.exists('LONG_DESCRIPTION.rst'): - with open('LONG_DESCRIPTION.rst') as f: - LONG_DESCRIPTION = f.read() - -elif len(glob.glob(readme_glob)) > 0: - with open(glob.glob(readme_glob)[0]) as f: - LONG_DESCRIPTION = f.read() - -else: - # Get the long description from the package's docstring - __import__(PACKAGENAME) - package = sys.modules[PACKAGENAME] - LONG_DESCRIPTION = package.__doc__ - -# Store the package name in a built-in variable so it's easy -# to get from other parts of the setup infrastructure -builtins._ASTROPY_PACKAGE_NAME_ = PACKAGENAME - -# VERSION should be PEP440 compatible (http://www.python.org/dev/peps/pep-0440) -VERSION = metadata.get('version', '0.1.dev') - -# Indicates if this version is a release version -RELEASE = 'dev' not in VERSION - -if not RELEASE: - VERSION += get_git_devstr(False) - -# Populate the dict of setup command overrides; this should be done before -# invoking any other functionality from distutils since it can potentially -# modify distutils' behavior. -cmdclassd = register_commands(PACKAGENAME, VERSION, RELEASE) - -# Freeze build information in version.py -generate_version_py(PACKAGENAME, VERSION, RELEASE, - get_debug_option(PACKAGENAME)) - -# Treat everything in scripts except README* as a script to be installed -scripts = [fname for fname in glob.glob(os.path.join('scripts', '*')) - if not os.path.basename(fname).startswith('README')] - - -# Get configuration information from all of the various subpackages. -# See the docstring for setup_helpers.update_package_files for more -# details. -package_info = get_package_info() - -# Add the project-global data -package_info['package_data'].setdefault(PACKAGENAME, []) -package_info['package_data'][PACKAGENAME].append('data/*') - -# Define entry points for command-line scripts -entry_points = {'console_scripts': []} - -entry_point_list = conf.items('entry_points') -for entry_point in entry_point_list: - entry_points['console_scripts'].append('{0} = {1}'.format(entry_point[0], - entry_point[1])) - -# Include all .c files, recursively, including those generated by -# Cython, since we can not do this in MANIFEST.in with a "dynamic" -# directory name. -c_files = [] -for root, dirs, files in os.walk(PACKAGENAME): - for filename in files: - if filename.endswith('.c'): - c_files.append( - os.path.join( - os.path.relpath(root, PACKAGENAME), filename)) -package_info['package_data'][PACKAGENAME].extend(c_files) - -# Note that requires and provides should not be included in the call to -# ``setup``, since these are now deprecated. See this link for more details: -# https://groups.google.com/forum/#!topic/astropy-dev/urYO8ckB2uM - -setup(name=PACKAGENAME, - version=VERSION, - description=DESCRIPTION, - scripts=scripts, - install_requires=metadata.get('install_requires', 'astropy').strip().split(), - author=AUTHOR, - author_email=AUTHOR_EMAIL, - license=LICENSE, - url=URL, - long_description=LONG_DESCRIPTION, - cmdclass=cmdclassd, - zip_safe=False, - use_2to3=False, - entry_points=entry_points, - **package_info -) + from setuptools_scm import get_version + version = get_version(root='..', relative_to=__file__) +except Exception: + version = '{version}' +""".lstrip() + +setup(use_scm_version={'write_to': os.path.join('gaussfit_catalog', 'version.py'), + 'write_to_template': VERSION_TEMPLATE}) diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..6f17940 --- /dev/null +++ b/tox.ini @@ -0,0 +1,95 @@ +[tox] +envlist = + py{36,37,38}-test{,-alldeps,-devdeps}{,-cov} + py{36,37,38}-test-numpy{116,117,118} + py{36,37,38}-test-astropy{30,40,lts} + build_docs + linkcheck + codestyle +requires = + setuptools >= 30.3.0 + pip >= 19.3.1 +isolated_build = true +indexserver = + NIGHTLY = https://pypi.anaconda.org/scipy-wheels-nightly/simple + +[testenv] +# Suppress display of matplotlib plots generated during docs build +setenv = MPLBACKEND=agg + +# Pass through the following environment variables which may be needed for the CI +passenv = HOME WINDIR LC_ALL LC_CTYPE CC CI TRAVIS + +# Run the tests in a temporary directory to make sure that we don't import +# this package from the source tree +changedir = .tmp/{envname} + +# tox environments are constructed with so-called 'factors' (or terms) +# separated by hyphens, e.g. test-devdeps-cov. Lines below starting with factor: +# will only take effect if that factor is included in the environment name. To +# see a list of example environments that can be run, along with a description, +# run: +# +# tox -l -v +# +description = + run tests + alldeps: with all optional dependencies + devdeps: with the latest developer version of key dependencies + oldestdeps: with the oldest supported version of key dependencies + cov: and test coverage + numpy116: with numpy 1.16.* + numpy117: with numpy 1.17.* + numpy118: with numpy 1.18.* + astropy30: with astropy 3.0.* + astropy40: with astropy 4.0.* + astropylts: with the latest astropy LTS + +# The following provides some specific pinnings for key packages +deps = + + cov: coverage + numpy116: numpy==1.16.* + numpy117: numpy==1.17.* + numpy118: numpy==1.18.* + + astropy30: astropy==3.0.* + astropy40: astropy==4.0.* + astropylts: astropy==4.0.* + + devdeps: :NIGHTLY:numpy + devdeps: git+https://github.com/astropy/astropy.git#egg=astropy + +# The following indicates which extras_require from setup.cfg will be installed +extras = + test + alldeps: all + +commands = + pip freeze + !cov: pytest --pyargs gaussfit_catalog {toxinidir}/docs {posargs} + cov: pytest --pyargs gaussfit_catalog {toxinidir}/docs --cov gaussfit_catalog --cov-config={toxinidir}/setup.cfg {posargs} + cov: coverage xml -o {toxinidir}/coverage.xml + +[testenv:build_docs] +changedir = docs +description = invoke sphinx-build to build the HTML docs +extras = docs +commands = + pip freeze + sphinx-build -W -b html . _build/html + +[testenv:linkcheck] +changedir = docs +description = check the links in the HTML docs +extras = docs +commands = + pip freeze + sphinx-build -W -b linkcheck . _build/html + +[testenv:codestyle] +skip_install = true +changedir = . +description = check code style, e.g. with flake8 +deps = flake8 +commands = flake8 gaussfit_catalog --count --max-line-length=100