Skip to content

Commit

Permalink
Merge pull request #33 from Cadair/no_more_setup_py
Browse files Browse the repository at this point in the history
  • Loading branch information
astrofrog committed Feb 23, 2022
2 parents 46f6c46 + b3273e0 commit 55b7859
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 16 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,6 @@ htmlcov

# PyCharm
.idea

# Hypothesis
.hypothesis
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

* Remove support for the undocumented --compiler argument to setup.py. [#36]

* Added support for enabling extension-helpers from setup.cfg. [#33]

0.1 (2019-12-18)
----------------

Expand Down
6 changes: 6 additions & 0 deletions docs/using.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,9 @@ To use this, you should modify your ``setup.py`` file to use
Note that if you use this, extension-helpers will also we create a
``packagename.compiler_version`` submodule that contain information about the
compilers used.

It is also possible to enable extension-helpers in ``setup.cfg`` instead of
``setup.py`` by adding the following configuration to the ``setup.cfg`` file::

[extension-helpers]
use_extension_helpers = true
17 changes: 17 additions & 0 deletions extension_helpers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
from configparser import ConfigParser

from ._openmp_helpers import add_openmp_flags_if_available
from ._setup_helpers import get_compiler, get_extensions, pkg_config
from ._utils import import_file, write_if_different
from .version import version as __version__


def _finalize_distribution_hook(distribution):
"""
Entry point for setuptools which allows extension-helpers to be enabled
from setup.cfg without the need for setup.py.
"""
config_files = distribution.find_config_files()
if len(config_files) == 0:
return
cfg = ConfigParser()
cfg.read(config_files[0])
if (cfg.has_option("extension-helpers", "use_extension_helpers") and
cfg.get("extension-helpers", "use_extension_helpers").lower() == 'true'):
distribution.ext_modules = get_extensions()
126 changes: 113 additions & 13 deletions extension_helpers/tests/test_setup_helpers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import os
import sys
import uuid
import importlib
import subprocess
from textwrap import dedent

import pytest
Expand Down Expand Up @@ -31,14 +33,14 @@ def _extension_test_package(tmpdir, request, extension_type='c',
"""Creates a simple test package with an extension module."""

test_pkg = tmpdir.mkdir('test_pkg')
test_pkg.mkdir('apyhtest_eva').ensure('__init__.py')
test_pkg.mkdir('helpers_test_package').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("""\
test_pkg.join('helpers_test_package', 'unit01.c').write(dedent("""\
#include <Python.h>
static struct PyModuleDef moduledef = {
Expand All @@ -56,7 +58,7 @@ def _extension_test_package(tmpdir, request, extension_type='c',

if extension_type in ('pyx', 'both'):
# A minimal Cython extension for testing
test_pkg.join('apyhtest_eva', 'unit02.pyx').write(dedent("""\
test_pkg.join('helpers_test_package', 'unit02.pyx').write(dedent("""\
print("Hello cruel angel.")
"""))

Expand All @@ -70,11 +72,13 @@ def _extension_test_package(tmpdir, request, extension_type='c',
include_dirs = ['numpy'] if include_numpy else []

extensions_list = [
"Extension('apyhtest_eva.{0}', [join('apyhtest_eva', '{1}')], include_dirs={2})".format(
"Extension('helpers_test_package.{0}', "
"[join('helpers_test_package', '{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("""\
test_pkg.join('helpers_test_package', 'setup_package.py').write(dedent("""\
from setuptools import Extension
from os.path import join
def get_extensions():
Expand All @@ -89,7 +93,7 @@ def get_extensions():
from extension_helpers import get_extensions
setup(
name='apyhtest_eva',
name='helpers_test_package',
version='0.1',
packages=find_packages(),
ext_modules=get_extensions()
Expand All @@ -102,7 +106,7 @@ def get_extensions():
sys.path.insert(0, '')

def finalize():
cleanup_import('apyhtest_eva')
cleanup_import('helpers_test_package')

request.addfinalizer(finalize)

Expand Down Expand Up @@ -169,11 +173,107 @@ def test_compiler_module(capsys, c_extension_test_package):
'--record={0}'.format(install_temp.join('record.txt'))])

with install_temp.as_cwd():
import apyhtest_eva
import helpers_test_package

# 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'))
# Make sure we imported the helpers_test_package package from the correct place
dirname = os.path.abspath(os.path.dirname(helpers_test_package.__file__))
assert dirname == str(install_temp.join('helpers_test_package'))

import apyhtest_eva.compiler_version
assert apyhtest_eva.compiler_version != 'unknown'
import helpers_test_package.compiler_version
assert helpers_test_package.compiler_version != 'unknown'


@pytest.mark.parametrize('use_extension_helpers', [None, False, True])
def test_no_setup_py(tmpdir, use_extension_helpers):
"""
Test that makes sure that extension-helpers can be enabled without a
setup.py file.
"""

package_name = 'helpers_test_package_' + str(uuid.uuid4()).replace('-', '_')

test_pkg = tmpdir.mkdir('test_pkg')
test_pkg.mkdir(package_name).ensure('__init__.py')

simple_c = test_pkg.join(package_name, 'simple.c')

simple_c.write(dedent("""\
#include <Python.h>
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"simple",
NULL,
-1,
NULL
};
PyMODINIT_FUNC
PyInit_simple(void) {
return PyModule_Create(&moduledef);
}
"""))

test_pkg.join(package_name, 'setup_package.py').write(dedent(f"""\
from setuptools import Extension
from os.path import join
def get_extensions():
return [Extension('{package_name}.simple', [join('{package_name}', 'simple.c')])]
"""))

if use_extension_helpers is None:
test_pkg.join('setup.cfg').write(dedent(f"""\
[metadata]
name = {package_name}
version = 0.1
[options]
packages = find:
"""))
else:
test_pkg.join('setup.cfg').write(dedent(f"""\
[metadata]
name = {package_name}
version = 0.1
[options]
packages = find:
[extension-helpers]
use_extension_helpers = {str(use_extension_helpers).lower()}
"""))

test_pkg.join('pyproject.toml').write(dedent("""\
[build-system]
requires = ["setuptools>=43.0.0",
"wheel"]
build-backend = 'setuptools.build_meta'
"""))

install_temp = test_pkg.mkdir('install_temp')

with test_pkg.as_cwd():
# NOTE: we disable build isolation as we need to pick up the current
# developer version of extension-helpers
subprocess.call([sys.executable, '-m', 'pip', 'install', '.',
'--no-build-isolation',
f'--target={install_temp}'])

if '' in sys.path:
sys.path.remove('')

sys.path.insert(0, '')

with install_temp.as_cwd():

importlib.import_module(package_name)

if use_extension_helpers:
compiler_version_mod = importlib.import_module(package_name + '.compiler_version')
assert compiler_version_mod.compiler != 'unknown'
else:
try:
importlib.import_module(package_name + '.compiler_version')
except ImportError:
pass
else:
raise AssertionError(package_name + '.compiler_version should not exist')
4 changes: 4 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ install_requires =
[options.package_data]
extension_helpers = src/compiler.c

[options.entry_points]
setuptools.finalize_distribution_options =
extension_helpers_get_extensions = extension_helpers:_finalize_distribution_hook

[options.extras_require]
test =
pytest
Expand Down
7 changes: 4 additions & 3 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ passenv =
CONDA_BUILD_SYSROOT
setenv =
osxclang: CC=clang-10
linuxgcc: CC=x86_64-conda_cos6-linux-gnu-gcc
linuxgcc: CC=gcc_linux-64
changedir =
test: .tmp/{envname}
build_docs: docs
Expand All @@ -31,13 +31,14 @@ conda_deps =
osxclang: clang_osx-64==10
osxclang: llvm-openmp
linuxgcc: gcc_linux-64
conda_channels =
linuxgcc: 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
dev: pip install git+https://github.com/pypa/setuptools.git
pip freeze
test: python -c 'import setuptools; print(setuptools.__version__)'
test: pytest --pyargs extension_helpers {toxinidir}/docs --cov extension_helpers --cov-config={toxinidir}/setup.cfg {posargs}
Expand Down

0 comments on commit 55b7859

Please sign in to comment.