From c80fab93073603289f41df8e32b7ecc25f37763e Mon Sep 17 00:00:00 2001 From: Jason Madden Date: Wed, 11 Nov 2020 15:40:36 -0600 Subject: [PATCH] Remove custom my_build_ext and stop trying to build extensions at test time. Building the extensions and getting them in the right place is the job of tox. This includes a temporary minor regression in that the test extensions are now also packaged in the binary distributions. This will be resolved with #189 and #184. Removes some (maybe all, didnt check yet) uses of distutils so partly addresses #185. Fixes #187 Specify an image for appveyor that works for Python 2.7 (the right visual studio is missing on other images) Add installation of missing python versions. Use PYTHON, not PYTHON_ROOT on appveyor. The former is more common in my experience. Use a newer image for 3.9 on appveyor --- CHANGES.rst | 2 + appveyor.yml | 118 ++++++++++++++++++---- appveyor/install.ps1 | 229 +++++++++++++++++++++++++++++++++++++++++++ appveyor/run_pip.py | 6 -- my_build_ext.py | 60 ------------ run-tests.py | 50 +++------- setup.py | 39 +++++--- tests/__init__.py | 48 +-------- tox.ini | 11 +-- 9 files changed, 377 insertions(+), 186 deletions(-) create mode 100644 appveyor/install.ps1 delete mode 100644 appveyor/run_pip.py delete mode 100644 my_build_ext.py diff --git a/CHANGES.rst b/CHANGES.rst index 2ee08289..835d9f42 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -14,6 +14,8 @@ - (Packaging) Require setuptools to build from source. - (Packaging) Stop asking setuptools to build both .tar.gz and .zip sdists. PyPI has standardized on .tar.gz for all platforms. +- (Packaging) Stop using a custom distutils command to build + extensions. distutils is deprecated. - (Documentation) Publish the change log to https://greenlet.readthedocs.io - Drop support for Python 2.4, 2.5, 2.6, 3.0, 3.1, 3.2 and 3.4. The project metadata now includes the ``python_requires`` data to diff --git a/appveyor.yml b/appveyor.yml index 10975c9e..5aae0e3c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,67 +1,145 @@ +clone_depth: 50 +max_jobs: 8 +shallow_clone: true +build: + parallel: true + verbosity: minimal +# The VS 2019 image doesn't have +# the MSVC needed for Python 2.7. +# Note that as of 2020-11-11, this does not include +# a Python 3.9 build. +image: Visual Studio 2015 + environment: global: + APPVEYOR_SAVE_CACHE_ON_ERROR: "true" # SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the - # /E:ON and /V:ON options are not enabled in the batch script intepreter + # /E:ON and /V:ON options are not enabled in the batch script interpreter # See: http://stackoverflow.com/a/13751649/163740 CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\appveyor\\run_with_env.cmd" + # Use a fixed hash seed for reproducability + PYTHONHASHSEED: 8675309 + # Don't get warnings about Python 2 support being deprecated. We + # know. + PIP_NO_PYTHON_VERSION_WARNING: 1 + PIP_UPGRADE_STRATEGY: eager + # Enable this if debugging a resource leak. Otherwise + # it slows things down. + # PYTHONTRACEMALLOC: 10 matrix: # http://www.appveyor.com/docs/installed-software#python - - PYTHON_ROOT: "C:\\Python27" + + - PYTHON: "C:\\Python39-x64" + PYTHON_ARCH: "64" + PYTHON_VERSION: "3.9.x" + PYTHON_EXE: python + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + + - PYTHON: "C:\\Python27" PYTHON_ARCH: "32" PYTHON_VERSION: "2.7.x" + PYTHON_EXE: python - - PYTHON_ROOT: "C:\\Python27-x64" + - PYTHON: "C:\\Python27-x64" PYTHON_ARCH: "64" PYTHON_VERSION: "2.7.x" + PYTHON_EXE: python - - PYTHON_ROOT: "C:\\Python35" + - PYTHON: "C:\\Python35" PYTHON_ARCH: "32" PYTHON_VERSION: "3.5.x" + PYTHON_EXE: python - - PYTHON_ROOT: "C:\\Python35-x64" + - PYTHON: "C:\\Python35-x64" PYTHON_ARCH: "64" PYTHON_VERSION: "3.5.x" + PYTHON_EXE: python - - PYTHON_ROOT: "C:\\Python36" + - PYTHON: "C:\\Python36" PYTHON_ARCH: "32" PYTHON_VERSION: "3.6.x" + PYTHON_EXE: python - - PYTHON_ROOT: "C:\\Python36-x64" + - PYTHON: "C:\\Python36-x64" PYTHON_ARCH: "64" PYTHON_VERSION: "3.6.x" + PYTHON_EXE: python - - PYTHON_ROOT: "C:\\Python37" + - PYTHON: "C:\\Python37" PYTHON_ARCH: "32" PYTHON_VERSION: "3.7.x" + PYTHON_EXE: python - - PYTHON_ROOT: "C:\\Python37-x64" + - PYTHON: "C:\\Python37-x64" PYTHON_ARCH: "64" PYTHON_VERSION: "3.7.x" + PYTHON_EXE: python - - PYTHON_ROOT: "C:\\Python38" + - PYTHON: "C:\\Python38" PYTHON_ARCH: "32" PYTHON_VERSION: "3.8.x" + PYTHON_EXE: python - - PYTHON_ROOT: "C:\\Python38-x64" + - PYTHON: "C:\\Python38-x64" PYTHON_ARCH: "64" PYTHON_VERSION: "3.8.x" + PYTHON_EXE: python - - PYTHON_ROOT: "C:\\Python39" - PYTHON_ARCH: "32" - PYTHON_VERSION: "3.9.x" - - PYTHON_ROOT: "C:\\Python39-x64" - PYTHON_ARCH: "64" - PYTHON_VERSION: "3.9.x" +cache: + - "%TMP%\\py\\" + - '%LOCALAPPDATA%\pip\Cache -> appveyor.yml,setup.py' install: - - "SET PATH=%PYTHON_ROOT%;%PYTHON_ROOT%\\Scripts;%PATH%" + # If there is a newer build queued for the same PR, cancel this one. + # The AppVeyor 'rollout builds' option is supposed to serve the same + # purpose but it is problematic because it tends to cancel builds pushed + # directly to master instead of just PR builds (or the converse). + # credits: JuliaLang developers. + - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod ` + https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | ` + Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { ` + throw "There are newer queued builds for this pull request, failing early." } + - ECHO "Filesystem root:" + - ps: "ls \"C:/\"" + + - ECHO "Installed SDKs:" + - ps: "if(Test-Path(\"C:/Program Files/Microsoft SDKs/Windows\")) {ls \"C:/Program Files/Microsoft SDKs/Windows\";}" + + # Install Python (from the official .msi of http://python.org) and pip when + # not already installed. + # PyPy portion based on https://github.com/wbond/asn1crypto/blob/master/appveyor.yml + - ps: + $env:PYTMP = "${env:TMP}\py"; + if (!(Test-Path "$env:PYTMP")) { + New-Item -ItemType directory -Path "$env:PYTMP" | Out-Null; + } + if ("${env:PYTHON_ID}" -eq "pypy") { + if (!(Test-Path "${env:PYTMP}\pypy2-v7.3.1-win32.zip")) { + (New-Object Net.WebClient).DownloadFile('https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.3.1-win32.zip', "${env:PYTMP}\pypy2-v7.3.1-win32.zip"); + } + 7z x -y "${env:PYTMP}\pypy2-v7.3.1-win32.zip" -oC:\ | Out-Null; + } + elseif (-not(Test-Path($env:PYTHON))) { + & appveyor\install.ps1; + } + + # Prepend newly installed Python to the PATH of this build (this cannot be + # done from inside the powershell script as it would require to restart + # the parent CMD process). + - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PYTHON%\\bin;%PATH%" + - "SET PYEXE=%PYTHON%\\%PYTHON_EXE%.exe" + - "python --version" - "python -c \"import struct; print(struct.calcsize('P') * 8)\"" - # Upgrade pip to the latest version - #- "python appveyor\\run_pip.py install --disable-pip-version-check --upgrade pip" + # Upgrade to the latest version of pip to avoid it displaying warnings + # about it being out of date. Do this here instead of above in + # powershell because the annoying 'DEPRECATION:blahblahblah 2.7 blahblahblah' + # breaks powershell. + - "%CMD_IN_ENV% %PYEXE% -mensurepip -U --user" + - "%CMD_IN_ENV% %PYEXE% -mpip install -U --user pip" # Install requirements for running tests and building artifacts - "%CMD_IN_ENV% pip install --upgrade -r dev-requirements.txt" diff --git a/appveyor/install.ps1 b/appveyor/install.ps1 new file mode 100644 index 00000000..160ba55c --- /dev/null +++ b/appveyor/install.ps1 @@ -0,0 +1,229 @@ +# Sample script to install Python and pip under Windows +# Authors: Olivier Grisel, Jonathan Helmus, Kyle Kastner, and Alex Willmer +# License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/ + +$MINICONDA_URL = "http://repo.continuum.io/miniconda/" +$BASE_URL = "https://www.python.org/ftp/python/" +$GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py" +$GET_PIP_PATH = "C:\get-pip.py" + +$PYTHON_PRERELEASE_REGEX = @" +(?x) +(?\d+) +\. +(?\d+) +\. +(?\d+) +(?[a-z]{1,2}\d+) +"@ + + +function Download ($filename, $url) { + $webclient = New-Object System.Net.WebClient + + $basedir = $pwd.Path + "\" + $filepath = $basedir + $filename + if (Test-Path $filename) { + Write-Host "Reusing" $filepath + return $filepath + } + + # Download and retry up to 3 times in case of network transient errors. + Write-Host "Downloading" $filename "from" $url + $retry_attempts = 2 + for ($i = 0; $i -lt $retry_attempts; $i++) { + try { + $webclient.DownloadFile($url, $filepath) + break + } + Catch [Exception]{ + Start-Sleep 1 + } + } + if (Test-Path $filepath) { + Write-Host "File saved at" $filepath + } else { + # Retry once to get the error message if any at the last try + $webclient.DownloadFile($url, $filepath) + } + return $filepath +} + + +function ParsePythonVersion ($python_version) { + if ($python_version -match $PYTHON_PRERELEASE_REGEX) { + return ([int]$matches.major, [int]$matches.minor, [int]$matches.micro, + $matches.prerelease) + } + $version_obj = [version]$python_version + return ($version_obj.major, $version_obj.minor, $version_obj.build, "") +} + + +function DownloadPython ($python_version, $platform_suffix) { + $major, $minor, $micro, $prerelease = ParsePythonVersion $python_version + + if (($major -le 2 -and $micro -eq 0) ` + -or ($major -eq 3 -and $minor -le 2 -and $micro -eq 0) ` + ) { + $dir = "$major.$minor" + $python_version = "$major.$minor$prerelease" + } else { + $dir = "$major.$minor.$micro" + } + + if ($prerelease) { + if (($major -le 2) ` + -or ($major -eq 3 -and $minor -eq 1) ` + -or ($major -eq 3 -and $minor -eq 2) ` + -or ($major -eq 3 -and $minor -eq 3) ` + ) { + $dir = "$dir/prev" + } + } + + if (($major -le 2) -or ($major -le 3 -and $minor -le 4)) { + $ext = "msi" + if ($platform_suffix) { + $platform_suffix = ".$platform_suffix" + } + } else { + $ext = "exe" + if ($platform_suffix) { + $platform_suffix = "-$platform_suffix" + } + } + + $filename = "python-$python_version$platform_suffix.$ext" + $url = "$BASE_URL$dir/$filename" + $filepath = Download $filename $url + return $filepath +} + + +function InstallPython ($python_version, $architecture, $python_home) { + Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home + if (Test-Path $python_home) { + Write-Host $python_home "already exists, skipping." + return $false + } + if ($architecture -eq "32") { + $platform_suffix = "" + } else { + $platform_suffix = "amd64" + } + $installer_path = DownloadPython $python_version $platform_suffix + $installer_ext = [System.IO.Path]::GetExtension($installer_path) + Write-Host "Installing $installer_path to $python_home" + $install_log = $python_home + ".log" + if ($installer_ext -eq '.msi') { + InstallPythonMSI $installer_path $python_home $install_log + } else { + InstallPythonEXE $installer_path $python_home $install_log + } + if (Test-Path $python_home) { + Write-Host "Python $python_version ($architecture) installation complete" + } else { + Write-Host "Failed to install Python in $python_home" + Get-Content -Path $install_log + Exit 1 + } +} + + +function InstallPythonEXE ($exepath, $python_home, $install_log) { + $install_args = "/quiet InstallAllUsers=1 TargetDir=$python_home" + RunCommand $exepath $install_args +} + + +function InstallPythonMSI ($msipath, $python_home, $install_log) { + $install_args = "/qn /log $install_log /i $msipath TARGETDIR=$python_home" + $uninstall_args = "/qn /x $msipath" + RunCommand "msiexec.exe" $install_args + if (-not(Test-Path $python_home)) { + Write-Host "Python seems to be installed else-where, reinstalling." + RunCommand "msiexec.exe" $uninstall_args + RunCommand "msiexec.exe" $install_args + } +} + +function RunCommand ($command, $command_args) { + Write-Host $command $command_args + Start-Process -FilePath $command -ArgumentList $command_args -Wait -Passthru +} + + +function InstallPip ($python_home) { + $pip_path = $python_home + "\Scripts\pip.exe" + $python_path = $python_home + "\python.exe" + if (-not(Test-Path $pip_path)) { + Write-Host "Installing pip..." + $webclient = New-Object System.Net.WebClient + $webclient.DownloadFile($GET_PIP_URL, $GET_PIP_PATH) + Write-Host "Executing:" $python_path $GET_PIP_PATH + & $python_path $GET_PIP_PATH + } else { + Write-Host "pip already installed." + } +} + + +function DownloadMiniconda ($python_version, $platform_suffix) { + if ($python_version -eq "3.4") { + $filename = "Miniconda3-3.5.5-Windows-" + $platform_suffix + ".exe" + } else { + $filename = "Miniconda-3.5.5-Windows-" + $platform_suffix + ".exe" + } + $url = $MINICONDA_URL + $filename + $filepath = Download $filename $url + return $filepath +} + + +function InstallMiniconda ($python_version, $architecture, $python_home) { + Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home + if (Test-Path $python_home) { + Write-Host $python_home "already exists, skipping." + return $false + } + if ($architecture -eq "32") { + $platform_suffix = "x86" + } else { + $platform_suffix = "x86_64" + } + $filepath = DownloadMiniconda $python_version $platform_suffix + Write-Host "Installing" $filepath "to" $python_home + $install_log = $python_home + ".log" + $args = "/S /D=$python_home" + Write-Host $filepath $args + Start-Process -FilePath $filepath -ArgumentList $args -Wait -Passthru + if (Test-Path $python_home) { + Write-Host "Python $python_version ($architecture) installation complete" + } else { + Write-Host "Failed to install Python in $python_home" + Get-Content -Path $install_log + Exit 1 + } +} + + +function InstallMinicondaPip ($python_home) { + $pip_path = $python_home + "\Scripts\pip.exe" + $conda_path = $python_home + "\Scripts\conda.exe" + if (-not(Test-Path $pip_path)) { + Write-Host "Installing pip..." + $args = "install --yes pip" + Write-Host $conda_path $args + Start-Process -FilePath "$conda_path" -ArgumentList $args -Wait -Passthru + } else { + Write-Host "pip already installed." + } +} + +function main () { + InstallPython $env:PYTHON_VERSION $env:PYTHON_ARCH $env:PYTHON + InstallPip $env:PYTHON +} + +main diff --git a/appveyor/run_pip.py b/appveyor/run_pip.py deleted file mode 100644 index 7f638d88..00000000 --- a/appveyor/run_pip.py +++ /dev/null @@ -1,6 +0,0 @@ -# -*- coding: utf-8 -*- -import sys -from pip import main - -if __name__ == '__main__': - sys.exit(main()) diff --git a/my_build_ext.py b/my_build_ext.py deleted file mode 100644 index 6ff733c6..00000000 --- a/my_build_ext.py +++ /dev/null @@ -1,60 +0,0 @@ -# this code has been taken from gevent's setup.py file. it provides a -# build_ext command that puts .so/.pyd files in-place (like "setup.py -# build_ext -i"). it uses symlinks if possible and will rebuild when -# changing the python version (unlike "setup.py build_ext -i") - -import sys, os, shutil - -from distutils.command.build_ext import build_ext as _build_ext - - -def symlink_or_copy(src, dst): - if hasattr(os, 'symlink'): - try: - os.symlink(src, dst) - return - except OSError: # symbolic link privilege not held?? - pass - except NotImplementedError: # running on XP/'CreateSymbolicLinkW not found' - pass - - shutil.copyfile(src, dst) - - -class build_ext(_build_ext): - """Command for building extensions - - Prepends library directory to sys.path on normal builds (for tests). - Otherwise it forces a non-inplace build and symlinks libraries instead. - """ - - def initialize_options(self): - self.my_inplace = None - _build_ext.initialize_options(self) - - def finalize_options(self): - if self.my_inplace is None: - self.my_inplace = self.inplace - self.inplace = 0 - _build_ext.finalize_options(self) - - def build_extension(self, ext): - _build_ext.build_extension(self, ext) - if not self.my_inplace: - build_lib = os.path.abspath(self.build_lib) - if build_lib not in sys.path: - sys.path.insert(0, build_lib) - return - filename = self.get_ext_filename(ext.name) - build_path = os.path.abspath(os.path.join(self.build_lib, filename)) - src_path = os.path.abspath(filename) - if build_path != src_path: - try: - os.unlink(src_path) - except OSError: - pass - - if self.verbose: - sys.stderr.write('Linking %s to %s\n' % (build_path, src_path)) - - symlink_or_copy(build_path, src_path) diff --git a/run-tests.py b/run-tests.py index 9a9c0ed0..ec40fd61 100755 --- a/run-tests.py +++ b/run-tests.py @@ -1,11 +1,13 @@ #! /usr/bin/env python +from __future__ import print_function + +import sys +import os +import struct +import unittest -import sys, os, getopt, struct, unittest -from distutils.spawn import spawn -build = True verbosity = 2 -build_base = None here = os.path.dirname(os.path.abspath(__file__)) os.chdir(here) @@ -15,49 +17,21 @@ def bits(): """ return struct.calcsize("P") * 8 -# -- parse options -try: - opts, args = getopt.getopt(sys.argv[1:], "nqb:") - if args: - raise getopt.GetoptError("too many arguments") -except getopt.GetoptError: - sys.exit("run-tests.py: error: %s" % sys.exc_info()[1]) - -for o, a in opts: - if o == "-q": - verbosity = 0 - elif o == "-n": - build = False - elif o == "-b": - build_base = a - -# -- build greenlet -if build: - if verbosity == 0: - cmd = [sys.executable, "setup.py", "-q", "build_ext", "-q", "-i"] - else: - cmd = [sys.executable, "setup.py", "build_ext", "-i"] - - spawn(cmd, search_path=0) # -- find greenlet but skip the one in "." -if not build: - oldpath = sys.path[:] - sys.path.remove(here) +oldpath = sys.path[:] +sys.path.remove(here) import greenlet -if not build: - sys.path[:] = oldpath - -sys.stdout.write("python %s (%s bit) using greenlet %s from %s\n" % - (sys.version.split()[0], bits(), greenlet.__version__, greenlet.__file__)) -sys.stdout.flush() +sys.path[:] = oldpath +print("python %s (%s bit) using greenlet %s from %s\n" % + (sys.version.split()[0], bits(), greenlet.__version__, greenlet.__file__)) # -- run tests from tests import test_collector -result = unittest.TextTestRunner(verbosity=verbosity).run(test_collector(build_base)) +result = unittest.TextTestRunner(verbosity=verbosity).run(test_collector()) if result.failures or result.errors: sys.exit(1) diff --git a/setup.py b/setup.py index 1fb295da..459633fa 100755 --- a/setup.py +++ b/setup.py @@ -9,10 +9,6 @@ from setuptools import setup from setuptools import Extension -# XXX: This uses distutils directly and is probably -# unnecessary with setuptools. -from my_build_ext import build_ext - # workaround segfaults on openbsd and RHEL 3 / CentOS 3 . see # https://bitbucket.org/ambroff/greenlet/issue/11/segfault-on-openbsd-i386 # https://github.com/python-greenlet/greenlet/issues/4 @@ -53,12 +49,34 @@ def _find_platform_headers(): else: extra_compile_args = [] - ext_modules = [Extension( - name='greenlet', - sources=['greenlet.c'], - extra_objects=extra_objects, - extra_compile_args=extra_compile_args, - depends=['greenlet.h', 'slp_platformselect.h'] + _find_platform_headers())] + ext_modules = [ + Extension( + name='greenlet', + sources=['greenlet.c'], + extra_objects=extra_objects, + extra_compile_args=extra_compile_args, + depends=['greenlet.h', 'slp_platformselect.h'] + _find_platform_headers() + ), + # Test extensions. + # XXX: We used to try hard to not include these in built + # distributions. That's really not important, at least not once we have a clean + # layout with the test directory nested inside a greenlet directory. + # See https://github.com/python-greenlet/greenlet/issues/184 and 189 + Extension( + '_test_extension', + [os.path.join('tests', '_test_extension.c')], + include_dirs=[os.path.curdir] + ), + ] + + if os.environ.get('GREENLET_TEST_CPP', 'yes').lower() not in ('0', 'no', 'false'): + ext_modules.append( + Extension( + '_test_extension_cpp', + [os.path.join('tests', '_test_extension_cpp.cpp')], + language="c++", + include_dirs=[os.path.curdir]), + ) setup( name="greenlet", @@ -75,7 +93,6 @@ def _find_platform_headers(): platforms=['any'], headers=headers, ext_modules=ext_modules, - cmdclass=dict(build_ext=build_ext), classifiers=[ 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', diff --git a/tests/__init__.py b/tests/__init__.py index fb95c8d7..3ea15d89 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,56 +1,14 @@ import os import glob import unittest -from distutils.core import setup, Extension -TEST_EXTENSIONS = [ - Extension('_test_extension', - [os.path.join('tests', '_test_extension.c')], - include_dirs=[os.path.curdir]), -] - -if os.environ.get('GREENLET_TEST_CPP', 'yes').lower() not in ('0', 'no', 'false'): - TEST_EXTENSIONS_CPP = [ - Extension('_test_extension_cpp', - [os.path.join('tests', '_test_extension_cpp.cpp')], - language="c++", - include_dirs=[os.path.curdir]), - ] -else: - TEST_EXTENSIONS_CPP = [] - - -def test_collector(build_base=None): +def test_collector(): """Collect all tests under the tests directory and return a unittest.TestSuite """ - build_test_extensions(build_base) tests_dir = os.path.realpath(os.path.dirname(__file__)) test_module_list = [ 'tests.%s' % os.path.splitext(os.path.basename(t))[0] - for t in glob.glob(os.path.join(tests_dir, 'test_*.py'))] - if not TEST_EXTENSIONS_CPP: - test_module_list.remove('tests.test_cpp') + for t in glob.glob(os.path.join(tests_dir, 'test_*.py')) + ] return unittest.TestLoader().loadTestsFromNames(test_module_list) - - -def build_test_extensions(build_base=None): - """Because distutils sucks, it just copies the entire contents of the build - results dir (e.g. build/lib.linux-i686-2.6) during installation. That means - that we can't put any files there that we don't want to distribute. - - To deal with it, this code will compile test extensions in a separate - directory, prepending it to sys.path afterwards. This way testing with - multiple Python release and pydebug versions works and test extensions - are not distributed. - """ - if build_base is None: - build_base = os.path.join('build', 'tests') - from my_build_ext import build_ext - setup( - options={ - 'build': {'build_base': build_base}, - }, - cmdclass=dict(build_ext=build_ext), - script_args=['-q', 'build_ext', '-q'], - ext_modules=TEST_EXTENSIONS + TEST_EXTENSIONS_CPP) diff --git a/tox.ini b/tox.ini index f0735cbc..3095831d 100644 --- a/tox.ini +++ b/tox.ini @@ -1,12 +1,11 @@ +[tox] +envlist = + py27,py35,py36,py37,py38,py39,docs + [testenv] -commands={envpython} run-tests.py -nq +commands=python run-tests.py sitepackages=False -[testenv:x-py27] -commands=/usr/bin/arch -i386 {envpython} run-tests.py -qn - /usr/bin/arch -x86_64 {envpython} run-tests.py -qn -basepython=/usr/bin/python2.7 - [testenv:docs] # usedevelop to save rebuilding the extension