52
52
from mesonpy ._compat import cached_property , read_binary
53
53
54
54
55
- _MESON_ARGS_KEYS = ['dist' , 'setup' , 'compile' , 'install' ]
56
-
57
55
if typing .TYPE_CHECKING : # pragma: no cover
58
56
from typing import Any , Callable , DefaultDict , Dict , List , Literal , Optional , Sequence , TextIO , Tuple , Type , TypeVar , Union
59
57
69
67
__version__ = '0.15.0.dev0'
70
68
71
69
72
- _COLORS = {
73
- 'red' : '\33 [31m' ,
74
- 'cyan' : '\33 [36m' ,
75
- 'yellow' : '\33 [93m' ,
76
- 'light_blue' : '\33 [94m' ,
77
- 'bold' : '\33 [1m' ,
78
- 'dim' : '\33 [2m' ,
79
- 'underline' : '\33 [4m' ,
80
- 'reset' : '\33 [0m' ,
81
- }
82
- _NO_COLORS = {color : '' for color in _COLORS }
83
-
84
70
_NINJA_REQUIRED_VERSION = '1.8.2'
85
71
_MESON_REQUIRED_VERSION = '0.63.3' # keep in sync with the version requirement in pyproject.toml
86
72
87
-
88
- def _init_colors () -> Dict [str , str ]:
89
- """Detect if we should be using colors in the output. We will enable colors
90
- if running in a TTY, and no environment variable overrides it. Setting the
91
- NO_COLOR (https://no-color.org/) environment variable force-disables colors,
92
- and FORCE_COLOR forces color to be used, which is useful for thing like
93
- Github actions.
94
- """
95
- if 'NO_COLOR' in os .environ :
96
- if 'FORCE_COLOR' in os .environ :
97
- warnings .warn (
98
- 'Both NO_COLOR and FORCE_COLOR environment variables are set, disabling color' ,
99
- stacklevel = 1 ,
100
- )
101
- return _NO_COLORS
102
- elif 'FORCE_COLOR' in os .environ or sys .stdout .isatty ():
103
- return _COLORS
104
- return _NO_COLORS
105
-
106
-
107
- _STYLES = _init_colors () # holds the color values, should be _COLORS or _NO_COLORS
108
-
73
+ _MESON_ARGS_KEYS = ['dist' , 'setup' , 'compile' , 'install' ]
109
74
110
75
_SUFFIXES = importlib .machinery .all_suffixes ()
111
76
_EXTENSION_SUFFIX_REGEX = re .compile (r'^[^.]+\.(?:(?P<abi>[^.]+)\.)?(?:so|pyd|dll)$' )
112
77
assert all (re .match (_EXTENSION_SUFFIX_REGEX , f'foo{ x } ' ) for x in importlib .machinery .EXTENSION_SUFFIXES )
113
78
114
-
115
79
# Map Meson installation path placeholders to wheel installation paths.
116
80
# See https://docs.python.org/3/library/sysconfig.html#installation-paths
117
81
_INSTALLATION_PATH_MAP = {
@@ -178,29 +142,40 @@ def _map_to_wheel(sources: Dict[str, Dict[str, Any]]) -> DefaultDict[str, List[T
178
142
return wheel_files
179
143
180
144
181
- def _is_native (file : Path ) -> bool :
182
- """Check if file is a native file."""
145
+ class style :
146
+ ERROR = '\33 [31m' , # red
147
+ WARNING = '\33 [93m' # bright yellow
148
+ INFO = '\33 [36m\33 [1m' # cyan, bold
149
+ RESET = '\33 [0m'
183
150
184
- with open (file , 'rb' ) as f :
185
- if sys .platform == 'linux' :
186
- return f .read (4 ) == b'\x7f ELF' # ELF
187
- elif sys .platform == 'darwin' :
188
- return f .read (4 ) in (
189
- b'\xfe \xed \xfa \xce ' , # 32-bit
190
- b'\xfe \xed \xfa \xcf ' , # 64-bit
191
- b'\xcf \xfa \xed \xfe ' , # arm64
192
- b'\xca \xfe \xba \xbe ' , # universal / fat (same as java class so beware!)
193
- )
194
- elif sys .platform == 'win32' :
195
- return f .read (2 ) == b'MZ'
151
+ @staticmethod
152
+ def strip (string : str ) -> str :
153
+ """Strip ANSI escape sequences from string."""
154
+ return re .sub (r'\033\[[;?0-9]*[a-zA-Z]' , '' , string )
196
155
197
- # For unknown platforms, check for file extensions.
198
- _ , ext = os .path .splitext (file )
199
- if ext in ('.so' , '.a' , '.out' , '.exe' , '.dll' , '.dylib' , '.pyd' ):
156
+
157
+ @functools .lru_cache ()
158
+ def _use_ansi_colors () -> bool :
159
+ """Determine whether logging should use ANSI color escapes."""
160
+ if 'NO_COLOR' in os .environ :
161
+ return False
162
+ if 'FORCE_COLOR' in os .environ or sys .stdout .isatty () and os .environ .get ('TERM' ) != 'dumb' :
163
+ try :
164
+ import colorama
165
+ except ModuleNotFoundError :
166
+ pass
167
+ else :
168
+ colorama .init ()
200
169
return True
201
170
return False
202
171
203
172
173
+ def _log (string : str , ** kwargs : Any ) -> None :
174
+ if not _use_ansi_colors ():
175
+ string = style .strip (string )
176
+ print (string , ** kwargs )
177
+
178
+
204
179
def _showwarning (
205
180
message : Union [Warning , str ],
206
181
category : Type [Warning ],
@@ -210,21 +185,7 @@ def _showwarning(
210
185
line : Optional [str ] = None ,
211
186
) -> None : # pragma: no cover
212
187
"""Callable to override the default warning handler, to have colored output."""
213
- print ('{yellow}meson-python: warning:{reset} {}' .format (message , ** _STYLES ))
214
-
215
-
216
- def _setup_cli () -> None :
217
- """Setup CLI stuff (eg. handlers, hooks, etc.). Should only be called when
218
- actually we are in control of the CLI, not on a normal import.
219
- """
220
- warnings .showwarning = _showwarning
221
-
222
- try : # pragma: no cover
223
- import colorama
224
- except ModuleNotFoundError : # pragma: no cover
225
- pass
226
- else : # pragma: no cover
227
- colorama .init () # fix colors on windows
188
+ _log (f'{ style .WARNING } meson-python: warning:{ style .RESET } { message } ' )
228
189
229
190
230
191
class Error (RuntimeError ):
@@ -273,6 +234,27 @@ def _update_dynamic(self, value: Any) -> None:
273
234
self .dynamic .remove ('version' )
274
235
275
236
237
+ def _is_native (file : Path ) -> bool :
238
+ """Check if file is a native file."""
239
+
240
+ with open (file , 'rb' ) as f :
241
+ if sys .platform == 'linux' :
242
+ return f .read (4 ) == b'\x7f ELF' # ELF
243
+ elif sys .platform == 'darwin' :
244
+ return f .read (4 ) in (
245
+ b'\xfe \xed \xfa \xce ' , # 32-bit
246
+ b'\xfe \xed \xfa \xcf ' , # 64-bit
247
+ b'\xcf \xfa \xed \xfe ' , # arm64
248
+ b'\xca \xfe \xba \xbe ' , # universal / fat (same as java class so beware!)
249
+ )
250
+ elif sys .platform == 'win32' :
251
+ return f .read (2 ) == b'MZ'
252
+
253
+ # For unknown platforms, check for file extensions.
254
+ _ , ext = os .path .splitext (file )
255
+ return ext in ('.so' , '.a' , '.out' , '.exe' , '.dll' , '.dylib' , '.pyd' )
256
+
257
+
276
258
class _WheelBuilder ():
277
259
"""Helper class to build wheels from projects."""
278
260
@@ -729,7 +711,7 @@ def _run(self, cmd: Sequence[str]) -> None:
729
711
# Flush the line to ensure that the log line with the executed
730
712
# command line appears before the command output. Without it,
731
713
# the lines appear in the wrong order in pip output.
732
- print ('{cyan}{bold} + {}{reset }' .format (' ' .join (cmd ), ** _STYLES ), flush = True )
714
+ _log ('{style.INFO} + {cmd}{style.RESET }' .format (style = style , cmd = ' ' .join (cmd )), flush = True )
733
715
r = subprocess .run (cmd , cwd = self ._build_dir )
734
716
if r .returncode != 0 :
735
717
raise SystemExit (r .returncode )
@@ -991,11 +973,12 @@ def _add_ignore_files(directory: pathlib.Path) -> None:
991
973
def _pyproject_hook (func : Callable [P , T ]) -> Callable [P , T ]:
992
974
@functools .wraps (func )
993
975
def wrapper (* args : P .args , ** kwargs : P .kwargs ) -> T :
976
+ warnings .showwarning = _showwarning
994
977
try :
995
978
return func (* args , ** kwargs )
996
979
except (Error , pyproject_metadata .ConfigurationError ) as exc :
997
- prefix = '{red }meson-python: error:{reset } '. format ( ** _STYLES )
998
- print ('\n ' + textwrap .indent (str (exc ), prefix ))
980
+ prefix = f' { style . ERROR } meson-python: error:{ style . RESET } '
981
+ _log ('\n ' + textwrap .indent (str (exc ), prefix ))
999
982
raise SystemExit (1 ) from exc
1000
983
return wrapper
1001
984
@@ -1010,18 +993,6 @@ def get_requires_for_build_sdist(config_settings: Optional[Dict[str, str]] = Non
1010
993
return dependencies
1011
994
1012
995
1013
- @_pyproject_hook
1014
- def build_sdist (
1015
- sdist_directory : str ,
1016
- config_settings : Optional [Dict [Any , Any ]] = None ,
1017
- ) -> str :
1018
- _setup_cli ()
1019
-
1020
- out = pathlib .Path (sdist_directory )
1021
- with _project (config_settings ) as project :
1022
- return project .sdist (out ).name
1023
-
1024
-
1025
996
@_pyproject_hook
1026
997
def get_requires_for_build_wheel (config_settings : Optional [Dict [str , str ]] = None ) -> List [str ]:
1027
998
dependencies = []
@@ -1035,13 +1006,26 @@ def get_requires_for_build_wheel(config_settings: Optional[Dict[str, str]] = Non
1035
1006
return dependencies
1036
1007
1037
1008
1009
+ get_requires_for_build_editable = get_requires_for_build_wheel
1010
+
1011
+
1038
1012
@_pyproject_hook
1039
- def build_wheel (
1040
- wheel_directory : str ,
1013
+ def build_sdist (
1014
+ sdist_directory : str ,
1041
1015
config_settings : Optional [Dict [Any , Any ]] = None ,
1016
+ ) -> str :
1017
+
1018
+ out = pathlib .Path (sdist_directory )
1019
+ with _project (config_settings ) as project :
1020
+ return project .sdist (out ).name
1021
+
1022
+
1023
+ @_pyproject_hook
1024
+ def build_wheel (
1025
+ wheel_directory : str , config_settings :
1026
+ Optional [Dict [Any , Any ]] = None ,
1042
1027
metadata_directory : Optional [str ] = None ,
1043
1028
) -> str :
1044
- _setup_cli ()
1045
1029
1046
1030
out = pathlib .Path (wheel_directory )
1047
1031
with _project (config_settings ) as project :
@@ -1054,7 +1038,6 @@ def build_editable(
1054
1038
config_settings : Optional [Dict [Any , Any ]] = None ,
1055
1039
metadata_directory : Optional [str ] = None ,
1056
1040
) -> str :
1057
- _setup_cli ()
1058
1041
1059
1042
# Force set a permanent build directory.
1060
1043
if not config_settings :
@@ -1069,10 +1052,3 @@ def build_editable(
1069
1052
out = pathlib .Path (wheel_directory )
1070
1053
with _project (config_settings ) as project :
1071
1054
return project .editable (out ).name
1072
-
1073
-
1074
- @_pyproject_hook
1075
- def get_requires_for_build_editable (
1076
- config_settings : Optional [Dict [str , str ]] = None ,
1077
- ) -> List [str ]:
1078
- return get_requires_for_build_wheel ()
0 commit comments