5252from mesonpy ._compat import cached_property , read_binary
5353
5454
55- _MESON_ARGS_KEYS = ['dist' , 'setup' , 'compile' , 'install' ]
56-
5755if typing .TYPE_CHECKING : # pragma: no cover
5856 from typing import Any , Callable , DefaultDict , Dict , List , Literal , Optional , Sequence , TextIO , Tuple , Type , TypeVar , Union
5957
6967__version__ = '0.15.0.dev0'
7068
7169
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-
8470_NINJA_REQUIRED_VERSION = '1.8.2'
8571_MESON_REQUIRED_VERSION = '0.63.3' # keep in sync with the version requirement in pyproject.toml
8672
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' ]
10974
11075_SUFFIXES = importlib .machinery .all_suffixes ()
11176_EXTENSION_SUFFIX_REGEX = re .compile (r'^[^.]+\.(?:(?P<abi>[^.]+)\.)?(?:so|pyd|dll)$' )
11277assert all (re .match (_EXTENSION_SUFFIX_REGEX , f'foo{ x } ' ) for x in importlib .machinery .EXTENSION_SUFFIXES )
11378
114-
11579# Map Meson installation path placeholders to wheel installation paths.
11680# See https://docs.python.org/3/library/sysconfig.html#installation-paths
11781_INSTALLATION_PATH_MAP = {
@@ -178,29 +142,40 @@ def _map_to_wheel(sources: Dict[str, Dict[str, Any]]) -> DefaultDict[str, List[T
178142 return wheel_files
179143
180144
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'
183150
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 )
196155
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 ()
200169 return True
201170 return False
202171
203172
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+
204179def _showwarning (
205180 message : Union [Warning , str ],
206181 category : Type [Warning ],
@@ -210,21 +185,7 @@ def _showwarning(
210185 line : Optional [str ] = None ,
211186) -> None : # pragma: no cover
212187 """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 } ' )
228189
229190
230191class Error (RuntimeError ):
@@ -273,6 +234,27 @@ def _update_dynamic(self, value: Any) -> None:
273234 self .dynamic .remove ('version' )
274235
275236
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+
276258class _WheelBuilder ():
277259 """Helper class to build wheels from projects."""
278260
@@ -729,7 +711,7 @@ def _run(self, cmd: Sequence[str]) -> None:
729711 # Flush the line to ensure that the log line with the executed
730712 # command line appears before the command output. Without it,
731713 # 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 )
733715 r = subprocess .run (cmd , cwd = self ._build_dir )
734716 if r .returncode != 0 :
735717 raise SystemExit (r .returncode )
@@ -991,11 +973,12 @@ def _add_ignore_files(directory: pathlib.Path) -> None:
991973def _pyproject_hook (func : Callable [P , T ]) -> Callable [P , T ]:
992974 @functools .wraps (func )
993975 def wrapper (* args : P .args , ** kwargs : P .kwargs ) -> T :
976+ warnings .showwarning = _showwarning
994977 try :
995978 return func (* args , ** kwargs )
996979 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 ))
999982 raise SystemExit (1 ) from exc
1000983 return wrapper
1001984
@@ -1010,18 +993,6 @@ def get_requires_for_build_sdist(config_settings: Optional[Dict[str, str]] = Non
1010993 return dependencies
1011994
1012995
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-
1025996@_pyproject_hook
1026997def get_requires_for_build_wheel (config_settings : Optional [Dict [str , str ]] = None ) -> List [str ]:
1027998 dependencies = []
@@ -1035,13 +1006,26 @@ def get_requires_for_build_wheel(config_settings: Optional[Dict[str, str]] = Non
10351006 return dependencies
10361007
10371008
1009+ get_requires_for_build_editable = get_requires_for_build_wheel
1010+
1011+
10381012@_pyproject_hook
1039- def build_wheel (
1040- wheel_directory : str ,
1013+ def build_sdist (
1014+ sdist_directory : str ,
10411015 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 ,
10421027 metadata_directory : Optional [str ] = None ,
10431028) -> str :
1044- _setup_cli ()
10451029
10461030 out = pathlib .Path (wheel_directory )
10471031 with _project (config_settings ) as project :
@@ -1054,7 +1038,6 @@ def build_editable(
10541038 config_settings : Optional [Dict [Any , Any ]] = None ,
10551039 metadata_directory : Optional [str ] = None ,
10561040) -> str :
1057- _setup_cli ()
10581041
10591042 # Force set a permanent build directory.
10601043 if not config_settings :
@@ -1069,10 +1052,3 @@ def build_editable(
10691052 out = pathlib .Path (wheel_directory )
10701053 with _project (config_settings ) as project :
10711054 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