Util.py (SCons-4.3.0) | : | Util.py (SCons-4.4.0) | ||
---|---|---|---|---|
skipping to change at line 32 | skipping to change at line 32 | |||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||
"""Various SCons utility functions.""" | """Various SCons utility functions.""" | |||
import copy | import copy | |||
import hashlib | import hashlib | |||
import os | import os | |||
import pprint | import pprint | |||
import re | import re | |||
import sys | import sys | |||
import time | ||||
from collections import UserDict, UserList, UserString, OrderedDict | from collections import UserDict, UserList, UserString, OrderedDict | |||
from collections.abc import MappingView | from collections.abc import MappingView | |||
from contextlib import suppress | from contextlib import suppress | |||
from types import MethodType, FunctionType | from types import MethodType, FunctionType | |||
from typing import Optional, Union | from typing import Optional, Union | |||
# Note: Util module cannot import other bits of SCons globally without getting | # Note: Util module cannot import other bits of SCons globally without getting | |||
# into import loops. Both the below modules import SCons.Util early on. | # into import loops. Both the below modules import SCons.Util early on. | |||
# --> SCons.Warnings | # --> SCons.Warnings | |||
# --> SCons.Errors | # --> SCons.Errors | |||
skipping to change at line 54 | skipping to change at line 55 | |||
PYPY = hasattr(sys, 'pypy_translation_info') | PYPY = hasattr(sys, 'pypy_translation_info') | |||
# this string will be hashed if a Node refers to a file that doesn't exist | # this string will be hashed if a Node refers to a file that doesn't exist | |||
# in order to distinguish from a file that exists but is empty. | # in order to distinguish from a file that exists but is empty. | |||
NOFILE = "SCONS_MAGIC_MISSING_FILE_STRING" | NOFILE = "SCONS_MAGIC_MISSING_FILE_STRING" | |||
# unused? | # unused? | |||
def dictify(keys, values, result=None) -> dict: | def dictify(keys, values, result=None) -> dict: | |||
if result is None: | if result is None: | |||
result = {} | result = {} | |||
result.update(dict(zip(keys, values))) | result.update(zip(keys, values)) | |||
return result | return result | |||
_ALTSEP = os.altsep | _ALTSEP = os.altsep | |||
if _ALTSEP is None and sys.platform == 'win32': | if _ALTSEP is None and sys.platform == 'win32': | |||
# My ActivePython 2.0.1 doesn't set os.altsep! What gives? | # My ActivePython 2.0.1 doesn't set os.altsep! What gives? | |||
_ALTSEP = '/' | _ALTSEP = '/' | |||
if _ALTSEP: | if _ALTSEP: | |||
def rightmost_separator(path, sep): | def rightmost_separator(path, sep): | |||
return max(path.rfind(sep), path.rfind(_ALTSEP)) | return max(path.rfind(sep), path.rfind(_ALTSEP)) | |||
else: | else: | |||
def rightmost_separator(path, sep): | def rightmost_separator(path, sep): | |||
return path.rfind(sep) | return path.rfind(sep) | |||
# First two from the Python Cookbook, just for completeness. | # First two from the Python Cookbook, just for completeness. | |||
# (Yeah, yeah, YAGNI...) | # (Yeah, yeah, YAGNI...) | |||
def containsAny(s, pat) -> bool: | def containsAny(s, pat) -> bool: | |||
"""Check whether string `s` contains ANY of the items in `pat`.""" | """Check whether string `s` contains ANY of the items in `pat`.""" | |||
for c in pat: | return any(c in s for c in pat) | |||
if c in s: | ||||
return True | ||||
return False | ||||
def containsAll(s, pat) -> bool: | def containsAll(s, pat) -> bool: | |||
"""Check whether string `s` contains ALL of the items in `pat`.""" | """Check whether string `s` contains ALL of the items in `pat`.""" | |||
for c in pat: | return all(c in s for c in pat) | |||
if c not in s: | ||||
return False | ||||
return True | ||||
def containsOnly(s, pat) -> bool: | def containsOnly(s, pat) -> bool: | |||
"""Check whether string `s` contains ONLY items in `pat`.""" | """Check whether string `s` contains ONLY items in `pat`.""" | |||
for c in s: | for c in s: | |||
if c not in pat: | if c not in pat: | |||
return False | return False | |||
return True | return True | |||
# TODO: Verify this method is STILL faster than os.path.splitext | # TODO: Verify this method is STILL faster than os.path.splitext | |||
def splitext(path) -> tuple: | def splitext(path) -> tuple: | |||
"""Split `path` into a (root, ext) pair. | """Split `path` into a (root, ext) pair. | |||
Same as :mod:`os.path.splitext` but faster. | Same as :mod:`os.path.splitext` but faster. | |||
""" | """ | |||
sep = rightmost_separator(path, os.sep) | sep = rightmost_separator(path, os.sep) | |||
dot = path.rfind('.') | dot = path.rfind('.') | |||
# An ext is only real if it has at least one non-digit char | # An ext is only real if it has at least one non-digit char | |||
if dot > sep and not containsOnly(path[dot:], "0123456789."): | if dot > sep and not path[dot + 1:].isdigit(): | |||
return path[:dot], path[dot:] | return path[:dot], path[dot:] | |||
return path, "" | return path, "" | |||
def updrive(path) -> str: | def updrive(path) -> str: | |||
"""Make the drive letter (if any) upper case. | """Make the drive letter (if any) upper case. | |||
This is useful because Windows is inconsistent on the case | This is useful because Windows is inconsistent on the case | |||
of the drive letter, which can cause inconsistencies when | of the drive letter, which can cause inconsistencies when | |||
calculating command signatures. | calculating command signatures. | |||
skipping to change at line 1198 | skipping to change at line 1193 | |||
def __add__(self, other): | def __add__(self, other): | |||
return super().__add__(CLVar(other)) | return super().__add__(CLVar(other)) | |||
def __radd__(self, other): | def __radd__(self, other): | |||
return super().__radd__(CLVar(other)) | return super().__radd__(CLVar(other)) | |||
def __iadd__(self, other): | def __iadd__(self, other): | |||
return super().__iadd__(CLVar(other)) | return super().__iadd__(CLVar(other)) | |||
def __str__(self): | def __str__(self): | |||
return ' '.join(self.data) | # Some cases the data can contain Nodes, so make sure they | |||
# processed to string before handing them over to join. | ||||
return ' '.join([str(d) for d in self.data]) | ||||
class Selector(OrderedDict): | class Selector(OrderedDict): | |||
"""A callable ordered dictionary that maps file suffixes to | """A callable ordered dictionary that maps file suffixes to | |||
dictionary values. We preserve the order in which items are added | dictionary values. We preserve the order in which items are added | |||
so that :func:`get_suffix` calls always return the first suffix added. | so that :func:`get_suffix` calls always return the first suffix added. | |||
""" | """ | |||
def __call__(self, env, source, ext=None): | def __call__(self, env, source, ext=None): | |||
if ext is None: | if ext is None: | |||
try: | try: | |||
ext = source[0].get_suffix() | ext = source[0].get_suffix() | |||
skipping to change at line 2076 | skipping to change at line 2073 | |||
Conversion is the same as for :func:`get_env_bool`. | Conversion is the same as for :func:`get_env_bool`. | |||
""" | """ | |||
return get_env_bool(os.environ, name, default) | return get_env_bool(os.environ, name, default) | |||
def print_time(): | def print_time(): | |||
"""Hack to return a value from Main if can't import Main.""" | """Hack to return a value from Main if can't import Main.""" | |||
# pylint: disable=redefined-outer-name,import-outside-toplevel | # pylint: disable=redefined-outer-name,import-outside-toplevel | |||
from SCons.Script.Main import print_time | from SCons.Script.Main import print_time | |||
return print_time | return print_time | |||
def wait_for_process_to_die(pid): | ||||
""" | ||||
Wait for specified process to die, or alternatively kill it | ||||
NOTE: This function operates best with psutil pypi package | ||||
TODO: Add timeout which raises exception | ||||
""" | ||||
# wait for the process to fully killed | ||||
try: | ||||
import psutil | ||||
while True: | ||||
if pid not in [proc.pid for proc in psutil.process_iter()]: | ||||
break | ||||
else: | ||||
time.sleep(0.1) | ||||
except ImportError: | ||||
# if psutil is not installed we can do this the hard way | ||||
while True: | ||||
if sys.platform == 'win32': | ||||
import ctypes | ||||
PROCESS_QUERY_INFORMATION = 0x1000 | ||||
processHandle = ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY | ||||
_INFORMATION, 0,pid) | ||||
if processHandle == 0: | ||||
break | ||||
else: | ||||
ctypes.windll.kernel32.CloseHandle(processHandle) | ||||
time.sleep(0.1) | ||||
else: | ||||
try: | ||||
os.kill(pid, 0) | ||||
except OSError: | ||||
break | ||||
else: | ||||
time.sleep(0.1) | ||||
# Local Variables: | # Local Variables: | |||
# tab-width:4 | # tab-width:4 | |||
# indent-tabs-mode:nil | # indent-tabs-mode:nil | |||
# End: | # End: | |||
# vim: set expandtab tabstop=4 shiftwidth=4: | # vim: set expandtab tabstop=4 shiftwidth=4: | |||
End of changes. 7 change blocks. | ||||
11 lines changed or deleted | 43 lines changed or added |