././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1578142609.0
specutils-0.7/ 0000755 0000765 0000024 00000000000 00000000000 013451 5 ustar 00erik staff 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1578132243.0
specutils-0.7/CHANGES.rst 0000644 0000765 0000024 00000002504 00000000000 015254 0 ustar 00erik staff 0000000 0000000 0.6 (unreleased)
----------------
API Changes
^^^^^^^^^^^
New Features
^^^^^^^^^^^^
Bug Fixes
^^^^^^^^^
Other Changes and Additions
^^^^^^^^^^^^^^^^^^^^^^^^^^^
0.5.3
-----
Bug Fixes
^^^^^^^^^
- Fix comparison of FITSWCS objects in arithmetic operations.
- Fix example documentation when run in python interpreter.
0.5.2 (2019-02-06)
----------------
Bug Fixes
^^^^^^^^^
- Bugfixes for astropy helpers, pep8 syntax checking, and plotting in docs [#416,#417,#419]
- All automatically generated ``SpectrumList`` loaders now have identifiers. [#440]
- ``SpectralRange.from_center`` parameters corrected after change to SpectralRange interface. [#433]
Other Changes and Additions
^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Improve explanation on creating spectrum continua. [#420]
- Wrap IO identifier functions to ensure they always return True or False and log any errors. [#404]
0.5.1 (2018-11-29)
------------------
Bug Fixes
^^^^^^^^^
- Fixed a bug in using spectral regions that have been inverted. [#403]
- Use the pytest-remotedata plugin to control tests that require access to remote data. [#401,#408]
0.5 (2018-11-21)
----------------
This was the first release of specutils executing the
[APE14](https://github.com/astropy/astropy-APEs/blob/master/APE14.rst)
plan (i.e. the "new" specutils) and therefore intended for broad use.
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1578142609.0
specutils-0.7/PKG-INFO 0000644 0000765 0000024 00000000575 00000000000 014555 0 ustar 00erik staff 0000000 0000000 Metadata-Version: 1.2
Name: specutils
Version: 0.7
Summary: Package for spectroscopic astronomical data
Home-page: http://specutils.readthedocs.io/
Author: Specutils team
Author-email: coordinators@astropy.org
License: BSD
Description: Provides data objects and analysis tools for creating and manipulating spectroscopic astronomical data.
Platform: UNKNOWN
Requires-Python: >=3.5
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1537452672.0
specutils-0.7/README.rst 0000644 0000765 0000024 00000004036 00000000000 015143 0 ustar 00erik staff 0000000 0000000 Specutils
=========
.. image:: https://travis-ci.org/astropy/specutils.svg?branch=master
:target: https://travis-ci.org/astropy/specutils
.. image:: https://readthedocs.org/projects/specutils/badge/?version=latest
:target: http://specutils.readthedocs.io/en/latest/?badge=latest
:alt: Documentation Status
.. image:: http://img.shields.io/badge/powered%20by-AstroPy-orange.svg?style=flat
:target: http://www.astropy.org/
Specutils is an `Astropy affiliated package `_
with the goal of providing a shared set of Python representations of
astronomical spectra and basic tools to operate on these spectra. The effort is
also meant to be a "hub", helping to unite the Python astronomical spectroscopy
community around shared effort, much as Astropy is meant to for the wider
astronomy Python ecosystem. This broader effort is outlined in the
`APE13 document `_.
Note that Specutils is not intended to meet all possible spectroscopic analysis or
reduction needs. While it provides some standard analysis functionality
(following the Python philosophy of "batteries included"), it is best thought
of as a "tool box" that provides pieces of functionality that can be used to
build a particular scientific workflow or higher-level analysis tool. To that
end, it is also meant to facilitate connecting together disparate reduction
pipelines and analysis tools through shared Python representations of
spectroscopic data.
Getting Started with Specutils
------------------------------
For details on installing and using Specutils, see the
`specutils documentation `_.
Note that Specutils now only supports Python 3. While some functionality may
continue to work on Python 2, it is not tested and support cannot be guaranteed
(due to the sunsetting of Python 2 support by the Python and Astropy development
teams).
License
-------
Specutils is licensed under a 3-clause BSD style license. Please see the LICENSE.rst file.
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1578132243.0
specutils-0.7/ah_bootstrap.py 0000644 0000765 0000024 00000110634 00000000000 016515 0 ustar 00erik staff 0000000 0000000 """
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 and by default the system-installed version
of astropy-helpers will be used (however, ``ah_bootstrap.use_astropy_helpers``
may be called manually from within the setup.py script).
This behavior can also be controlled using the ``--auto-use`` and
``--no-auto-use`` command-line flags. For clarity, an alias for
``--no-auto-use`` is ``--use-system-astropy-helpers``, and we recommend using
the latter if needed.
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 io
import locale
import os
import re
import subprocess as sp
import sys
from distutils import log
from distutils.debug import DEBUG
from configparser import ConfigParser, RawConfigParser
import pkg_resources
from setuptools import Distribution
from setuptools.package_index import PackageIndex
# This is the minimum Python version required for astropy-helpers
__minimum_python_version__ = (3, 5)
# TODO: Maybe enable checking for a specific version of astropy_helpers?
DIST_NAME = 'astropy-helpers'
PACKAGE_NAME = 'astropy_helpers'
UPPER_VERSION_EXCLUSIVE = None
# 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)
]
# Start off by parsing the setup.cfg file
SETUP_CFG = ConfigParser()
if os.path.exists('setup.cfg'):
try:
SETUP_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))
# We used package_name in the package template for a while instead of name
if SETUP_CFG.has_option('metadata', 'name'):
parent_package = SETUP_CFG.get('metadata', 'name')
elif SETUP_CFG.has_option('metadata', 'package_name'):
parent_package = SETUP_CFG.get('metadata', 'package_name')
else:
parent_package = None
if SETUP_CFG.has_option('options', 'python_requires'):
python_requires = SETUP_CFG.get('options', 'python_requires')
# The python_requires key has a syntax that can be parsed by SpecifierSet
# in the packaging package. However, we don't want to have to depend on that
# package, so instead we can use setuptools (which bundles packaging). We
# have to add 'python' to parse it with Requirement.
from pkg_resources import Requirement
req = Requirement.parse('python' + python_requires)
# We want the Python version as a string, which we can get from the platform module
import platform
# strip off trailing '+' incase this is a dev install of python
python_version = platform.python_version().strip('+')
# allow pre-releases to count as 'new enough'
if not req.specifier.contains(python_version, True):
if parent_package is None:
message = "ERROR: Python {} is required by this package\n".format(req.specifier)
else:
message = "ERROR: Python {} is required by {}\n".format(req.specifier, parent_package)
sys.stderr.write(message)
sys.exit(1)
if sys.version_info < __minimum_python_version__:
if parent_package is None:
message = "ERROR: Python {} or later is required by astropy-helpers\n".format(
__minimum_python_version__)
else:
message = "ERROR: Python {} or later is required by astropy-helpers for {}\n".format(
__minimum_python_version__, parent_package)
sys.stderr.write(message)
sys.exit(1)
_str_types = (str, bytes)
# 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):
# Check that setuptools 30.3 or later is present
from distutils.version import LooseVersion
try:
import setuptools
assert LooseVersion(setuptools.__version__) >= LooseVersion('30.3')
except (ImportError, AssertionError):
sys.stderr.write("ERROR: setuptools 30.3 or later is required by astropy-helpers\n")
sys.exit(1)
# typing as a dependency for 1.6.1+ Sphinx causes issues when imported after
# initializing submodule with ah_boostrap.py
# See discussion and references in
# https://github.com/astropy/astropy-helpers/issues/302
try:
import typing # noqa
except ImportError:
pass
# 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 # noqa
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...
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 not isinstance(path, str):
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 SETUP_CFG.has_section('ah_bootstrap'):
return {}
config = {}
for option, type_ in CFG_OPTIONS:
if not SETUP_CFG.has_option('ah_bootstrap', option):
continue
if type_ is bool:
value = SETUP_CFG.getboolean('ah_bootstrap', option)
else:
value = SETUP_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')
if '--auto-use' in argv:
config['auto_use'] = True
argv.remove('--auto-use')
if '--no-auto-use' in argv:
config['auto_use'] = False
argv.remove('--no-auto-use')
if '--use-system-astropy-helpers' in argv:
config['auto_use'] = False
argv.remove('--use-system-astropy-helpers')
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):
# We use subprocess instead of run_setup from setuptools to
# avoid segmentation faults - see the following for more details:
# https://github.com/cython/cython/issues/2104
sp.check_output([sys.executable, 'setup.py', 'egg_info'], cwd=path)
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:
if UPPER_VERSION_EXCLUSIVE is None:
req = DIST_NAME
else:
req = '{0}<{1}'.format(DIST_NAME, UPPER_VERSION_EXCLUSIVE)
attrs = {'setup_requires': [req]}
# NOTE: we need to parse the config file (e.g. setup.cfg) to make sure
# it honours the options set in the [easy_install] section, and we need
# to explicitly fetch the requirement eggs as setup_requires does not
# get honored in recent versions of setuptools:
# https://github.com/pypa/setuptools/issues/1273
try:
context = _verbose if DEBUG else _silence
with context():
dist = _Distribution(attrs=attrs)
try:
dist.parse_config_files(ignore_option_errors=True)
dist.fetch_build_eggs(req)
except TypeError:
# On older versions of setuptools, ignore_option_errors
# doesn't exist, and the above two lines are not needed
# so we can just continue
pass
# 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(
r'^(?P[+-U ])(?P[0-9a-f]{40}) '
r'(?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 _AHBootstrapSystemExit(
'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 _AHBootstrapSystemExit(
'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, str):
stdout = stdout.decode(stdio_encoding, 'replace')
if not isinstance(stderr, str):
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 _verbose():
yield
@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:])
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()
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1578142609.0
specutils-0.7/astropy_helpers/ 0000755 0000765 0000024 00000000000 00000000000 016674 5 ustar 00erik staff 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1565986938.0
specutils-0.7/astropy_helpers/CHANGES.rst 0000644 0000765 0000024 00000052523 00000000000 020505 0 ustar 00erik staff 0000000 0000000 astropy-helpers Changelog
*************************
3.2.1 (2019-06-13)
------------------
- Reverting issuing deprecation warning for the ``build_sphinx`` command. [#482]
- Make sure that all data files get included in tar file releases. [#485]
3.2 (2019-05-29)
----------------
- Make sure that ``[options.package_data]`` in setup.cfg is taken into account
when collecting package data. [#453]
- Simplified the code for the custom build_ext command. [#446]
- Avoid importing the astropy package when trying to get the test command
when testing astropy itself. [#450]
- Avoid importing whole package when trying to get version information. Note
that this has also introduced a small API change - ``cython_version`` and
``compiler`` can no longer be imported from the ``package.version`` module
generated by astropy-helpers. Instead, you can import these from
``package.cython_version`` and ``package.compiler_version`` respectively. [#442]
- Make it possible to call ``generate_version_py`` and ``register_commands``
without any arguments, which causes information to be read in from the
``setup.cfg`` file. [#440]
- Simplified setup.py and moved most of the configuration to setup.cfg. [#445]
- Add a new ``astropy_helpers.setup_helpers.setup`` function that does all
the default boilerplate in typical ``setup.py`` files that use
astropy-helpers. [#443]
- Remove ``deprecated``, ``deprecated_attribute``, and ``minversion`` from
``astropy_helpers.utils``. [#447]
- Updated minimum required version of setuptools to 30.3.0. [#440]
- Remove functionality to adjust compilers if a broken compiler is detected.
This is not useful anymore as only a single compiler was previously patched
(now unlikely to be used) and this was only to fix a compilation issue in the
core astropy package. [#421]
- ``sphinx-astropy`` is now a required dependency to build the docs, the
machinery to install it as eggs have been removed. [#474]
3.1.1 (2019-02-22)
------------------
- Moved documentation from README to Sphinx. [#444]
- Fixed broken OpenMP detection when building with ``-coverage``. [#434]
3.1 (2018-12-04)
----------------
- Added extensive documentation about astropy-helpers to the README.rst file. [#416]
- Fixed the compatibility of the build_docs command with Sphinx 1.8 and above. [#413]
- Removing deprecated test_helpers.py file. [#369]
- Removing ez_setup.py file and requiring setuptools 1.0 or later. [#384]
- Remove all sphinx components from ``astropy-helpers``. These are now replaced
by the ``sphinx-astropy`` package in conjunction with the ``astropy-theme-sphinx``,
``sphinx-automodapi``, and ``numpydoc`` packages. [#368]
- openmp_helpers.py: Make add_openmp_flags_if_available() work for clang.
The necessary include, library, and runtime paths now get added to the C test code
used to determine if openmp works.
Autogenerator utility added ``openmp_enabled.is_openmp_enabled()``
which can be called post build to determine state of OpenMP support.
[#382]
- Add version_info tuple to autogenerated version.py. Allows for simple
version checking, i.e. version_info > (2,0,1). [#385]
3.0.2 (2018-06-01)
------------------
- Nothing changed.
3.0.1 (2018-02-22)
------------------
- Nothing changed.
3.0 (2018-02-09)
----------------
- Removing Python 2 support, including 2to3. Packages wishing to keep Python
2 support should NOT update to this version. [#340]
- Removing deprecated _test_compat making astropy a hard dependency for
packages wishing to use the astropy tests machinery. [#314]
- Removing unused 'register' command since packages should be uploaded
with twine and get registered automatically. [#332]
2.0.10 (2019-05-29)
-------------------
- Removed ``tocdepthfix`` sphinx extension that worked around a big in
Sphinx that has been long fixed. [#475]
- Allow Python dev versions to pass the python version check. [#476]
- Updated bundled version of sphinx-automodapi to v0.11. [#478]
2.0.9 (2019-02-22)
------------------
- Updated bundled version of sphinx-automodapi to v0.10. [#439]
- Updated bundled sphinx extensions version to sphinx-astropy v1.1.1. [#454]
- Include package name in error message for Python version in
``ah_bootstrap.py``. [#441]
2.0.8 (2018-12-04)
------------------
- Fixed compatibility with Sphinx 1.8+. [#428]
- Fixed error that occurs when installing a package in an environment where
``numpy`` is not already installed. [#404]
- Updated bundled version of sphinx-automodapi to v0.9. [#422]
- Updated bundled version of numpydoc to v0.8.0. [#423]
2.0.7 (2018-06-01)
------------------
- Removing ez_setup.py file and requiring setuptools 1.0 or later. [#384]
2.0.6 (2018-02-24)
------------------
- Avoid deprecation warning due to ``exclude=`` keyword in ``setup.py``. [#379]
2.0.5 (2018-02-22)
------------------
- Fix segmentation faults that occurred when the astropy-helpers submodule
was first initialized in packages that also contained Cython code. [#375]
2.0.4 (2018-02-09)
------------------
- Support dotted package names as namespace packages in generate_version_py.
[#370]
- Fix compatibility with setuptools 36.x and above. [#372]
- Fix false negative in add_openmp_flags_if_available when measuring code
coverage with gcc. [#374]
2.0.3 (2018-01-20)
------------------
- Make sure that astropy-helpers 3.x.x is not downloaded on Python 2. [#362, #363]
- The bundled version of sphinx-automodapi has been updated to v0.7. [#365]
- Add --auto-use and --no-auto-use command-line flags to match the
``auto_use`` configuration option, and add an alias
``--use-system-astropy-helpers`` for ``--no-auto-use``. [#366]
2.0.2 (2017-10-13)
------------------
- Added new helper function add_openmp_flags_if_available that can add
OpenMP compilation flags to a C/Cython extension if needed. [#346]
- Update numpydoc to v0.7. [#343]
- The function ``get_git_devstr`` now returns ``'0'`` instead of ``None`` when
no git repository is present. This allows generation of development version
strings that are in a format that ``setuptools`` expects (e.g. "1.1.3.dev0"
instead of "1.1.3.dev"). [#330]
- It is now possible to override generated timestamps to make builds
reproducible by setting the ``SOURCE_DATE_EPOCH`` environment variable [#341]
- Mark Sphinx extensions as parallel-safe. [#344]
- Switch to using mathjax instead of imgmath for local builds. [#342]
- Deprecate ``exclude`` parameter of various functions in setup_helpers since
it could not work as intended. Add new function ``add_exclude_packages`` to
provide intended behavior. [#331]
- Allow custom Sphinx doctest extension to recognize and process standard
doctest directives ``testsetup`` and ``doctest``. [#335]
2.0.1 (2017-07-28)
------------------
- Fix compatibility with Sphinx <1.5. [#326]
2.0 (2017-07-06)
----------------
- Add support for package that lies in a subdirectory. [#249]
- Removing ``compat.subprocess``. [#298]
- Python 3.3 is no longer supported. [#300]
- The 'automodapi' Sphinx extension (and associated dependencies) has now
been moved to a standalone package which can be found at
https://github.com/astropy/sphinx-automodapi - this is now bundled in
astropy-helpers under astropy_helpers.extern.automodapi for
convenience. Version shipped with astropy-helpers is v0.6.
[#278, #303, #309, #323]
- The ``numpydoc`` Sphinx extension has now been moved to
``astropy_helpers.extern``. [#278]
- Fix ``build_docs`` error catching, so it doesn't hide Sphinx errors. [#292]
- Fix compatibility with Sphinx 1.6. [#318]
- Updating ez_setup.py to the last version before it's removal. [#321]
1.3.1 (2017-03-18)
------------------
- Fixed the missing button to hide output in documentation code
blocks. [#287]
- Fixed bug when ``build_docs`` when running with the clean (-l) option. [#289]
- Add alternative location for various intersphinx inventories to fall back
to. [#293]
1.3 (2016-12-16)
----------------
- ``build_sphinx`` has been deprecated in favor of the ``build_docs`` command.
[#246]
- Force the use of Cython's old ``build_ext`` command. A new ``build_ext``
command was added in Cython 0.25, but it does not work with astropy-helpers
currently. [#261]
1.2 (2016-06-18)
----------------
- Added sphinx configuration value ``automodsumm_inherited_members``.
If ``True`` this will include members that are inherited from a base
class in the generated API docs. Defaults to ``False`` which matches
the previous behavior. [#215]
- Fixed ``build_sphinx`` to recognize builds that succeeded but have output
*after* the "build succeeded." statement. This only applies when
``--warnings-returncode`` is given (which is primarily relevant for Travis
documentation builds). [#223]
- Fixed ``build_sphinx`` the sphinx extensions to not output a spurious warning
for sphinx versions > 1.4. [#229]
- Add Python version dependent local sphinx inventories that contain
otherwise missing references. [#216]
- ``astropy_helpers`` now require Sphinx 1.3 or later. [#226]
1.1.2 (2016-03-9)
-----------------
- The CSS for the sphinx documentation was altered to prevent some text overflow
problems. [#217]
1.1.1 (2015-12-23)
------------------
- Fixed crash in build with ``AttributeError: cython_create_listing`` with
older versions of setuptools. [#209, #210]
1.1 (2015-12-10)
----------------
- The original ``AstropyTest`` class in ``astropy_helpers``, which implements
the ``setup.py test`` command, is deprecated in favor of moving the
implementation of that command closer to the actual Astropy test runner in
``astropy.tests``. Now a dummy ``test`` command is provided solely for
informing users that they need ``astropy`` installed to run the tests
(however, the previous, now deprecated implementation is still provided and
continues to work with older versions of Astropy). See the related issue for
more details. [#184]
- Added a useful new utility function to ``astropy_helpers.utils`` called
``find_data_files``. This is similar to the ``find_packages`` function in
setuptools in that it can be used to search a package for data files
(matching a pattern) that can be passed to the ``package_data`` argument for
``setup()``. See the docstring to ``astropy_helpers.utils.find_data_files``
for more details. [#42]
- The ``astropy_helpers`` module now sets the global ``_ASTROPY_SETUP_``
flag upon import (from within a ``setup.py``) script, so it's not necessary
to have this in the ``setup.py`` script explicitly. If in doubt though,
there's no harm in setting it twice. Putting it in ``astropy_helpers``
just ensures that any other imports that occur during build will have this
flag set. [#191]
- It is now possible to use Cython as a ``setup_requires`` build requirement,
and still build Cython extensions even if Cython wasn't available at the
beginning of the build processes (that is, is automatically downloaded via
setuptools' processing of ``setup_requires``). [#185]
- Moves the ``adjust_compiler`` check into the ``build_ext`` command itself,
so it's only used when actually building extension modules. This also
deprecates the stand-alone ``adjust_compiler`` function. [#76]
- When running the ``build_sphinx`` / ``build_docs`` command with the ``-w``
option, the output from Sphinx is streamed as it runs instead of silently
buffering until the doc build is complete. [#197]
1.0.7 (unreleased)
------------------
- Fix missing import in ``astropy_helpers/utils.py``. [#196]
1.0.6 (2015-12-04)
------------------
- Fixed bug where running ``./setup.py build_sphinx`` could return successfully
even when the build was not successful (and should have returned a non-zero
error code). [#199]
1.0.5 (2015-10-02)
------------------
- Fixed a regression in the ``./setup.py test`` command that was introduced in
v1.0.4.
1.0.4 (2015-10-02)
------------------
- Fixed issue with the sphinx documentation css where the line numbers for code
blocks were not aligned with the code. [#179, #180]
- Fixed crash that could occur when trying to build Cython extension modules
when Cython isn't installed. Normally this still results in a failed build,
but was supposed to provide a useful error message rather than crash
outright (this was a regression introduced in v1.0.3). [#181]
- Fixed a crash that could occur on Python 3 when a working C compiler isn't
found. [#182]
- Quieted warnings about deprecated Numpy API in Cython extensions, when
building Cython extensions against Numpy >= 1.7. [#183, #186]
- Improved support for py.test >= 2.7--running the ``./setup.py test`` command
now copies all doc pages into the temporary test directory as well, so that
all test files have a "common root directory". [#189, #190]
1.0.3 (2015-07-22)
------------------
- Added workaround for sphinx-doc/sphinx#1843, a but in Sphinx which
prevented descriptor classes with a custom metaclass from being documented
correctly. [#158]
- Added an alias for the ``./setup.py build_sphinx`` command as
``./setup.py build_docs`` which, to a new contributor, should hopefully be
less cryptic. [#161]
- The fonts in graphviz diagrams now match the font of the HTML content. [#169]
- When the documentation is built on readthedocs.org, MathJax will be
used for math rendering. When built elsewhere, the "pngmath"
extension is still used for math rendering. [#170]
- Fix crash when importing astropy_helpers when running with ``python -OO``
[#171]
- The ``build`` and ``build_ext`` stages now correctly recognize the presence
of C++ files in Cython extensions (previously only vanilla C worked). [#173]
1.0.2 (2015-04-02)
------------------
- Various fixes enabling the astropy-helpers Sphinx build command and
Sphinx extensions to work with Sphinx 1.3. [#148]
- More improvement to the ability to handle multiple versions of
astropy-helpers being imported in the same Python interpreter session
in the (somewhat rare) case of nested installs. [#147]
- To better support high resolution displays, use SVG for the astropy
logo and linkout image, falling back to PNGs for browsers that
support it. [#150, #151]
- Improve ``setup_helpers.get_compiler_version`` to work with more compilers,
and to return more info. This will help fix builds of Astropy on less
common compilers, like Sun C. [#153]
1.0.1 (2015-03-04)
------------------
- Released in concert with v0.4.8 to address the same issues.
0.4.8 (2015-03-04)
------------------
- Improved the ``ah_bootstrap`` script's ability to override existing
installations of astropy-helpers with new versions in the context of
installing multiple packages simultaneously within the same Python
interpreter (e.g. when one package has in its ``setup_requires`` another
package that uses a different version of astropy-helpers. [#144]
- Added a workaround to an issue in matplotlib that can, in rare cases, lead
to a crash when installing packages that import matplotlib at build time.
[#144]
1.0 (2015-02-17)
----------------
- Added new pre-/post-command hook points for ``setup.py`` commands. Now any
package can define code to run before and/or after any ``setup.py`` command
without having to manually subclass that command by adding
``pre__hook`` and ``post__hook`` callables to
the package's ``setup_package.py`` module. See the PR for more details.
[#112]
- The following objects in the ``astropy_helpers.setup_helpers`` module have
been relocated:
- ``get_dummy_distribution``, ``get_distutils_*``, ``get_compiler_option``,
``add_command_option``, ``is_distutils_display_option`` ->
``astropy_helpers.distutils_helpers``
- ``should_build_with_cython``, ``generate_build_ext_command`` ->
``astropy_helpers.commands.build_ext``
- ``AstropyBuildPy`` -> ``astropy_helpers.commands.build_py``
- ``AstropyBuildSphinx`` -> ``astropy_helpers.commands.build_sphinx``
- ``AstropyInstall`` -> ``astropy_helpers.commands.install``
- ``AstropyInstallLib`` -> ``astropy_helpers.commands.install_lib``
- ``AstropyRegister`` -> ``astropy_helpers.commands.register``
- ``get_pkg_version_module`` -> ``astropy_helpers.version_helpers``
- ``write_if_different``, ``import_file``, ``get_numpy_include_path`` ->
``astropy_helpers.utils``
All of these are "soft" deprecations in the sense that they are still
importable from ``astropy_helpers.setup_helpers`` for now, and there is
no (easy) way to produce deprecation warnings when importing these objects
from ``setup_helpers`` rather than directly from the modules they are
defined in. But please consider updating any imports to these objects.
[#110]
- Use of the ``astropy.sphinx.ext.astropyautosummary`` extension is deprecated
for use with Sphinx < 1.2. Instead it should suffice to remove this
extension for the ``extensions`` list in your ``conf.py`` and add the stock
``sphinx.ext.autosummary`` instead. [#131]
0.4.7 (2015-02-17)
------------------
- Fixed incorrect/missing git hash being added to the generated ``version.py``
when creating a release. [#141]
0.4.6 (2015-02-16)
------------------
- Fixed problems related to the automatically generated _compiler
module not being created properly. [#139]
0.4.5 (2015-02-11)
------------------
- Fixed an issue where ah_bootstrap.py could blow up when astropy_helper's
version number is 1.0.
- Added a workaround for documentation of properties in the rare case
where the class's metaclass has a property of the same name. [#130]
- Fixed an issue on Python 3 where importing a package using astropy-helper's
generated version.py module would crash when the current working directory
is an empty git repository. [#114, #137]
- Fixed an issue where the "revision count" appended to .dev versions by
the generated version.py did not accurately reflect the revision count for
the package it belongs to, and could be invalid if the current working
directory is an unrelated git repository. [#107, #137]
- Likewise, fixed a confusing warning message that could occur in the same
circumstances as the above issue. [#121, #137]
0.4.4 (2014-12-31)
------------------
- More improvements for building the documentation using Python 3.x. [#100]
- Additional minor fixes to Python 3 support. [#115]
- Updates to support new test features in Astropy [#92, #106]
0.4.3 (2014-10-22)
------------------
- The generated ``version.py`` file now preserves the git hash of installed
copies of the package as well as when building a source distribution. That
is, the git hash of the changeset that was installed/released is preserved.
[#87]
- In smart resolver add resolution for class links when they exist in the
intersphinx inventory, but not the mapping of the current package
(e.g. when an affiliated package uses an astropy core class of which
"actual" and "documented" location differs) [#88]
- Fixed a bug that could occur when running ``setup.py`` for the first time
in a repository that uses astropy-helpers as a submodule:
``AttributeError: 'NoneType' object has no attribute 'mkdtemp'`` [#89]
- Fixed a bug where optional arguments to the ``doctest-skip`` Sphinx
directive were sometimes being left in the generated documentation output.
[#90]
- Improved support for building the documentation using Python 3.x. [#96]
- Avoid error message if .git directory is not present. [#91]
0.4.2 (2014-08-09)
------------------
- Fixed some CSS issues in generated API docs. [#69]
- Fixed the warning message that could be displayed when generating a
version number with some older versions of git. [#77]
- Fixed automodsumm to work with new versions of Sphinx (>= 1.2.2). [#80]
0.4.1 (2014-08-08)
------------------
- Fixed git revision count on systems with git versions older than v1.7.2.
[#70]
- Fixed display of warning text when running a git command fails (previously
the output of stderr was not being decoded properly). [#70]
- The ``--offline`` flag to ``setup.py`` understood by ``ah_bootstrap.py``
now also prevents git from going online to fetch submodule updates. [#67]
- The Sphinx extension for converting issue numbers to links in the changelog
now supports working on arbitrary pages via a new ``conf.py`` setting:
``changelog_links_docpattern``. By default it affects the ``changelog``
and ``whatsnew`` pages in one's Sphinx docs. [#61]
- Fixed crash that could result from users with missing/misconfigured
locale settings. [#58]
- The font used for code examples in the docs is now the
system-defined ``monospace`` font, rather than ``Minaco``, which is
not available on all platforms. [#50]
0.4 (2014-07-15)
----------------
- Initial release of astropy-helpers. See `APE4
`_ for
details of the motivation and design of this package.
- The ``astropy_helpers`` package replaces the following modules in the
``astropy`` package:
- ``astropy.setup_helpers`` -> ``astropy_helpers.setup_helpers``
- ``astropy.version_helpers`` -> ``astropy_helpers.version_helpers``
- ``astropy.sphinx`` - > ``astropy_helpers.sphinx``
These modules should be considered deprecated in ``astropy``, and any new,
non-critical changes to those modules will be made in ``astropy_helpers``
instead. Affiliated packages wishing to make use those modules (as in the
Astropy package-template) should use the versions from ``astropy_helpers``
instead, and include the ``ah_bootstrap.py`` script in their project, for
bootstrapping the ``astropy_helpers`` package in their setup.py script.
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1537500356.0
specutils-0.7/astropy_helpers/LICENSE.rst 0000644 0000765 0000024 00000002723 00000000000 020514 0 ustar 00erik staff 0000000 0000000 Copyright (c) 2014, Astropy Developers
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
* Neither the name of the Astropy Team nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1565986938.0
specutils-0.7/astropy_helpers/README.rst 0000644 0000765 0000024 00000002663 00000000000 020372 0 ustar 00erik staff 0000000 0000000 astropy-helpers
===============
.. image:: https://travis-ci.org/astropy/astropy-helpers.svg
:target: https://travis-ci.org/astropy/astropy-helpers
.. image:: https://ci.appveyor.com/api/projects/status/rt9161t9mhx02xp7/branch/master?svg=true
:target: https://ci.appveyor.com/project/Astropy/astropy-helpers
.. image:: https://codecov.io/gh/astropy/astropy-helpers/branch/master/graph/badge.svg
:target: https://codecov.io/gh/astropy/astropy-helpers
The **astropy-helpers** package includes many build, installation, and
documentation-related tools used by the Astropy project, but packaged separately
for use by other projects that wish to leverage this work. The motivation behind
this package and details of its implementation are in the accepted
`Astropy Proposal for Enhancement (APE) 4 `_.
Astropy-helpers is not a traditional package in the sense that it is not
intended to be installed directly by users or developers. Instead, it is meant
to be accessed when the ``setup.py`` command is run - see the "Using
astropy-helpers in a package" section in the documentation for how
to do this. For a real-life example of how to implement astropy-helpers in a
project, see the ``setup.py`` and ``setup.cfg`` files of the
`Affiliated package template `_.
For more information, see the documentation at http://astropy-helpers.readthedocs.io
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1565986938.0
specutils-0.7/astropy_helpers/ah_bootstrap.py 0000644 0000765 0000024 00000110634 00000000000 021740 0 ustar 00erik staff 0000000 0000000 """
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 and by default the system-installed version
of astropy-helpers will be used (however, ``ah_bootstrap.use_astropy_helpers``
may be called manually from within the setup.py script).
This behavior can also be controlled using the ``--auto-use`` and
``--no-auto-use`` command-line flags. For clarity, an alias for
``--no-auto-use`` is ``--use-system-astropy-helpers``, and we recommend using
the latter if needed.
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 io
import locale
import os
import re
import subprocess as sp
import sys
from distutils import log
from distutils.debug import DEBUG
from configparser import ConfigParser, RawConfigParser
import pkg_resources
from setuptools import Distribution
from setuptools.package_index import PackageIndex
# This is the minimum Python version required for astropy-helpers
__minimum_python_version__ = (3, 5)
# TODO: Maybe enable checking for a specific version of astropy_helpers?
DIST_NAME = 'astropy-helpers'
PACKAGE_NAME = 'astropy_helpers'
UPPER_VERSION_EXCLUSIVE = None
# 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)
]
# Start off by parsing the setup.cfg file
SETUP_CFG = ConfigParser()
if os.path.exists('setup.cfg'):
try:
SETUP_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))
# We used package_name in the package template for a while instead of name
if SETUP_CFG.has_option('metadata', 'name'):
parent_package = SETUP_CFG.get('metadata', 'name')
elif SETUP_CFG.has_option('metadata', 'package_name'):
parent_package = SETUP_CFG.get('metadata', 'package_name')
else:
parent_package = None
if SETUP_CFG.has_option('options', 'python_requires'):
python_requires = SETUP_CFG.get('options', 'python_requires')
# The python_requires key has a syntax that can be parsed by SpecifierSet
# in the packaging package. However, we don't want to have to depend on that
# package, so instead we can use setuptools (which bundles packaging). We
# have to add 'python' to parse it with Requirement.
from pkg_resources import Requirement
req = Requirement.parse('python' + python_requires)
# We want the Python version as a string, which we can get from the platform module
import platform
# strip off trailing '+' incase this is a dev install of python
python_version = platform.python_version().strip('+')
# allow pre-releases to count as 'new enough'
if not req.specifier.contains(python_version, True):
if parent_package is None:
message = "ERROR: Python {} is required by this package\n".format(req.specifier)
else:
message = "ERROR: Python {} is required by {}\n".format(req.specifier, parent_package)
sys.stderr.write(message)
sys.exit(1)
if sys.version_info < __minimum_python_version__:
if parent_package is None:
message = "ERROR: Python {} or later is required by astropy-helpers\n".format(
__minimum_python_version__)
else:
message = "ERROR: Python {} or later is required by astropy-helpers for {}\n".format(
__minimum_python_version__, parent_package)
sys.stderr.write(message)
sys.exit(1)
_str_types = (str, bytes)
# 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):
# Check that setuptools 30.3 or later is present
from distutils.version import LooseVersion
try:
import setuptools
assert LooseVersion(setuptools.__version__) >= LooseVersion('30.3')
except (ImportError, AssertionError):
sys.stderr.write("ERROR: setuptools 30.3 or later is required by astropy-helpers\n")
sys.exit(1)
# typing as a dependency for 1.6.1+ Sphinx causes issues when imported after
# initializing submodule with ah_boostrap.py
# See discussion and references in
# https://github.com/astropy/astropy-helpers/issues/302
try:
import typing # noqa
except ImportError:
pass
# 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 # noqa
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...
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 not isinstance(path, str):
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 SETUP_CFG.has_section('ah_bootstrap'):
return {}
config = {}
for option, type_ in CFG_OPTIONS:
if not SETUP_CFG.has_option('ah_bootstrap', option):
continue
if type_ is bool:
value = SETUP_CFG.getboolean('ah_bootstrap', option)
else:
value = SETUP_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')
if '--auto-use' in argv:
config['auto_use'] = True
argv.remove('--auto-use')
if '--no-auto-use' in argv:
config['auto_use'] = False
argv.remove('--no-auto-use')
if '--use-system-astropy-helpers' in argv:
config['auto_use'] = False
argv.remove('--use-system-astropy-helpers')
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):
# We use subprocess instead of run_setup from setuptools to
# avoid segmentation faults - see the following for more details:
# https://github.com/cython/cython/issues/2104
sp.check_output([sys.executable, 'setup.py', 'egg_info'], cwd=path)
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:
if UPPER_VERSION_EXCLUSIVE is None:
req = DIST_NAME
else:
req = '{0}<{1}'.format(DIST_NAME, UPPER_VERSION_EXCLUSIVE)
attrs = {'setup_requires': [req]}
# NOTE: we need to parse the config file (e.g. setup.cfg) to make sure
# it honours the options set in the [easy_install] section, and we need
# to explicitly fetch the requirement eggs as setup_requires does not
# get honored in recent versions of setuptools:
# https://github.com/pypa/setuptools/issues/1273
try:
context = _verbose if DEBUG else _silence
with context():
dist = _Distribution(attrs=attrs)
try:
dist.parse_config_files(ignore_option_errors=True)
dist.fetch_build_eggs(req)
except TypeError:
# On older versions of setuptools, ignore_option_errors
# doesn't exist, and the above two lines are not needed
# so we can just continue
pass
# 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(
r'^(?P[+-U ])(?P[0-9a-f]{40}) '
r'(?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 _AHBootstrapSystemExit(
'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 _AHBootstrapSystemExit(
'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, str):
stdout = stdout.decode(stdio_encoding, 'replace')
if not isinstance(stderr, str):
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 _verbose():
yield
@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:])
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()
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1578142609.0
specutils-0.7/astropy_helpers/astropy_helpers/ 0000755 0000765 0000024 00000000000 00000000000 022117 5 ustar 00erik staff 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1537500356.0
specutils-0.7/astropy_helpers/astropy_helpers/__init__.py 0000644 0000765 0000024 00000003312 00000000000 024227 0 ustar 00erik staff 0000000 0000000 try:
from .version import version as __version__
from .version import githash as __githash__
except ImportError:
__version__ = ''
__githash__ = ''
# If we've made it as far as importing astropy_helpers, we don't need
# ah_bootstrap in sys.modules anymore. Getting rid of it is actually necessary
# if the package we're installing has a setup_requires of another package that
# uses astropy_helpers (and possibly a different version at that)
# See https://github.com/astropy/astropy/issues/3541
import sys
if 'ah_bootstrap' in sys.modules:
del sys.modules['ah_bootstrap']
# Note, this is repeated from ah_bootstrap.py, but is here too in case this
# astropy-helpers was upgraded to from an older version that did not have this
# check in its ah_bootstrap.
# 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
import os
# Ensure that all module-level code in astropy or other packages know that
# we're in setup mode:
if ('__main__' in sys.modules and
hasattr(sys.modules['__main__'], '__file__')):
filename = os.path.basename(sys.modules['__main__'].__file__)
if filename.rstrip('co') == 'setup.py':
import builtins
builtins._ASTROPY_SETUP_ = True
del filename
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1578142609.0
specutils-0.7/astropy_helpers/astropy_helpers/commands/ 0000755 0000765 0000024 00000000000 00000000000 023720 5 ustar 00erik staff 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1537500356.0
specutils-0.7/astropy_helpers/astropy_helpers/commands/__init__.py 0000644 0000765 0000024 00000000000 00000000000 026017 0 ustar 00erik staff 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1537500356.0
specutils-0.7/astropy_helpers/astropy_helpers/commands/_dummy.py 0000644 0000765 0000024 00000005262 00000000000 025571 0 ustar 00erik staff 0000000 0000000 """
Provides a base class for a 'dummy' setup.py command that has no functionality
(probably due to a missing requirement). This dummy command can raise an
exception when it is run, explaining to the user what dependencies must be met
to use this command.
The reason this is at all tricky is that we want the command to be able to
provide this message even when the user passes arguments to the command. If we
don't know ahead of time what arguments the command can take, this is
difficult, because distutils does not allow unknown arguments to be passed to a
setup.py command. This hacks around that restriction to provide a useful error
message even when a user passes arguments to the dummy implementation of a
command.
Use this like:
try:
from some_dependency import SetupCommand
except ImportError:
from ._dummy import _DummyCommand
class SetupCommand(_DummyCommand):
description = \
'Implementation of SetupCommand from some_dependency; '
'some_dependency must be installed to run this command'
# This is the message that will be raised when a user tries to
# run this command--define it as a class attribute.
error_msg = \
"The 'setup_command' command requires the some_dependency "
"package to be installed and importable."
"""
import sys
from setuptools import Command
from distutils.errors import DistutilsArgError
from textwrap import dedent
class _DummyCommandMeta(type):
"""
Causes an exception to be raised on accessing attributes of a command class
so that if ``./setup.py command_name`` is run with additional command-line
options we can provide a useful error message instead of the default that
tells users the options are unrecognized.
"""
def __init__(cls, name, bases, members):
if bases == (Command, object):
# This is the _DummyCommand base class, presumably
return
if not hasattr(cls, 'description'):
raise TypeError(
"_DummyCommand subclass must have a 'description' "
"attribute.")
if not hasattr(cls, 'error_msg'):
raise TypeError(
"_DummyCommand subclass must have an 'error_msg' "
"attribute.")
def __getattribute__(cls, attr):
if attr in ('description', 'error_msg'):
# Allow cls.description to work so that `./setup.py
# --help-commands` still works
return super(_DummyCommandMeta, cls).__getattribute__(attr)
raise DistutilsArgError(cls.error_msg)
class _DummyCommand(Command, object, metaclass=_DummyCommandMeta):
pass
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1565986938.0
specutils-0.7/astropy_helpers/astropy_helpers/commands/build_ext.py 0000644 0000765 0000024 00000020742 00000000000 026256 0 ustar 00erik staff 0000000 0000000 import errno
import os
import shutil
from distutils.core import Extension
from distutils.ccompiler import get_default_compiler
from distutils.command.build_ext import build_ext as DistutilsBuildExt
from ..distutils_helpers import get_main_package_directory
from ..utils import get_numpy_include_path, import_file
__all__ = ['AstropyHelpersBuildExt']
def should_build_with_cython(previous_cython_version, is_release):
"""
Returns the previously used Cython version (or 'unknown' if not
previously built) if Cython should be used to build extension modules from
pyx files.
"""
# Only build with Cython if, of course, Cython is installed, we're in a
# development version (i.e. not release) or the Cython-generated source
# files haven't been created yet (cython_version == 'unknown'). The latter
# case can happen even when release is True if checking out a release tag
# from the repository
have_cython = False
try:
from Cython import __version__ as cython_version # noqa
have_cython = True
except ImportError:
pass
if have_cython and (not is_release or previous_cython_version == 'unknown'):
return cython_version
else:
return False
class AstropyHelpersBuildExt(DistutilsBuildExt):
"""
A custom 'build_ext' command that allows for manipulating some of the C
extension options at build time.
"""
_uses_cython = False
_force_rebuild = False
def __new__(cls, value, **kwargs):
# NOTE: we need to wait until AstropyHelpersBuildExt is initialized to
# import setuptools.command.build_ext because when that package is
# imported, setuptools tries to import Cython - and if it's not found
# it will affect the rest of the build process. This is an issue because
# if we import that module at the top of this one, setup_requires won't
# have been honored yet, so Cython may not yet be available - and if we
# import build_ext too soon, it will think Cython is not available even
# if it is then intalled when setup_requires is processed. To get around
# this we dynamically create a new class that inherits from the
# setuptools build_ext, and by this point setup_requires has been
# processed.
from setuptools.command.build_ext import build_ext as SetuptoolsBuildExt
class FinalBuildExt(AstropyHelpersBuildExt, SetuptoolsBuildExt):
pass
new_type = type(cls.__name__, (FinalBuildExt,), dict(cls.__dict__))
obj = SetuptoolsBuildExt.__new__(new_type)
obj.__init__(value)
return obj
def finalize_options(self):
# First let's find the package folder, then we can check if the
# version and cython_version are accessible
self.package_dir = get_main_package_directory(self.distribution)
version = import_file(os.path.join(self.package_dir, 'version.py'),
name='version').version
self.is_release = 'dev' not in version
try:
self.previous_cython_version = import_file(os.path.join(self.package_dir,
'cython_version.py'),
name='cython_version').cython_version
except (FileNotFoundError, ImportError):
self.previous_cython_version = 'unknown'
self._uses_cython = should_build_with_cython(self.previous_cython_version, self.is_release)
# Add a copy of the _compiler.so module as well, but only if there
# are in fact C modules to compile (otherwise there's no reason to
# include a record of the compiler used). Note that self.extensions
# may not be set yet, but self.distribution.ext_modules is where any
# extension modules passed to setup() can be found
extensions = self.distribution.ext_modules
if extensions:
build_py = self.get_finalized_command('build_py')
package_dir = build_py.get_package_dir(self.package_dir)
src_path = os.path.relpath(
os.path.join(os.path.dirname(__file__), 'src'))
shutil.copy(os.path.join(src_path, 'compiler.c'),
os.path.join(package_dir, '_compiler.c'))
ext = Extension(self.package_dir + '.compiler_version',
[os.path.join(package_dir, '_compiler.c')])
extensions.insert(0, ext)
super().finalize_options()
# If we are using Cython, then make sure we re-build if the version
# of Cython that is installed is different from the version last
# used to generate the C files.
if self._uses_cython and self._uses_cython != self.previous_cython_version:
self._force_rebuild = True
# Regardless of the value of the '--force' option, force a rebuild
# if the debug flag changed from the last build
if self._force_rebuild:
self.force = True
def run(self):
# For extensions that require 'numpy' in their include dirs,
# replace 'numpy' with the actual paths
np_include = None
for extension in self.extensions:
if 'numpy' in extension.include_dirs:
if np_include is None:
np_include = get_numpy_include_path()
idx = extension.include_dirs.index('numpy')
extension.include_dirs.insert(idx, np_include)
extension.include_dirs.remove('numpy')
self._check_cython_sources(extension)
# Note that setuptools automatically uses Cython to discover and
# build extensions if available, so we don't have to explicitly call
# e.g. cythonize.
super().run()
# Update cython_version.py if building with Cython
if self._uses_cython and self._uses_cython != self.previous_cython_version:
build_py = self.get_finalized_command('build_py')
package_dir = build_py.get_package_dir(self.package_dir)
cython_py = os.path.join(package_dir, 'cython_version.py')
with open(cython_py, 'w') as f:
f.write('# Generated file; do not modify\n')
f.write('cython_version = {0!r}\n'.format(self._uses_cython))
if os.path.isdir(self.build_lib):
# The build/lib directory may not exist if the build_py
# command was not previously run, which may sometimes be
# the case
self.copy_file(cython_py,
os.path.join(self.build_lib, cython_py),
preserve_mode=False)
def _check_cython_sources(self, extension):
"""
Where relevant, make sure that the .c files associated with .pyx
modules are present (if building without Cython installed).
"""
# Determine the compiler we'll be using
if self.compiler is None:
compiler = get_default_compiler()
else:
compiler = self.compiler
# Replace .pyx with C-equivalents, unless c files are missing
for jdx, src in enumerate(extension.sources):
base, ext = os.path.splitext(src)
pyxfn = base + '.pyx'
cfn = base + '.c'
cppfn = base + '.cpp'
if not os.path.isfile(pyxfn):
continue
if self._uses_cython:
extension.sources[jdx] = pyxfn
else:
if os.path.isfile(cfn):
extension.sources[jdx] = cfn
elif os.path.isfile(cppfn):
extension.sources[jdx] = cppfn
else:
msg = (
'Could not find C/C++ file {0}.(c/cpp) for Cython '
'file {1} when building extension {2}. Cython '
'must be installed to build from a git '
'checkout.'.format(base, pyxfn, extension.name))
raise IOError(errno.ENOENT, msg, cfn)
# Cython (at least as of 0.29.2) uses deprecated Numpy API features
# the use of which produces a few warnings when compiling.
# These additional flags should squelch those warnings.
# TODO: Feel free to remove this if/when a Cython update
# removes use of the deprecated Numpy API
if compiler == 'unix':
extension.extra_compile_args.extend([
'-Wp,-w', '-Wno-unused-function'])
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1565986938.0
specutils-0.7/astropy_helpers/astropy_helpers/commands/build_sphinx.py 0000644 0000765 0000024 00000021777 00000000000 027000 0 ustar 00erik staff 0000000 0000000 from __future__ import print_function
import os
import pkgutil
import re
import shutil
import subprocess
import sys
from distutils.version import LooseVersion
from distutils import log
from sphinx import __version__ as sphinx_version
from sphinx.setup_command import BuildDoc as SphinxBuildDoc
SPHINX_LT_16 = LooseVersion(sphinx_version) < LooseVersion('1.6')
SPHINX_LT_17 = LooseVersion(sphinx_version) < LooseVersion('1.7')
SUBPROCESS_TEMPLATE = """
import os
import sys
{build_main}
os.chdir({srcdir!r})
{sys_path_inserts}
for builder in {builders!r}:
retcode = build_main(argv={argv!r} + ['-b', builder, '.', os.path.join({output_dir!r}, builder)])
if retcode != 0:
sys.exit(retcode)
"""
def ensure_sphinx_astropy_installed():
"""
Make sure that sphinx-astropy is available.
This returns the available version of sphinx-astropy as well as any
paths that should be added to sys.path for sphinx-astropy to be available.
"""
# We've split out the Sphinx part of astropy-helpers into sphinx-astropy
# but we want it to be auto-installed seamlessly for anyone using
# build_docs. We check if it's already installed, and if not, we install
# it to a local .eggs directory and add the eggs to the path (these
# have to each be added to the path, we can't add them by simply adding
# .eggs to the path)
sys_path_inserts = []
sphinx_astropy_version = None
try:
from sphinx_astropy import __version__ as sphinx_astropy_version # noqa
except ImportError:
raise ImportError("sphinx-astropy needs to be installed to build"
"the documentation.")
return sphinx_astropy_version, sys_path_inserts
class AstropyBuildDocs(SphinxBuildDoc):
"""
A version of the ``build_docs`` command that uses the version of Astropy
that is built by the setup ``build`` command, rather than whatever is
installed on the system. To build docs against the installed version, run
``make html`` in the ``astropy/docs`` directory.
"""
description = 'Build Sphinx documentation for Astropy environment'
user_options = SphinxBuildDoc.user_options[:]
user_options.append(
('warnings-returncode', 'w',
'Parses the sphinx output and sets the return code to 1 if there '
'are any warnings. Note that this will cause the sphinx log to '
'only update when it completes, rather than continuously as is '
'normally the case.'))
user_options.append(
('clean-docs', 'l',
'Completely clean previous builds, including '
'automodapi-generated files before building new ones'))
user_options.append(
('no-intersphinx', 'n',
'Skip intersphinx, even if conf.py says to use it'))
user_options.append(
('open-docs-in-browser', 'o',
'Open the docs in a browser (using the webbrowser module) if the '
'build finishes successfully.'))
boolean_options = SphinxBuildDoc.boolean_options[:]
boolean_options.append('warnings-returncode')
boolean_options.append('clean-docs')
boolean_options.append('no-intersphinx')
boolean_options.append('open-docs-in-browser')
_self_iden_rex = re.compile(r"self\.([^\d\W][\w]+)", re.UNICODE)
def initialize_options(self):
SphinxBuildDoc.initialize_options(self)
self.clean_docs = False
self.no_intersphinx = False
self.open_docs_in_browser = False
self.warnings_returncode = False
self.traceback = False
def finalize_options(self):
# This has to happen before we call the parent class's finalize_options
if self.build_dir is None:
self.build_dir = 'docs/_build'
SphinxBuildDoc.finalize_options(self)
# Clear out previous sphinx builds, if requested
if self.clean_docs:
dirstorm = [os.path.join(self.source_dir, 'api'),
os.path.join(self.source_dir, 'generated')]
dirstorm.append(self.build_dir)
for d in dirstorm:
if os.path.isdir(d):
log.info('Cleaning directory ' + d)
shutil.rmtree(d)
else:
log.info('Not cleaning directory ' + d + ' because '
'not present or not a directory')
def run(self):
# TODO: Break this method up into a few more subroutines and
# document them better
import webbrowser
from urllib.request import pathname2url
# This is used at the very end of `run` to decide if sys.exit should
# be called. If it's None, it won't be.
retcode = None
# Now make sure Astropy is built and determine where it was built
build_cmd = self.reinitialize_command('build')
build_cmd.inplace = 0
self.run_command('build')
build_cmd = self.get_finalized_command('build')
build_cmd_path = os.path.abspath(build_cmd.build_lib)
ah_importer = pkgutil.get_importer('astropy_helpers')
if ah_importer is None:
ah_path = '.'
else:
ah_path = os.path.abspath(ah_importer.path)
if SPHINX_LT_17:
build_main = 'from sphinx import build_main'
else:
build_main = 'from sphinx.cmd.build import build_main'
# We need to make sure sphinx-astropy is installed
sphinx_astropy_version, extra_paths = ensure_sphinx_astropy_installed()
sys_path_inserts = [build_cmd_path, ah_path] + extra_paths
sys_path_inserts = os.linesep.join(['sys.path.insert(0, {0!r})'.format(path) for path in sys_path_inserts])
argv = []
if self.warnings_returncode:
argv.append('-W')
if self.no_intersphinx:
# Note, if sphinx_astropy_version is None, this could indicate an
# old version of setuptools, but sphinx-astropy is likely ok, so
# we can proceed.
if sphinx_astropy_version is None or LooseVersion(sphinx_astropy_version) >= LooseVersion('1.1'):
argv.extend(['-D', 'disable_intersphinx=1'])
else:
log.warn('The -n option to disable intersphinx requires '
'sphinx-astropy>=1.1. Ignoring.')
# We now need to adjust the flags based on the parent class's options
if self.fresh_env:
argv.append('-E')
if self.all_files:
argv.append('-a')
if getattr(self, 'pdb', False):
argv.append('-P')
if getattr(self, 'nitpicky', False):
argv.append('-n')
if self.traceback:
argv.append('-T')
# The default verbosity level is 1, so in that case we just don't add a flag
if self.verbose == 0:
argv.append('-q')
elif self.verbose > 1:
argv.append('-v')
if SPHINX_LT_17:
argv.insert(0, 'sphinx-build')
if isinstance(self.builder, str):
builders = [self.builder]
else:
builders = self.builder
subproccode = SUBPROCESS_TEMPLATE.format(build_main=build_main,
srcdir=self.source_dir,
sys_path_inserts=sys_path_inserts,
builders=builders,
argv=argv,
output_dir=os.path.abspath(self.build_dir))
log.debug('Starting subprocess of {0} with python code:\n{1}\n'
'[CODE END])'.format(sys.executable, subproccode))
proc = subprocess.Popen([sys.executable], stdin=subprocess.PIPE)
proc.communicate(subproccode.encode('utf-8'))
if proc.returncode != 0:
retcode = proc.returncode
if retcode is None:
if self.open_docs_in_browser:
if self.builder == 'html':
absdir = os.path.abspath(self.builder_target_dir)
index_path = os.path.join(absdir, 'index.html')
fileurl = 'file://' + pathname2url(index_path)
webbrowser.open(fileurl)
else:
log.warn('open-docs-in-browser option was given, but '
'the builder is not html! Ignoring.')
# Here we explicitly check proc.returncode since we only want to output
# this for cases where the return code really wasn't 0.
if proc.returncode:
log.warn('Sphinx Documentation subprocess failed with return '
'code ' + str(proc.returncode))
if retcode is not None:
# this is potentially dangerous in that there might be something
# after the call to `setup` in `setup.py`, and exiting here will
# prevent that from running. But there's no other apparent way
# to signal what the return code should be.
sys.exit(retcode)
class AstropyBuildSphinx(AstropyBuildDocs): # pragma: no cover
def run(self):
AstropyBuildDocs.run(self)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1578142609.0
specutils-0.7/astropy_helpers/astropy_helpers/commands/src/ 0000755 0000765 0000024 00000000000 00000000000 024507 5 ustar 00erik staff 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1565986938.0
specutils-0.7/astropy_helpers/astropy_helpers/commands/src/compiler.c 0000644 0000765 0000024 00000005240 00000000000 026466 0 ustar 00erik staff 0000000 0000000 #include
/***************************************************************************
* Macros for determining the compiler version.
*
* These are borrowed from boost, and majorly abridged to include only
* the compilers we care about.
***************************************************************************/
#define STRINGIZE(X) DO_STRINGIZE(X)
#define DO_STRINGIZE(X) #X
#if defined __clang__
/* Clang C++ emulates GCC, so it has to appear early. */
# define COMPILER "Clang version " __clang_version__
#elif defined(__INTEL_COMPILER) || defined(__ICL) || defined(__ICC) || defined(__ECC)
/* Intel */
# if defined(__INTEL_COMPILER)
# define INTEL_VERSION __INTEL_COMPILER
# elif defined(__ICL)
# define INTEL_VERSION __ICL
# elif defined(__ICC)
# define INTEL_VERSION __ICC
# elif defined(__ECC)
# define INTEL_VERSION __ECC
# endif
# define COMPILER "Intel C compiler version " STRINGIZE(INTEL_VERSION)
#elif defined(__GNUC__)
/* gcc */
# define COMPILER "GCC version " __VERSION__
#elif defined(__SUNPRO_CC)
/* Sun Workshop Compiler */
# define COMPILER "Sun compiler version " STRINGIZE(__SUNPRO_CC)
#elif defined(_MSC_VER)
/* Microsoft Visual C/C++
Must be last since other compilers define _MSC_VER for compatibility as well */
# if _MSC_VER < 1200
# define COMPILER_VERSION 5.0
# elif _MSC_VER < 1300
# define COMPILER_VERSION 6.0
# elif _MSC_VER == 1300
# define COMPILER_VERSION 7.0
# elif _MSC_VER == 1310
# define COMPILER_VERSION 7.1
# elif _MSC_VER == 1400
# define COMPILER_VERSION 8.0
# elif _MSC_VER == 1500
# define COMPILER_VERSION 9.0
# elif _MSC_VER == 1600
# define COMPILER_VERSION 10.0
# else
# define COMPILER_VERSION _MSC_VER
# endif
# define COMPILER "Microsoft Visual C++ version " STRINGIZE(COMPILER_VERSION)
#else
/* Fallback */
# define COMPILER "Unknown compiler"
#endif
/***************************************************************************
* Module-level
***************************************************************************/
struct module_state {
/* The Sun compiler can't handle empty structs */
#if defined(__SUNPRO_C) || defined(_MSC_VER)
int _dummy;
#endif
};
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"compiler_version",
NULL,
sizeof(struct module_state),
NULL,
NULL,
NULL,
NULL,
NULL
};
#define INITERROR return NULL
PyMODINIT_FUNC
PyInit_compiler_version(void)
{
PyObject* m;
m = PyModule_Create(&moduledef);
if (m == NULL)
INITERROR;
PyModule_AddStringConstant(m, "compiler", COMPILER);
return m;
}
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1565986938.0
specutils-0.7/astropy_helpers/astropy_helpers/commands/test.py 0000644 0000765 0000024 00000002672 00000000000 025260 0 ustar 00erik staff 0000000 0000000 """
Different implementations of the ``./setup.py test`` command depending on
what's locally available.
If Astropy v1.1 or later is available it should be possible to import
AstropyTest from ``astropy.tests.command``. Otherwise there is a skeleton
implementation that allows users to at least discover the ``./setup.py test``
command and learn that they need Astropy to run it.
"""
import os
from ..utils import import_file
# Previously these except statements caught only ImportErrors, but there are
# some other obscure exceptional conditions that can occur when importing
# astropy.tests (at least on older versions) that can cause these imports to
# fail
try:
# If we are testing astropy itself, we need to use import_file to avoid
# actually importing astropy (just the file we need).
command_file = os.path.join('astropy', 'tests', 'command.py')
if os.path.exists(command_file):
AstropyTest = import_file(command_file, 'astropy_tests_command').AstropyTest
else:
import astropy # noqa
from astropy.tests.command import AstropyTest
except Exception:
# No astropy at all--provide the dummy implementation
from ._dummy import _DummyCommand
class AstropyTest(_DummyCommand):
command_name = 'test'
description = 'Run the tests for this package'
error_msg = (
"The 'test' command requires the astropy package to be "
"installed and importable.")
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1537500356.0
specutils-0.7/astropy_helpers/astropy_helpers/conftest.py 0000644 0000765 0000024 00000003612 00000000000 024320 0 ustar 00erik staff 0000000 0000000 # This file contains settings for pytest that are specific to astropy-helpers.
# Since we run many of the tests in sub-processes, we need to collect coverage
# data inside each subprocess and then combine it into a single .coverage file.
# To do this we set up a list which run_setup appends coverage objects to.
# This is not intended to be used by packages other than astropy-helpers.
import os
import glob
try:
from coverage import CoverageData
except ImportError:
HAS_COVERAGE = False
else:
HAS_COVERAGE = True
if HAS_COVERAGE:
SUBPROCESS_COVERAGE = []
def pytest_configure(config):
if HAS_COVERAGE:
SUBPROCESS_COVERAGE.clear()
def pytest_unconfigure(config):
if HAS_COVERAGE:
# We create an empty coverage data object
combined_cdata = CoverageData()
# Add all files from astropy_helpers to make sure we compute the total
# coverage, not just the coverage of the files that have non-zero
# coverage.
lines = {}
for filename in glob.glob(os.path.join('astropy_helpers', '**', '*.py'), recursive=True):
lines[os.path.abspath(filename)] = []
for cdata in SUBPROCESS_COVERAGE:
# For each CoverageData object, we go through all the files and
# change the filename from one which might be a temporary path
# to the local filename. We then only keep files that actually
# exist.
for filename in cdata.measured_files():
try:
pos = filename.rindex('astropy_helpers')
except ValueError:
continue
short_filename = filename[pos:]
if os.path.exists(short_filename):
lines[os.path.abspath(short_filename)].extend(cdata.lines(filename))
combined_cdata.add_lines(lines)
combined_cdata.write_file('.coverage.subprocess')
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1565986938.0
specutils-0.7/astropy_helpers/astropy_helpers/distutils_helpers.py 0000644 0000765 0000024 00000017644 00000000000 026253 0 ustar 00erik staff 0000000 0000000 """
This module contains various utilities for introspecting the distutils
module and the setup process.
Some of these utilities require the
`astropy_helpers.setup_helpers.register_commands` function to be called first,
as it will affect introspection of setuptools command-line arguments. Other
utilities in this module do not have that restriction.
"""
import os
import sys
from distutils import ccompiler, log
from distutils.dist import Distribution
from distutils.errors import DistutilsError
from .utils import silence
# This function, and any functions that call it, require the setup in
# `astropy_helpers.setup_helpers.register_commands` to be run first.
def get_dummy_distribution():
"""
Returns a distutils Distribution object used to instrument the setup
environment before calling the actual setup() function.
"""
from .setup_helpers import _module_state
if _module_state['registered_commands'] is None:
raise RuntimeError(
'astropy_helpers.setup_helpers.register_commands() must be '
'called before using '
'astropy_helpers.setup_helpers.get_dummy_distribution()')
# Pre-parse the Distutils command-line options and config files to if
# the option is set.
dist = Distribution({'script_name': os.path.basename(sys.argv[0]),
'script_args': sys.argv[1:]})
dist.cmdclass.update(_module_state['registered_commands'])
with silence():
try:
dist.parse_config_files()
dist.parse_command_line()
except (DistutilsError, AttributeError, SystemExit):
# Let distutils handle DistutilsErrors itself AttributeErrors can
# get raise for ./setup.py --help SystemExit can be raised if a
# display option was used, for example
pass
return dist
def get_main_package_directory(distribution):
"""
Given a Distribution object, return the main package directory.
"""
return min(distribution.packages, key=len)
def get_distutils_option(option, commands):
""" Returns the value of the given distutils option.
Parameters
----------
option : str
The name of the option
commands : list of str
The list of commands on which this option is available
Returns
-------
val : str or None
the value of the given distutils option. If the option is not set,
returns None.
"""
dist = get_dummy_distribution()
for cmd in commands:
cmd_opts = dist.command_options.get(cmd)
if cmd_opts is not None and option in cmd_opts:
return cmd_opts[option][1]
else:
return None
def get_distutils_build_option(option):
""" Returns the value of the given distutils build option.
Parameters
----------
option : str
The name of the option
Returns
-------
val : str or None
The value of the given distutils build option. If the option
is not set, returns None.
"""
return get_distutils_option(option, ['build', 'build_ext', 'build_clib'])
def get_distutils_install_option(option):
""" Returns the value of the given distutils install option.
Parameters
----------
option : str
The name of the option
Returns
-------
val : str or None
The value of the given distutils build option. If the option
is not set, returns None.
"""
return get_distutils_option(option, ['install'])
def get_distutils_build_or_install_option(option):
""" Returns the value of the given distutils build or install option.
Parameters
----------
option : str
The name of the option
Returns
-------
val : str or None
The value of the given distutils build or install option. If the
option is not set, returns None.
"""
return get_distutils_option(option, ['build', 'build_ext', 'build_clib',
'install'])
def get_compiler_option():
""" Determines the compiler that will be used to build extension modules.
Returns
-------
compiler : str
The compiler option specified for the build, build_ext, or build_clib
command; or the default compiler for the platform if none was
specified.
"""
compiler = get_distutils_build_option('compiler')
if compiler is None:
return ccompiler.get_default_compiler()
return compiler
def add_command_option(command, name, doc, is_bool=False):
"""
Add a custom option to a setup command.
Issues a warning if the option already exists on that command.
Parameters
----------
command : str
The name of the command as given on the command line
name : str
The name of the build option
doc : str
A short description of the option, for the `--help` message
is_bool : bool, optional
When `True`, the option is a boolean option and doesn't
require an associated value.
"""
dist = get_dummy_distribution()
cmdcls = dist.get_command_class(command)
if (hasattr(cmdcls, '_astropy_helpers_options') and
name in cmdcls._astropy_helpers_options):
return
attr = name.replace('-', '_')
if hasattr(cmdcls, attr):
raise RuntimeError(
'{0!r} already has a {1!r} class attribute, barring {2!r} from '
'being usable as a custom option name.'.format(cmdcls, attr, name))
for idx, cmd in enumerate(cmdcls.user_options):
if cmd[0] == name:
log.warn('Overriding existing {0!r} option '
'{1!r}'.format(command, name))
del cmdcls.user_options[idx]
if name in cmdcls.boolean_options:
cmdcls.boolean_options.remove(name)
break
cmdcls.user_options.append((name, None, doc))
if is_bool:
cmdcls.boolean_options.append(name)
# Distutils' command parsing requires that a command object have an
# attribute with the same name as the option (with '-' replaced with '_')
# in order for that option to be recognized as valid
setattr(cmdcls, attr, None)
# This caches the options added through add_command_option so that if it is
# run multiple times in the same interpreter repeated adds are ignored
# (this way we can still raise a RuntimeError if a custom option overrides
# a built-in option)
if not hasattr(cmdcls, '_astropy_helpers_options'):
cmdcls._astropy_helpers_options = set([name])
else:
cmdcls._astropy_helpers_options.add(name)
def get_distutils_display_options():
""" Returns a set of all the distutils display options in their long and
short forms. These are the setup.py arguments such as --name or --version
which print the project's metadata and then exit.
Returns
-------
opts : set
The long and short form display option arguments, including the - or --
"""
short_display_opts = set('-' + o[1] for o in Distribution.display_options
if o[1])
long_display_opts = set('--' + o[0] for o in Distribution.display_options)
# Include -h and --help which are not explicitly listed in
# Distribution.display_options (as they are handled by optparse)
short_display_opts.add('-h')
long_display_opts.add('--help')
# This isn't the greatest approach to hardcode these commands.
# However, there doesn't seem to be a good way to determine
# whether build *will be* run as part of the command at this
# phase.
display_commands = set([
'clean', 'register', 'setopt', 'saveopts', 'egg_info',
'alias'])
return short_display_opts.union(long_display_opts.union(display_commands))
def is_distutils_display_option():
""" Returns True if sys.argv contains any of the distutils display options
such as --version or --name.
"""
display_options = get_distutils_display_options()
return bool(set(sys.argv[1:]).intersection(display_options))
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1565986938.0
specutils-0.7/astropy_helpers/astropy_helpers/git_helpers.py 0000644 0000765 0000024 00000014537 00000000000 025010 0 ustar 00erik staff 0000000 0000000 # Licensed under a 3-clause BSD style license - see LICENSE.rst
"""
Utilities for retrieving revision information from a project's git repository.
"""
# Do not remove the following comment; it is used by
# astropy_helpers.version_helpers to determine the beginning of the code in
# this module
# BEGIN
import locale
import os
import subprocess
import warnings
__all__ = ['get_git_devstr']
def _decode_stdio(stream):
try:
stdio_encoding = locale.getdefaultlocale()[1] or 'utf-8'
except ValueError:
stdio_encoding = 'utf-8'
try:
text = stream.decode(stdio_encoding)
except UnicodeDecodeError:
# Final fallback
text = stream.decode('latin1')
return text
def update_git_devstr(version, path=None):
"""
Updates the git revision string if and only if the path is being imported
directly from a git working copy. This ensures that the revision number in
the version string is accurate.
"""
try:
# Quick way to determine if we're in git or not - returns '' if not
devstr = get_git_devstr(sha=True, show_warning=False, path=path)
except OSError:
return version
if not devstr:
# Probably not in git so just pass silently
return version
if 'dev' in version: # update to the current git revision
version_base = version.split('.dev', 1)[0]
devstr = get_git_devstr(sha=False, show_warning=False, path=path)
return version_base + '.dev' + devstr
else:
# otherwise it's already the true/release version
return version
def get_git_devstr(sha=False, show_warning=True, path=None):
"""
Determines the number of revisions in this repository.
Parameters
----------
sha : bool
If True, the full SHA1 hash will be returned. Otherwise, the total
count of commits in the repository will be used as a "revision
number".
show_warning : bool
If True, issue a warning if git returns an error code, otherwise errors
pass silently.
path : str or None
If a string, specifies the directory to look in to find the git
repository. If `None`, the current working directory is used, and must
be the root of the git repository.
If given a filename it uses the directory containing that file.
Returns
-------
devversion : str
Either a string with the revision number (if `sha` is False), the
SHA1 hash of the current commit (if `sha` is True), or an empty string
if git version info could not be identified.
"""
if path is None:
path = os.getcwd()
if not os.path.isdir(path):
path = os.path.abspath(os.path.dirname(path))
if sha:
# Faster for getting just the hash of HEAD
cmd = ['rev-parse', 'HEAD']
else:
cmd = ['rev-list', '--count', 'HEAD']
def run_git(cmd):
try:
p = subprocess.Popen(['git'] + cmd, cwd=path,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE)
stdout, stderr = p.communicate()
except OSError as e:
if show_warning:
warnings.warn('Error running git: ' + str(e))
return (None, b'', b'')
if p.returncode == 128:
if show_warning:
warnings.warn('No git repository present at {0!r}! Using '
'default dev version.'.format(path))
return (p.returncode, b'', b'')
if p.returncode == 129:
if show_warning:
warnings.warn('Your git looks old (does it support {0}?); '
'consider upgrading to v1.7.2 or '
'later.'.format(cmd[0]))
return (p.returncode, stdout, stderr)
elif p.returncode != 0:
if show_warning:
warnings.warn('Git failed while determining revision '
'count: {0}'.format(_decode_stdio(stderr)))
return (p.returncode, stdout, stderr)
return p.returncode, stdout, stderr
returncode, stdout, stderr = run_git(cmd)
if not sha and returncode == 128:
# git returns 128 if the command is not run from within a git
# repository tree. In this case, a warning is produced above but we
# return the default dev version of '0'.
return '0'
elif not sha and returncode == 129:
# git returns 129 if a command option failed to parse; in
# particular this could happen in git versions older than 1.7.2
# where the --count option is not supported
# Also use --abbrev-commit and --abbrev=0 to display the minimum
# number of characters needed per-commit (rather than the full hash)
cmd = ['rev-list', '--abbrev-commit', '--abbrev=0', 'HEAD']
returncode, stdout, stderr = run_git(cmd)
# Fall back on the old method of getting all revisions and counting
# the lines
if returncode == 0:
return str(stdout.count(b'\n'))
else:
return ''
elif sha:
return _decode_stdio(stdout)[:40]
else:
return _decode_stdio(stdout).strip()
# This function is tested but it is only ever executed within a subprocess when
# creating a fake package, so it doesn't get picked up by coverage metrics.
def _get_repo_path(pathname, levels=None): # pragma: no cover
"""
Given a file or directory name, determine the root of the git repository
this path is under. If given, this won't look any higher than ``levels``
(that is, if ``levels=0`` then the given path must be the root of the git
repository and is returned if so.
Returns `None` if the given path could not be determined to belong to a git
repo.
"""
if os.path.isfile(pathname):
current_dir = os.path.abspath(os.path.dirname(pathname))
elif os.path.isdir(pathname):
current_dir = os.path.abspath(pathname)
else:
return None
current_level = 0
while levels is None or current_level <= levels:
if os.path.exists(os.path.join(current_dir, '.git')):
return current_dir
current_level += 1
if current_dir == os.path.dirname(current_dir):
break
current_dir = os.path.dirname(current_dir)
return None
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1565986938.0
specutils-0.7/astropy_helpers/astropy_helpers/openmp_helpers.py 0000644 0000765 0000024 00000022567 00000000000 025525 0 ustar 00erik staff 0000000 0000000 # This module defines functions that can be used to check whether OpenMP is
# available and if so what flags to use. To use this, import the
# add_openmp_flags_if_available function in a setup_package.py file where you
# are defining your extensions:
#
# from astropy_helpers.openmp_helpers import add_openmp_flags_if_available
#
# then call it with a single extension as the only argument:
#
# add_openmp_flags_if_available(extension)
#
# this will add the OpenMP flags if available.
from __future__ import absolute_import, print_function
import os
import sys
import glob
import time
import datetime
import tempfile
import subprocess
from distutils import log
from distutils.ccompiler import new_compiler
from distutils.sysconfig import customize_compiler, get_config_var
from distutils.errors import CompileError, LinkError
from .distutils_helpers import get_compiler_option
__all__ = ['add_openmp_flags_if_available']
try:
# Check if this has already been instantiated, only set the default once.
_ASTROPY_DISABLE_SETUP_WITH_OPENMP_
except NameError:
import builtins
# It hasn't, so do so.
builtins._ASTROPY_DISABLE_SETUP_WITH_OPENMP_ = False
CCODE = """
#include
#include
int main(void) {
#pragma omp parallel
printf("nthreads=%d\\n", omp_get_num_threads());
return 0;
}
"""
def _get_flag_value_from_var(flag, var, delim=' '):
"""
Extract flags from an environment variable.
Parameters
----------
flag : str
The flag to extract, for example '-I' or '-L'
var : str
The environment variable to extract the flag from, e.g. CFLAGS or LDFLAGS.
delim : str, optional
The delimiter separating flags inside the environment variable
Examples
--------
Let's assume the LDFLAGS is set to '-L/usr/local/include -customflag'. This
function will then return the following:
>>> _get_flag_value_from_var('-L', 'LDFLAGS')
'/usr/local/include'
Notes
-----
Environment variables are first checked in ``os.environ[var]``, then in
``distutils.sysconfig.get_config_var(var)``.
This function is not supported on Windows.
"""
if sys.platform.startswith('win'):
return None
# Simple input validation
if not var or not flag:
return None
flag_length = len(flag)
if not flag_length:
return None
# Look for var in os.eviron then in get_config_var
if var in os.environ:
flags = os.environ[var]
else:
try:
flags = get_config_var(var)
except KeyError:
return None
# Extract flag from {var:value}
if flags:
for item in flags.split(delim):
if item.startswith(flag):
return item[flag_length:]
def get_openmp_flags():
"""
Utility for returning compiler and linker flags possibly needed for
OpenMP support.
Returns
-------
result : `{'compiler_flags':, 'linker_flags':}`
Notes
-----
The flags returned are not tested for validity, use
`check_openmp_support(openmp_flags=get_openmp_flags())` to do so.
"""
compile_flags = []
link_flags = []
if get_compiler_option() == 'msvc':
compile_flags.append('-openmp')
else:
include_path = _get_flag_value_from_var('-I', 'CFLAGS')
if include_path:
compile_flags.append('-I' + include_path)
lib_path = _get_flag_value_from_var('-L', 'LDFLAGS')
if lib_path:
link_flags.append('-L' + lib_path)
link_flags.append('-Wl,-rpath,' + lib_path)
compile_flags.append('-fopenmp')
link_flags.append('-fopenmp')
return {'compiler_flags': compile_flags, 'linker_flags': link_flags}
def check_openmp_support(openmp_flags=None):
"""
Check whether OpenMP test code can be compiled and run.
Parameters
----------
openmp_flags : dict, optional
This should be a dictionary with keys ``compiler_flags`` and
``linker_flags`` giving the compiliation and linking flags respectively.
These are passed as `extra_postargs` to `compile()` and
`link_executable()` respectively. If this is not set, the flags will
be automatically determined using environment variables.
Returns
-------
result : bool
`True` if the test passed, `False` otherwise.
"""
ccompiler = new_compiler()
customize_compiler(ccompiler)
if not openmp_flags:
# customize_compiler() extracts info from os.environ. If certain keys
# exist it uses these plus those from sysconfig.get_config_vars().
# If the key is missing in os.environ it is not extracted from
# sysconfig.get_config_var(). E.g. 'LDFLAGS' get left out, preventing
# clang from finding libomp.dylib because -L is not passed to
# linker. Call get_openmp_flags() to get flags missed by
# customize_compiler().
openmp_flags = get_openmp_flags()
compile_flags = openmp_flags.get('compiler_flags')
link_flags = openmp_flags.get('linker_flags')
# Pass -coverage flag to linker.
# https://github.com/astropy/astropy-helpers/pull/374
if '-coverage' in compile_flags and '-coverage' not in link_flags:
link_flags.append('-coverage')
tmp_dir = tempfile.mkdtemp()
start_dir = os.path.abspath('.')
try:
os.chdir(tmp_dir)
# Write test program
with open('test_openmp.c', 'w') as f:
f.write(CCODE)
os.mkdir('objects')
# Compile, test program
ccompiler.compile(['test_openmp.c'], output_dir='objects',
extra_postargs=compile_flags)
# Link test program
objects = glob.glob(os.path.join('objects', '*' + ccompiler.obj_extension))
ccompiler.link_executable(objects, 'test_openmp',
extra_postargs=link_flags)
# Run test program
output = subprocess.check_output('./test_openmp')
output = output.decode(sys.stdout.encoding or 'utf-8').splitlines()
if 'nthreads=' in output[0]:
nthreads = int(output[0].strip().split('=')[1])
if len(output) == nthreads:
is_openmp_supported = True
else:
log.warn("Unexpected number of lines from output of test OpenMP "
"program (output was {0})".format(output))
is_openmp_supported = False
else:
log.warn("Unexpected output from test OpenMP "
"program (output was {0})".format(output))
is_openmp_supported = False
except (CompileError, LinkError, subprocess.CalledProcessError):
is_openmp_supported = False
finally:
os.chdir(start_dir)
return is_openmp_supported
def is_openmp_supported():
"""
Determine whether the build compiler has OpenMP support.
"""
log_threshold = log.set_threshold(log.FATAL)
ret = check_openmp_support()
log.set_threshold(log_threshold)
return ret
def add_openmp_flags_if_available(extension):
"""
Add OpenMP compilation flags, if supported (if not a warning will be
printed to the console and no flags will be added.)
Returns `True` if the flags were added, `False` otherwise.
"""
if _ASTROPY_DISABLE_SETUP_WITH_OPENMP_:
log.info("OpenMP support has been explicitly disabled.")
return False
openmp_flags = get_openmp_flags()
using_openmp = check_openmp_support(openmp_flags=openmp_flags)
if using_openmp:
compile_flags = openmp_flags.get('compiler_flags')
link_flags = openmp_flags.get('linker_flags')
log.info("Compiling Cython/C/C++ extension with OpenMP support")
extension.extra_compile_args.extend(compile_flags)
extension.extra_link_args.extend(link_flags)
else:
log.warn("Cannot compile Cython/C/C++ extension with OpenMP, reverting "
"to non-parallel code")
return using_openmp
_IS_OPENMP_ENABLED_SRC = """
# Autogenerated by {packagetitle}'s setup.py on {timestamp!s}
def is_openmp_enabled():
\"\"\"
Determine whether this package was built with OpenMP support.
\"\"\"
return {return_bool}
"""[1:]
def generate_openmp_enabled_py(packagename, srcdir='.', disable_openmp=None):
"""
Generate ``package.openmp_enabled.is_openmp_enabled``, which can then be used
to determine, post build, whether the package was built with or without
OpenMP support.
"""
if packagename.lower() == 'astropy':
packagetitle = 'Astropy'
else:
packagetitle = packagename
epoch = int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))
timestamp = datetime.datetime.utcfromtimestamp(epoch)
if disable_openmp is not None:
import builtins
builtins._ASTROPY_DISABLE_SETUP_WITH_OPENMP_ = disable_openmp
if _ASTROPY_DISABLE_SETUP_WITH_OPENMP_:
log.info("OpenMP support has been explicitly disabled.")
openmp_support = False if _ASTROPY_DISABLE_SETUP_WITH_OPENMP_ else is_openmp_supported()
src = _IS_OPENMP_ENABLED_SRC.format(packagetitle=packagetitle,
timestamp=timestamp,
return_bool=openmp_support)
package_srcdir = os.path.join(srcdir, *packagename.split('.'))
is_openmp_enabled_py = os.path.join(package_srcdir, 'openmp_enabled.py')
with open(is_openmp_enabled_py, 'w') as f:
f.write(src)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1565986938.0
specutils-0.7/astropy_helpers/astropy_helpers/setup_helpers.py 0000644 0000765 0000024 00000070622 00000000000 025362 0 ustar 00erik staff 0000000 0000000 # Licensed under a 3-clause BSD style license - see LICENSE.rst
"""
This module contains a number of utilities for use during
setup/build/packaging that are useful to astropy as a whole.
"""
from __future__ import absolute_import
import collections
import os
import re
import subprocess
import sys
import traceback
import warnings
from configparser import ConfigParser
import builtins
from distutils import log
from distutils.errors import DistutilsOptionError, DistutilsModuleError
from distutils.core import Extension
from distutils.core import Command
from distutils.command.sdist import sdist as DistutilsSdist
from setuptools import setup as setuptools_setup
from setuptools.config import read_configuration
from setuptools import find_packages as _find_packages
from .distutils_helpers import (add_command_option, get_compiler_option,
get_dummy_distribution, get_distutils_build_option,
get_distutils_build_or_install_option)
from .version_helpers import get_pkg_version_module, generate_version_py
from .utils import (walk_skip_hidden, import_file, extends_doc,
resolve_name, AstropyDeprecationWarning)
from .commands.build_ext import AstropyHelpersBuildExt
from .commands.test import AstropyTest
# These imports are not used in this module, but are included for backwards
# compat with older versions of this module
from .utils import get_numpy_include_path, write_if_different # noqa
__all__ = ['register_commands', 'get_package_info']
_module_state = {'registered_commands': None,
'have_sphinx': False,
'package_cache': None,
'exclude_packages': set(),
'excludes_too_late': False}
try:
import sphinx # noqa
_module_state['have_sphinx'] = True
except ValueError as e:
# This can occur deep in the bowels of Sphinx's imports by way of docutils
# and an occurrence of this bug: http://bugs.python.org/issue18378
# In this case sphinx is effectively unusable
if 'unknown locale' in e.args[0]:
log.warn(
"Possible misconfiguration of one of the environment variables "
"LC_ALL, LC_CTYPES, LANG, or LANGUAGE. For an example of how to "
"configure your system's language environment on OSX see "
"http://blog.remibergsma.com/2012/07/10/"
"setting-locales-correctly-on-mac-osx-terminal-application/")
except ImportError:
pass
except SyntaxError:
# occurs if markupsafe is recent version, which doesn't support Python 3.2
pass
def setup(**kwargs):
"""
A wrapper around setuptools' setup() function that automatically sets up
custom commands, generates a version file, and customizes the setup process
via the ``setup_package.py`` files.
"""
# DEPRECATED: store the package name in a built-in variable so it's easy
# to get from other parts of the setup infrastructure. We should phase this
# out in packages that use it - the cookiecutter template should now be
# able to put the right package name where needed.
conf = read_configuration('setup.cfg')
builtins._ASTROPY_PACKAGE_NAME_ = conf['metadata']['name']
# Create a dictionary with setup command overrides. Note that this gets
# information about the package (name and version) from the setup.cfg file.
cmdclass = register_commands()
# Freeze build information in version.py. Note that this gets information
# about the package (name and version) from the setup.cfg file.
version = generate_version_py()
# 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()
package_info['cmdclass'] = cmdclass
package_info['version'] = version
# Override using any specified keyword arguments
package_info.update(kwargs)
setuptools_setup(**package_info)
def adjust_compiler(package):
warnings.warn(
'The adjust_compiler function in setup.py is '
'deprecated and can be removed from your setup.py.',
AstropyDeprecationWarning)
def get_debug_option(packagename):
""" Determines if the build is in debug mode.
Returns
-------
debug : bool
True if the current build was started with the debug option, False
otherwise.
"""
try:
current_debug = get_pkg_version_module(packagename,
fromlist=['debug'])[0]
except (ImportError, AttributeError):
current_debug = None
# Only modify the debug flag if one of the build commands was explicitly
# run (i.e. not as a sub-command of something else)
dist = get_dummy_distribution()
if any(cmd in dist.commands for cmd in ['build', 'build_ext']):
debug = bool(get_distutils_build_option('debug'))
else:
debug = bool(current_debug)
if current_debug is not None and current_debug != debug:
build_ext_cmd = dist.get_command_class('build_ext')
build_ext_cmd._force_rebuild = True
return debug
def add_exclude_packages(excludes):
if _module_state['excludes_too_late']:
raise RuntimeError(
"add_package_excludes must be called before all other setup helper "
"functions in order to properly handle excluded packages")
_module_state['exclude_packages'].update(set(excludes))
def register_commands(package=None, version=None, release=None, srcdir='.'):
"""
This function generates a dictionary containing customized commands that
can then be passed to the ``cmdclass`` argument in ``setup()``.
"""
if package is not None:
warnings.warn('The package argument to generate_version_py has '
'been deprecated and will be removed in future. Specify '
'the package name in setup.cfg instead', AstropyDeprecationWarning)
if version is not None:
warnings.warn('The version argument to generate_version_py has '
'been deprecated and will be removed in future. Specify '
'the version number in setup.cfg instead', AstropyDeprecationWarning)
if release is not None:
warnings.warn('The release argument to generate_version_py has '
'been deprecated and will be removed in future. We now '
'use the presence of the "dev" string in the version to '
'determine whether this is a release', AstropyDeprecationWarning)
# We use ConfigParser instead of read_configuration here because the latter
# only reads in keys recognized by setuptools, but we need to access
# package_name below.
conf = ConfigParser()
conf.read('setup.cfg')
if conf.has_option('metadata', 'name'):
package = conf.get('metadata', 'name')
elif conf.has_option('metadata', 'package_name'):
# The package-template used package_name instead of name for a while
warnings.warn('Specifying the package name using the "package_name" '
'option in setup.cfg is deprecated - use the "name" '
'option instead.', AstropyDeprecationWarning)
package = conf.get('metadata', 'package_name')
elif package is not None: # deprecated
pass
else:
sys.stderr.write('ERROR: Could not read package name from setup.cfg\n')
sys.exit(1)
if _module_state['registered_commands'] is not None:
return _module_state['registered_commands']
if _module_state['have_sphinx']:
try:
from .commands.build_sphinx import (AstropyBuildSphinx,
AstropyBuildDocs)
except ImportError:
AstropyBuildSphinx = AstropyBuildDocs = FakeBuildSphinx
else:
AstropyBuildSphinx = AstropyBuildDocs = FakeBuildSphinx
_module_state['registered_commands'] = registered_commands = {
'test': generate_test_command(package),
# Use distutils' sdist because it respects package_data.
# setuptools/distributes sdist requires duplication of information in
# MANIFEST.in
'sdist': DistutilsSdist,
'build_ext': AstropyHelpersBuildExt,
'build_sphinx': AstropyBuildSphinx,
'build_docs': AstropyBuildDocs
}
# Need to override the __name__ here so that the commandline options are
# presented as being related to the "build" command, for example; normally
# this wouldn't be necessary since commands also have a command_name
# attribute, but there is a bug in distutils' help display code that it
# uses __name__ instead of command_name. Yay distutils!
for name, cls in registered_commands.items():
cls.__name__ = name
# Add a few custom options; more of these can be added by specific packages
# later
for option in [
('use-system-libraries',
"Use system libraries whenever possible", True)]:
add_command_option('build', *option)
add_command_option('install', *option)
add_command_hooks(registered_commands, srcdir=srcdir)
return registered_commands
def add_command_hooks(commands, srcdir='.'):
"""
Look through setup_package.py modules for functions with names like
``pre__hook`` and ``post__hook`` where
```` is the name of a ``setup.py`` command (e.g. build_ext).
If either hook is present this adds a wrapped version of that command to
the passed in ``commands`` `dict`. ``commands`` may be pre-populated with
other custom distutils command classes that should be wrapped if there are
hooks for them (e.g. `AstropyBuildPy`).
"""
hook_re = re.compile(r'^(pre|post)_(.+)_hook$')
# Distutils commands have a method of the same name, but it is not a
# *classmethod* (which probably didn't exist when distutils was first
# written)
def get_command_name(cmdcls):
if hasattr(cmdcls, 'command_name'):
return cmdcls.command_name
else:
return cmdcls.__name__
packages = find_packages(srcdir)
dist = get_dummy_distribution()
hooks = collections.defaultdict(dict)
for setuppkg in iter_setup_packages(srcdir, packages):
for name, obj in vars(setuppkg).items():
match = hook_re.match(name)
if not match:
continue
hook_type = match.group(1)
cmd_name = match.group(2)
if hook_type not in hooks[cmd_name]:
hooks[cmd_name][hook_type] = []
hooks[cmd_name][hook_type].append((setuppkg.__name__, obj))
for cmd_name, cmd_hooks in hooks.items():
commands[cmd_name] = generate_hooked_command(
cmd_name, dist.get_command_class(cmd_name), cmd_hooks)
def generate_hooked_command(cmd_name, cmd_cls, hooks):
"""
Returns a generated subclass of ``cmd_cls`` that runs the pre- and
post-command hooks for that command before and after the ``cmd_cls.run``
method.
"""
def run(self, orig_run=cmd_cls.run):
self.run_command_hooks('pre_hooks')
orig_run(self)
self.run_command_hooks('post_hooks')
return type(cmd_name, (cmd_cls, object),
{'run': run, 'run_command_hooks': run_command_hooks,
'pre_hooks': hooks.get('pre', []),
'post_hooks': hooks.get('post', [])})
def run_command_hooks(cmd_obj, hook_kind):
"""Run hooks registered for that command and phase.
*cmd_obj* is a finalized command object; *hook_kind* is either
'pre_hook' or 'post_hook'.
"""
hooks = getattr(cmd_obj, hook_kind, None)
if not hooks:
return
for modname, hook in hooks:
if isinstance(hook, str):
try:
hook_obj = resolve_name(hook)
except ImportError as exc:
raise DistutilsModuleError(
'cannot find hook {0}: {1}'.format(hook, exc))
else:
hook_obj = hook
if not callable(hook_obj):
raise DistutilsOptionError('hook {0!r} is not callable' % hook)
log.info('running {0} from {1} for {2} command'.format(
hook_kind.rstrip('s'), modname, cmd_obj.get_command_name()))
try:
hook_obj(cmd_obj)
except Exception:
log.error('{0} command hook {1} raised an exception: %s\n'.format(
hook_obj.__name__, cmd_obj.get_command_name()))
log.error(traceback.format_exc())
sys.exit(1)
def generate_test_command(package_name):
"""
Creates a custom 'test' command for the given package which sets the
command's ``package_name`` class attribute to the name of the package being
tested.
"""
return type(package_name.title() + 'Test', (AstropyTest,),
{'package_name': package_name})
def update_package_files(srcdir, extensions, package_data, packagenames,
package_dirs):
"""
This function is deprecated and maintained for backward compatibility
with affiliated packages. Affiliated packages should update their
setup.py to use `get_package_info` instead.
"""
info = get_package_info(srcdir)
extensions.extend(info['ext_modules'])
package_data.update(info['package_data'])
packagenames = list(set(packagenames + info['packages']))
package_dirs.update(info['package_dir'])
def get_package_info(srcdir='.', exclude=()):
"""
Collates all of the information for building all subpackages
and returns a dictionary of keyword arguments that can
be passed directly to `distutils.setup`.
The purpose of this function is to allow subpackages to update the
arguments to the package's ``setup()`` function in its setup.py
script, rather than having to specify all extensions/package data
directly in the ``setup.py``. See Astropy's own
``setup.py`` for example usage and the Astropy development docs
for more details.
This function obtains that information by iterating through all
packages in ``srcdir`` and locating a ``setup_package.py`` module.
This module can contain the following functions:
``get_extensions()``, ``get_package_data()``,
``get_build_options()``, and ``get_external_libraries()``.
Each of those functions take no arguments.
- ``get_extensions`` returns a list of
`distutils.extension.Extension` objects.
- ``get_package_data()`` returns a dict formatted as required by
the ``package_data`` argument to ``setup()``.
- ``get_build_options()`` returns a list of tuples describing the
extra build options to add.
- ``get_external_libraries()`` returns
a list of libraries that can optionally be built using external
dependencies.
"""
ext_modules = []
packages = []
package_dir = {}
# Read in existing package data, and add to it below
setup_cfg = os.path.join(srcdir, 'setup.cfg')
if os.path.exists(setup_cfg):
conf = read_configuration(setup_cfg)
if 'options' in conf and 'package_data' in conf['options']:
package_data = conf['options']['package_data']
else:
package_data = {}
else:
package_data = {}
if exclude:
warnings.warn(
"Use of the exclude parameter is no longer supported since it does "
"not work as expected. Use add_exclude_packages instead. Note that "
"it must be called prior to any other calls from setup helpers.",
AstropyDeprecationWarning)
# Use the find_packages tool to locate all packages and modules
packages = find_packages(srcdir, exclude=exclude)
# Update package_dir if the package lies in a subdirectory
if srcdir != '.':
package_dir[''] = srcdir
# For each of the setup_package.py modules, extract any
# information that is needed to install them. The build options
# are extracted first, so that their values will be available in
# subsequent calls to `get_extensions`, etc.
for setuppkg in iter_setup_packages(srcdir, packages):
if hasattr(setuppkg, 'get_build_options'):
options = setuppkg.get_build_options()
for option in options:
add_command_option('build', *option)
if hasattr(setuppkg, 'get_external_libraries'):
libraries = setuppkg.get_external_libraries()
for library in libraries:
add_external_library(library)
for setuppkg in iter_setup_packages(srcdir, packages):
# get_extensions must include any Cython extensions by their .pyx
# filename.
if hasattr(setuppkg, 'get_extensions'):
ext_modules.extend(setuppkg.get_extensions())
if hasattr(setuppkg, 'get_package_data'):
package_data.update(setuppkg.get_package_data())
# Locate any .pyx files not already specified, and add their extensions in.
# The default include dirs include numpy to facilitate numerical work.
ext_modules.extend(get_cython_extensions(srcdir, packages, ext_modules,
['numpy']))
# Now remove extensions that have the special name 'skip_cython', as they
# exist Only to indicate that the cython extensions shouldn't be built
for i, ext in reversed(list(enumerate(ext_modules))):
if ext.name == 'skip_cython':
del ext_modules[i]
# On Microsoft compilers, we need to pass the '/MANIFEST'
# commandline argument. This was the default on MSVC 9.0, but is
# now required on MSVC 10.0, but it doesn't seem to hurt to add
# it unconditionally.
if get_compiler_option() == 'msvc':
for ext in ext_modules:
ext.extra_link_args.append('/MANIFEST')
return {
'ext_modules': ext_modules,
'packages': packages,
'package_dir': package_dir,
'package_data': package_data,
}
def iter_setup_packages(srcdir, packages):
""" A generator that finds and imports all of the ``setup_package.py``
modules in the source packages.
Returns
-------
modgen : generator
A generator that yields (modname, mod), where `mod` is the module and
`modname` is the module name for the ``setup_package.py`` modules.
"""
for packagename in packages:
package_parts = packagename.split('.')
package_path = os.path.join(srcdir, *package_parts)
setup_package = os.path.relpath(
os.path.join(package_path, 'setup_package.py'))
if os.path.isfile(setup_package):
module = import_file(setup_package,
name=packagename + '.setup_package')
yield module
def iter_pyx_files(package_dir, package_name):
"""
A generator that yields Cython source files (ending in '.pyx') in the
source packages.
Returns
-------
pyxgen : generator
A generator that yields (extmod, fullfn) where `extmod` is the
full name of the module that the .pyx file would live in based
on the source directory structure, and `fullfn` is the path to
the .pyx file.
"""
for dirpath, dirnames, filenames in walk_skip_hidden(package_dir):
for fn in filenames:
if fn.endswith('.pyx'):
fullfn = os.path.relpath(os.path.join(dirpath, fn))
# Package must match file name
extmod = '.'.join([package_name, fn[:-4]])
yield (extmod, fullfn)
break # Don't recurse into subdirectories
def get_cython_extensions(srcdir, packages, prevextensions=tuple(),
extincludedirs=None):
"""
Looks for Cython files and generates Extensions if needed.
Parameters
----------
srcdir : str
Path to the root of the source directory to search.
prevextensions : list of `~distutils.core.Extension` objects
The extensions that are already defined. Any .pyx files already here
will be ignored.
extincludedirs : list of str or None
Directories to include as the `include_dirs` argument to the generated
`~distutils.core.Extension` objects.
Returns
-------
exts : list of `~distutils.core.Extension` objects
The new extensions that are needed to compile all .pyx files (does not
include any already in `prevextensions`).
"""
# Vanilla setuptools and old versions of distribute include Cython files
# as .c files in the sources, not .pyx, so we cannot simply look for
# existing .pyx sources in the previous sources, but we should also check
# for .c files with the same remaining filename. So we look for .pyx and
# .c files, and we strip the extension.
prevsourcepaths = []
ext_modules = []
for ext in prevextensions:
for s in ext.sources:
if s.endswith(('.pyx', '.c', '.cpp')):
sourcepath = os.path.realpath(os.path.splitext(s)[0])
prevsourcepaths.append(sourcepath)
for package_name in packages:
package_parts = package_name.split('.')
package_path = os.path.join(srcdir, *package_parts)
for extmod, pyxfn in iter_pyx_files(package_path, package_name):
sourcepath = os.path.realpath(os.path.splitext(pyxfn)[0])
if sourcepath not in prevsourcepaths:
ext_modules.append(Extension(extmod, [pyxfn],
include_dirs=extincludedirs))
return ext_modules
class DistutilsExtensionArgs(collections.defaultdict):
"""
A special dictionary whose default values are the empty list.
This is useful for building up a set of arguments for
`distutils.Extension` without worrying whether the entry is
already present.
"""
def __init__(self, *args, **kwargs):
def default_factory():
return []
super(DistutilsExtensionArgs, self).__init__(
default_factory, *args, **kwargs)
def update(self, other):
for key, val in other.items():
self[key].extend(val)
def pkg_config(packages, default_libraries, executable='pkg-config'):
"""
Uses pkg-config to update a set of distutils Extension arguments
to include the flags necessary to link against the given packages.
If the pkg-config lookup fails, default_libraries is applied to
libraries.
Parameters
----------
packages : list of str
A list of pkg-config packages to look up.
default_libraries : list of str
A list of library names to use if the pkg-config lookup fails.
Returns
-------
config : dict
A dictionary containing keyword arguments to
`distutils.Extension`. These entries include:
- ``include_dirs``: A list of include directories
- ``library_dirs``: A list of library directories
- ``libraries``: A list of libraries
- ``define_macros``: A list of macro defines
- ``undef_macros``: A list of macros to undefine
- ``extra_compile_args``: A list of extra arguments to pass to
the compiler
"""
flag_map = {'-I': 'include_dirs', '-L': 'library_dirs', '-l': 'libraries',
'-D': 'define_macros', '-U': 'undef_macros'}
command = "{0} --libs --cflags {1}".format(executable, ' '.join(packages)),
result = DistutilsExtensionArgs()
try:
pipe = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
output = pipe.communicate()[0].strip()
except subprocess.CalledProcessError as e:
lines = [
("{0} failed. This may cause the build to fail below."
.format(executable)),
" command: {0}".format(e.cmd),
" returncode: {0}".format(e.returncode),
" output: {0}".format(e.output)
]
log.warn('\n'.join(lines))
result['libraries'].extend(default_libraries)
else:
if pipe.returncode != 0:
lines = [
"pkg-config could not lookup up package(s) {0}.".format(
", ".join(packages)),
"This may cause the build to fail below."
]
log.warn('\n'.join(lines))
result['libraries'].extend(default_libraries)
else:
for token in output.split():
# It's not clear what encoding the output of
# pkg-config will come to us in. It will probably be
# some combination of pure ASCII (for the compiler
# flags) and the filesystem encoding (for any argument
# that includes directories or filenames), but this is
# just conjecture, as the pkg-config documentation
# doesn't seem to address it.
arg = token[:2].decode('ascii')
value = token[2:].decode(sys.getfilesystemencoding())
if arg in flag_map:
if arg == '-D':
value = tuple(value.split('=', 1))
result[flag_map[arg]].append(value)
else:
result['extra_compile_args'].append(value)
return result
def add_external_library(library):
"""
Add a build option for selecting the internal or system copy of a library.
Parameters
----------
library : str
The name of the library. If the library is `foo`, the build
option will be called `--use-system-foo`.
"""
for command in ['build', 'build_ext', 'install']:
add_command_option(command, str('use-system-' + library),
'Use the system {0} library'.format(library),
is_bool=True)
def use_system_library(library):
"""
Returns `True` if the build configuration indicates that the given
library should use the system copy of the library rather than the
internal one.
For the given library `foo`, this will be `True` if
`--use-system-foo` or `--use-system-libraries` was provided at the
commandline or in `setup.cfg`.
Parameters
----------
library : str
The name of the library
Returns
-------
use_system : bool
`True` if the build should use the system copy of the library.
"""
return (
get_distutils_build_or_install_option('use_system_{0}'.format(library)) or
get_distutils_build_or_install_option('use_system_libraries'))
@extends_doc(_find_packages)
def find_packages(where='.', exclude=(), invalidate_cache=False):
"""
This version of ``find_packages`` caches previous results to speed up
subsequent calls. Use ``invalide_cache=True`` to ignore cached results
from previous ``find_packages`` calls, and repeat the package search.
"""
if exclude:
warnings.warn(
"Use of the exclude parameter is no longer supported since it does "
"not work as expected. Use add_exclude_packages instead. Note that "
"it must be called prior to any other calls from setup helpers.",
AstropyDeprecationWarning)
# Calling add_exclude_packages after this point will have no effect
_module_state['excludes_too_late'] = True
if not invalidate_cache and _module_state['package_cache'] is not None:
return _module_state['package_cache']
packages = _find_packages(
where=where, exclude=list(_module_state['exclude_packages']))
_module_state['package_cache'] = packages
return packages
class FakeBuildSphinx(Command):
"""
A dummy build_sphinx command that is called if Sphinx is not
installed and displays a relevant error message
"""
# user options inherited from sphinx.setup_command.BuildDoc
user_options = [
('fresh-env', 'E', ''),
('all-files', 'a', ''),
('source-dir=', 's', ''),
('build-dir=', None, ''),
('config-dir=', 'c', ''),
('builder=', 'b', ''),
('project=', None, ''),
('version=', None, ''),
('release=', None, ''),
('today=', None, ''),
('link-index', 'i', '')]
# user options appended in astropy.setup_helpers.AstropyBuildSphinx
user_options.append(('warnings-returncode', 'w', ''))
user_options.append(('clean-docs', 'l', ''))
user_options.append(('no-intersphinx', 'n', ''))
user_options.append(('open-docs-in-browser', 'o', ''))
def initialize_options(self):
try:
raise RuntimeError("Sphinx and its dependencies must be installed "
"for build_docs.")
except:
log.error('error: Sphinx and its dependencies must be installed '
'for build_docs.')
sys.exit(1)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1578142609.0
specutils-0.7/astropy_helpers/astropy_helpers/sphinx/ 0000755 0000765 0000024 00000000000 00000000000 023430 5 ustar 00erik staff 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1565986938.0
specutils-0.7/astropy_helpers/astropy_helpers/sphinx/__init__.py 0000644 0000765 0000024 00000000000 00000000000 025527 0 ustar 00erik staff 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1565986938.0
specutils-0.7/astropy_helpers/astropy_helpers/sphinx/conf.py 0000644 0000765 0000024 00000000234 00000000000 024726 0 ustar 00erik staff 0000000 0000000 import warnings
from sphinx_astropy.conf import *
warnings.warn("Note that astropy_helpers.sphinx.conf is deprecated - use sphinx_astropy.conf instead")
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1565986938.0
specutils-0.7/astropy_helpers/astropy_helpers/utils.py 0000644 0000765 0000024 00000020701 00000000000 023631 0 ustar 00erik staff 0000000 0000000 # Licensed under a 3-clause BSD style license - see LICENSE.rst
from __future__ import absolute_import, unicode_literals
import contextlib
import functools
import imp
import os
import sys
import glob
from importlib import machinery as import_machinery
# Note: The following Warning subclasses are simply copies of the Warnings in
# Astropy of the same names.
class AstropyWarning(Warning):
"""
The base warning class from which all Astropy warnings should inherit.
Any warning inheriting from this class is handled by the Astropy logger.
"""
class AstropyDeprecationWarning(AstropyWarning):
"""
A warning class to indicate a deprecated feature.
"""
class AstropyPendingDeprecationWarning(PendingDeprecationWarning,
AstropyWarning):
"""
A warning class to indicate a soon-to-be deprecated feature.
"""
def _get_platlib_dir(cmd):
"""
Given a build command, return the name of the appropriate platform-specific
build subdirectory directory (e.g. build/lib.linux-x86_64-2.7)
"""
plat_specifier = '.{0}-{1}'.format(cmd.plat_name, sys.version[0:3])
return os.path.join(cmd.build_base, 'lib' + plat_specifier)
def get_numpy_include_path():
"""
Gets the path to the numpy headers.
"""
# We need to go through this nonsense in case setuptools
# downloaded and installed Numpy for us as part of the build or
# install, since Numpy may still think it's in "setup mode", when
# in fact we're ready to use it to build astropy now.
import builtins
if hasattr(builtins, '__NUMPY_SETUP__'):
del builtins.__NUMPY_SETUP__
import imp
import numpy
imp.reload(numpy)
try:
numpy_include = numpy.get_include()
except AttributeError:
numpy_include = numpy.get_numpy_include()
return numpy_include
class _DummyFile(object):
"""A noop writeable object."""
errors = ''
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
if sys.platform == 'win32':
import ctypes
def _has_hidden_attribute(filepath):
"""
Returns True if the given filepath has the hidden attribute on
MS-Windows. Based on a post here:
http://stackoverflow.com/questions/284115/cross-platform-hidden-file-detection
"""
if isinstance(filepath, bytes):
filepath = filepath.decode(sys.getfilesystemencoding())
try:
attrs = ctypes.windll.kernel32.GetFileAttributesW(filepath)
assert attrs != -1
result = bool(attrs & 2)
except (AttributeError, AssertionError):
result = False
return result
else:
def _has_hidden_attribute(filepath):
return False
def is_path_hidden(filepath):
"""
Determines if a given file or directory is hidden.
Parameters
----------
filepath : str
The path to a file or directory
Returns
-------
hidden : bool
Returns `True` if the file is hidden
"""
name = os.path.basename(os.path.abspath(filepath))
if isinstance(name, bytes):
is_dotted = name.startswith(b'.')
else:
is_dotted = name.startswith('.')
return is_dotted or _has_hidden_attribute(filepath)
def walk_skip_hidden(top, onerror=None, followlinks=False):
"""
A wrapper for `os.walk` that skips hidden files and directories.
This function does not have the parameter `topdown` from
`os.walk`: the directories must always be recursed top-down when
using this function.
See also
--------
os.walk : For a description of the parameters
"""
for root, dirs, files in os.walk(
top, topdown=True, onerror=onerror,
followlinks=followlinks):
# These lists must be updated in-place so os.walk will skip
# hidden directories
dirs[:] = [d for d in dirs if not is_path_hidden(d)]
files[:] = [f for f in files if not is_path_hidden(f)]
yield root, dirs, files
def write_if_different(filename, data):
"""Write `data` to `filename`, if the content of the file is different.
Parameters
----------
filename : str
The file name to be written to.
data : bytes
The data to be written to `filename`.
"""
assert isinstance(data, bytes)
if os.path.exists(filename):
with open(filename, 'rb') as fd:
original_data = fd.read()
else:
original_data = None
if original_data != data:
with open(filename, 'wb') as fd:
fd.write(data)
def import_file(filename, name=None):
"""
Imports a module from a single file as if it doesn't belong to a
particular package.
The returned module will have the optional ``name`` if given, or else
a name generated from the filename.
"""
# Specifying a traditional dot-separated fully qualified name here
# results in a number of "Parent module 'astropy' not found while
# handling absolute import" warnings. Using the same name, the
# namespaces of the modules get merged together. So, this
# generates an underscore-separated name which is more likely to
# be unique, and it doesn't really matter because the name isn't
# used directly here anyway.
mode = 'r'
if name is None:
basename = os.path.splitext(filename)[0]
name = '_'.join(os.path.relpath(basename).split(os.sep)[1:])
if not os.path.exists(filename):
raise ImportError('Could not import file {0}'.format(filename))
if import_machinery:
loader = import_machinery.SourceFileLoader(name, filename)
mod = loader.load_module()
else:
with open(filename, mode) as fd:
mod = imp.load_module(name, fd, filename, ('.py', mode, 1))
return mod
def resolve_name(name):
"""Resolve a name like ``module.object`` to an object and return it.
Raise `ImportError` if the module or name is not found.
"""
parts = name.split('.')
cursor = len(parts) - 1
module_name = parts[:cursor]
attr_name = parts[-1]
while cursor > 0:
try:
ret = __import__('.'.join(module_name), fromlist=[attr_name])
break
except ImportError:
if cursor == 0:
raise
cursor -= 1
module_name = parts[:cursor]
attr_name = parts[cursor]
ret = ''
for part in parts[cursor:]:
try:
ret = getattr(ret, part)
except AttributeError:
raise ImportError(name)
return ret
def extends_doc(extended_func):
"""
A function decorator for use when wrapping an existing function but adding
additional functionality. This copies the docstring from the original
function, and appends to it (along with a newline) the docstring of the
wrapper function.
Examples
--------
>>> def foo():
... '''Hello.'''
...
>>> @extends_doc(foo)
... def bar():
... '''Goodbye.'''
...
>>> print(bar.__doc__)
Hello.
Goodbye.
"""
def decorator(func):
if not (extended_func.__doc__ is None or func.__doc__ is None):
func.__doc__ = '\n\n'.join([extended_func.__doc__.rstrip('\n'),
func.__doc__.lstrip('\n')])
return func
return decorator
def find_data_files(package, pattern):
"""
Include files matching ``pattern`` inside ``package``.
Parameters
----------
package : str
The package inside which to look for data files
pattern : str
Pattern (glob-style) to match for the data files (e.g. ``*.dat``).
This supports the``**``recursive syntax. For example, ``**/*.fits``
matches all files ending with ``.fits`` recursively. Only one
instance of ``**`` can be included in the pattern.
"""
return glob.glob(os.path.join(package, pattern), recursive=True)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1537500356.0
specutils-0.7/astropy_helpers/astropy_helpers/version.py 0000644 0000765 0000024 00000001066 00000000000 024161 0 ustar 00erik staff 0000000 0000000 # Autogenerated by Astropy-affiliated package astropy_helpers's setup.py on 2018-09-21 03:25:56 UTC
from __future__ import unicode_literals
import datetime
version = "3.0.2"
githash = "ca630405b54d40695b7bbd2824cb0f142b5811b6"
major = 3
minor = 0
bugfix = 2
release = True
timestamp = datetime.datetime(2018, 9, 21, 3, 25, 56)
debug = False
astropy_helpers_version = ""
try:
from ._compiler import compiler
except ImportError:
compiler = "unknown"
try:
from .cython_version import cython_version
except ImportError:
cython_version = "unknown"
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1565986938.0
specutils-0.7/astropy_helpers/astropy_helpers/version_helpers.py 0000644 0000765 0000024 00000030737 00000000000 025712 0 ustar 00erik staff 0000000 0000000 # Licensed under a 3-clause BSD style license - see LICENSE.rst
"""
Utilities for generating the version string for Astropy (or an affiliated
package) and the version.py module, which contains version info for the
package.
Within the generated astropy.version module, the `major`, `minor`, and `bugfix`
variables hold the respective parts of the version number (bugfix is '0' if
absent). The `release` variable is True if this is a release, and False if this
is a development version of astropy. For the actual version string, use::
from astropy.version import version
or::
from astropy import __version__
"""
from __future__ import division
import datetime
import os
import pkgutil
import sys
import time
import warnings
from distutils import log
from configparser import ConfigParser
import pkg_resources
from . import git_helpers
from .distutils_helpers import is_distutils_display_option
from .git_helpers import get_git_devstr
from .utils import AstropyDeprecationWarning, import_file
__all__ = ['generate_version_py']
def _version_split(version):
"""
Split a version string into major, minor, and bugfix numbers. If any of
those numbers are missing the default is zero. Any pre/post release
modifiers are ignored.
Examples
========
>>> _version_split('1.2.3')
(1, 2, 3)
>>> _version_split('1.2')
(1, 2, 0)
>>> _version_split('1.2rc1')
(1, 2, 0)
>>> _version_split('1')
(1, 0, 0)
>>> _version_split('')
(0, 0, 0)
"""
parsed_version = pkg_resources.parse_version(version)
if hasattr(parsed_version, 'base_version'):
# New version parsing for setuptools >= 8.0
if parsed_version.base_version:
parts = [int(part)
for part in parsed_version.base_version.split('.')]
else:
parts = []
else:
parts = []
for part in parsed_version:
if part.startswith('*'):
# Ignore any .dev, a, b, rc, etc.
break
parts.append(int(part))
if len(parts) < 3:
parts += [0] * (3 - len(parts))
# In principle a version could have more parts (like 1.2.3.4) but we only
# support ..
return tuple(parts[:3])
# This is used by setup.py to create a new version.py - see that file for
# details. Note that the imports have to be absolute, since this is also used
# by affiliated packages.
_FROZEN_VERSION_PY_TEMPLATE = """
# Autogenerated by {packagetitle}'s setup.py on {timestamp!s} UTC
from __future__ import unicode_literals
import datetime
{header}
major = {major}
minor = {minor}
bugfix = {bugfix}
version_info = (major, minor, bugfix)
release = {rel}
timestamp = {timestamp!r}
debug = {debug}
astropy_helpers_version = "{ahver}"
"""[1:]
_FROZEN_VERSION_PY_WITH_GIT_HEADER = """
{git_helpers}
_packagename = "{packagename}"
_last_generated_version = "{verstr}"
_last_githash = "{githash}"
# Determine where the source code for this module
# lives. If __file__ is not a filesystem path then
# it is assumed not to live in a git repo at all.
if _get_repo_path(__file__, levels=len(_packagename.split('.'))):
version = update_git_devstr(_last_generated_version, path=__file__)
githash = get_git_devstr(sha=True, show_warning=False,
path=__file__) or _last_githash
else:
# The file does not appear to live in a git repo so don't bother
# invoking git
version = _last_generated_version
githash = _last_githash
"""[1:]
_FROZEN_VERSION_PY_STATIC_HEADER = """
version = "{verstr}"
githash = "{githash}"
"""[1:]
def _get_version_py_str(packagename, version, githash, release, debug,
uses_git=True):
try:
from astropy_helpers import __version__ as ahver
except ImportError:
ahver = "unknown"
epoch = int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))
timestamp = datetime.datetime.utcfromtimestamp(epoch)
major, minor, bugfix = _version_split(version)
if packagename.lower() == 'astropy':
packagetitle = 'Astropy'
else:
packagetitle = 'Astropy-affiliated package ' + packagename
header = ''
if uses_git:
header = _generate_git_header(packagename, version, githash)
elif not githash:
# _generate_git_header will already generate a new git has for us, but
# for creating a new version.py for a release (even if uses_git=False)
# we still need to get the githash to include in the version.py
# See https://github.com/astropy/astropy-helpers/issues/141
githash = git_helpers.get_git_devstr(sha=True, show_warning=True)
if not header: # If _generate_git_header fails it returns an empty string
header = _FROZEN_VERSION_PY_STATIC_HEADER.format(verstr=version,
githash=githash)
return _FROZEN_VERSION_PY_TEMPLATE.format(packagetitle=packagetitle,
timestamp=timestamp,
header=header,
major=major,
minor=minor,
bugfix=bugfix,
ahver=ahver,
rel=release, debug=debug)
def _generate_git_header(packagename, version, githash):
"""
Generates a header to the version.py module that includes utilities for
probing the git repository for updates (to the current git hash, etc.)
These utilities should only be available in development versions, and not
in release builds.
If this fails for any reason an empty string is returned.
"""
loader = pkgutil.get_loader(git_helpers)
source = loader.get_source(git_helpers.__name__) or ''
source_lines = source.splitlines()
if not source_lines:
log.warn('Cannot get source code for astropy_helpers.git_helpers; '
'git support disabled.')
return ''
idx = 0
for idx, line in enumerate(source_lines):
if line.startswith('# BEGIN'):
break
git_helpers_py = '\n'.join(source_lines[idx + 1:])
verstr = version
new_githash = git_helpers.get_git_devstr(sha=True, show_warning=False)
if new_githash:
githash = new_githash
return _FROZEN_VERSION_PY_WITH_GIT_HEADER.format(
git_helpers=git_helpers_py, packagename=packagename,
verstr=verstr, githash=githash)
def generate_version_py(packagename=None, version=None, release=None, debug=None,
uses_git=None, srcdir='.'):
"""
Generate a version.py file in the package with version information, and
update developer version strings.
This function should normally be called without any arguments. In this case
the package name and version is read in from the ``setup.cfg`` file (from
the ``name`` or ``package_name`` entry and the ``version`` entry in the
``[metadata]`` section).
If the version is a developer version (of the form ``3.2.dev``), the
version string will automatically be expanded to include a sequential
number as a suffix (e.g. ``3.2.dev13312``), and the updated version string
will be returned by this function.
Based on this updated version string, a ``version.py`` file will be
generated inside the package, containing the version string as well as more
detailed information (for example the major, minor, and bugfix version
numbers, a ``release`` flag indicating whether the current version is a
stable or developer version, and so on.
"""
if packagename is not None:
warnings.warn('The packagename argument to generate_version_py has '
'been deprecated and will be removed in future. Specify '
'the package name in setup.cfg instead', AstropyDeprecationWarning)
if version is not None:
warnings.warn('The version argument to generate_version_py has '
'been deprecated and will be removed in future. Specify '
'the version number in setup.cfg instead', AstropyDeprecationWarning)
if release is not None:
warnings.warn('The release argument to generate_version_py has '
'been deprecated and will be removed in future. We now '
'use the presence of the "dev" string in the version to '
'determine whether this is a release', AstropyDeprecationWarning)
# We use ConfigParser instead of read_configuration here because the latter
# only reads in keys recognized by setuptools, but we need to access
# package_name below.
conf = ConfigParser()
conf.read('setup.cfg')
if conf.has_option('metadata', 'name'):
packagename = conf.get('metadata', 'name')
elif conf.has_option('metadata', 'package_name'):
# The package-template used package_name instead of name for a while
warnings.warn('Specifying the package name using the "package_name" '
'option in setup.cfg is deprecated - use the "name" '
'option instead.', AstropyDeprecationWarning)
packagename = conf.get('metadata', 'package_name')
elif packagename is not None: # deprecated
pass
else:
sys.stderr.write('ERROR: Could not read package name from setup.cfg\n')
sys.exit(1)
if conf.has_option('metadata', 'version'):
version = conf.get('metadata', 'version')
add_git_devstr = True
elif version is not None: # deprecated
add_git_devstr = False
else:
sys.stderr.write('ERROR: Could not read package version from setup.cfg\n')
sys.exit(1)
if release is None:
release = 'dev' not in version
if not release and add_git_devstr:
version += get_git_devstr(False)
if uses_git is None:
uses_git = not release
# In some cases, packages have a - but this is a _ in the module. Since we
# are only interested in the module here, we replace - by _
packagename = packagename.replace('-', '_')
try:
version_module = get_pkg_version_module(packagename)
try:
last_generated_version = version_module._last_generated_version
except AttributeError:
last_generated_version = version_module.version
try:
last_githash = version_module._last_githash
except AttributeError:
last_githash = version_module.githash
current_release = version_module.release
current_debug = version_module.debug
except ImportError:
version_module = None
last_generated_version = None
last_githash = None
current_release = None
current_debug = None
if release is None:
# Keep whatever the current value is, if it exists
release = bool(current_release)
if debug is None:
# Likewise, keep whatever the current value is, if it exists
debug = bool(current_debug)
package_srcdir = os.path.join(srcdir, *packagename.split('.'))
version_py = os.path.join(package_srcdir, 'version.py')
if (last_generated_version != version or current_release != release or
current_debug != debug):
if '-q' not in sys.argv and '--quiet' not in sys.argv:
log.set_threshold(log.INFO)
if is_distutils_display_option():
# Always silence unnecessary log messages when display options are
# being used
log.set_threshold(log.WARN)
log.info('Freezing version number to {0}'.format(version_py))
with open(version_py, 'w') as f:
# This overwrites the actual version.py
f.write(_get_version_py_str(packagename, version, last_githash,
release, debug, uses_git=uses_git))
return version
def get_pkg_version_module(packagename, fromlist=None):
"""Returns the package's .version module generated by
`astropy_helpers.version_helpers.generate_version_py`. Raises an
ImportError if the version module is not found.
If ``fromlist`` is an iterable, return a tuple of the members of the
version module corresponding to the member names given in ``fromlist``.
Raises an `AttributeError` if any of these module members are not found.
"""
version = import_file(os.path.join(packagename, 'version.py'), name='version')
if fromlist:
return tuple(getattr(version, member) for member in fromlist)
else:
return version
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1578142609.0
specutils-0.7/astropy_helpers/astropy_helpers.egg-info/ 0000755 0000765 0000024 00000000000 00000000000 023611 5 ustar 00erik staff 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1537500357.0
specutils-0.7/astropy_helpers/astropy_helpers.egg-info/PKG-INFO 0000644 0000765 0000024 00000010567 00000000000 024717 0 ustar 00erik staff 0000000 0000000 Metadata-Version: 1.2
Name: astropy-helpers
Version: 3.0.2
Summary: Utilities for building and installing Astropy, Astropy affiliated packages, and their respective documentation.
Home-page: https://github.com/astropy/astropy-helpers
Author: The Astropy Developers
Author-email: astropy.team@gmail.com
License: BSD
Description: astropy-helpers
===============
* Stable versions: https://pypi.org/project/astropy-helpers/
* Development version, issue tracker: https://github.com/astropy/astropy-helpers
.. warning::
Please note that version ``v3.0`` and later of ``astropy-helpers`` does
require Python 3.5 or later. If you wish to maintain Python 2 support
for your package that uses ``astropy-helpers``, then do not upgrade the
helpers to ``v3.0+``. We will still provide Python 2.7 compatible
releases on the ``v2.0.x`` branch during the lifetime of the ``astropy``
core package LTS of ``v2.0.x``.
This project provides a Python package, ``astropy_helpers``, which includes
many build, installation, and documentation-related tools used by the Astropy
project, but packaged separately for use by other projects that wish to
leverage this work. The motivation behind this package and details of its
implementation are in the accepted
`Astropy Proposal for Enhancement (APE) 4 `_.
The ``astropy_helpers.extern`` sub-module includes modules developed elsewhere
that are bundled here for convenience. At the moment, this consists of the
following two sphinx extensions:
* `numpydoc `_, a Sphinx extension
developed as part of the Numpy project. This is used to parse docstrings
in Numpy format
* `sphinx-automodapi `_, a Sphinx
extension developed as part of the Astropy project. This used to be developed
directly in ``astropy-helpers`` but is now a standalone package.
Issues with these sub-modules should be reported in their respective repositories,
and we will regularly update the bundled versions to reflect the latest released
versions.
``astropy_helpers`` includes a special "bootstrap" module called
``ah_bootstrap.py`` which is intended to be used by a project's setup.py in
order to ensure that the ``astropy_helpers`` package is available for
build/installation. This is similar to the ``ez_setup.py`` module that is
shipped with some projects to bootstrap `setuptools
`_.
As described in APE4, the version numbers for ``astropy_helpers`` follow the
corresponding major/minor version of the `astropy core package
`_, but with an independent sequence of micro (bugfix)
version numbers. Hence, the initial release is 0.4, in parallel with Astropy
v0.4, which will be the first version of Astropy to use ``astropy-helpers``.
For examples of how to implement ``astropy-helpers`` in a project,
see the ``setup.py`` and ``setup.cfg`` files of the
`Affiliated package template `_.
.. image:: https://travis-ci.org/astropy/astropy-helpers.svg
:target: https://travis-ci.org/astropy/astropy-helpers
.. image:: https://coveralls.io/repos/astropy/astropy-helpers/badge.svg
:target: https://coveralls.io/r/astropy/astropy-helpers
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Framework :: Setuptools Plugin
Classifier: Framework :: Sphinx :: Extension
Classifier: Framework :: Sphinx :: Theme
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Build Tools
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Archiving :: Packaging
Requires-Python: >=3.5
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1537500357.0
specutils-0.7/astropy_helpers/astropy_helpers.egg-info/SOURCES.txt 0000644 0000765 0000024 00000006016 00000000000 025500 0 ustar 00erik staff 0000000 0000000 CHANGES.rst
LICENSE.rst
MANIFEST.in
README.rst
ah_bootstrap.py
setup.cfg
setup.py
astropy_helpers/__init__.py
astropy_helpers/conftest.py
astropy_helpers/distutils_helpers.py
astropy_helpers/git_helpers.py
astropy_helpers/openmp_helpers.py
astropy_helpers/setup_helpers.py
astropy_helpers/test_helpers.py
astropy_helpers/utils.py
astropy_helpers/version.py
astropy_helpers/version_helpers.py
astropy_helpers.egg-info/PKG-INFO
astropy_helpers.egg-info/SOURCES.txt
astropy_helpers.egg-info/dependency_links.txt
astropy_helpers.egg-info/not-zip-safe
astropy_helpers.egg-info/top_level.txt
astropy_helpers/commands/__init__.py
astropy_helpers/commands/_dummy.py
astropy_helpers/commands/build_ext.py
astropy_helpers/commands/build_sphinx.py
astropy_helpers/commands/setup_package.py
astropy_helpers/commands/test.py
astropy_helpers/commands/src/compiler.c
astropy_helpers/extern/__init__.py
astropy_helpers/extern/setup_package.py
astropy_helpers/extern/automodapi/__init__.py
astropy_helpers/extern/automodapi/autodoc_enhancements.py
astropy_helpers/extern/automodapi/automodapi.py
astropy_helpers/extern/automodapi/automodsumm.py
astropy_helpers/extern/automodapi/smart_resolver.py
astropy_helpers/extern/automodapi/utils.py
astropy_helpers/extern/automodapi/templates/autosummary_core/base.rst
astropy_helpers/extern/automodapi/templates/autosummary_core/class.rst
astropy_helpers/extern/automodapi/templates/autosummary_core/module.rst
astropy_helpers/extern/numpydoc/__init__.py
astropy_helpers/extern/numpydoc/docscrape.py
astropy_helpers/extern/numpydoc/docscrape_sphinx.py
astropy_helpers/extern/numpydoc/numpydoc.py
astropy_helpers/extern/numpydoc/templates/numpydoc_docstring.rst
astropy_helpers/sphinx/__init__.py
astropy_helpers/sphinx/conf.py
astropy_helpers/sphinx/setup_package.py
astropy_helpers/sphinx/ext/__init__.py
astropy_helpers/sphinx/ext/changelog_links.py
astropy_helpers/sphinx/ext/doctest.py
astropy_helpers/sphinx/ext/edit_on_github.py
astropy_helpers/sphinx/ext/tocdepthfix.py
astropy_helpers/sphinx/ext/tests/__init__.py
astropy_helpers/sphinx/local/python3_local_links.inv
astropy_helpers/sphinx/themes/bootstrap-astropy/globaltoc.html
astropy_helpers/sphinx/themes/bootstrap-astropy/layout.html
astropy_helpers/sphinx/themes/bootstrap-astropy/localtoc.html
astropy_helpers/sphinx/themes/bootstrap-astropy/searchbox.html
astropy_helpers/sphinx/themes/bootstrap-astropy/theme.conf
astropy_helpers/sphinx/themes/bootstrap-astropy/static/astropy_linkout.svg
astropy_helpers/sphinx/themes/bootstrap-astropy/static/astropy_linkout_20.png
astropy_helpers/sphinx/themes/bootstrap-astropy/static/astropy_logo.ico
astropy_helpers/sphinx/themes/bootstrap-astropy/static/astropy_logo.svg
astropy_helpers/sphinx/themes/bootstrap-astropy/static/astropy_logo_32.png
astropy_helpers/sphinx/themes/bootstrap-astropy/static/bootstrap-astropy.css
astropy_helpers/sphinx/themes/bootstrap-astropy/static/copybutton.js
astropy_helpers/sphinx/themes/bootstrap-astropy/static/sidebar.js
licenses/LICENSE_ASTROSCRAPPY.rst
licenses/LICENSE_COPYBUTTON.rst
licenses/LICENSE_NUMPYDOC.rst ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1537500357.0
specutils-0.7/astropy_helpers/astropy_helpers.egg-info/dependency_links.txt 0000644 0000765 0000024 00000000001 00000000000 027657 0 ustar 00erik staff 0000000 0000000
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1537500357.0
specutils-0.7/astropy_helpers/astropy_helpers.egg-info/not-zip-safe 0000644 0000765 0000024 00000000001 00000000000 026037 0 ustar 00erik staff 0000000 0000000
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1537500357.0
specutils-0.7/astropy_helpers/astropy_helpers.egg-info/top_level.txt 0000644 0000765 0000024 00000000020 00000000000 026333 0 ustar 00erik staff 0000000 0000000 astropy_helpers
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1578142609.0
specutils-0.7/astropy_helpers/licenses/ 0000755 0000765 0000024 00000000000 00000000000 020501 5 ustar 00erik staff 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1537500356.0
specutils-0.7/astropy_helpers/licenses/LICENSE_ASTROSCRAPPY.rst 0000644 0000765 0000024 00000003154 00000000000 024332 0 ustar 00erik staff 0000000 0000000 # The OpenMP helpers include code heavily adapted from astroscrappy, released
# under the following license:
#
# Copyright (c) 2015, Curtis McCully
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice, this
# list of conditions and the following disclaimer in the documentation and/or
# other materials provided with the distribution.
# * Neither the name of the Astropy Team nor the names of its contributors may be
# used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1578142609.0
specutils-0.7/docs/ 0000755 0000765 0000024 00000000000 00000000000 014401 5 ustar 00erik staff 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1537503308.0
specutils-0.7/docs/Makefile 0000644 0000765 0000024 00000010745 00000000000 016050 0 ustar 00erik staff 0000000 0000000 # Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
#This is needed with git because git doesn't create a dir if it's empty
$(shell [ -d "_static" ] || mkdir -p _static)
help:
@echo "Please use \`make ' where is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
clean:
-rm -rf $(BUILDDIR)
-rm -rf api
-rm -rf generated
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Astropy.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Astropy.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/Astropy"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Astropy"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
make -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
@echo "Run 'python setup.py test' in the root directory to run doctests " \
@echo "in the documentation."
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1578142609.0
specutils-0.7/docs/_static/ 0000755 0000765 0000024 00000000000 00000000000 016027 5 ustar 00erik staff 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1578132243.0
specutils-0.7/docs/_static/logo_icon.png 0000644 0000765 0000024 00000021623 00000000000 020511 0 ustar 00erik staff 0000000 0000000 ‰PNG
IHDR ` _ ´Â@ sRGB ®Îé pHYs
×
×B(›x YiTXtXML:com.adobe.xmp
1
LÂ'Y !ÓIDATxí] ]e•þïëÎ! aI „ I [Pã‚bY…€…Q™:Áq†Yœ®©™‘TÐÂAQ)÷AÊQQÂNB6ÙÈFºûÝ9ßwÎùÿÿ¾î×Kºµj®ÿü÷,ßùÎùß»ï¾{oÇþû“v ø“fßÁÉ/¾õÖ]ÚÛG)[;w«okí(ÃÛõ"”†
ߥ¼ýâs×ïàô}‚ÿ‹_€þùÎý;Šâˆê3ZZjS˲˜QáÀPc¤ÃÙ ¡]ô#IQlÕ«"_-ŠrA(‹gêCZuÇEg/„çÎÚHkg%Œ<çµ}e|Kky¢4ô}!ÔD“«v)eá¥]7i:`¦F8bT¿Z±´(Ã/CQûyÑ1ì'·]2{;hÐÌ;|°`çþËmû†²åŒ²Ÿ”.'ýB÷¬±Ö8$£&Xáb2Ÿg:†ˆ-J`rOc‹âMYĈö[·\xæ¯ÅXÒyÍ>ˆ€ƒuF[ÛÐѵÝgÞ¹Ò„÷JGZ´Á¢A£Qþ.¯Ø¬ùluî's_µiùŽ¡’NÑÏu’ï)‰ýÂÍžùÀ`Õe0˜ˆÄúTÛ{µ–¹!” ½ñÚK4…«HÐçBTlÚÀh£•™ûQ…ønlš‘€±œ6ÅïkeqÝM~âá–ÊpË>PØÓvã>aÈÕeΓ¦Œ`å¬Y×f(]oZöîd“À ¢C"TdwºØô,¶’ÏðD÷–zÇ箿àoÖ
¤rh Œ=ë²›FYûl(kWKÍ£¤C±i,½“ÿá¥6¥\iŠÙ»Õ¹2-Ѱ:Ý-Œøú¹¬ø%.ËÅõüçÎþ÷ímƒV³½Ñˆ;ãŒû[FM_úé²V´I¹û ?Ú`—F
Š6Õy£µ)¢ã+—ùœ¨l4‘LBÅG›
ºd?
Ž2bɳØnºáïþú*ë÷—4q¶379ÎO)ËòBø=ÈÛØŒn×°‘Í`{Ìî±Ä]ÕF'Ë—ò²ù:D.wÒ"tá,Iåõ÷GÕϺmvÿN[•ÑNê~[[[ë’ÎÝ.µâ¥üáZ(° ÊTx*˜$ÙYóCÁÚD"¼‚AWê3¿èà ºoaŠáRù4Ñ!“&¨.tex4´;ýúNéó÷‚±A;v›Óvó´¢^~[~àí…FÉ‚ª«Mé5¬î¹M¼¹›dµÁRä?uË%ô¨=ÅVòE'‘K™ó•Ëâ·[G¶ ¯Ÿ„ÚŽm»¢ŸÓvËiE=<®Íg(7î\çújÁا–!ô£éÜ¡ÍÛF·¤swq\sõD@7TŒH6æÑdDUó¬]7»ÿŒûïoAºÞ¶ÖÞbÇíˆéKÿEÞùW€=ùɘÈzqÊã…Áu.Ejµ–P/ëƒXÃ9Ã1$JÅPoK`6äñ|*
ŽvE1,š
C‚,Œ\}Xe(O9xÓð;Åð„ô´í°OÀ¹Ÿÿ×½w›¶ä?åê㕨DÉZ!ƼZ¸Ðô¢¤µ©Ž˜íÇ«øBë3»¤Õ@Ò¦ú¡‚“c˜G#é4£èÃ¥ÖQžõ78{ÚvÈœÝvýIJuˆ\; 'æA$¾4ú%MUq¬\ki '{d·çîŒñ‚GŠ#cÊéù r,Z1È‹É="é\mÀ¾€žë:—Š«ù¤¹_n»÷‰êßý8è€/ÛZgËcBz*S²V®’u›’Öí€û)Df8!ŒÜu—0ã ýÑOD™ìm®âTmÐk¬ËØ`(ÌutQ?Ïä1î6Ò-7!¾ä92v·
ê̹îæcÊÎW
÷g2!ÏÌä¤Ä¬B5+cÝ&V+Øeˆ£§OaÜ¡“ ï1*eD°¹sOç2Vmt„+Lj,4¹.qpZ1(ªc¨¢¢s`Á8éß~ð<Åî:ÚœsÝ'È×Ï/$á^Õ‚AžŒ³âP8½¨ƒYW]´±¡;´µ5qðpï¿o6tHÄU|Ã3\ÇðF30Ú°*="Ñ7qR7—Ð*§„Á™úG\ÑY¾²(ÚÚîyd¸9TÄ ,Àœën=\èá2íHmƒ“Õ=« l¼ÀPM í´Üè6ÖZHóÇ¥W«|`ÄHOc±^¸&¤SÌ 6Õ€aT9»¡3N̨ÊXMÄäuˆÌbÄ}BçÐr…·ë6à8çÚåŽTçC=ÆI£1,#‰€é¢L«Š£…6ÚÒá(Øf4AFLõšNŠK+]MÃxÎÅÁ¥*5Ö›93“Ûà)Q ÜòºQÀh«•åçÛîz`Wª³a@p^Û
ã%®‹ï뤑ы2JJB
™M¢œ¬É¸X ˜Ùv6̃n‡â‹Sñ³ÖTb’„àÈçê<+º,/31Lc=“%6H·)ÄD|1ÑI•û„Cºœ–n÷ÌmkÛµÞQ{PLÊ›å9óâ¬C(½BZy1"’ŽÅ‘ºÚŽšzPÀa'ßö=2ŒÝcws7:X›b#¡]\B¥1´1*Ó‰TwH™ñ¥Rý»êÏ1Tf~ÈÑRœc©¢ØîØÖ1â«Bl&K°ÏdFV©8Qç¤##ÍiÏ~"k›ð0dMsÝeÒȧ2QHœ
²¡†é`±–hâ¸5]”ÌŠAýËò]m÷=4-æ’Év-Àœko¼H¹šš²¡*˜KÔa~JGyD²Ž‘Hc6j·]õ7gmsü.ÀV-œÕÆŠª:àéÒóÉŠ“ãR#QmGÄß2¡
ý^€s¯¹åxIrâµpfÀ!]ç¤U>ôh,\ÌM1<ïœ6Y®^›žÒ0eÂ893¢Šˆ›
N…w¯óFújÍÅXAvKp£GÌ—s³¨Š-(VQ|4UÐÏOÀ™W}qLY«ß/ Cœ´Ò$‘43d:N1ÈØLÆÅR¥™à#HtÅá‡ýÐÖ8à{aò„}ˆ§î` 3‹ué\s[¢BçÈIÃþHšá9†ª46â»cܦ ^`Mk»÷¡±Ðbë×'`ØÖÛ%f?o ¤çtbd«¹Å&¾T"a" M~ô¡gØk̨0qŸ½±Ót›qÀDX}tÇv…©³‰úËè\`sçìœ ÍªÒðÏ1TŠ‘/:¾DÇ¢-Ã[x'„û¼ rÜ?¹AN£ŒNFÚ²¢1™«ÚrbpW%˜‰úh9ühÝoÓÚOU\/Ø%Ìô¨p6<Ñ8nO¹Ì]3èó:Ô/¯GqÅ+M̉‘LƺTK<å`DJ½sŸ`î•×–ŸÓw!@s¼ˆ:Ò|Ô™*„`lisWkÚÿ÷Îi“ÒÓ°ç¨Ýätt”bX¤u€{bP›äÕyª{ÿQÓÃá“äÜDg\\j àˆ
“tn³XKýmå1^SŸ`ÛÐÚõ;!8•ëªdŦ¯
ÁÉ*F•¬7aìžaß=ñ|mïÛôäS@(OŠ]»$Šu-ç¥T„ŽöaÞã̹ö¦÷ ʇ=Ø¥!K¸¤Ve$Fi£1Ùbá´š«ú(O™ËkÒ~û„=äØÞ×§£ã\R£#V¦ÛkÔˆ°ÿØ=¢iÂÞcÂäñzÖå&S§¨ ˆË ÒUÎi±A^¹Mæ²9nQ¶på{X ¹‹S„½(†¨S¥Nt¤D•6‡ˆ:ÚÄ«*(ûòåKÌl˜vÀx$±¤!*YÙ~Ê‚Ð#åݯY±§ÛûŽ<„ÜsŒ'Z×E OÍK|d&Î݆P5B–EÉ+£M@ÞýGý¶v ¦rjâÄ¡¡pºUuZµè×x±8œÎ<ø Dõk›>±ëýKHIÊWŽÄ™OÃ6yß½Ãr€X¥Ç‘ûtKêt'«ÇbÜÒ‹4YµaHÝtäqü‹ÉD/•ÜQ…¦ˆ6™`‹I°o¤áO“é¸+C”ê7MÎhFîÚíÍ#Æ7pÈ»»<ÛË
Û,MÜDF8Ü$5ùøžÃ§-ñæ)T¦»ë\Z™ì–æõ‹»8¹N&,²ÛøTÛM‡És¦LÖFNÅŽ/•`”ÈÒ™vsWÕåd©óp1lÏá‡aÚD9y‘Ȧ‰’Ž6M6sŠÞ¶öØ\N•³¡}ùäB3¤ÆÆ:¬™M§ÌK\Çк÷ø ¨×‹K)Q'†4e$F›3 !q’qÕf#ªê|†´¶„#&§³Âöc˜vÀ¾–"å aMëR>öE-Á;j݃Ãÿ݇ɯð¬z’.ÁTÉžØÜu*’z¡X)¦ÂFØ»|æ´Ý&ÝŸd¸P!°ª´F½Z‰hâjá)±cxqN ‡Kóq³}{·IûŽ
C->á-qÂÞäýöîõ0w¨,æžr–TáëAËÓéŨIíµz±\º,@è¬TåødÁ.¹«ÁÞ4UYj_, JŒÛlWu†a®âFE´½ãIto6,_»!ttv63Ë]³Z˜2~¬À*®J™ó•t3ùXKSp ü݇¦ïFWjÓ(cb°,‘C›8ÀK΂Ö"Q—(j¥¼ûéEGLYAƒN}4'ñ‚ #HG