././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 011451 x ustar 00 0000000 0000000 27 mtime=1576672145.164062
extension-helpers-0.1/ 0000755 0000770 0000024 00000000000 00000000000 014745 5 ustar 00tom staff 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 011452 x ustar 00 0000000 0000000 28 mtime=1576672145.1426864
extension-helpers-0.1/.circleci/ 0000755 0000770 0000024 00000000000 00000000000 016600 5 ustar 00tom staff 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576529417.0
extension-helpers-0.1/.circleci/config.yml 0000644 0000770 0000024 00000001561 00000000000 020573 0 ustar 00tom staff 0000000 0000000 version: 2
jobs:
html-docs:
docker:
- image: circleci/python:3.6
steps:
- checkout
- run:
name: Install Python dependencies
command: |
python3 -m venv venv
. venv/bin/activate
pip install .[docs]
- run:
name: Build Documentation
command: |
. venv/bin/activate
cd docs
make html
make linkcheck
- store_artifacts:
path: docs/_build/html
- run:
name: "Built documentation is available at:"
command: DOCS_URL="${CIRCLE_BUILD_URL}/artifacts/${CIRCLE_NODE_INDEX}/${CIRCLE_WORKING_DIRECTORY/#\~/$HOME}/docs/_build/html/index.html"; echo $DOCS_URL
workflows:
version: 2
tests_and_docs:
jobs:
- html-docs
notify:
webhooks:
- url: https://giles.cadair.com/circleci
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576529417.0
extension-helpers-0.1/.coveragerc 0000644 0000770 0000024 00000001257 00000000000 017073 0 ustar 00tom staff 0000000 0000000 [run]
source = extension_helpers
omit =
extension_helpers/*/setup_package.py
extension_helpers/tests/*
extension_helpers/conftest.py
[report]
omit =
extension_helpers/*/setup_package.py
extension_helpers/tests/*
extension_helpers/conftest.py
exclude_lines =
# Have to re-enable the standard pragma
pragma: no cover
# Don't complain about packages we have installed
except ImportError
# Don't complain if tests don't hit assertions
raise AssertionError
raise NotImplementedError
# Don't complain about script hooks
def main\(.*\):
# Ignore branches that don't pertain to this version of Python
pragma: py{ignore_python_version}
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576529417.0
extension-helpers-0.1/.gitignore 0000644 0000770 0000024 00000001163 00000000000 016736 0 ustar 00tom staff 0000000 0000000 # Compiled files
*.py[cod]
*.a
*.o
*.so
*.pyd
__pycache__
# Ignore .c files by default to avoid including generated code. If you want to
# add a non-generated .c extension, use `git add -f filename.c`.
*.c
# Other generated files
MANIFEST
extension_helpers/version.py
extension_helpers/cython_version.py
# Sphinx
_build
_generated
api
# Packages/installer info
*.egg
*.egg-info
dist
build
eggs
parts
bin
var
sdist
develop-eggs
.installed.cfg
distribute-*.tar.gz
# Other
.cache
.tox
.*.swp
*~
.project
.pydevproject
.settings
.coverage
.coverage.subprocess
cover
htmlcov
.pytest_cache
# Mac OSX
.DS_Store
# PyCharm
.idea
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576529417.0
extension-helpers-0.1/.readthedocs.yml 0000644 0000770 0000024 00000000217 00000000000 020033 0 ustar 00tom staff 0000000 0000000 build:
image: latest
python:
version: 3.6
pip_install: true
extra_requirements: ['docs']
# Don't build any extra formats
formats: []
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576672082.0
extension-helpers-0.1/CHANGES.rst 0000644 0000770 0000024 00000000164 00000000000 016550 0 ustar 00tom staff 0000000 0000000 0.1 (2019-12-18)
----------------
Initial release of extension-helpers, which was forked from astropy-helpers 4.0.
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576529417.0
extension-helpers-0.1/CONTRIBUTING.md 0000644 0000770 0000024 00000002177 00000000000 017205 0 ustar 00tom staff 0000000 0000000 Contributing to extension-helpers
===============================
The guidelines for contributing to ``extension-helpers`` are generally the same
as the [contributing guidelines for the astropy core
package](http://github.com/astropy/astropy/blob/master/CONTRIBUTING.md).
Basically, report relevant issues in the ``extension-helpers`` issue tracker, and
we welcome pull requests that broadly follow the [Astropy coding
guidelines](http://docs.astropy.org/en/latest/development/codeguide.html).
The key subtlety lies in understanding the relationship between ``astropy`` and
``extension-helpers``. This package contains the build, installation, and
documentation tools used by astropy. It also includes support for the
``setup.py test`` command, though Astropy is still required for this to
function (it does not currently include the full Astropy test runner). So
issues or improvements to that functionality should be addressed in this
package. Any other aspect of the [astropy core
package](http://github.com/astropy/astropy) (or any other package that uses
``extension-helpers``) should be addressed in the github repository for that
package.
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576672090.0
extension-helpers-0.1/LICENSE.rst 0000644 0000770 0000024 00000002723 00000000000 016565 0 ustar 00tom staff 0000000 0000000 Copyright (c) 2019, 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=1576672040.0
extension-helpers-0.1/MANIFEST.in 0000644 0000770 0000024 00000000220 00000000000 016475 0 ustar 00tom staff 0000000 0000000 include README.rst
include CHANGES.rst
include LICENSE.rst
recursive-include licenses *
include ah_bootstrap.py
exclude *.pyc *.o
prune build
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 011452 x ustar 00 0000000 0000000 28 mtime=1576672145.1644526
extension-helpers-0.1/PKG-INFO 0000644 0000770 0000024 00000003425 00000000000 016046 0 ustar 00tom staff 0000000 0000000 Metadata-Version: 2.1
Name: extension-helpers
Version: 0.1
Summary: Utilities for building and installing packages in the Astropy ecosystem
Home-page: https://github.com/astropy/astropy-helpers
Author: The Astropy Developers
Author-email: astropy.team@gmail.com
License: BSD 3-Clause License
Description: extension-helpers
=================
.. image:: https://dev.azure.com/astropy-project/extension-helpers/_apis/build/status/astropy.extension-helpers?branchName=master
:target: https://dev.azure.com/astropy-project/extension-helpers/_build/latest?definitionId=4&branchName=master
.. image:: https://codecov.io/gh/astropy/extension-helpers/branch/master/graph/badge.svg
:target: https://codecov.io/gh/astropy/extension-helpers
The **extension-helpers** package includes convenience helpers to assist with
building Python packages with compiled C/Cython extensions. It is developed by
the Astropy project but is intended to be general and usable by any Python
package.
For more information, see the documentation at http://extension-helpers.readthedocs.io
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Framework :: Setuptools Plugin
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
Provides: extension_helpers
Requires-Python: >=3.6
Provides-Extra: test
Provides-Extra: docs
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576672040.0
extension-helpers-0.1/README.rst 0000644 0000770 0000024 00000001370 00000000000 016435 0 ustar 00tom staff 0000000 0000000 extension-helpers
=================
.. image:: https://dev.azure.com/astropy-project/extension-helpers/_apis/build/status/astropy.extension-helpers?branchName=master
:target: https://dev.azure.com/astropy-project/extension-helpers/_build/latest?definitionId=4&branchName=master
.. image:: https://codecov.io/gh/astropy/extension-helpers/branch/master/graph/badge.svg
:target: https://codecov.io/gh/astropy/extension-helpers
The **extension-helpers** package includes convenience helpers to assist with
building Python packages with compiled C/Cython extensions. It is developed by
the Astropy project but is intended to be general and usable by any Python
package.
For more information, see the documentation at http://extension-helpers.readthedocs.io
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576672040.0
extension-helpers-0.1/azure-pipelines.yml 0000644 0000770 0000024 00000001476 00000000000 020614 0 ustar 00tom staff 0000000 0000000 resources:
repositories:
- repository: OpenAstronomy
type: github
endpoint: astropy
name: OpenAstronomy/azure-pipelines-templates
ref: master
jobs:
- template: run-tox-env.yml@OpenAstronomy
parameters:
posargs: --openmp-expected=True
coverage: codecov
envs:
# Code style
- linux: style
# Docs
- linux: build_docs
# Standard tests
- linux: py36-test
- linux: py37-test
- linux: py38-test
- linux: py38-test-dev
- macos: py36-test
posargs: --openmp-expected=False
- macos: py38-test-dev
posargs: --openmp-expected=False
- windows: py36-test
- windows: py38-test-dev
# Test with more compilers, for the OpenMP helpers
- macos: py38-test-osxgcc-conda
- macos: py38-test-osxclang-conda
- linux: py38-test-linuxgcc-conda
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576672040.0
extension-helpers-0.1/conftest.py 0000644 0000770 0000024 00000000210 00000000000 017135 0 ustar 00tom staff 0000000 0000000 def pytest_addoption(parser):
parser.addoption("--openmp-expected", action="store",
default=None, help="help")
././@PaxHeader 0000000 0000000 0000000 00000000032 00000000000 011450 x ustar 00 0000000 0000000 26 mtime=1576672145.14892
extension-helpers-0.1/docs/ 0000755 0000770 0000024 00000000000 00000000000 015675 5 ustar 00tom staff 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576529417.0
extension-helpers-0.1/docs/Makefile 0000644 0000770 0000024 00000001110 00000000000 017326 0 ustar 00tom staff 0000000 0000000 # Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS = -W
SPHINXBUILD = sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576672040.0
extension-helpers-0.1/docs/api.rst 0000644 0000770 0000024 00000000133 00000000000 017175 0 ustar 00tom staff 0000000 0000000 API Documentation
=================
.. automodapi:: extension_helpers
:no-main-docstr:
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576672040.0
extension-helpers-0.1/docs/conf.py 0000644 0000770 0000024 00000003053 00000000000 017175 0 ustar 00tom staff 0000000 0000000 # -*- coding: utf-8 -*-
import sys
from pkg_resources import get_distribution
project = 'extension-helpers'
copyright = '2019, The Astropy Developers'
author = 'The Astropy Developers'
# We need to get the version number from the package
version = release = get_distribution('extension-helpers').version
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.doctest',
'sphinx.ext.intersphinx',
'sphinx.ext.napoleon',
'sphinx_automodapi.automodapi'
]
intersphinx_mapping = {'https://docs.python.org/3/': None}
# The suffix(es) of source filenames.
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'alabaster'
html_theme_options = {
"description": "A build time package to simplify C/Cython extensions.",
"code_font_family": "'Fira Code', monospace",
"github_user": "astropy",
"github_repo": "extension-helpers",
"sidebar_width": "300px"
}
# Enable nitpicky mode to pick reference issues
default_role = 'obj'
nitpicky = True
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576529417.0
extension-helpers-0.1/docs/index.rst 0000644 0000770 0000024 00000001153 00000000000 017536 0 ustar 00tom staff 0000000 0000000 Extension Helpers
=================
The **extension-helpers** package includes convenience helpers to assist with
building Python packages with compiled C/Cython extensions. It is developed by
the Astropy project but is intended to be general and usable by any Python
package.
This 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 and should be defined as a build-time
dependency in ``pyproject.toml`` files.
.. toctree::
:maxdepth: 1
using.rst
openmp.rst
api.rst
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576529417.0
extension-helpers-0.1/docs/make.bat 0000644 0000770 0000024 00000001423 00000000000 017302 0 ustar 00tom staff 0000000 0000000 @ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
:end
popd
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576672040.0
extension-helpers-0.1/docs/openmp.rst 0000644 0000770 0000024 00000001147 00000000000 017730 0 ustar 00tom staff 0000000 0000000 OpenMP helpers
==============
We provide a helper function
:func:`~extension_helpers.add_openmp_flags_if_available` that can
be used to automatically add OpenMP flags for C/Cython extensions, based on
whether OpenMP is available and produces executable code. To use this, edit the
``setup_package.py`` file where you define a C extension, import the helper
function::
from extension_helpers import add_openmp_flags_if_available
then once you have defined the extension and before returning it, use it as::
extension = Extension(...)
add_openmp_flags_if_available(extension)
return [extension]
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576672040.0
extension-helpers-0.1/docs/using.rst 0000644 0000770 0000024 00000003163 00000000000 017557 0 ustar 00tom staff 0000000 0000000 Using extension-helpers
=======================
To use extension-helpers in your package, you will need to make sure your
package uses a ``pyproject.toml`` file as described in `PEP 518
`_.
You can then add extension-helpers to the build-time dependencies in your
``pyproject.toml`` file::
[build-system]
requires = ["setuptools", "wheel", "extension-helpers"]
If you have Cython extensions, you will need to make sure ``cython`` is included
in the above list too.
The main functionality in extension-helpers is the
:func:`~extension_helpers.get_extensions` function which can be
used to collect package extensions. Defining functions is then done in two ways:
* For simple Cython extensions, :func:`~extension_helpers.get_extensions`
will automatically generate extension modules with no further work.
* For other extensions, you can create ``setup_package.py`` files anywhere
in your package, and these files can then include a ``get_extensions``
function that returns a list of :class:`distutils.core.Extension` objects.
In the second case, the idea is that for large packages, extensions can be defined
in the relevant sub-packages rather than having to all be listed in the main
``setup.py`` file.
To use this, you should modify your ``setup.py`` file to use
:func:`~extension_helpers.get_extensions` as follows::
from extension_helpers import get_extensions
...
setup(..., ext_modules=get_extensions())
Note that if you use this, extension-helpers will also we create a
``packagename.compiler_version`` submodule that contain information about the
compilers used.
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 011452 x ustar 00 0000000 0000000 28 mtime=1576672145.1551697
extension-helpers-0.1/extension_helpers/ 0000755 0000770 0000024 00000000000 00000000000 020503 5 ustar 00tom staff 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576672040.0
extension-helpers-0.1/extension_helpers/__init__.py 0000644 0000770 0000024 00000000377 00000000000 022623 0 ustar 00tom staff 0000000 0000000 from ._distutils_helpers import get_compiler
from ._openmp_helpers import add_openmp_flags_if_available
from ._setup_helpers import get_extensions, pkg_config
from ._utils import import_file, write_if_different
from .version import version as __version__
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576672040.0
extension-helpers-0.1/extension_helpers/_distutils_helpers.py 0000644 0000770 0000024 00000006066 00000000000 024772 0 ustar 00tom staff 0000000 0000000 """
This module contains various utilities for introspecting the distutils
module and the setup process.
Some of these utilities require the
`extension_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
from distutils.dist import Distribution
from distutils.errors import DistutilsError
from ._utils import silence
__all__ = ['get_compiler']
def get_dummy_distribution():
"""
Returns a distutils Distribution object used to instrument the setup
environment before calling the actual setup() function.
"""
# 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:]})
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).replace('.', os.sep)
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_compiler():
"""
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
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576672040.0
extension-helpers-0.1/extension_helpers/_openmp_helpers.py 0000644 0000770 0000024 00000022145 00000000000 024240 0 ustar 00tom 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 extension_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.
import os
import sys
import glob
import time
import datetime
import tempfile
import subprocess
from distutils import log
from distutils.errors import LinkError, CompileError
from distutils.ccompiler import new_compiler
from distutils.sysconfig import get_config_var, customize_compiler
from ._distutils_helpers import get_compiler
__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() == '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')
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=1576672040.0
extension-helpers-0.1/extension_helpers/_setup_helpers.py 0000644 0000770 0000024 00000023147 00000000000 024105 0 ustar 00tom 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.
"""
import os
import sys
import shutil
import subprocess
from distutils import log
from collections import defaultdict
from distutils.core import Extension
from setuptools import find_packages
from setuptools.config import read_configuration
from ._distutils_helpers import get_compiler
from ._utils import import_file, walk_skip_hidden
__all__ = ['get_extensions', 'pkg_config']
def get_extensions(srcdir='.'):
"""
Collect all extensions from Cython files and ``setup_package.py`` files.
If numpy is importable, the numpy include path will be added to all Cython
extensions which are automatically generated.
This function obtains that information by iterating through all
packages in ``srcdir`` and locating a ``setup_package.py`` module.
This module can contain the ``get_extensions()`` function which returns
a list of :class:`distutils.core.Extension` objects.
"""
ext_modules = []
packages = []
package_dir = {}
# Use the find_packages tool to locate all packages and modules
packages = find_packages(srcdir)
# Update package_dir if the package lies in a subdirectory
if srcdir != '.':
package_dir[''] = srcdir
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())
# Locate any .pyx files not already specified, and add their extensions in.
# The default include dirs include numpy to facilitate numerical work.
includes = []
try:
import numpy
includes = [numpy.get_include()]
except ImportError:
pass
ext_modules.extend(get_cython_extensions(srcdir, packages, ext_modules, includes))
# 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() == 'msvc':
for ext in ext_modules:
ext.extra_link_args.append('/MANIFEST')
if len(ext_modules) > 0:
main_package_dir = min(packages, key=len)
src_path = os.path.join(os.path.dirname(__file__), 'src')
shutil.copy(os.path.join(src_path, 'compiler.c'),
os.path.join(srcdir, main_package_dir, '_compiler.c'))
ext = Extension(main_package_dir + '.compiler_version',
[os.path.join(main_package_dir, '_compiler.c')])
ext_modules.append(ext)
return ext_modules
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.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.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
The extensions that are already defined, as a list of of
`~distutils.core.Extension` objects. Any .pyx files already here will
be ignored.
extincludedirs : list or None
Directories to include as the `include_dirs` argument to the generated
`~distutils.core.Extension` objects, as a list of strings.
Returns
-------
exts : list
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
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
The pkg-config packages to look up, as a list of strings.
default_libraries : list
The ibrary names to use if the pkg-config lookup fails, a a list of
strings.
Returns
-------
config : dict
A dictionary containing keyword arguments to
:class:`~distutils.core.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 = defaultdict(list)
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
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576672040.0
extension-helpers-0.1/extension_helpers/_utils.py 0000644 0000770 0000024 00000012646 00000000000 022365 0 ustar 00tom staff 0000000 0000000 # Licensed under a 3-clause BSD style license - see LICENSE.rst
import os
import sys
import glob
import contextlib
from importlib import machinery as import_machinery
__all__ = ['write_if_different', 'import_file']
# 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.
"""
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: # noqa
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.
This can be useful if e.g. generating ``.c`` or ``.h`` files, to make sure
that Python does not re-build unchanged files.
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 without importing the package that
the file is in.
This is useful for cases where a file needs to be imported from
``setup_package.py`` files without importing the parent 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))
loader = import_machinery.SourceFileLoader(name, filename)
mod = loader.load_module()
return mod
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576529417.0
extension-helpers-0.1/extension_helpers/conftest.py 0000644 0000770 0000024 00000003624 00000000000 022707 0 ustar 00tom staff 0000000 0000000 # This file contains settings for pytest that are specific to extension-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 extension-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 extension_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('extension_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('extension_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 00000000034 00000000000 011452 x ustar 00 0000000 0000000 28 mtime=1576672145.1601388
extension-helpers-0.1/extension_helpers/src/ 0000755 0000770 0000024 00000000000 00000000000 021272 5 ustar 00tom staff 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576529417.0
extension-helpers-0.1/extension_helpers/src/compiler.c 0000644 0000770 0000024 00000005240 00000000000 023251 0 ustar 00tom 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 00000000034 00000000000 011452 x ustar 00 0000000 0000000 28 mtime=1576672145.1625779
extension-helpers-0.1/extension_helpers/tests/ 0000755 0000770 0000024 00000000000 00000000000 021645 5 ustar 00tom staff 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576672040.0
extension-helpers-0.1/extension_helpers/tests/__init__.py 0000644 0000770 0000024 00000011153 00000000000 023757 0 ustar 00tom staff 0000000 0000000 import os
import sys
import subprocess as sp
import pytest
try:
from coverage import CoverageData
except ImportError:
HAS_COVERAGE = False
else:
HAS_COVERAGE = True
from ..conftest import SUBPROCESS_COVERAGE
PACKAGE_DIR = os.path.dirname(__file__)
def run_cmd(cmd, args, path=None, raise_error=True):
"""
Runs a shell command with the given argument list. Changes directory to
``path`` if given, otherwise runs the command in the current directory.
Returns a 3-tuple of (stdout, stderr, exit code)
If ``raise_error=True`` raise an exception on non-zero exit codes.
"""
if path is not None:
# Transparently support py.path objects
path = str(path)
p = sp.Popen([cmd] + list(args), stdout=sp.PIPE, stderr=sp.PIPE,
cwd=path)
streams = tuple(s.decode('latin1').strip() for s in p.communicate())
return_code = p.returncode
if raise_error and return_code != 0:
raise RuntimeError(
"The command `{0}` with args {1!r} exited with code {2}.\n"
"Stdout:\n\n{3}\n\nStderr:\n\n{4}".format(
cmd, list(args), return_code, streams[0], streams[1]))
return streams + (return_code,)
def run_setup(setup_script, args):
# This used to call setuptools.sandbox's run_setup, but due to issues with
# this and Cython (which caused segmentation faults), we now use subprocess.
setup_script = os.path.abspath(setup_script)
path = os.path.dirname(setup_script)
setup_script = os.path.basename(setup_script)
if HAS_COVERAGE:
# In this case, we run the command using the coverage command and we
# then collect the coverage data into a SUBPROCESS_COVERAGE list which
# is set up at the start of the testing process and is then combined
# into a single .coverage file at the end of the testing process.
p = sp.Popen(['coverage', 'run', setup_script] + list(args), cwd=path,
stdout=sp.PIPE, stderr=sp.PIPE)
stdout, stderr = p.communicate()
cdata = CoverageData()
cdata.read_file(os.path.join(path, '.coverage'))
SUBPROCESS_COVERAGE.append(cdata)
else:
# Otherwise we just run the tests with Python
p = sp.Popen([sys.executable, setup_script] + list(args), cwd=path,
stdout=sp.PIPE, stderr=sp.PIPE)
stdout, stderr = p.communicate()
sys.stdout.write(stdout.decode('utf-8'))
sys.stderr.write(stderr.decode('utf-8'))
if p.returncode != 0:
raise SystemExit(p.returncode)
@pytest.fixture(scope='function', autouse=True)
def reset_distutils_log():
"""
This is a setup/teardown fixture that ensures the log-level of the
distutils log is always set to a default of WARN, since different
settings could affect tests that check the contents of stdout.
"""
from distutils import log
log.set_threshold(log.WARN)
TEST_PACKAGE_SETUP_PY = """\
#!/usr/bin/env python
from setuptools import setup
NAME = 'extension-helpers-test'
VERSION = {version!r}
setup(name=NAME, version=VERSION,
packages=['_extension_helpers_test_'],
zip_safe=False)
"""
def create_testpackage(tmpdir, version='0.1'):
source = tmpdir.mkdir('testpkg')
with source.as_cwd():
source.mkdir('_extension_helpers_test_')
init = source.join('_extension_helpers_test_', '__init__.py')
init.write('__version__ = {0!r}'.format(version))
setup_py = TEST_PACKAGE_SETUP_PY.format(version=version)
source.join('setup.py').write(setup_py)
# Make the new test package into a git repo
run_cmd('git', ['init'])
run_cmd('git', ['add', '--all'])
run_cmd('git', ['commit', '-m', 'test package'])
return source
@pytest.fixture
def testpackage(tmpdir, version='0.1'):
"""
This fixture creates a simplified package called _extension_helpers_test_
used primarily for testing ah_boostrap, but without using the
extension_helpers package directly and getting it confused with the
extension_helpers package already under test.
"""
return create_testpackage(tmpdir, version=version)
def cleanup_import(package_name):
"""Remove all references to package_name from sys.modules"""
for k in list(sys.modules):
if not isinstance(k, str):
# Some things will actually do this =_=
continue
elif k.startswith('extension_helpers.tests'):
# Don't delete imported test modules or else the tests will break,
# badly
continue
if k == package_name or k.startswith(package_name + '.'):
del sys.modules[k]
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576672040.0
extension-helpers-0.1/extension_helpers/tests/test_openmp_helpers.py 0000644 0000770 0000024 00000003025 00000000000 026276 0 ustar 00tom staff 0000000 0000000 import os
import sys
import types
from copy import deepcopy
from importlib import machinery
from distutils.core import Extension
import pytest
from .._openmp_helpers import add_openmp_flags_if_available, generate_openmp_enabled_py
@pytest.fixture
def openmp_expected(request):
openmp_expected = request.config.getoption("--openmp-expected")
if openmp_expected is not None:
return openmp_expected.lower() == 'true'
def test_add_openmp_flags_if_available(openmp_expected):
using_openmp = add_openmp_flags_if_available(Extension('test', []))
# Make sure that on Travis (Linux) and AppVeyor OpenMP does get used (for
# MacOS X usually it will not work but this will depend on the compiler).
# Having this is useful because we'll find out if OpenMP no longer works
# for any reason on platforms on which it does work at the time of writing.
if openmp_expected is not None:
assert openmp_expected is using_openmp
def test_generate_openmp_enabled_py(openmp_expected):
# Test file generation
generate_openmp_enabled_py('')
assert os.path.isfile('openmp_enabled.py')
# Load openmp_enabled file as a module to check the result
loader = machinery.SourceFileLoader('openmp_enabled', 'openmp_enabled.py')
mod = types.ModuleType(loader.name)
loader.exec_module(mod)
is_openmp_enabled = mod.is_openmp_enabled()
# Test is_openmp_enabled()
assert isinstance(is_openmp_enabled, bool)
if openmp_expected is not None:
assert openmp_expected is is_openmp_enabled
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576672040.0
extension-helpers-0.1/extension_helpers/tests/test_setup_helpers.py 0000644 0000770 0000024 00000012575 00000000000 026152 0 ustar 00tom staff 0000000 0000000 import os
import sys
import importlib
from textwrap import dedent
import pytest
from .._setup_helpers import get_extensions
from . import reset_distutils_log # noqa
from . import cleanup_import, run_setup
extension_helpers_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')) # noqa
def teardown_module(module):
# Remove file generated by test_generate_openmp_enabled_py but
# somehow needed in test_cython_autoextensions
tmpfile = 'openmp_enabled.py'
if os.path.exists(tmpfile):
os.remove(tmpfile)
def _extension_test_package(tmpdir, request, extension_type='c',
include_numpy=False):
"""Creates a simple test package with an extension module."""
test_pkg = tmpdir.mkdir('test_pkg')
test_pkg.mkdir('apyhtest_eva').ensure('__init__.py')
# TODO: It might be later worth making this particular test package into a
# reusable fixture for other build_ext tests
if extension_type in ('c', 'both'):
# A minimal C extension for testing
test_pkg.join('apyhtest_eva', 'unit01.c').write(dedent("""\
#include
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"unit01",
NULL,
-1,
NULL
};
PyMODINIT_FUNC
PyInit_unit01(void) {
return PyModule_Create(&moduledef);
}
"""))
if extension_type in ('pyx', 'both'):
# A minimal Cython extension for testing
test_pkg.join('apyhtest_eva', 'unit02.pyx').write(dedent("""\
print("Hello cruel angel.")
"""))
if extension_type == 'c':
extensions = ['unit01.c']
elif extension_type == 'pyx':
extensions = ['unit02.pyx']
elif extension_type == 'both':
extensions = ['unit01.c', 'unit02.pyx']
include_dirs = ['numpy'] if include_numpy else []
extensions_list = [
"Extension('apyhtest_eva.{0}', [join('apyhtest_eva', '{1}')], include_dirs={2})".format(
os.path.splitext(extension)[0], extension, include_dirs)
for extension in extensions]
test_pkg.join('apyhtest_eva', 'setup_package.py').write(dedent("""\
from setuptools import Extension
from os.path import join
def get_extensions():
return [{0}]
""".format(', '.join(extensions_list))))
test_pkg.join('setup.py').write(dedent("""\
import sys
from os.path import join
from setuptools import setup, find_packages
sys.path.insert(0, r'{extension_helpers_path}')
from extension_helpers import get_extensions
setup(
name='apyhtest_eva',
version='0.1',
packages=find_packages(),
ext_modules=get_extensions()
)
""".format(extension_helpers_path=extension_helpers_PATH)))
if '' in sys.path:
sys.path.remove('')
sys.path.insert(0, '')
def finalize():
cleanup_import('apyhtest_eva')
request.addfinalizer(finalize)
return test_pkg
@pytest.fixture
def extension_test_package(tmpdir, request):
return _extension_test_package(tmpdir, request, extension_type='both')
@pytest.fixture
def c_extension_test_package(tmpdir, request):
# Check whether numpy is installed in the test environment
has_numpy = bool(importlib.util.find_spec('numpy'))
return _extension_test_package(tmpdir, request, extension_type='c',
include_numpy=has_numpy)
@pytest.fixture
def pyx_extension_test_package(tmpdir, request):
return _extension_test_package(tmpdir, request, extension_type='pyx')
def test_cython_autoextensions(tmpdir):
"""
Regression test for https://github.com/astropy/extension-helpers/pull/19
Ensures that Cython extensions in sub-packages are discovered and built
only once.
"""
# Make a simple test package
test_pkg = tmpdir.mkdir('test_pkg')
test_pkg.mkdir('yoda').mkdir('luke')
test_pkg.ensure('yoda', '__init__.py')
test_pkg.ensure('yoda', 'luke', '__init__.py')
test_pkg.join('yoda', 'luke', 'dagobah.pyx').write(
"""def testfunc(): pass""")
# Required, currently, for get_extensions to work
ext_modules = get_extensions(str(test_pkg))
assert len(ext_modules) == 2
assert ext_modules[0].name == 'yoda.luke.dagobah'
def test_compiler_module(capsys, c_extension_test_package):
"""
Test ensuring that the compiler module is built and installed for packages
that have extension modules.
"""
test_pkg = c_extension_test_package
install_temp = test_pkg.mkdir('install_temp')
with test_pkg.as_cwd():
# This is one of the simplest ways to install just a package into a
# test directory
run_setup('setup.py',
['install',
'--single-version-externally-managed',
'--install-lib={0}'.format(install_temp),
'--record={0}'.format(install_temp.join('record.txt'))])
with install_temp.as_cwd():
import apyhtest_eva
# Make sure we imported the apyhtest_eva package from the correct place
dirname = os.path.abspath(os.path.dirname(apyhtest_eva.__file__))
assert dirname == str(install_temp.join('apyhtest_eva'))
import apyhtest_eva.compiler_version
assert apyhtest_eva.compiler_version != 'unknown'
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576672144.0
extension-helpers-0.1/extension_helpers/version.py 0000644 0000770 0000024 00000000162 00000000000 022541 0 ustar 00tom staff 0000000 0000000 # coding: utf-8
# file generated by setuptools_scm
# don't change, don't track in version control
version = '0.1'
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 011451 x ustar 00 0000000 0000000 27 mtime=1576672145.159395
extension-helpers-0.1/extension_helpers.egg-info/ 0000755 0000770 0000024 00000000000 00000000000 022175 5 ustar 00tom staff 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576672144.0
extension-helpers-0.1/extension_helpers.egg-info/PKG-INFO 0000644 0000770 0000024 00000003425 00000000000 023276 0 ustar 00tom staff 0000000 0000000 Metadata-Version: 2.1
Name: extension-helpers
Version: 0.1
Summary: Utilities for building and installing packages in the Astropy ecosystem
Home-page: https://github.com/astropy/astropy-helpers
Author: The Astropy Developers
Author-email: astropy.team@gmail.com
License: BSD 3-Clause License
Description: extension-helpers
=================
.. image:: https://dev.azure.com/astropy-project/extension-helpers/_apis/build/status/astropy.extension-helpers?branchName=master
:target: https://dev.azure.com/astropy-project/extension-helpers/_build/latest?definitionId=4&branchName=master
.. image:: https://codecov.io/gh/astropy/extension-helpers/branch/master/graph/badge.svg
:target: https://codecov.io/gh/astropy/extension-helpers
The **extension-helpers** package includes convenience helpers to assist with
building Python packages with compiled C/Cython extensions. It is developed by
the Astropy project but is intended to be general and usable by any Python
package.
For more information, see the documentation at http://extension-helpers.readthedocs.io
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Framework :: Setuptools Plugin
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
Provides: extension_helpers
Requires-Python: >=3.6
Provides-Extra: test
Provides-Extra: docs
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576672145.0
extension-helpers-0.1/extension_helpers.egg-info/SOURCES.txt 0000644 0000770 0000024 00000001707 00000000000 024066 0 ustar 00tom staff 0000000 0000000 .coveragerc
.gitignore
.readthedocs.yml
CHANGES.rst
CONTRIBUTING.md
LICENSE.rst
MANIFEST.in
README.rst
azure-pipelines.yml
conftest.py
pyproject.toml
setup.cfg
setup.py
tox.ini
.circleci/config.yml
docs/Makefile
docs/api.rst
docs/conf.py
docs/index.rst
docs/make.bat
docs/openmp.rst
docs/using.rst
extension_helpers/__init__.py
extension_helpers/_distutils_helpers.py
extension_helpers/_openmp_helpers.py
extension_helpers/_setup_helpers.py
extension_helpers/_utils.py
extension_helpers/conftest.py
extension_helpers/version.py
extension_helpers.egg-info/PKG-INFO
extension_helpers.egg-info/SOURCES.txt
extension_helpers.egg-info/dependency_links.txt
extension_helpers.egg-info/not-zip-safe
extension_helpers.egg-info/requires.txt
extension_helpers.egg-info/top_level.txt
extension_helpers/src/compiler.c
extension_helpers/tests/__init__.py
extension_helpers/tests/test_openmp_helpers.py
extension_helpers/tests/test_setup_helpers.py
licenses/LICENSE_ASTROSCRAPPY.rst ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576672144.0
extension-helpers-0.1/extension_helpers.egg-info/dependency_links.txt 0000644 0000770 0000024 00000000001 00000000000 026243 0 ustar 00tom staff 0000000 0000000
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576672144.0
extension-helpers-0.1/extension_helpers.egg-info/not-zip-safe 0000644 0000770 0000024 00000000001 00000000000 024423 0 ustar 00tom staff 0000000 0000000
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576672144.0
extension-helpers-0.1/extension_helpers.egg-info/requires.txt 0000644 0000770 0000024 00000000104 00000000000 024570 0 ustar 00tom staff 0000000 0000000
[docs]
sphinx-astropy
[test]
pytest-astropy
pytest-cov
coverage<5
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576672144.0
extension-helpers-0.1/extension_helpers.egg-info/top_level.txt 0000644 0000770 0000024 00000000022 00000000000 024721 0 ustar 00tom staff 0000000 0000000 extension_helpers
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 011452 x ustar 00 0000000 0000000 28 mtime=1576672145.1634433
extension-helpers-0.1/licenses/ 0000755 0000770 0000024 00000000000 00000000000 016552 5 ustar 00tom staff 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576529417.0
extension-helpers-0.1/licenses/LICENSE_ASTROSCRAPPY.rst 0000644 0000770 0000024 00000003154 00000000000 022403 0 ustar 00tom 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=1576672040.0
extension-helpers-0.1/pyproject.toml 0000644 0000770 0000024 00000000204 00000000000 017655 0 ustar 00tom staff 0000000 0000000 [build-system]
requires = ["setuptools",
"setuptools_scm",
"wheel"]
build-backend = 'setuptools.build_meta'
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 011452 x ustar 00 0000000 0000000 28 mtime=1576672145.1662316
extension-helpers-0.1/setup.cfg 0000644 0000770 0000024 00000002636 00000000000 016575 0 ustar 00tom staff 0000000 0000000 [metadata]
name = extension-helpers
provides = extension_helpers
author = The Astropy Developers
author_email = astropy.team@gmail.com
license = BSD 3-Clause License
license_file = LICENSE.rst
url = https://github.com/astropy/astropy-helpers
description = Utilities for building and installing packages in the Astropy ecosystem
long_description = file: README.rst
classifiers =
Development Status :: 5 - Production/Stable
Intended Audience :: Developers
Framework :: Setuptools Plugin
License :: OSI Approved :: BSD License
Operating System :: OS Independent
Programming Language :: Python
Programming Language :: Python :: 3
Topic :: Software Development :: Build Tools
Topic :: Software Development :: Libraries :: Python Modules
Topic :: System :: Archiving :: Packaging
[options]
zip_safe = False
python_requires = >=3.6
packages = find:
[options.package_data]
extension_helpers = src/compiler.c
[options.extras_require]
test =
pytest-astropy
pytest-cov
coverage<5
docs =
sphinx-astropy
[tool:pytest]
norecursedirs =
.tox
extension_helpers/tests/package_template
python_functions = test_
[isort]
line_length = 100
sections = FUTURE,STDLIB,THIRDPARTY,NUMPY,FIRSTPARTY,LOCALFOLDER
default_section = THIRDPARTY
known_first_party = extension_helpers
known_numpy = numpy
multi_line_output = 0
balanced_wrapping = True
include_trailing_comma = false
length_sort_stdlib = true
[egg_info]
tag_build =
tag_date = 0
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576672040.0
extension-helpers-0.1/setup.py 0000755 0000770 0000024 00000001027 00000000000 016462 0 ustar 00tom staff 0000000 0000000 #!/usr/bin/env python
# Licensed under a 3-clause BSD style license - see LICENSE.rst
# NOTE: most of the configuration, including the version number,
# is defined in setup.cfg
import os
import sys
from distutils.version import LooseVersion
import setuptools
from setuptools import setup
if LooseVersion(setuptools.__version__) < '30.3':
sys.stderr.write("ERROR: setuptools 30.3 or later is required by extension-helpers\n")
sys.exit(1)
setup(use_scm_version={'write_to': os.path.join('extension_helpers', 'version.py')})
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1576672040.0
extension-helpers-0.1/tox.ini 0000644 0000770 0000024 00000002677 00000000000 016274 0 ustar 00tom staff 0000000 0000000 [tox]
envlist =
py{36,37,38}-test{,-conda}
build_docs
pycodestyle
requires =
setuptools >= 30.3.0
pip >= 19.3.1
isolated_build = true
[testenv]
setenv =
osxgcc: CC=gcc
osxclang: CC=clang-4.0
linuxgcc: CC=x86_64-conda_cos6-linux-gnu-gcc
changedir =
test: .tmp/{envname}
build_docs: docs
whitelist_externals =
dev: bash
description =
test: run tests with pytest
build_docs: invoke sphinx-build to build the HTML docs
all: run tests with all optional dependencies
dev: run tests with numpy and astropy dev versions
conda_deps =
osxgcc: gcc
osxclang: clang_osx-64
osxclang: llvm-openmp
linuxgcc: gcc_linux-64
conda_channels =
osxgcc: conda-forge
extras =
test: test
build_docs: docs
all: all
commands =
dev: bash -ec "rm -rf setuptools_repo; git clone https://github.com/pypa/setuptools.git setuptools_repo && cd setuptools_repo && python bootstrap.py"
dev: pip install setuptools_repo/ --no-build-isolation
pip freeze
test: python -c 'import setuptools; print(setuptools.__version__)'
test: pytest --pyargs extension_helpers {toxinidir}/docs --cov extension_helpers {posargs}
build_docs: sphinx-build -W -b html . _build/html
[testenv:style]
skip_install = true
description = invoke pycodestyle and isort on package code
deps =
pycodestyle
isort
commands =
pycodestyle extension_helpers --max-line-length=100
isort -c -rc extension_helpers