diff --git a/.circleci/config.yml b/.circleci/config.yml index b881c8a8b..3d2d09a1b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,11 +1,11 @@ version: 2 jobs: - osx-python2: + osx-python3.6: macos: - xcode: "10.0.0" + xcode: "9.4.1" environment: - PYTHON: python2 + PYTHON: python3 steps: - checkout @@ -16,7 +16,7 @@ jobs: name: Test. command: venv/bin/python ./bin/run_tests.py - osx-python3: + osx-python3.7: macos: xcode: "10.0.0" environment: @@ -31,23 +31,7 @@ jobs: name: Test. command: venv/bin/python ./bin/run_tests.py - linux-python2: - docker: - - image: circleci/python:2.7 - environment: - PYTHON: python2 - steps: - - checkout - - setup_remote_docker - - - run: - name: Prepare the environment. - command: bash .circleci/prepare.sh - - run: - name: Test. - command: venv/bin/python ./bin/run_tests.py - - linux-python3: + linux-python3.6: docker: - image: circleci/python:3.6 environment: @@ -67,7 +51,6 @@ workflows: version: 2 all-tests: jobs: - - osx-python2 - - osx-python3 - - linux-python2 - - linux-python3 + - osx-python3.6 + - osx-python3.7 + - linux-python3.6 diff --git a/.travis.yml b/.travis.yml index ea03f8e0f..d5215b043 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,13 +2,6 @@ language: generic matrix: include: - # Linux Python 2 - - sudo: required - language: python - python: 2.7 - services: docker - env: PYTHON=python - # Linux Python 3 - sudo: required language: python diff --git a/CI.md b/CI.md new file mode 100644 index 000000000..3b43a2e68 --- /dev/null +++ b/CI.md @@ -0,0 +1,9 @@ +This is a summary of the Python versions and platforms covered by the different CI platforms: + +| | 3.5 | 3.6 | 3.7 | 3.8 | +|----------|------------------|------------------|-----------------------|------------------| +| Linux | Travis CI | CircleCI | AppVeyor | Azure Pipelines | +| macOS | Azure Pipelines | CircleCI | Travis CI¹ / CircleCI | Azure Pipelines | +| Windows | TravisCI | Azure Pipelines | AppVeyor | Azure Pipelines | + +> ¹ Python version not really pinned, but dependent on the (default) version of image used. diff --git a/appveyor.yml b/appveyor.yml index 6f611d94c..d3f321d93 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,9 +3,9 @@ image: - Visual Studio 2015 build_script: - - cmd: "C:\\Python27\\python.exe -m pip install -r requirements-dev.txt" + - cmd: "C:\\Python37\\python.exe -m pip install -r requirements-dev.txt" - sh: "${HOME}/.localpython3.7.4/bin/python3 -m pip install -r requirements-dev.txt" # the '-u' flag is required so the output is in the correct order. # See https://github.com/joerick/cibuildwheel/pull/24 for more info. - - cmd: "C:\\Python27\\python.exe -u ./bin/run_tests.py" + - cmd: "C:\\Python37\\python.exe -u ./bin/run_tests.py" - sh: "${HOME}/.localpython3.7.4/bin/python3 ./bin/run_tests.py" diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 72ff2b858..74d783956 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,5 +1,5 @@ jobs: -- job: linux +- job: linux_38 pool: {vmImage: 'Ubuntu-18.04'} steps: - task: UsePythonVersion@0 @@ -9,7 +9,17 @@ jobs: python -m pip install -r requirements-dev.txt python ./bin/run_tests.py -- job: macos +- job: macos_35 + pool: {vmImage: 'macOS-10.13'} + steps: + - task: UsePythonVersion@0 + inputs: + versionSpec: '3.5' + - bash: | + python -m pip install -r requirements-dev.txt + python ./bin/run_tests.py + +- job: macos_38 pool: {vmImage: 'macOS-10.13'} steps: - task: UsePythonVersion@0 @@ -19,7 +29,19 @@ jobs: python -m pip install -r requirements-dev.txt python ./bin/run_tests.py -- job: windows +- job: windows_36 + pool: {vmImage: 'vs2017-win2016'} + steps: + - task: UsePythonVersion@0 + inputs: + versionSpec: '3.6' + - script: choco install vcpython27 -f -y + displayName: Install Visual C++ for Python 2.7 + - bash: | + python -m pip install -r requirements-dev.txt + python ./bin/run_tests.py + +- job: windows_38 pool: {vmImage: 'vs2017-win2016'} steps: - task: UsePythonVersion@0 diff --git a/bin/run_test.py b/bin/run_test.py index 70a7825c4..075c4d089 100755 --- a/bin/run_test.py +++ b/bin/run_test.py @@ -1,6 +1,5 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 -from __future__ import print_function import os, sys, subprocess, shutil diff --git a/bin/run_tests.py b/bin/run_tests.py index 279a992cb..a0413dcff 100755 --- a/bin/run_tests.py +++ b/bin/run_tests.py @@ -1,6 +1,5 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 -from __future__ import print_function import os, sys, subprocess, shutil, json from glob import glob diff --git a/cibuildwheel/__main__.py b/cibuildwheel/__main__.py index bdc34aaf4..314512a90 100644 --- a/cibuildwheel/__main__.py +++ b/cibuildwheel/__main__.py @@ -1,4 +1,3 @@ -from __future__ import print_function import argparse, os, subprocess, sys, textwrap import cibuildwheel diff --git a/cibuildwheel/bashlex_eval.py b/cibuildwheel/bashlex_eval.py index af1ef5bad..1caddd8fb 100644 --- a/cibuildwheel/bashlex_eval.py +++ b/cibuildwheel/bashlex_eval.py @@ -60,12 +60,8 @@ def evaluate_word_node(node, context): def evaluate_command_node(node, context): words = [evaluate_node(part, context=context) for part in node.parts] command = ' '.join(words) - output = subprocess.check_output(shlex.split(command), env=context.environment) + return subprocess.check_output(shlex.split(command), env=context.environment, universal_newlines=True) - if sys.version_info[0] >= 3: - return output.decode('utf8', 'replace') - else: - return output def evaluate_parameter_node(node, context): return context.environment.get(node.value, '') diff --git a/cibuildwheel/environment.py b/cibuildwheel/environment.py index 1b2adec41..5d548f579 100644 --- a/cibuildwheel/environment.py +++ b/cibuildwheel/environment.py @@ -41,7 +41,7 @@ def split_env_items(env_string): return result -class EnvironmentAssignment(object): +class EnvironmentAssignment: def __init__(self, assignment): name, equals, value = assignment.partition('=') if not equals: @@ -60,7 +60,7 @@ def __repr__(self): return '%s=%s' % (self.name, self.value) -class ParsedEnvironment(object): +class ParsedEnvironment: def __init__(self, assignments): self.assignments = assignments diff --git a/cibuildwheel/linux.py b/cibuildwheel/linux.py index 30841794b..11a154139 100644 --- a/cibuildwheel/linux.py +++ b/cibuildwheel/linux.py @@ -1,13 +1,7 @@ -from __future__ import print_function -import os, subprocess, sys, uuid, textwrap +import os, shlex, subprocess, sys, textwrap, uuid from collections import namedtuple from .util import prepare_command, get_build_verbosity_extra_flags -try: - from shlex import quote as shlex_quote -except ImportError: - from pipes import quote as shlex_quote - def get_python_configurations(build_selector): PythonConfiguration = namedtuple('PythonConfiguration', ['identifier', 'path']) @@ -132,14 +126,14 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef pybin_paths=' '.join(c.path+'/bin' for c in platform_configs), test_requires=' '.join(test_requires), test_extras=test_extras, - test_command=shlex_quote( + test_command=shlex.quote( prepare_command(test_command, project='/project') if test_command else '' ), - before_build=shlex_quote( + before_build=shlex.quote( prepare_command(before_build, project='/project') if before_build else '' ), build_verbosity_flag=' '.join(get_build_verbosity_extra_flags(build_verbosity)), - repair_command=shlex_quote( + repair_command=shlex.quote( prepare_command(repair_command, wheel='"$1"', dest_dir='/tmp/repaired_wheels') if repair_command else '' ), environment_exports='\n'.join(environment.as_shell_commands()), @@ -147,38 +141,24 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef gid=os.getgid(), ) - def run_docker(command, stdin_str=None): - print('docker command: docker {}'.format(' '.join(map(shlex_quote, command)))) - if stdin_str is None: - subprocess.check_call(['docker'] + command) - else: - args = ['docker'] + command - process = subprocess.Popen(args, stdin=subprocess.PIPE, universal_newlines=True) - try: - process.communicate(stdin_str) - except KeyboardInterrupt: - process.kill() - process.wait() - if process.returncode != 0: - raise subprocess.CalledProcessError(process.returncode, args) - container_name = 'cibuildwheel-{}'.format(uuid.uuid4()) try: - run_docker(['create', - '--env', 'CIBUILDWHEEL', - '--name', container_name, - '-i', - '-v', '/:/host', # ignored on Circle - docker_image, '/bin/bash']) - run_docker(['cp', os.path.abspath(project_dir) + '/.', container_name + ':/project']) - run_docker(['start', '-i', '-a', container_name], stdin_str=bash_script) - run_docker(['cp', container_name + ':/output/.', os.path.abspath(output_dir)]) + subprocess.run(['docker', 'create', + '--env', 'CIBUILDWHEEL', + '--name', container_name, + '-i', + '-v', '/:/host', # ignored on CircleCI + docker_image, '/bin/bash'], + check=True) + subprocess.run(['docker', 'cp', os.path.abspath(project_dir) + '/.', container_name + ':/project'], check=True) + subprocess.run(['docker', 'start', '-i', '-a', container_name], input=bash_script, universal_newlines=True, check=True) + subprocess.run(['docker', 'cp', container_name + ':/output/.', os.path.abspath(output_dir)], check=True) except subprocess.CalledProcessError as error: troubleshoot(project_dir, error) exit(1) finally: # Still gets executed, even when 'exit(1)' gets called - run_docker(['rm', '--force', '-v', container_name]) + subprocess.run(['docker', 'rm', '--force', '-v', container_name], check=True) def troubleshoot(project_dir, error): diff --git a/cibuildwheel/macos.py b/cibuildwheel/macos.py index 7480990fb..6185070f6 100644 --- a/cibuildwheel/macos.py +++ b/cibuildwheel/macos.py @@ -1,12 +1,7 @@ -from __future__ import print_function import tempfile import os, subprocess, shlex, sys, shutil from collections import namedtuple from glob import glob -try: - from shlex import quote as shlex_quote -except ImportError: - from pipes import quote as shlex_quote from .util import prepare_command, get_build_verbosity_extra_flags, download @@ -36,9 +31,7 @@ def build(project_dir, output_dir, test_command, test_requires, test_extras, bef get_pip_url = 'https://bootstrap.pypa.io/get-pip.py' get_pip_script = '/tmp/get-pip.py' - pkgs_output = subprocess.check_output(['pkgutil', '--pkgs']) - if sys.version_info[0] >= 3: - pkgs_output = pkgs_output.decode('utf8') + pkgs_output = subprocess.check_output(['pkgutil', '--pkgs'], universal_newlines=True) installed_system_packages = pkgs_output.splitlines() def call(args, env=None, cwd=None, shell=False): @@ -46,7 +39,7 @@ def call(args, env=None, cwd=None, shell=False): if shell: print('+ %s' % args) else: - print('+ ' + ' '.join(shlex_quote(a) for a in args)) + print('+ ' + ' '.join(shlex.quote(a) for a in args)) return subprocess.check_call(args, env=env, cwd=cwd, shell=shell) diff --git a/cibuildwheel/util.py b/cibuildwheel/util.py index 0aedbcac9..fbee1728b 100644 --- a/cibuildwheel/util.py +++ b/cibuildwheel/util.py @@ -1,13 +1,8 @@ from fnmatch import fnmatch import warnings -import os +import os, urllib.request from time import sleep -try: - from urllib.request import urlopen -except ImportError: - from urllib2 import urlopen - def prepare_command(command, **kwargs): ''' @@ -28,7 +23,7 @@ def get_build_verbosity_extra_flags(level): return [] -class BuildSelector(object): +class BuildSelector: def __init__(self, build_config, skip_config): self.build_patterns = build_config.split() self.skip_patterns = skip_config.split() @@ -43,7 +38,7 @@ def __repr__(self): # Taken from https://stackoverflow.com/a/107717 -class Unbuffered(object): +class Unbuffered: def __init__(self, stream): self.stream = stream @@ -68,7 +63,7 @@ def download(url, dest): repeat_num = 3 for i in range(repeat_num): try: - response = urlopen(url) + response = urllib.request.urlopen(url) except: if i == repeat_num - 1: raise diff --git a/cibuildwheel/windows.py b/cibuildwheel/windows.py index c50c98104..175edda3a 100644 --- a/cibuildwheel/windows.py +++ b/cibuildwheel/windows.py @@ -1,13 +1,7 @@ -from __future__ import print_function import os, tempfile, subprocess, shutil, sys from collections import namedtuple from glob import glob -try: - from shlex import quote as shlex_quote -except ImportError: - from pipes import quote as shlex_quote - from .util import prepare_command, get_build_verbosity_extra_flags, download diff --git a/setup.py b/setup.py index b521a6f50..bb6db2ca3 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- import os, io @@ -28,12 +27,11 @@ 'cibuildwheel': ['resources/*'], }, # Supported python versions - python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*', + python_requires='>=3.5', keywords='ci wheel packaging pypi travis appveyor macos linux windows', classifiers=[ 'Intended Audience :: Developers', 'Natural Language :: English', - 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 3', 'Development Status :: 5 - Production/Stable', 'License :: OSI Approved :: BSD License', diff --git a/unit_test/main_util_fixtures.py b/unit_test/main_util_fixtures.py index 3c993b352..21a7f36a7 100644 --- a/unit_test/main_util_fixtures.py +++ b/unit_test/main_util_fixtures.py @@ -7,7 +7,7 @@ from cibuildwheel import linux, macos, windows, util -class ArgsInterceptor(object): +class ArgsInterceptor: def __call__(self, *args, **kwargs): self.args = args self.kwargs = kwargs