summaryrefslogtreecommitdiff
path: root/engine/SCons/Util.py
diff options
context:
space:
mode:
Diffstat (limited to 'engine/SCons/Util.py')
-rw-r--r--engine/SCons/Util.py238
1 files changed, 120 insertions, 118 deletions
diff --git a/engine/SCons/Util.py b/engine/SCons/Util.py
index c9aa2b5..7bb22cd 100644
--- a/engine/SCons/Util.py
+++ b/engine/SCons/Util.py
@@ -3,7 +3,7 @@
Various utility functions go here.
"""
#
-# Copyright (c) 2001 - 2017 The SCons Foundation
+# Copyright (c) 2001 - 2019 The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -24,7 +24,7 @@ Various utility functions go here.
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-__revision__ = "src/engine/SCons/Util.py 74b2c53bc42290e911b334a6b44f187da698a668 2017/11/14 13:16:53 bdbaddog"
+__revision__ = "src/engine/SCons/Util.py 72ae09dc35ac2626f8ff711d8c4b30b6138e08e3 2019-08-08 14:50:06 bdeegan"
import os
import sys
@@ -33,25 +33,23 @@ import re
import types
import codecs
import pprint
+import hashlib
PY3 = sys.version_info[0] == 3
try:
+ from collections import UserDict, UserList, UserString
+except ImportError:
from UserDict import UserDict
-except ImportError as e:
- from collections import UserDict
-
-try:
from UserList import UserList
-except ImportError as e:
- from collections import UserList
-
-from collections import Iterable
+ from UserString import UserString
try:
- from UserString import UserString
-except ImportError as e:
- from collections import UserString
+ from collections.abc import Iterable, MappingView
+except ImportError:
+ from collections import Iterable
+
+from collections import OrderedDict
# Don't "from types import ..." these because we need to get at the
# types module later to look for UnicodeType.
@@ -63,7 +61,7 @@ MethodType = types.MethodType
FunctionType = types.FunctionType
try:
- unicode
+ _ = type(unicode)
except NameError:
UnicodeType = str
else:
@@ -106,7 +104,7 @@ def containsOnly(str, set):
return 1
def splitext(path):
- "Same as os.path.splitext() but faster."
+ """Same as os.path.splitext() but faster."""
sep = rightmost_separator(path, os.sep)
dot = path.rfind('.')
# An ext is only real if it has at least one non-digit char
@@ -371,7 +369,13 @@ def print_tree(root, child_func, prune=0, showtags=0, margin=[0], visited=None):
DictTypes = (dict, UserDict)
ListTypes = (list, UserList)
-SequenceTypes = (list, tuple, UserList)
+
+try:
+ # Handle getting dictionary views.
+ SequenceTypes = (list, tuple, UserList, MappingView)
+except NameError:
+ SequenceTypes = (list, tuple, UserList)
+
# Note that profiling data shows a speed-up when comparing
# explicitly with str and unicode instead of simply comparing
@@ -653,13 +657,15 @@ except ImportError:
pass
RegError = _NoError
-WinError = None
+
# Make sure we have a definition of WindowsError so we can
# run platform-independent tests of Windows functionality on
# platforms other than Windows. (WindowsError is, in fact, an
# OSError subclass on Windows.)
+
class PlainWindowsError(OSError):
pass
+
try:
WinError = WindowsError
except NameError:
@@ -673,7 +679,7 @@ if can_read_reg:
HKEY_USERS = hkey_mod.HKEY_USERS
def RegGetValue(root, key):
- """This utility function returns a value in the registry
+ r"""This utility function returns a value in the registry
without having to open the key first. Only available on
Windows platforms with a version of Python that can read the
registry. Returns the same thing as
@@ -880,7 +886,7 @@ def PrependPath(oldpath, newpath, sep = os.pathsep,
# now we add them only if they are unique
for path in newpaths:
normpath = os.path.normpath(os.path.normcase(path))
- if path and not normpath in normpaths:
+ if path and normpath not in normpaths:
paths.append(path)
normpaths.append(normpath)
@@ -960,7 +966,7 @@ def AppendPath(oldpath, newpath, sep = os.pathsep,
# now we add them only if they are unique
for path in newpaths:
normpath = os.path.normpath(os.path.normcase(path))
- if path and not normpath in normpaths:
+ if path and normpath not in normpaths:
paths.append(path)
normpaths.append(normpath)
paths.reverse()
@@ -996,7 +1002,9 @@ if sys.platform == 'cygwin':
def get_native_path(path):
"""Transforms an absolute path into a native path for the system. In
Cygwin, this converts from a Cygwin path to a Windows one."""
- return os.popen('cygpath -w ' + path).read().replace('\n', '')
+ with os.popen('cygpath -w ' + path) as p:
+ npath = p.read().replace('\n', '')
+ return npath
else:
def get_native_path(path):
"""Transforms an absolute path into a native path for the system.
@@ -1029,64 +1037,9 @@ class CLVar(UserList):
return UserList.__add__(self, CLVar(other))
def __radd__(self, other):
return UserList.__radd__(self, CLVar(other))
- def __coerce__(self, other):
- return (self, CLVar(other))
def __str__(self):
return ' '.join(self.data)
-# A dictionary that preserves the order in which items are added.
-# Submitted by David Benjamin to ActiveState's Python Cookbook web site:
-# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747
-# Including fixes/enhancements from the follow-on discussions.
-class OrderedDict(UserDict):
- def __init__(self, dict = None):
- self._keys = []
- UserDict.__init__(self, dict)
-
- def __delitem__(self, key):
- UserDict.__delitem__(self, key)
- self._keys.remove(key)
-
- def __setitem__(self, key, item):
- UserDict.__setitem__(self, key, item)
- if key not in self._keys: self._keys.append(key)
-
- def clear(self):
- UserDict.clear(self)
- self._keys = []
-
- def copy(self):
- dict = OrderedDict()
- dict.update(self)
- return dict
-
- def items(self):
- return list(zip(self._keys, list(self.values())))
-
- def keys(self):
- return self._keys[:]
-
- def popitem(self):
- try:
- key = self._keys[-1]
- except IndexError:
- raise KeyError('dictionary is empty')
-
- val = self[key]
- del self[key]
-
- return (key, val)
-
- def setdefault(self, key, failobj = None):
- UserDict.setdefault(self, key, failobj)
- if key not in self._keys: self._keys.append(key)
-
- def update(self, dict):
- for (key, val) in dict.items():
- self.__setitem__(key, val)
-
- def values(self):
- return list(map(self.get, self._keys))
class Selector(OrderedDict):
"""A callable ordered dictionary that maps file suffixes to
@@ -1228,10 +1181,13 @@ def unique(s):
# ASPN: Python Cookbook: Remove duplicates from a sequence
# First comment, dated 2001/10/13.
# (Also in the printed Python Cookbook.)
+# This not currently used, in favor of the next function...
def uniquer(seq, idfun=None):
- if idfun is None:
- def idfun(x): return x
+ def default_idfun(x):
+ return x
+ if not idfun:
+ idfun = default_idfun
seen = {}
result = []
for item in seq:
@@ -1428,8 +1384,8 @@ def make_path_relative(path):
# The original idea for AddMethod() and RenameFunction() come from the
# following post to the ActiveState Python Cookbook:
#
-# ASPN: Python Cookbook : Install bound methods in an instance
-# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/223613
+# ASPN: Python Cookbook : Install bound methods in an instance
+# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/223613
#
# That code was a little fragile, though, so the following changes
# have been wrung on it:
@@ -1446,8 +1402,8 @@ def make_path_relative(path):
# the "new" module, as alluded to in Alex Martelli's response to the
# following Cookbook post:
#
-# ASPN: Python Cookbook : Dynamically added methods to a class
-# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732
+# ASPN: Python Cookbook : Dynamically added methods to a class
+# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732
def AddMethod(obj, function, name=None):
"""
@@ -1496,46 +1452,54 @@ def RenameFunction(function, name):
function.__defaults__)
-md5 = False
-
-
-def MD5signature(s):
- return str(s)
-
+if hasattr(hashlib, 'md5'):
+ md5 = True
-def MD5filesignature(fname, chunksize=65536):
- with open(fname, "rb") as f:
- result = f.read()
- return result
+ def MD5signature(s):
+ """
+ Generate md5 signature of a string
-try:
- import hashlib
-except ImportError:
- pass
-else:
- if hasattr(hashlib, 'md5'):
- md5 = True
+ :param s: either string or bytes. Normally should be bytes
+ :return: String of hex digits representing the signature
+ """
+ m = hashlib.md5()
- def MD5signature(s):
- m = hashlib.md5()
+ try:
+ m.update(to_bytes(s))
+ except TypeError as e:
+ m.update(to_bytes(str(s)))
- try:
- m.update(to_bytes(s))
- except TypeError as e:
- m.update(to_bytes(str(s)))
+ return m.hexdigest()
- return m.hexdigest()
+ def MD5filesignature(fname, chunksize=65536):
+ """
+ Generate the md5 signature of a file
- def MD5filesignature(fname, chunksize=65536):
- m = hashlib.md5()
- f = open(fname, "rb")
+ :param fname: file to hash
+ :param chunksize: chunk size to read
+ :return: String of Hex digits representing the signature
+ """
+ m = hashlib.md5()
+ with open(fname, "rb") as f:
while True:
blck = f.read(chunksize)
if not blck:
break
m.update(to_bytes(blck))
- f.close()
- return m.hexdigest()
+ return m.hexdigest()
+else:
+ # if md5 algorithm not available, just return data unmodified
+ # could add alternative signature scheme here
+ md5 = False
+
+ def MD5signature(s):
+ return str(s)
+
+ def MD5filesignature(fname, chunksize=65536):
+ with open(fname, "rb") as f:
+ result = f.read()
+ return result
+
def MD5collect(signatures):
"""
@@ -1550,7 +1514,6 @@ def MD5collect(signatures):
return MD5signature(', '.join(signatures))
-
def silent_intern(x):
"""
Perform sys.intern() on the passed argument and return the result.
@@ -1574,7 +1537,7 @@ def silent_intern(x):
class Null(object):
""" Null objects always and reliably "do nothing." """
def __new__(cls, *args, **kwargs):
- if not '_instance' in vars(cls):
+ if '_instance' not in vars(cls):
cls._instance = super(Null, cls).__new__(cls, *args, **kwargs)
return cls._instance
def __init__(self, *args, **kwargs):
@@ -1609,19 +1572,27 @@ class NullSeq(Null):
del __revision__
-def to_bytes (s):
+
+def to_bytes(s):
+ if s is None:
+ return b'None'
+ if not PY3 and isinstance(s, UnicodeType):
+ # PY2, must encode unicode
+ return bytearray(s, 'utf-8')
if isinstance (s, (bytes, bytearray)) or bytes is str:
+ # Above case not covered here as py2 bytes and strings are the same
return s
- return bytes (s, 'utf-8')
+ return bytes(s, 'utf-8')
+
-def to_str (s):
+def to_str(s):
+ if s is None:
+ return 'None'
if bytes is str or is_String(s):
return s
return str (s, 'utf-8')
-
-# No cmp in py3, so we'll define it.
def cmp(a, b):
"""
Define cmp because it's no longer available in python3
@@ -1630,6 +1601,37 @@ def cmp(a, b):
return (a > b) - (a < b)
+def get_env_bool(env, name, default=False):
+ """Get a value of env[name] converted to boolean. The value of env[name] is
+ interpreted as follows: 'true', 'yes', 'y', 'on' (case insensitive) and
+ anything convertible to int that yields non-zero integer are True values;
+ '0', 'false', 'no', 'n' and 'off' (case insensitive) are False values. For
+ all other cases, default value is returned.
+
+ :Parameters:
+ - `env` - dict or dict-like object, a convainer with variables
+ - `name` - name of the variable in env to be returned
+ - `default` - returned when env[name] does not exist or can't be converted to bool
+ """
+ try:
+ var = env[name]
+ except KeyError:
+ return default
+ try:
+ return bool(int(var))
+ except ValueError:
+ if str(var).lower() in ('true', 'yes', 'y', 'on'):
+ return True
+ elif str(var).lower() in ('false', 'no', 'n', 'off'):
+ return False
+ else:
+ return default
+
+
+def get_os_env_bool(name, default=False):
+ """Same as get_env_bool(os.environ, name, default)."""
+ return get_env_bool(os.environ, name, default)
+
# Local Variables:
# tab-width:4
# indent-tabs-mode:nil