Files
cibuildwheel/cibuildwheel/logger.py
T

258 lines
7.3 KiB
Python
Raw Normal View History

2020-11-13 18:37:30 +00:00
import codecs
2020-08-03 17:38:07 +01:00
import os
import re
2020-11-13 16:30:27 +00:00
import sys
import time
2020-11-01 21:36:44 +00:00
from typing import Optional, Union
2020-08-03 17:38:07 +01:00
2020-11-01 16:57:07 +00:00
DEFAULT_FOLD_PATTERN = ('{name}', '')
2020-08-03 17:38:07 +01:00
FOLD_PATTERNS = {
2020-11-01 16:57:07 +00:00
'azure': ('##[group]{name}', '##[endgroup]'),
'travis': ('travis_fold:start:{identifier}\n{name}', 'travis_fold:end:{identifier}'),
2020-11-01 16:57:07 +00:00
'github': ('::group::{name}', '::endgroup::{name}'),
2020-08-03 17:38:07 +01:00
}
PLATFORM_IDENTIFIER_DESCIPTIONS = {
2020-11-04 11:11:42 +00:00
'manylinux_x86_64': 'manylinux x86_64',
'manylinux_i686': 'manylinux i686',
'manylinux_aarch64': 'manylinux aarch64',
'manylinux_ppc64le': 'manylinux ppc64le',
'manylinux_s390x': 'manylinux s390x',
2020-08-03 17:38:07 +01:00
'win32': 'Windows 32bit',
'win_amd64': 'Windows 64bit',
'macosx_x86_64': 'macOS x86_64',
}
class Logger:
2020-11-01 11:34:02 +00:00
fold_mode: str
colors_enabled: bool
2020-11-13 18:37:30 +00:00
unicode_enabled: bool
2020-11-01 11:34:02 +00:00
active_build_identifier: Optional[str] = None
2020-11-01 11:43:28 +00:00
build_start_time: Optional[float] = None
step_start_time: Optional[float] = None
active_fold_group_name: Optional[str] = None
2020-11-01 11:34:02 +00:00
2020-08-03 17:38:07 +01:00
def __init__(self):
if sys.platform == 'win32' and hasattr(sys.stdout, 'reconfigure'):
# the encoding on Windows can be a 1-byte charmap, but all CIs
# support utf8, so we hardcode that
sys.stdout.reconfigure(encoding='utf8')
2020-11-13 18:37:30 +00:00
self.unicode_enabled = file_supports_unicode(sys.stdout)
2020-08-03 17:38:07 +01:00
if 'AZURE_HTTP_USER_AGENT' in os.environ:
self.fold_mode = 'azure'
self.colors_enabled = True
elif 'GITHUB_ACTIONS' in os.environ:
self.fold_mode = 'github'
self.colors_enabled = True
elif 'TRAVIS' in os.environ:
self.fold_mode = 'travis'
self.colors_enabled = True
elif 'APPVEYOR' in os.environ:
self.fold_mode = 'disabled'
self.colors_enabled = True
else:
self.fold_mode = 'disabled'
2020-11-01 16:57:07 +00:00
self.colors_enabled = file_supports_color(sys.stdout)
2020-08-03 17:38:07 +01:00
2020-11-01 11:34:02 +00:00
def build_start(self, identifier: str):
self.step_end()
2020-08-03 17:38:07 +01:00
c = self.colors
2020-11-01 12:33:14 +00:00
description = build_description_from_identifier(identifier)
2020-11-01 11:34:02 +00:00
print()
2020-11-01 12:33:14 +00:00
print(f'{c.bold}{c.blue}Building {identifier} wheel{c.end}')
print(f'{description}')
2020-11-01 11:34:02 +00:00
print()
2020-08-03 17:38:07 +01:00
2020-11-01 11:34:02 +00:00
self.build_start_time = time.time()
self.active_build_identifier = identifier
def build_end(self):
assert self.build_start_time is not None
2020-11-01 15:19:42 +00:00
assert self.active_build_identifier is not None
2020-11-01 11:43:28 +00:00
self.step_end()
2020-08-03 17:38:07 +01:00
2020-11-01 15:19:42 +00:00
c = self.colors
2020-11-13 18:37:30 +00:00
s = self.symbols
2020-11-01 15:19:42 +00:00
duration = time.time() - self.build_start_time
2020-11-01 11:34:02 +00:00
print()
2020-11-13 18:37:30 +00:00
print(f'{c.green}{s.done} {c.end}{self.active_build_identifier} finished in {duration:.2f}s')
2020-11-01 11:34:02 +00:00
self.build_start_time = None
2020-11-01 15:19:42 +00:00
self.active_build_identifier = None
2020-08-03 17:38:07 +01:00
2020-11-01 11:43:28 +00:00
def step(self, step_description: str):
self.step_end()
2020-11-01 11:34:02 +00:00
self.step_start_time = time.time()
2020-11-01 21:36:44 +00:00
self._start_fold_group(step_description)
2020-08-03 17:38:07 +01:00
2020-11-01 21:36:44 +00:00
def step_end(self, success=True):
2020-11-01 11:34:02 +00:00
if self.step_start_time is not None:
2020-11-01 21:36:44 +00:00
self._end_fold_group()
2020-11-01 11:34:02 +00:00
c = self.colors
2020-11-13 18:37:30 +00:00
s = self.symbols
2020-11-01 11:34:02 +00:00
duration = time.time() - self.step_start_time
2020-11-01 21:36:44 +00:00
if success:
2020-11-13 18:37:30 +00:00
print(f'{c.green}{s.done} {c.end}{duration:.2f}s'.rjust(78))
2020-11-01 21:36:44 +00:00
else:
2020-11-13 18:37:30 +00:00
print(f'{c.red}{s.error} {c.end}{duration:.2f}s'.rjust(78))
2020-11-01 21:36:44 +00:00
2020-11-01 11:34:02 +00:00
self.step_start_time = None
2020-11-01 21:36:44 +00:00
def error(self, error: Union[Exception, str]):
self.step_end(success=False)
print()
if self.fold_mode == 'github':
print(f'::error::{error}')
else:
c = self.colors
print(f'{c.bright_red}Error{c.end} {error}')
def _start_fold_group(self, name: str):
self._end_fold_group()
2020-11-01 11:43:28 +00:00
self.active_fold_group_name = name
2020-11-01 16:57:07 +00:00
fold_start_pattern = FOLD_PATTERNS.get(self.fold_mode, DEFAULT_FOLD_PATTERN)[0]
identifier = self._fold_group_identifier(name)
2020-11-01 12:00:59 +00:00
print(fold_start_pattern.format(name=self.active_fold_group_name, identifier=identifier))
2020-11-01 11:34:02 +00:00
2020-11-01 21:36:44 +00:00
def _end_fold_group(self):
2020-11-01 11:43:28 +00:00
if self.active_fold_group_name:
2020-11-01 16:57:07 +00:00
fold_start_pattern = FOLD_PATTERNS.get(self.fold_mode, DEFAULT_FOLD_PATTERN)[1]
identifier = self._fold_group_identifier(self.active_fold_group_name)
print(fold_start_pattern.format(name=self.active_fold_group_name, identifier=identifier))
2020-11-01 12:03:21 +00:00
sys.stdout.flush()
2020-11-01 11:43:28 +00:00
self.active_fold_group_name = None
2020-08-03 17:38:07 +01:00
def _fold_group_identifier(self, name: str):
'''
Travis doesn't like fold groups identifiers that have spaces in. This
method converts them to ascii identifiers
'''
2020-11-13 16:46:58 +00:00
# whitespace to underscores
identifier = re.sub(r'\s+', '_', name)
# remove non-alphanum
2020-11-13 16:46:58 +00:00
identifier = re.sub(r'[^A-Za-z\d_]+', '', identifier)
# trim underscores
identifier = identifier.strip('_')
# lowercase, shorten
return identifier.lower()[:20]
2020-08-03 17:38:07 +01:00
@property
def colors(self):
if self.colors_enabled:
2020-11-13 18:37:30 +00:00
return Colors.enabled
2020-08-03 17:38:07 +01:00
else:
2020-11-13 18:37:30 +00:00
return Colors.disabled
@property
def symbols(self):
if self.unicode_enabled:
return Symbols.unicode
else:
return Symbols.ascii
2020-08-03 17:38:07 +01:00
2020-11-01 11:34:02 +00:00
def build_description_from_identifier(identifier: str):
2020-08-03 17:38:07 +01:00
python_identifier, _, platform_identifier = identifier.partition('-')
build_description = ''
python_interpreter = python_identifier[0:2]
python_version = python_identifier[2:4]
if python_interpreter == 'cp':
build_description += 'CPython'
elif python_interpreter == 'pp':
build_description += 'PyPy'
else:
raise Exception('unknown python')
build_description += f' {python_version[0]}.{python_version[1]} '
try:
build_description += PLATFORM_IDENTIFIER_DESCIPTIONS[platform_identifier]
except KeyError as e:
raise Exception('unknown platform') from e
return build_description
2020-11-13 18:37:30 +00:00
class Colors:
class Enabled:
red = '\033[31m'
green = '\033[32m'
yellow = '\033[33m'
blue = '\033[34m'
cyan = '\033[36m'
bright_red = '\033[91m'
bright_green = '\033[92m'
white = '\033[37m\033[97m'
2020-08-03 17:38:07 +01:00
2020-11-13 18:37:30 +00:00
bg_grey = '\033[48;5;235m'
2020-08-03 17:38:07 +01:00
2020-11-13 18:37:30 +00:00
bold = '\033[1m'
faint = '\033[2m'
2020-08-03 17:38:07 +01:00
2020-11-13 18:37:30 +00:00
end = '\033[0m'
2020-08-03 17:38:07 +01:00
class Disabled:
2020-11-13 18:37:30 +00:00
def __getattr__(self, attr: str) -> str:
2020-08-03 17:38:07 +01:00
return ''
2020-11-13 18:37:30 +00:00
enabled = Enabled()
disabled = Disabled()
2020-08-03 17:38:07 +01:00
2020-11-13 18:37:30 +00:00
class Symbols:
class Unicode:
done = '✓'
error = '✕'
class Ascii:
done = 'done'
error = 'failed'
unicode = Unicode()
ascii = Ascii()
2020-11-01 16:57:07 +00:00
def file_supports_color(file_obj):
"""
Returns True if the running system's terminal supports color.
"""
plat = sys.platform
supported_platform = (plat != 'win32' or 'ANSICON' in os.environ)
is_a_tty = file_is_a_tty(file_obj)
return (supported_platform and is_a_tty)
def file_is_a_tty(file_obj):
return hasattr(file_obj, 'isatty') and file_obj.isatty()
2020-11-13 18:37:30 +00:00
def file_supports_unicode(file_obj):
encoding = getattr(file_obj, 'encoding', None)
if not encoding:
return False
codec_info = codecs.lookup(encoding)
return ('utf' in codec_info.name)
'''
Global instance of the Logger.
'''
# (there's only one stdout per-process, so a global instance is justified)
log = Logger()