Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experiments in removing setup.py #33

Merged
merged 15 commits into from
Feb 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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