Merge pull request #315 from YannickJadoul/type-annotations

Add type annotations and drop Python 3.5 host support
This commit is contained in:
Yannick Jadoul
2020-05-09 02:37:58 +02:00
committed by GitHub
15 changed files with 177 additions and 164 deletions
+9 -23
View File
@@ -1,18 +1,21 @@
version: 2
jobs:
flake8:
flake8-mypy:
docker:
- image: circleci/python:3.6
steps:
- checkout
- run:
name: Install flake8
command: sudo python -m pip install flake8
name: Install flake8 & mypy
command: sudo python -m pip install flake8 mypy
- run:
name: Test.
command: flake8 .
name: flake8
command: flake8
- run:
name: mypy
command: mypy
osx-python3.6:
macos:
@@ -30,22 +33,6 @@ jobs:
command: venv/bin/python ./bin/run_tests.py
no_output_timeout: 30m
osx-python3.7:
macos:
xcode: "10.0.0"
environment:
PYTHON: python3
steps:
- checkout
- run:
name: Prepare the environment.
command: bash .circleci/prepare.sh
- run:
name: Test.
command: venv/bin/python ./bin/run_tests.py
no_output_timeout: 30m
linux-python3.6:
docker:
- image: circleci/python:3.6
@@ -67,7 +54,6 @@ workflows:
version: 2
all-tests:
jobs:
- flake8
- flake8-mypy
- osx-python3.6
- osx-python3.7
- linux-python3.6
+3
View File
@@ -102,3 +102,6 @@ env3?/
# VSCode project settings
/.vscode
# MyPy cache
.mypy_cache/
+13 -13
View File
@@ -6,22 +6,22 @@ branches:
jobs:
include:
- name: Linux | x86_64 + i686 | Python 3.5
- name: Linux | x86_64 + i686 | Python 3.6
language: python
python: 3.5
python: 3.6
services: docker
env: PYTHON=python
- name: Linux | arm64 | Python 3.5
- name: Linux | arm64 | Python 3.6
language: python
python: 3.5
python: 3.6
services: docker
arch: arm64
env: PYTHON=python
- name: Linux | ppc64le | Python 3.5
- name: Linux | ppc64le | Python 3.6
language: python
python: 3.5
python: 3.6
services: docker
arch: ppc64le
env: PYTHON=python
@@ -30,25 +30,25 @@ jobs:
os: osx
env: PYTHON=python3
- name: Windows | x86_64 | Python 3.5
- name: Windows | x86_64 | Python 3.6
os: windows
language: shell
before_install:
- choco install python3 --version 3.5.4 --no-progress -y
- choco install python3 --version 3.6.8 --no-progress -y
env:
- PYTHON=C:\\Python35\\python
- PYTHON=C:\\Python36\\python
- &linux_s390x_35
name: Linux | s390x | Python 3.5
- &linux_s390x_36
name: Linux | s390x | Python 3.6
language: python
python: 3.5
python: 3.6
services: docker
arch: s390x
env: PYTHON=python
allow_failures:
# must repeat the s390x job above exactly to match
- *linux_s390x_35
- *linux_s390x_36
install: $PYTHON -m pip install -r requirements-dev.txt
+6 -6
View File
@@ -1,12 +1,12 @@
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² / GitHub Actions | Azure Pipelines |
| macOS | Azure Pipelines | CircleCI | AppVeyor² / Travis CI¹ / CircleCI / GitHub Actions | Azure Pipelines |
| Windows | TravisCI | Azure Pipelines | AppVeyor² / GitHub Actions | Azure Pipelines |
| | 3.6 | 3.7 | 3.8 |
|----------|------------------------------|------------------------------------------|------------------|
| Linux | Travis CI / CircleCI | AppVeyor² / GitHub Actions | Azure Pipelines |
| macOS | CircleCI | AppVeyor² / Travis CI¹ / GitHub Actions | Azure Pipelines |
| Windows | Travis CI / Azure Pipelines | AppVeyor² / GitHub Actions | Azure Pipelines |
> ¹ Python version not really pinned, but dependent on the (default) version of image used.
> ² AppVeyor only runs the "basic" test to reduce load.
Non-x86 architectures are covered on Travis CI using Python 3.5.
Non-x86 architectures are covered on Travis CI using Python 3.6.
-10
View File
@@ -9,16 +9,6 @@ jobs:
python -m pip install -r requirements-dev.txt
python ./bin/run_tests.py
- job: macos_35
pool: {vmImage: 'macOS-10.15'}
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.15'}
steps:
+20 -17
View File
@@ -5,6 +5,8 @@ import textwrap
import traceback
from configparser import ConfigParser
from typing import Any, Dict, List, Optional, overload
import cibuildwheel
import cibuildwheel.linux
import cibuildwheel.macos
@@ -14,14 +16,18 @@ from cibuildwheel.environment import (
parse_environment,
)
from cibuildwheel.util import (
BuildOptions,
BuildSelector,
DependencyConstraints,
Unbuffered,
BuildOptions
)
def get_option_from_environment(option_name, platform=None, default=None):
@overload
def get_option_from_environment(option_name: str, platform: Optional[str], default: str) -> str: ... # noqa: E704
@overload
def get_option_from_environment(option_name: str, platform: Optional[str] = None, default: None = None) -> Optional[str]: ... # noqa: E704 E302
def get_option_from_environment(option_name: str, platform: Optional[str] = None, default: Optional[str] = None) -> Optional[str]: # noqa: E302
'''
Returns an option from the environment, optionally scoped by the platform.
@@ -39,13 +45,13 @@ def get_option_from_environment(option_name, platform=None, default=None):
return os.environ.get(option_name, default)
def strtobool(val):
def strtobool(val: str) -> bool:
if val.lower() in ('y', 'yes', 't', 'true', 'on', '1'):
return True
return False
def main():
def main() -> None:
parser = argparse.ArgumentParser(
description='Build wheels for all the platforms.',
epilog='''
@@ -114,7 +120,7 @@ def main():
test_extras = get_option_from_environment('CIBW_TEST_EXTRAS', platform=platform, default='')
package_dir = args.package_dir
before_build = get_option_from_environment('CIBW_BEFORE_BUILD', platform=platform)
build_verbosity = get_option_from_environment('CIBW_BUILD_VERBOSITY', platform=platform, default='')
build_verbosity_str = get_option_from_environment('CIBW_BUILD_VERBOSITY', platform=platform, default='')
build_config, skip_config = os.environ.get('CIBW_BUILD', '*'), os.environ.get('CIBW_SKIP', '')
if platform == 'linux':
repair_command_default = 'auditwheel repair -w {dest_dir} {wheel}'
@@ -128,7 +134,7 @@ def main():
dependency_versions = get_option_from_environment('CIBW_DEPENDENCY_VERSIONS', platform=platform, default='pinned')
if dependency_versions == 'pinned':
dependency_constraints = DependencyConstraints.with_defaults()
dependency_constraints: Optional[DependencyConstraints] = DependencyConstraints.with_defaults()
elif dependency_versions == 'latest':
dependency_constraints = None
else:
@@ -138,7 +144,7 @@ def main():
test_extras = '[{0}]'.format(test_extras)
try:
build_verbosity = min(3, max(-3, int(build_verbosity)))
build_verbosity = min(3, max(-3, int(build_verbosity_str)))
except ValueError:
build_verbosity = 0
@@ -163,6 +169,7 @@ def main():
print_build_identifiers(platform, build_selector)
exit(0)
manylinux_images: Optional[Dict[str, str]] = None
if platform == 'linux':
pinned_docker_images_file = os.path.join(
os.path.dirname(__file__), 'resources', 'pinned_docker_images.cfg'
@@ -193,9 +200,6 @@ def main():
manylinux_images[build_platform] = image
else:
manylinux_images = None
build_options = BuildOptions(
package_dir=package_dir,
output_dir=output_dir,
@@ -213,7 +217,7 @@ def main():
)
# Python is buffering by default when running on the CI platforms, giving problems interleaving subprocess call output with unflushed calls to 'print'
sys.stdout = Unbuffered(sys.stdout)
sys.stdout = Unbuffered(sys.stdout) # type: ignore
print_preamble(platform, build_options)
@@ -231,7 +235,7 @@ def main():
exit(2)
def detect_obsolete_options():
def detect_obsolete_options() -> None:
# Check the old 'MANYLINUX1_*_IMAGE' options
for (deprecated, alternative) in [('CIBW_MANYLINUX1_X86_64_IMAGE', 'CIBW_MANYLINUX_X86_64_IMAGE'),
('CIBW_MANYLINUX1_I686_IMAGE', 'CIBW_MANYLINUX_I686_IMAGE')]:
@@ -258,7 +262,7 @@ def detect_obsolete_options():
os.environ[option] = os.environ[option].replace(deprecated, alternative)
def print_preamble(platform, build_options):
def print_preamble(platform: str, build_options: BuildOptions) -> None:
print(textwrap.dedent('''
_ _ _ _ _ _ _
___|_| |_ _ _|_| |_| |_ _ _| |_ ___ ___| |
@@ -282,21 +286,20 @@ def print_preamble(platform, build_options):
print('\nHere we go!\n')
def print_build_identifiers(platform, build_selector):
def print_build_identifiers(platform: str, build_selector: BuildSelector) -> None:
python_configurations: List[Any] = []
if platform == 'linux':
python_configurations = cibuildwheel.linux.get_python_configurations(build_selector)
elif platform == 'windows':
python_configurations = cibuildwheel.windows.get_python_configurations(build_selector)
elif platform == 'macos':
python_configurations = cibuildwheel.macos.get_python_configurations(build_selector)
else:
python_configurations = []
for config in python_configurations:
print(config.identifier)
def detect_warnings(platform, build_options):
def detect_warnings(platform: str, build_options: BuildOptions) -> List[str]:
warnings = []
# warn about deprecated {python} and {pip}
+14 -10
View File
@@ -1,13 +1,17 @@
import shlex
import subprocess
from collections import namedtuple
import bashlex
from typing import Dict, NamedTuple
NodeExecutionContext = namedtuple('NodeExecutionContext', ['environment', 'input'])
import bashlex # type: ignore
def evaluate(value, environment):
class NodeExecutionContext(NamedTuple):
environment: Dict[str, str]
input: str
def evaluate(value: str, environment: Dict[str, str]) -> str:
if not value:
# empty string evaluates to empty string
# (but trips up bashlex)
@@ -26,7 +30,7 @@ def evaluate(value, environment):
)
def evaluate_node(node, context):
def evaluate_node(node: bashlex.ast.node, context: NodeExecutionContext) -> str:
if node.kind == 'word':
return evaluate_word_node(node, context=context)
elif node.kind == 'commandsubstitution':
@@ -37,7 +41,7 @@ def evaluate_node(node, context):
raise ValueError('Unsupported bash construct: "%s"' % node.word)
def evaluate_word_node(node, context):
def evaluate_word_node(node: bashlex.ast.node, context: NodeExecutionContext) -> str:
word_start = node.pos[0]
word_end = node.pos[1]
word_string = context.input[word_start:word_end]
@@ -49,22 +53,22 @@ def evaluate_word_node(node, context):
# Set all the characters in the part to None
for i in range(part_start, part_end):
letters[i] = None
letters[i] = ''
letters[part_start] = evaluate_node(part, context=context)
# remove the None letters and concat
value = ''.join(l for l in letters if l is not None)
value = ''.join(letters)
# apply bash-like quotes/whitespace treatment
return ' '.join(word.strip() for word in shlex.split(value))
def evaluate_command_node(node, context):
def evaluate_command_node(node: bashlex.ast.node, context: NodeExecutionContext) -> str:
words = [evaluate_node(part, context=context) for part in node.parts]
command = ' '.join(words)
return subprocess.check_output(shlex.split(command), env=context.environment, universal_newlines=True)
def evaluate_parameter_node(node, context):
def evaluate_parameter_node(node: bashlex.ast.node, context: NodeExecutionContext) -> str:
return context.environment.get(node.value, '')
+18 -16
View File
@@ -1,4 +1,6 @@
import bashlex
import bashlex # type: ignore
from typing import Dict, List
from . import bashlex_eval
@@ -7,13 +9,7 @@ class EnvironmentParseError(Exception):
pass
def parse_environment(env_string):
env_items = split_env_items(env_string)
assignments = [EnvironmentAssignment(item) for item in env_items]
return ParsedEnvironment(assignments=assignments)
def split_env_items(env_string):
def split_env_items(env_string: str) -> List[str]:
'''Splits space-separated variable assignments into a list of individual assignments.
>>> split_env_items('VAR=abc')
@@ -43,29 +39,29 @@ def split_env_items(env_string):
class EnvironmentAssignment:
def __init__(self, assignment):
def __init__(self, assignment: str):
name, equals, value = assignment.partition('=')
if not equals:
raise EnvironmentParseError(assignment)
self.name = name
self.value = value
def evaluated_value(self, environment):
def evaluated_value(self, environment: Dict[str, str]) -> str:
'''Returns the value of this assignment, as evaluated in the environment'''
return bashlex_eval.evaluate(self.value, environment=environment)
def as_shell_assignment(self):
def as_shell_assignment(self) -> str:
return 'export %s=%s' % (self.name, self.value)
def __repr__(self):
def __repr__(self) -> str:
return '%s=%s' % (self.name, self.value)
class ParsedEnvironment:
def __init__(self, assignments):
def __init__(self, assignments: List[EnvironmentAssignment]):
self.assignments = assignments
def as_dictionary(self, prev_environment):
def as_dictionary(self, prev_environment: Dict[str, str]) -> Dict[str, str]:
environment = prev_environment.copy()
for assignment in self.assignments:
@@ -74,8 +70,14 @@ class ParsedEnvironment:
return environment
def as_shell_commands(self):
def as_shell_commands(self) -> List[str]:
return [a.as_shell_assignment() for a in self.assignments]
def __repr__(self):
def __repr__(self) -> str:
return 'ParsedEnvironment(%r)' % [repr(a) for a in self.assignments]
def parse_environment(env_string: str) -> ParsedEnvironment:
env_items = split_env_items(env_string)
assignments = [EnvironmentAssignment(item) for item in env_items]
return ParsedEnvironment(assignments=assignments)
+16 -8
View File
@@ -5,23 +5,25 @@ import subprocess
import sys
import textwrap
import uuid
from collections import namedtuple
from typing import List, NamedTuple, Optional, Union
from .util import (
BuildOptions,
BuildSelector,
get_build_verbosity_extra_flags,
prepare_command,
BuildOptions
)
def call(args, input=None, universal_newlines=False):
def call(args: List[str], input: Optional[Union[str, bytes]] = None, universal_newlines: bool = False) -> None:
print('+ ' + ' '.join(shlex.quote(a) for a in args))
subprocess.run(
args, input=input, universal_newlines=universal_newlines, check=True
)
def matches_platform(identifier):
def matches_platform(identifier: str) -> bool:
pm = platform.machine()
if pm == "x86_64":
# x86_64 machines can run i686 docker containers
@@ -42,8 +44,13 @@ def matches_platform(identifier):
return False
def get_python_configurations(build_selector):
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'identifier', 'path'])
class PythonConfiguration(NamedTuple):
version: str
identifier: str
path: str
def get_python_configurations(build_selector: BuildSelector) -> List[PythonConfiguration]:
python_configurations = [
PythonConfiguration(version='2.7', identifier='cp27-manylinux_x86_64', path='/opt/python/cp27-cp27m'),
PythonConfiguration(version='2.7', identifier='cp27-manylinux_x86_64', path='/opt/python/cp27-cp27mu'),
@@ -76,7 +83,7 @@ def get_python_configurations(build_selector):
return [c for c in python_configurations if matches_platform(c.identifier) and build_selector(c.identifier)]
def build(options: BuildOptions):
def build(options: BuildOptions) -> None:
try:
subprocess.check_call(['docker', '--version'])
except Exception:
@@ -86,6 +93,7 @@ def build(options: BuildOptions):
file=sys.stderr)
exit(2)
assert options.manylinux_images is not None
python_configurations = get_python_configurations(options.build_selector)
platforms = [
('cp', 'manylinux_x86_64', options.manylinux_images['x86_64']),
@@ -274,7 +282,7 @@ def build(options: BuildOptions):
call(['docker', 'rm', '--force', '-v', container_name])
def troubleshoot(package_dir, error):
def troubleshoot(package_dir: str, error: Exception) -> None:
if (isinstance(error, subprocess.CalledProcessError) and 'exec' in error.cmd):
# the bash script failed
print('Checking for common errors...')
+19 -11
View File
@@ -4,19 +4,22 @@ import shutil
import subprocess
import sys
import tempfile
from collections import namedtuple
from glob import glob
from typing import Dict, List, Optional, NamedTuple, Union
from .environment import ParsedEnvironment
from .util import (
BuildOptions,
BuildSelector,
download,
get_build_verbosity_extra_flags,
prepare_command,
get_pip_script,
BuildOptions
prepare_command,
)
def call(args, env=None, cwd=None, shell=False):
def call(args: Union[str, List[str]], env: Optional[Dict[str, str]] = None, cwd: Optional[str] = None, shell: bool = False) -> int:
# print the command executing for the logs
if shell:
print('+ %s' % args)
@@ -26,8 +29,13 @@ def call(args, env=None, cwd=None, shell=False):
return subprocess.check_call(args, env=env, cwd=cwd, shell=shell)
def get_python_configurations(build_selector):
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'identifier', 'url'])
class PythonConfiguration(NamedTuple):
version: str
identifier: str
url: str
def get_python_configurations(build_selector: BuildSelector) -> List[PythonConfiguration]:
python_configurations = [
# CPython
PythonConfiguration(version='2.7', identifier='cp27-macosx_x86_64', url='https://www.python.org/ftp/python/2.7.18/python-2.7.18-macosx10.9.pkg'),
@@ -47,7 +55,7 @@ def get_python_configurations(build_selector):
SYMLINKS_DIR = '/tmp/cibw_bin'
def make_symlinks(installation_bin_path, python_executable, pip_executable):
def make_symlinks(installation_bin_path: str, python_executable: str, pip_executable: str) -> None:
assert os.path.exists(os.path.join(installation_bin_path, python_executable))
# Python bin folders on Mac don't symlink `python3` to `python`, and neither
@@ -62,7 +70,7 @@ def make_symlinks(installation_bin_path, python_executable, pip_executable):
os.symlink(os.path.join(installation_bin_path, pip_executable), os.path.join(SYMLINKS_DIR, 'pip'))
def install_cpython(version, url):
def install_cpython(version: str, url: str) -> str:
installed_system_packages = subprocess.check_output(['pkgutil', '--pkgs'], universal_newlines=True).splitlines()
# if this version of python isn't installed, get it from python.org and install
@@ -86,7 +94,7 @@ def install_cpython(version, url):
return installation_bin_path
def install_pypy(version, url):
def install_pypy(version: str, url: str) -> str:
pypy_tar_bz2 = url.rsplit('/', 1)[-1]
assert pypy_tar_bz2.endswith(".tar.bz2")
pypy_base_filename = os.path.splitext(os.path.splitext(pypy_tar_bz2)[0])[0]
@@ -103,7 +111,7 @@ def install_pypy(version, url):
return installation_bin_path
def setup_python(python_configuration, dependency_constraint_flags, environment):
def setup_python(python_configuration: PythonConfiguration, dependency_constraint_flags: List[str], environment: ParsedEnvironment) -> Dict[str, str]:
if python_configuration.identifier.startswith('cp'):
installation_bin_path = install_cpython(python_configuration.version, python_configuration.url)
elif python_configuration.identifier.startswith('pp'):
@@ -161,7 +169,7 @@ def setup_python(python_configuration, dependency_constraint_flags, environment)
return env
def build(options: BuildOptions):
def build(options: BuildOptions) -> None:
temp_dir = tempfile.mkdtemp(prefix='cibuildwheel')
built_wheel_dir = os.path.join(temp_dir, 'built_wheel')
repaired_wheel_dir = os.path.join(temp_dir, 'repaired_wheel')
+32 -36
View File
@@ -2,12 +2,13 @@ import os
import urllib.request
from fnmatch import fnmatch
from time import sleep
from typing import NamedTuple, List, Optional, Dict
from typing import Dict, List, NamedTuple, Optional
from .environment import ParsedEnvironment
def prepare_command(command, **kwargs):
def prepare_command(command: str, **kwargs: str) -> str:
'''
Preprocesses a command by expanding variables like {python}.
@@ -17,7 +18,7 @@ def prepare_command(command, **kwargs):
return command.format(python='python', pip='pip', **kwargs)
def get_build_verbosity_extra_flags(level):
def get_build_verbosity_extra_flags(level: int) -> List[str]:
if level > 0:
return ['-' + level * 'v']
elif level < 0:
@@ -27,37 +28,37 @@ def get_build_verbosity_extra_flags(level):
class BuildSelector:
def __init__(self, build_config, skip_config):
def __init__(self, build_config: str, skip_config: str):
self.build_patterns = build_config.split()
self.skip_patterns = skip_config.split()
def __call__(self, build_id):
def match_any(patterns):
def __call__(self, build_id: str) -> bool:
def match_any(patterns: List[str]) -> bool:
return any(fnmatch(build_id, pattern) for pattern in patterns)
return match_any(self.build_patterns) and not match_any(self.skip_patterns)
def __repr__(self):
def __repr__(self) -> str:
return 'BuildSelector({!r} - {!r})'.format(' '.join(self.build_patterns), ' '.join(self.skip_patterns))
# Taken from https://stackoverflow.com/a/107717
class Unbuffered:
def __init__(self, stream):
def __init__(self, stream): # type: ignore
self.stream = stream
def write(self, data):
def write(self, data): # type: ignore
self.stream.write(data)
self.stream.flush()
def writelines(self, datas):
def writelines(self, datas): # type: ignore
self.stream.writelines(datas)
self.stream.flush()
def __getattr__(self, attr):
def __getattr__(self, attr): # type: ignore
return getattr(self.stream, attr)
def download(url, dest):
def download(url: str, dest: str) -> None:
print('+ Download ' + url + ' to ' + dest)
dest_dir = os.path.dirname(dest)
if not os.path.exists(dest_dir):
@@ -82,17 +83,17 @@ def download(url, dest):
class DependencyConstraints:
def __init__(self, base_file_path):
def __init__(self, base_file_path: str):
assert os.path.exists(base_file_path)
self.base_file_path = os.path.abspath(base_file_path)
@classmethod
def with_defaults(cls):
return cls(
@staticmethod
def with_defaults() -> 'DependencyConstraints':
return DependencyConstraints(
base_file_path=os.path.join(os.path.dirname(__file__), 'resources', 'constraints.txt')
)
def get_for_python_version(self, version):
def get_for_python_version(self, version: str) -> str:
version_parts = version.split('.')
# try to find a version-specific dependency file e.g. if
@@ -106,26 +107,21 @@ class DependencyConstraints:
return self.base_file_path
BuildOptions = NamedTuple("BuildOptions", [
("package_dir", str),
("output_dir", str),
("test_command", Optional[str]),
("test_requires", List[str]),
("test_extras", str),
("before_build", Optional[str]),
("build_verbosity", int),
("build_selector", BuildSelector),
("repair_command", str),
("environment", ParsedEnvironment),
("before_test", str),
("dependency_constraints", Optional[DependencyConstraints]),
("manylinux_images", Optional[Dict[str, str]]),
])
class BuildOptions(NamedTuple):
package_dir: str
output_dir: str
test_command: Optional[str]
test_requires: List[str]
test_extras: str
before_build: Optional[str]
build_verbosity: int
build_selector: BuildSelector
repair_command: str
environment: ParsedEnvironment
before_test: str
dependency_constraints: Optional[DependencyConstraints]
manylinux_images: Optional[Dict[str, str]]
"""
Replace this definition with a class-style NamedTuple in the
PEP526 style when Python 3.5 host support is dropped
"""
resources_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), 'resources'))
get_pip_script = os.path.join(resources_dir, 'get-pip.py')
+22 -12
View File
@@ -3,16 +3,19 @@ import shutil
import subprocess
import sys
import tempfile
from collections import namedtuple
from glob import glob
from zipfile import ZipFile
from typing import Dict, List, Optional, NamedTuple
from .environment import ParsedEnvironment
from .util import (
BuildOptions,
BuildSelector,
download,
get_build_verbosity_extra_flags,
prepare_command,
get_pip_script,
BuildOptions
prepare_command,
)
@@ -20,20 +23,26 @@ IS_RUNNING_ON_AZURE = os.path.exists('C:\\hostedtoolcache')
IS_RUNNING_ON_TRAVIS = os.environ.get('TRAVIS_OS_NAME') == 'windows'
def shell(args, env=None, cwd=None):
def shell(args: List[str], env: Optional[Dict[str, str]] = None, cwd: Optional[str] = None) -> int:
print('+ ' + ' '.join(args))
return subprocess.check_call(' '.join(args), env=env, cwd=cwd, shell=True)
def get_nuget_args(version, arch):
def get_nuget_args(version: str, arch: str) -> List[str]:
python_name = 'python' if version[0] == '3' else 'python2'
if arch == '32':
python_name = python_name + 'x86'
return [python_name, '-Version', version, '-OutputDirectory', 'C:\\cibw\\python']
def get_python_configurations(build_selector):
PythonConfiguration = namedtuple('PythonConfiguration', ['version', 'arch', 'identifier', 'url'])
class PythonConfiguration(NamedTuple):
version: str
arch: str
identifier: str
url: Optional[str]
def get_python_configurations(build_selector: BuildSelector) -> List[PythonConfiguration]:
python_configurations = [
# CPython
PythonConfiguration(version='2.7.18', arch='32', identifier='cp27-win32', url=None),
@@ -62,19 +71,19 @@ def get_python_configurations(build_selector):
return python_configurations
def extract_zip(zip_src, dest):
def extract_zip(zip_src: str, dest: str) -> None:
with ZipFile(zip_src) as zip:
zip.extractall(dest)
def install_cpython(version, arch, nuget):
def install_cpython(version: str, arch: str, nuget: str) -> str:
nuget_args = get_nuget_args(version, arch)
installation_path = os.path.join(nuget_args[-1], nuget_args[0] + '.' + version, 'tools')
shell([nuget, 'install'] + nuget_args)
return installation_path
def install_pypy(version, arch, url):
def install_pypy(version: str, arch: str, url: str) -> str:
assert arch == '32'
# Inside the PyPy zip file is a directory with the same name
zip_filename = url.rsplit('/', 1)[-1]
@@ -89,7 +98,7 @@ def install_pypy(version, arch, url):
return installation_path
def setup_python(python_configuration, dependency_constraint_flags, environment):
def setup_python(python_configuration: PythonConfiguration, dependency_constraint_flags: List[str], environment: ParsedEnvironment) -> Dict[str, str]:
nuget = 'C:\\cibw\\nuget.exe'
if not os.path.exists(nuget):
download('https://dist.nuget.org/win-x86-commandline/latest/nuget.exe', nuget)
@@ -97,6 +106,7 @@ def setup_python(python_configuration, dependency_constraint_flags, environment)
if python_configuration.identifier.startswith('cp'):
installation_path = install_cpython(python_configuration.version, python_configuration.arch, nuget)
elif python_configuration.identifier.startswith('pp'):
assert python_configuration.url is not None
installation_path = install_pypy(python_configuration.version, python_configuration.arch, python_configuration.url)
else:
raise ValueError("Unknown Python implementation")
@@ -141,7 +151,7 @@ def setup_python(python_configuration, dependency_constraint_flags, environment)
return env
def build(options: BuildOptions):
def build(options: BuildOptions) -> None:
temp_dir = tempfile.mkdtemp(prefix='cibuildwheel')
built_wheel_dir = os.path.join(temp_dir, 'built_wheel')
repaired_wheel_dir = os.path.join(temp_dir, 'repaired_wheel')
-1
View File
@@ -7,7 +7,6 @@
language: python
python:
- 3.5
- 3.6
- 3.7
- 3.8
+4
View File
@@ -11,3 +11,7 @@ exclude =
env??/,
.venv/,
site/
[mypy]
files=cibuildwheel/
strict=True
+1 -1
View File
@@ -28,7 +28,7 @@ setup(
'cibuildwheel': ['resources/*'],
},
# Supported python versions
python_requires='>=3.5',
python_requires='>=3.6',
keywords='ci wheel packaging pypi travis appveyor macos linux windows',
classifiers=[
'Intended Audience :: Developers',