From 7f45d674f1d288349f48c8218487cc473ce736d9 Mon Sep 17 00:00:00 2001
From: William Deegan <bill@baddogconsulting.com>
Date: Thu, 20 Jul 2017 15:24:49 -0400
Subject: change comment to docstring as it should have been

---
 src/engine/SCons/Environment.py | 24 +++++++++++++-----------
 1 file changed, 13 insertions(+), 11 deletions(-)

diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py
index 85f9fa7..60a45e4 100644
--- a/src/engine/SCons/Environment.py
+++ b/src/engine/SCons/Environment.py
@@ -2368,19 +2368,21 @@ class OverrideEnvironment(Base):
 
 Environment = Base
 
-# An entry point for returning a proxy subclass instance that overrides
-# the subst*() methods so they don't actually perform construction
-# variable substitution.  This is specifically intended to be the shim
-# layer in between global function calls (which don't want construction
-# variable substitution) and the DefaultEnvironment() (which would
-# substitute variables if left to its own devices)."""
-#
-# We have to wrap this in a function that allows us to delay definition of
-# the class until it's necessary, so that when it subclasses Environment
-# it will pick up whatever Environment subclass the wrapper interface
-# might have assigned to SCons.Environment.Environment.
 
 def NoSubstitutionProxy(subject):
+    """
+    An entry point for returning a proxy subclass instance that overrides
+    the subst*() methods so they don't actually perform construction
+    variable substitution.  This is specifically intended to be the shim
+    layer in between global function calls (which don't want construction
+    variable substitution) and the DefaultEnvironment() (which would
+    substitute variables if left to its own devices).
+
+    We have to wrap this in a function that allows us to delay definition of
+    the class until it's necessary, so that when it subclasses Environment
+    it will pick up whatever Environment subclass the wrapper interface
+    might have assigned to SCons.Environment.Environment.
+    """
     class _NoSubstitutionProxy(Environment):
         def __init__(self, subject):
             self.__dict__['__subject'] = subject
-- 
cgit v0.12


From e0bc3a04d5010e0c637edc59e034b91feaf343b6 Mon Sep 17 00:00:00 2001
From: William Deegan <bill@baddogconsulting.com>
Date: Sun, 30 Jul 2017 21:32:31 -0700
Subject: PY2/3 Fix action object content signiture to be stable. Note need
 expected value for each change in python bytecode. Currently that means one
 for each: py2.7, py3.5, py3.6

---
 src/engine/SCons/Action.py      | 67 ++++++++++++++++++++++++++++++++++++-----
 src/engine/SCons/ActionTests.py | 33 +++++++++-----------
 2 files changed, 75 insertions(+), 25 deletions(-)

diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py
index 118c495..bf611af 100644
--- a/src/engine/SCons/Action.py
+++ b/src/engine/SCons/Action.py
@@ -105,6 +105,8 @@ import pickle
 import re
 import sys
 import subprocess
+import itertools
+import inspect
 
 import SCons.Debug
 from SCons.Debug import logInstanceCreation
@@ -195,8 +197,11 @@ def _object_contents(obj):
                 except AttributeError as ae:
                     # Should be a pickle-able Python object.
                     try:
-                        return pickle.dumps(obj, ACTION_SIGNATURE_PICKLE_PROTOCOL)
-                    except (pickle.PicklingError, TypeError, AttributeError):
+                        return _object_instance_content(obj)
+                        # pickling an Action instance or object doesn't yield a stable
+                        # content as instance property may be dumped in different orders
+                        # return pickle.dumps(obj, ACTION_SIGNATURE_PICKLE_PROTOCOL)
+                    except (pickle.PicklingError, TypeError, AttributeError) as ex:
                         # This is weird, but it seems that nested classes
                         # are unpickable. The Python docs say it should
                         # always be a PicklingError, but some Python
@@ -308,15 +313,12 @@ def _function_contents(func):
         defaults.extend(b')')
 
         contents.append(defaults)
-
-        # contents.append(bytearray(',(','utf-8') + b','.join(function_defaults_contents) + bytearray(')','utf-8'))
     else:
         contents.append(b',()')
 
     # The function contents depends on the closure captured cell values.
     closure = func.__closure__ or []
 
-    #xxx = [_object_contents(x.cell_contents) for x in closure]
     try:
         closure_contents = [_object_contents(x.cell_contents) for x in closure]
     except AttributeError:
@@ -325,10 +327,61 @@ def _function_contents(func):
     contents.append(b',(')
     contents.append(bytearray(b',').join(closure_contents))
     contents.append(b')')
-    # contents.append(b'BBBBBBBB')
 
-    return bytearray(b'').join(contents)
+    retval = bytearray(b'').join(contents)
+
+    # print("ReTVAL:%s"%retval)
+    return retval
+
 
+def _object_instance_content(obj):
+    """
+    Returns consistant content for a action class or an instance thereof
+    :param obj: Should be either and action class or an instance thereof
+    :return: bytearray or bytes representing the obj suitable for generating
+             a signiture from.
+    """
+    retval = bytearray()
+
+
+    inst_class = obj.__class__
+    inst_class_name = bytearray(obj.__class__.__name__,'utf-8')
+    inst_class_module = bytearray(obj.__class__.__module__,'utf-8')
+    inst_class_hierarchy = bytearray(repr(inspect.getclasstree([obj.__class__,])),'utf-8')
+    # print("ICH:%s : %s"%(inst_class_hierarchy, repr(obj)))
+
+    properties = [(p, getattr(obj, p, "None")) for p in dir(obj) if p[:2] != '__' and not inspect.ismethod(getattr(obj, p))]
+    properties.sort()
+    properties_str = ','.join(["%s=%s"%(p[0],p[1]) for p in properties])
+    properties_bytes = bytearray(properties_str,'utf-8')
+
+    methods = [p for p in dir(obj) if inspect.ismethod(getattr(obj, p))]
+    methods.sort()
+
+    method_contents = []
+    for m in methods:
+        # print("Method:%s"%m)
+        v = _function_contents(getattr(obj, m))
+        # print("[%s->]V:%s [%s]"%(m,v,type(v)))
+        method_contents.append(v)
+
+    retval = bytearray(b'{')
+    retval.extend(inst_class_name)
+    retval.extend(b":")
+    retval.extend(inst_class_module)
+    retval.extend(b'}[[')
+    retval.extend(inst_class_hierarchy)
+    retval.extend(b']]{{')
+    retval.extend(bytearray(b",").join(method_contents))
+    retval.extend(b"}}")
+    return retval
+
+    # print("class          :%s"%inst_class)
+    # print("class_name     :%s"%inst_class_name)
+    # print("class_module   :%s"%inst_class_module)
+    # print("Class hier     :\n%s"%pp.pformat(inst_class_hierarchy))
+    # print("Inst Properties:\n%s"%pp.pformat(properties))
+    # print("Inst Methods   :\n%s"%pp.pformat(methods))
 
 def _actionAppend(act1, act2):
     # This function knows how to slap two actions together.
diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py
index 34d9ffc..7980e39 100644
--- a/src/engine/SCons/ActionTests.py
+++ b/src/engine/SCons/ActionTests.py
@@ -98,7 +98,9 @@ scons_env = SCons.Environment.Environment()
 
 # Capture all the stuff the Actions will print,
 # so it doesn't clutter the output.
-sys.stdout = io.StringIO()
+
+# TODO.. uncomment
+# sys.stdout = io.StringIO()
 
 class CmdStringHolder(object):
     def __init__(self, cmd, literal=None):
@@ -2054,7 +2056,7 @@ class ActionCompareTestCase(unittest.TestCase):
         assert dog.get_name(env) == 'DOG', dog.get_name(env)
 
 
-class TestClass:
+class TestClass(object):
     """A test class used by ObjectContentsTestCase.test_object_contents"""
     def __init__(self):
         self.a = "a"
@@ -2081,7 +2083,6 @@ class ObjectContentsTestCase(unittest.TestCase):
         assert c in expected, "Got\n"+repr(c)+"\nExpected one of \n"+"\n".join([repr(e) for e in expected])
 
 
-    # @unittest.skip("Results vary between py2 and py3, not sure if test makes sense to implement")
     def test_object_contents(self):
         """Test that Action._object_contents works"""
 
@@ -2089,20 +2090,18 @@ class ObjectContentsTestCase(unittest.TestCase):
         o = TestClass()
         c = SCons.Action._object_contents(o)
 
+        # c = SCons.Action._object_instance_content(o)
+
+        # Since the python bytecode has per version differences, we need different expected results per version
         if TestCmd.IS_PY3:
-            if TestCmd.IS_WINDOWS:
-                expected = [b'ccopy_reg\n_reconstructor\nq\x00(c__main__\nTestClass\nq\x01c__builtin__\nobject\nq\x02Ntq\x03Rq\x04}q\x05(X\x01\x00\x00\x00aq\x06h\x06X\x01\x00\x00\x00bq\x07h\x07ub.', # py 3.6
-                b'ccopy_reg\n_reconstructor\nq\x00(c__main__\nTestClass\nq\x01c__builtin__\nobject\nq\x02Ntq\x03Rq\x04}q\x05(X\x01\x00\x00\x00bq\x06h\x06X\x01\x00\x00\x00aq\x07h\x07ub.', # py 3.5
-                ]
-            else:
-                expected = [b'ccopy_reg\n_reconstructor\nq\x00(c__main__\nTestClass\nq\x01c__builtin__\nobject\nq\x02Ntq\x03Rq\x04}q\x05(X\x01\x00\x00\x00bq\x06h\x06X\x01\x00\x00\x00aq\x07h\x07ub.']
+            if sys.version_info[:2] == (3,5):
+                expected = bytearray(b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,({str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}},{str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}}),({str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}},{str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}}),(d\x01\x00|\x00\x00_\x00\x00d\x02\x00|\x00\x00_\x01\x00d\x00\x00S),(),(),2, 2, 0, 0,(),(),(d\x00\x00S),(),()}}")
+            elif sys.version_info[:2] == (3,6):
+                expected = bytearray(b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,({str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}},{str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}}),({str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}},{str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}}),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}")
         else:
-            if TestCmd.IS_WINDOWS:
-                expected = [b'(c__main__\nTestClass\nq\x01oq\x02}q\x03(U\x01aU\x01aU\x01bU\x01bub.']
-            else:
-                expected = [b'(c__main__\nTestClass\nq\x01oq\x02}q\x03(U\x01aU\x01aU\x01bU\x01bub.']
+            expected = bytearray(b"{TestClass:__main__}[[[(<type \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<type \'object\'>,))]]]]{{1, 1, 0, 0,({str:__builtin__}[[[(<type \'basestring\'>, (<type \'object\'>,)), [(<type \'str\'>, (<type \'basestring\'>,))]]]]{{}},{str:__builtin__}[[[(<type \'basestring\'>, (<type \'object\'>,)), [(<type \'str\'>, (<type \'basestring\'>,))]]]]{{}}),({str:__builtin__}[[[(<type \'basestring\'>, (<type \'object\'>,)), [(<type \'str\'>, (<type \'basestring\'>,))]]]]{{}},{str:__builtin__}[[[(<type \'basestring\'>, (<type \'object\'>,)), [(<type \'str\'>, (<type \'basestring\'>,))]]]]{{}}),(d\x01\x00|\x00\x00_\x00\x00d\x02\x00|\x00\x00_\x01\x00d\x00\x00S),(),(),2, 2, 0, 0,(),(),(d\x00\x00S),(),()}}")
 
-        assert c in expected, "Got\n"+repr(c)+"\nExpected one of \n"+"\n".join([repr(e) for e in expected])
+        assert c == expected, "Got\n"+repr(c)+"\nExpected \n"+"\n"+repr(expected)
 
     # @unittest.skip("Results vary between py2 and py3, not sure if test makes sense to implement")
     def test_code_contents(self):
@@ -2122,9 +2121,6 @@ class ObjectContentsTestCase(unittest.TestCase):
         assert c in expected, "Got\n"+repr(c)+"\nExpected one of \n"+"\n".join([repr(e) for e in expected])
 
 
-
-
-
 if __name__ == "__main__":
     suite = unittest.TestSuite()
     tclasses = [ _ActionActionTestCase,
@@ -2142,7 +2138,8 @@ if __name__ == "__main__":
         names = unittest.getTestCaseNames(tclass, 'test_')
         suite.addTests(list(map(tclass, names)))
 
-    TestUnit.run(suite)
+    # TestUnit.run(suite)
+    unittest.main()
 
 # Local Variables:
 # tab-width:4
-- 
cgit v0.12


From 7fa025432c147333167393d0b88edfe7266b2646 Mon Sep 17 00:00:00 2001
From: William Deegan <bill@baddogconsulting.com>
Date: Mon, 31 Jul 2017 15:23:04 -0700
Subject: PY2/3 Add property values for object/instance signature

---
 src/engine/SCons/Action.py      | 8 ++++----
 src/engine/SCons/ActionTests.py | 7 +++----
 2 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py
index bf611af..7f11d7b 100644
--- a/src/engine/SCons/Action.py
+++ b/src/engine/SCons/Action.py
@@ -329,8 +329,6 @@ def _function_contents(func):
     contents.append(b')')
 
     retval = bytearray(b'').join(contents)
-
-    # print("ReTVAL:%s"%retval)
     return retval
 
 
@@ -350,7 +348,7 @@ def _object_instance_content(obj):
     inst_class_hierarchy = bytearray(repr(inspect.getclasstree([obj.__class__,])),'utf-8')
     # print("ICH:%s : %s"%(inst_class_hierarchy, repr(obj)))
 
-    properties = [(p, getattr(obj, p, "None")) for p in dir(obj) if p[:2] != '__' and not inspect.ismethod(getattr(obj, p))]
+    properties = [(p, getattr(obj, p, "None")) for p in dir(obj) if not (p[:2] == '__' or inspect.ismethod(getattr(obj, p)) or inspect.isbuiltin(getattr(obj,p))) ]
     properties.sort()
     properties_str = ','.join(["%s=%s"%(p[0],p[1]) for p in properties])
     properties_bytes = bytearray(properties_str,'utf-8')
@@ -373,7 +371,9 @@ def _object_instance_content(obj):
     retval.extend(inst_class_hierarchy)
     retval.extend(b']]{{')
     retval.extend(bytearray(b",").join(method_contents))
-    retval.extend(b"}}")
+    retval.extend(b"}}{{{")
+    retval.extend(properties_bytes)
+    retval.extend(b'}}}')
     return retval
 
     # print("class          :%s"%inst_class)
diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py
index 7980e39..ba3d844 100644
--- a/src/engine/SCons/ActionTests.py
+++ b/src/engine/SCons/ActionTests.py
@@ -2095,15 +2095,14 @@ class ObjectContentsTestCase(unittest.TestCase):
         # Since the python bytecode has per version differences, we need different expected results per version
         if TestCmd.IS_PY3:
             if sys.version_info[:2] == (3,5):
-                expected = bytearray(b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,({str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}},{str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}}),({str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}},{str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}}),(d\x01\x00|\x00\x00_\x00\x00d\x02\x00|\x00\x00_\x01\x00d\x00\x00S),(),(),2, 2, 0, 0,(),(),(d\x00\x00S),(),()}}")
+                expected = bytearray(b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,({str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}}{{{}}},{str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}}{{{}}}),({str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}}{{{}}},{str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}}{{{}}}),(d\x01\x00|\x00\x00_\x00\x00d\x02\x00|\x00\x00_\x01\x00d\x00\x00S),(),(),2, 2, 0, 0,(),(),(d\x00\x00S),(),()}}{{{a=a,b=b}}}")
             elif sys.version_info[:2] == (3,6):
-                expected = bytearray(b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,({str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}},{str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}}),({str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}},{str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}}),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}")
+                expected = bytearray(b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,({str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}}{{{}}},{str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}}{{{}}}),({str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}}{{{}}},{str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}}{{{}}}),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}")
         else:
-            expected = bytearray(b"{TestClass:__main__}[[[(<type \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<type \'object\'>,))]]]]{{1, 1, 0, 0,({str:__builtin__}[[[(<type \'basestring\'>, (<type \'object\'>,)), [(<type \'str\'>, (<type \'basestring\'>,))]]]]{{}},{str:__builtin__}[[[(<type \'basestring\'>, (<type \'object\'>,)), [(<type \'str\'>, (<type \'basestring\'>,))]]]]{{}}),({str:__builtin__}[[[(<type \'basestring\'>, (<type \'object\'>,)), [(<type \'str\'>, (<type \'basestring\'>,))]]]]{{}},{str:__builtin__}[[[(<type \'basestring\'>, (<type \'object\'>,)), [(<type \'str\'>, (<type \'basestring\'>,))]]]]{{}}),(d\x01\x00|\x00\x00_\x00\x00d\x02\x00|\x00\x00_\x01\x00d\x00\x00S),(),(),2, 2, 0, 0,(),(),(d\x00\x00S),(),()}}")
+            expected = bytearray(b"{TestClass:__main__}[[[(<type \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<type \'object\'>,))]]]]{{1, 1, 0, 0,({str:__builtin__}[[[(<type \'basestring\'>, (<type \'object\'>,)), [(<type \'str\'>, (<type \'basestring\'>,))]]]]{{}}{{{}}},{str:__builtin__}[[[(<type \'basestring\'>, (<type \'object\'>,)), [(<type \'str\'>, (<type \'basestring\'>,))]]]]{{}}{{{}}}),({str:__builtin__}[[[(<type \'basestring\'>, (<type \'object\'>,)), [(<type \'str\'>, (<type \'basestring\'>,))]]]]{{}}{{{}}},{str:__builtin__}[[[(<type \'basestring\'>, (<type \'object\'>,)), [(<type \'str\'>, (<type \'basestring\'>,))]]]]{{}}{{{}}}),(d\x01\x00|\x00\x00_\x00\x00d\x02\x00|\x00\x00_\x01\x00d\x00\x00S),(),(),2, 2, 0, 0,(),(),(d\x00\x00S),(),()}}{{{a=a,b=b}}}")
 
         assert c == expected, "Got\n"+repr(c)+"\nExpected \n"+"\n"+repr(expected)
 
-    # @unittest.skip("Results vary between py2 and py3, not sure if test makes sense to implement")
     def test_code_contents(self):
         """Test that Action._code_contents works"""
 
-- 
cgit v0.12


From 7d36c95312ee64c7c17b374999857a00518a53b8 Mon Sep 17 00:00:00 2001
From: William Deegan <bill@baddogconsulting.com>
Date: Tue, 1 Aug 2017 09:47:49 -0700
Subject: PY2/3 Fix more function signiture logic. Previously was dropping
 first co_consts under the mistaken belief that first item was always
 docstring.  Shortcut logic for NoneType and strings.

---
 src/engine/SCons/Action.py | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py
index 7f11d7b..4449bea 100644
--- a/src/engine/SCons/Action.py
+++ b/src/engine/SCons/Action.py
@@ -257,13 +257,7 @@ def _code_contents(code):
     # function. Note that we have to call _object_contents on each
     # constants because the code object of nested functions can
     # show-up among the constants.
-    #
-    # Note that we also always ignore the first entry of co_consts
-    # which contains the function doc string. We assume that the
-    # function does not access its doc string.
-    # NOTE: This is not necessarily true. If no docstring, then co_consts[0] does
-    # have a string which is used.
-    z = [_object_contents(cc) for cc in code.co_consts[1:]]
+    z = [_object_contents(cc) for cc in code.co_consts]
     contents.extend(b',(')
     contents.extend(bytearray(',', 'utf-8').join(z))
     contents.extend(b')')
@@ -341,6 +335,11 @@ def _object_instance_content(obj):
     """
     retval = bytearray()
 
+    if obj is None:
+        return b'N.'
+
+    if isinstance(obj, SCons.Util.BaseStringTypes):
+        return SCons.Util.to_bytes(obj)
 
     inst_class = obj.__class__
     inst_class_name = bytearray(obj.__class__.__name__,'utf-8')
-- 
cgit v0.12


From dbb74a49d9de365200c996e0088f97f78fd659b1 Mon Sep 17 00:00:00 2001
From: William Deegan <bill@baddogconsulting.com>
Date: Tue, 1 Aug 2017 09:48:22 -0700
Subject: PY2/3 update signatures and simplify logic so we have expected
 signitures per version of python

---
 src/engine/SCons/ActionTests.py | 194 +++++++++++++++++++++-------------------
 1 file changed, 102 insertions(+), 92 deletions(-)

diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py
index ba3d844..a5432c6 100644
--- a/src/engine/SCons/ActionTests.py
+++ b/src/engine/SCons/ActionTests.py
@@ -98,9 +98,7 @@ scons_env = SCons.Environment.Environment()
 
 # Capture all the stuff the Actions will print,
 # so it doesn't clutter the output.
-
-# TODO.. uncomment
-# sys.stdout = io.StringIO()
+sys.stdout = io.StringIO()
 
 class CmdStringHolder(object):
     def __init__(self, cmd, literal=None):
@@ -1441,6 +1439,17 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
                 b"0, 0, 0, 0,(),(),(d\x00\x00S),(),()",
                 ]
 
+        # Since the python bytecode has per version differences, we need different expected results per version
+        func_matches = {
+            (2,7) : bytearray(b'0, 0, 0, 0,(N.),(),(d\x00\x00S),(),()'),
+            (3,5) : bytearray(b'0, 0, 0, 0,(N.),(),(d\x00\x00S),(),()'),
+            (3,6) : bytearray(b'0, 0, 0, 0,(N.),(),(d\x00S\x00),(),()'),
+        }
+
+        # c = SCons.Action._function_contents(func1)
+        # assert c == expected[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected \n"+"\n"+repr(expected[sys.version_info[:2]])
+
+
         meth_matches = [
             b"1, 1, 0, 0,(),(),(d\000\000S),(),()",
             b"1, 1, 0, 0,(),(),(d\x00\x00S),(),()",
@@ -1456,11 +1465,11 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
 
         a = self.factory(f_global)
         c = a.get_contents(target=[], source=[], env=env)
-        assert c in func_matches, "Got\n"+repr(c)+"\nExpected one of \n"+"\n".join([repr(f) for f in func_matches])
+        assert c == func_matches[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected \n"+repr(func_matches[sys.version_info[:2]])
 
         a = self.factory(f_local)
         c = a.get_contents(target=[], source=[], env=env)
-        assert c in func_matches, "Got\n"+repr(c)+"\nExpected one of \n"+"\n".join([repr(f) for f in func_matches])
+        assert c == func_matches[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected \n"+repr(func_matches[sys.version_info[:2]])
 
         def f_global(target, source, env, for_signature):
             return SCons.Action.Action(GlobalFunc, varlist=['XYZ'])
@@ -1468,7 +1477,7 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
         def f_local(target, source, env, for_signature):
             return SCons.Action.Action(LocalFunc, varlist=['XYZ'])
 
-        matches_foo = [x + b"foo" for x in func_matches]
+        matches_foo = func_matches[sys.version_info[:2]] + b'foo'
 
         a = self.factory(f_global)
         c = a.get_contents(target=[], source=[], env=env)
@@ -1598,52 +1607,48 @@ class FunctionActionTestCase(unittest.TestCase):
         def LocalFunc():
             pass
 
-        if TestCmd.IS_PY3 and TestCmd.IS_WINDOWS:
-            func_matches = [
-                b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()',  # py 3.6
-                b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'   # py 3.5
-                ]
-            meth_matches = [
-                b'1, 1, 0, 0,(),(),(d\x00S\x00),(),()',  # py 3.6
-                b'1, 1, 0, 0,(),(),(d\x00\x00S),(),()',  # py 3.5
-            ]
-
-        else:
-            func_matches = [
-                b"0, 0, 0, 0,(),(),(d\000\000S),(),()",
-                b"0, 0, 0, 0,(),(),(d\x00\x00S),(),()",
-                ]
+        func_matches = {
+            (2,7) : bytearray(b'0, 0, 0, 0,(N.),(),(d\x00\x00S),(),()'),
+            (3,5) : bytearray(b'0, 0, 0, 0,(N.),(),(d\x00\x00S),(),()'),
+            (3,6) : bytearray(b'0, 0, 0, 0,(N.),(),(d\x00S\x00),(),()'),
+        }
 
-            meth_matches = [
-                b"1, 1, 0, 0,(),(),(d\000\000S),(),()",
-                b"1, 1, 0, 0,(),(),(d\x00\x00S),(),()",
-            ]
+        meth_matches = {
+            (2,7) : bytearray(b'1, 1, 0, 0,(N.),(),(d\x00\x00S),(),()'),
+            (3,5) : bytearray(b'1, 1, 0, 0,(N.),(),(d\x00\x00S),(),()'),
+            (3,6) : bytearray(b'1, 1, 0, 0,(N.),(),(d\x00S\x00),(),()'),
+        }
 
         def factory(act, **kw):
             return SCons.Action.FunctionAction(act, kw)
 
         a = factory(GlobalFunc)
         c = a.get_contents(target=[], source=[], env=Environment())
-        assert c in func_matches, "Got\n"+repr(c)+"\nExpected one of \n"+"\n".join([repr(f) for f in func_matches])
+        assert c == func_matches[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected one of \n"+repr(func_matches[sys.version_info[:2]])
 
 
         a = factory(LocalFunc)
         c = a.get_contents(target=[], source=[], env=Environment())
-        assert c in func_matches, repr(c)
+        assert c == func_matches[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected one of \n"+repr(func_matches[sys.version_info[:2]])
 
-        matches_foo = [x + b"foo" for x in func_matches]
+        matches_foo = func_matches[sys.version_info[:2]] + b'foo'
 
         a = factory(GlobalFunc, varlist=['XYZ'])
         c = a.get_contents(target=[], source=[], env=Environment())
-        assert c in func_matches, repr(c)
+        assert c == func_matches[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected one of \n"+repr(func_matches[sys.version_info[:2]])
+        # assert c in func_matches, repr(c)
+
         c = a.get_contents(target=[], source=[], env=Environment(XYZ='foo'))
-        assert c in matches_foo, repr(c)
+        assert c == matches_foo, repr(c)
 
         ##TODO: is this set of tests still needed?
         # Make sure a bare string varlist works
         a = factory(GlobalFunc, varlist='XYZ')
         c = a.get_contents(target=[], source=[], env=Environment())
-        assert c in func_matches, repr(c)
+        # assert c in func_matches, repr(c)
+        assert c == func_matches[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected one of \n"+repr(func_matches[sys.version_info[:2]])
+
+
         c = a.get_contents(target=[], source=[], env=Environment(XYZ='foo'))
         assert c in matches_foo, repr(c)
 
@@ -1660,7 +1665,7 @@ class FunctionActionTestCase(unittest.TestCase):
         lc = LocalClass()
         a = factory(lc.LocalMethod)
         c = a.get_contents(target=[], source=[], env=Environment())
-        assert c in meth_matches,  "Got\n"+repr(c)+"\nExpected one of \n"+"\n".join([repr(f) for f in meth_matches])
+        assert c == meth_matches[sys.version_info[:2]],  "Got\n"+repr(c)+"\nExpected one of \n"+repr(meth_matches[sys.version_info[:2]])
 
     def test_strfunction(self):
         """Test the FunctionAction.strfunction() method
@@ -1826,16 +1831,23 @@ class LazyActionTestCase(unittest.TestCase):
         def LocalFunc():
             pass
 
-        if TestCmd.IS_PY3 and TestCmd.IS_WINDOWS:
-            func_matches = [
-                b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()',
-                b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'
-                ]
-        else:
-            func_matches = [
-                b"0, 0, 0, 0,(),(),(d\000\000S),(),()",
-                b"0, 0, 0, 0,(),(),(d\x00\x00S),(),()",
-                ]
+
+        func_matches = {
+            (2,7) : bytearray(b'0, 0, 0, 0,(N.),(),(d\x00\x00S),(),()'),
+            (3,5) : bytearray(b'0, 0, 0, 0,(N.),(),(d\x00\x00S),(),()'),
+            (3,6) : bytearray(b'0, 0, 0, 0,(N.),(),(d\x00S\x00),(),()'),
+        }
+
+        # if TestCmd.IS_PY3 and TestCmd.IS_WINDOWS:
+        #     func_matches = [
+        #         b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()',
+        #         b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'
+        #         ]
+        # else:
+        #     func_matches = [
+        #         b"0, 0, 0, 0,(),(),(d\000\000S),(),()",
+        #         b"0, 0, 0, 0,(),(),(d\x00\x00S),(),()",
+        #         ]
 
         meth_matches = [
             b"1, 1, 0, 0,(),(),(d\000\000S),(),()",
@@ -1850,17 +1862,22 @@ class LazyActionTestCase(unittest.TestCase):
 
         env = Environment(FOO = factory(GlobalFunc))
         c = a.get_contents(target=[], source=[], env=env)
-        assert c in func_matches, "Got\n"+repr(c)+"\nExpected one of \n"+"\n".join([repr(f) for f in func_matches])
+        # assert c in func_matches, "Got\n"+repr(c)+"\nExpected one of \n"+"\n".join([repr(f) for f in func_matches])
+        assert c == func_matches[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected one of \n"+repr(func_matches[sys.version_info[:2]])
+
+
 
         env = Environment(FOO = factory(LocalFunc))
         c = a.get_contents(target=[], source=[], env=env)
-        assert c in func_matches, repr(c)
+        assert c == func_matches[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected one of \n"+repr(func_matches[sys.version_info[:2]])
+
+        # matches_foo = [x + b"foo" for x in func_matches]
+        matches_foo = func_matches[sys.version_info[:2]] + b'foo'
 
-        matches_foo = [x + b"foo" for x in func_matches]
 
         env = Environment(FOO = factory(GlobalFunc, varlist=['XYZ']))
         c = a.get_contents(target=[], source=[], env=env)
-        assert c in func_matches, repr(c)
+        assert c == func_matches[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected one of \n"+repr(func_matches[sys.version_info[:2]])
 
         env['XYZ'] = 'foo'
         c = a.get_contents(target=[], source=[], env=env)
@@ -1883,32 +1900,23 @@ class ActionCallerTestCase(unittest.TestCase):
         def LocalFunc():
             pass
 
-        if TestCmd.IS_PY3 and TestCmd.IS_WINDOWS:
-            matches = [
-                b'd\x00S\x00',
-                b'd\x00\x00S'
-            ]
-        else:
-            matches = [
-                b"d\000\000S",
-                b"d\\x00\\x00S"
-            ]
+
+        matches = {
+            (2,7) : b'd\x00\x00S',
+            (3,5) : b'd\x00\x00S',
+            (3,6) : b'd\x00S\x00',
+        }
+
 
         af = SCons.Action.ActionFactory(GlobalFunc, strfunc)
         ac = SCons.Action.ActionCaller(af, [], {})
         c = ac.get_contents([], [], Environment())
-        assert c in matches, "Got\n"+repr(c)+"\nExpected one of \n"+"\n".join([repr(f) for f in matches])
+        assert c == matches[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected one of \n"+repr(matches[sys.version_info[:2]])
 
         af = SCons.Action.ActionFactory(LocalFunc, strfunc)
         ac = SCons.Action.ActionCaller(af, [], {})
         c = ac.get_contents([], [], Environment())
-        assert c in matches, "Got\n"+repr(c)+"\nExpected one of \n"+"\n".join([repr(f) for f in matches])
-
-        # TODO: Same as above, why redefine?
-        # matches = [
-        #     b'd\000\000S',
-        #     b"d\x00\x00S"
-        # ]
+        assert c == matches[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected one of \n"+repr(matches[sys.version_info[:2]])
 
         class LocalActFunc(object):
             def __call__(self):
@@ -1917,12 +1925,12 @@ class ActionCallerTestCase(unittest.TestCase):
         af = SCons.Action.ActionFactory(GlobalActFunc(), strfunc)
         ac = SCons.Action.ActionCaller(af, [], {})
         c = ac.get_contents([], [], Environment())
-        assert c in matches, "C [%s] not in matches [%s]"%(repr(c),matches)
+        assert c == matches[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected one of \n"+repr(matches[sys.version_info[:2]])
 
         af = SCons.Action.ActionFactory(LocalActFunc(), strfunc)
         ac = SCons.Action.ActionCaller(af, [], {})
         c = ac.get_contents([], [], Environment())
-        assert c in matches, repr(c)
+        assert c == matches[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected one of \n"+repr(matches[sys.version_info[:2]])
 
         matches = [
             b"<built-in function str>",
@@ -2074,13 +2082,15 @@ class ObjectContentsTestCase(unittest.TestCase):
             """A test function"""
             return a
 
+        # Since the python bytecode has per version differences, we need different expected results per version
+        expected = {
+            (2,7) : bytearray(b'3, 3, 0, 0,(A test function),(),(|\x00\x00S),(),()'),
+            (3,5) : bytearray(b'3, 3, 0, 0,(A test function),(),(|\x00\x00S),(),()'),
+            (3,6) : bytearray(b'3, 3, 0, 0,(A test function),(),(|\x00S\x00),(),()'),
+        }
+
         c = SCons.Action._function_contents(func1)
-        if TestCmd.IS_PY3 and TestCmd.IS_WINDOWS:
-            expected = [b'3, 3, 0, 0,(),(),(|\x00S\x00),(),()',
-                        b'3, 3, 0, 0,(),(),(|\x00\x00S),(),()']
-        else:
-            expected = [b'3, 3, 0, 0,(),(),(|\x00\x00S),(),()']
-        assert c in expected, "Got\n"+repr(c)+"\nExpected one of \n"+"\n".join([repr(e) for e in expected])
+        assert c == expected[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected \n"+"\n"+repr(expected[sys.version_info[:2]])
 
 
     def test_object_contents(self):
@@ -2093,31 +2103,29 @@ class ObjectContentsTestCase(unittest.TestCase):
         # c = SCons.Action._object_instance_content(o)
 
         # Since the python bytecode has per version differences, we need different expected results per version
-        if TestCmd.IS_PY3:
-            if sys.version_info[:2] == (3,5):
-                expected = bytearray(b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,({str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}}{{{}}},{str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}}{{{}}}),({str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}}{{{}}},{str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}}{{{}}}),(d\x01\x00|\x00\x00_\x00\x00d\x02\x00|\x00\x00_\x01\x00d\x00\x00S),(),(),2, 2, 0, 0,(),(),(d\x00\x00S),(),()}}{{{a=a,b=b}}}")
-            elif sys.version_info[:2] == (3,6):
-                expected = bytearray(b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,({str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}}{{{}}},{str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}}{{{}}}),({str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}}{{{}}},{str:builtins}[[[(<class \'object\'>, ()), [(<class \'str\'>, (<class \'object\'>,))]]]]{{}}{{{}}}),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}")
-        else:
-            expected = bytearray(b"{TestClass:__main__}[[[(<type \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<type \'object\'>,))]]]]{{1, 1, 0, 0,({str:__builtin__}[[[(<type \'basestring\'>, (<type \'object\'>,)), [(<type \'str\'>, (<type \'basestring\'>,))]]]]{{}}{{{}}},{str:__builtin__}[[[(<type \'basestring\'>, (<type \'object\'>,)), [(<type \'str\'>, (<type \'basestring\'>,))]]]]{{}}{{{}}}),({str:__builtin__}[[[(<type \'basestring\'>, (<type \'object\'>,)), [(<type \'str\'>, (<type \'basestring\'>,))]]]]{{}}{{{}}},{str:__builtin__}[[[(<type \'basestring\'>, (<type \'object\'>,)), [(<type \'str\'>, (<type \'basestring\'>,))]]]]{{}}{{{}}}),(d\x01\x00|\x00\x00_\x00\x00d\x02\x00|\x00\x00_\x01\x00d\x00\x00S),(),(),2, 2, 0, 0,(),(),(d\x00\x00S),(),()}}{{{a=a,b=b}}}")
+        expected = {
+            (2,7): bytearray(b"{TestClass:__main__}[[[(<type \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<type \'object\'>,))]]]]{{1, 1, 0, 0,(N.,a,b),(a,b),(d\x01\x00|\x00\x00_\x00\x00d\x02\x00|\x00\x00_\x01\x00d\x00\x00S),(),(),2, 2, 0, 0,(N.),(),(d\x00\x00S),(),()}}{{{a=a,b=b}}}"),
+            (3,5): bytearray(b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,(N.,a,b),(a,b),(d\x01\x00|\x00\x00_\x00\x00d\x02\x00|\x00\x00_\x01\x00d\x00\x00S),(),(),2, 2, 0, 0,(N.),(),(d\x00\x00S),(),()}}{{{a=a,b=b}}}"),
+            (3,6): bytearray(b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,(N.,a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(N.),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}"),
+        }
 
-        assert c == expected, "Got\n"+repr(c)+"\nExpected \n"+"\n"+repr(expected)
+        assert c == expected[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected \n"+"\n"+repr(expected[sys.version_info[:2]])
 
     def test_code_contents(self):
         """Test that Action._code_contents works"""
 
         code = compile("print('Hello, World!')", '<string>', 'exec')
         c = SCons.Action._code_contents(code)
-        if TestCmd.IS_PY3:
-            if TestCmd.IS_WINDOWS:
-                expected = [b'0, 0, 0, 0,(N.),(X\x05\x00\x00\x00printq\x00.),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)',
-                            b'0, 0, 0, 0,(N.),(X\x05\x00\x00\x00printq\x00.),(e\x00\x00d\x00\x00\x83\x01\x00\x01d\x01\x00S)'
-                            ]
-            else:
-                expected = [b'0, 0, 0, 0,(N.),(X\x05\x00\x00\x00printq\x00.),(e\x00\x00d\x00\x00\x83\x01\x00\x01d\x01\x00S)']
-        else:
-            expected = [b"0, 0, 0, 0,(N.),(),(d\x00\x00GHd\x01\x00S)"]
-        assert c in expected, "Got\n"+repr(c)+"\nExpected one of \n"+"\n".join([repr(e) for e in expected])
+
+        # Since the python bytecode has per version differences, we need different expected results per version
+        expected = {
+            (2,7) : bytearray(b'0, 0, 0, 0,(Hello, World!,N.),(),(d\x00\x00GHd\x01\x00S)'),
+            (3,5) : bytearray(b'0, 0, 0, 0,(Hello, World!,N.),(print),(e\x00\x00d\x00\x00\x83\x01\x00\x01d\x01\x00S)'),
+            (3,6) : bytearray(b'0, 0, 0, 0,(Hello, World!,N.),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)'),
+        }
+
+        assert c == expected[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected \n"+"\n"+expected[sys.version_info[:2]]
+
 
 
 if __name__ == "__main__":
@@ -2137,8 +2145,10 @@ if __name__ == "__main__":
         names = unittest.getTestCaseNames(tclass, 'test_')
         suite.addTests(list(map(tclass, names)))
 
-    # TestUnit.run(suite)
-    unittest.main()
+    TestUnit.run(suite)
+
+    # Swap this for above to debug otherwise you can't run individual tests as TestUnit is swallowing arguments
+    # unittest.main()
 
 # Local Variables:
 # tab-width:4
-- 
cgit v0.12


From 9b2ccf37addb93b870abce4a2d36e3936167b514 Mon Sep 17 00:00:00 2001
From: William Deegan <bill@baddogconsulting.com>
Date: Tue, 1 Aug 2017 13:32:40 -0700
Subject: Remove dead code left after creating python version specific golden
 values

---
 src/engine/SCons/ActionTests.py | 26 --------------------------
 1 file changed, 26 deletions(-)

diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py
index a5432c6..cf42307 100644
--- a/src/engine/SCons/ActionTests.py
+++ b/src/engine/SCons/ActionTests.py
@@ -1428,17 +1428,6 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
         def LocalFunc():
             pass
 
-        if TestCmd.IS_PY3 and TestCmd.IS_WINDOWS:
-            func_matches = [
-                b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()',  # PY 3.6
-                b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()',  # PY 3.5
-                ]
-        else:
-            func_matches = [
-                b"0, 0, 0, 0,(),(),(d\000\000S),(),()",
-                b"0, 0, 0, 0,(),(),(d\x00\x00S),(),()",
-                ]
-
         # Since the python bytecode has per version differences, we need different expected results per version
         func_matches = {
             (2,7) : bytearray(b'0, 0, 0, 0,(N.),(),(d\x00\x00S),(),()'),
@@ -1446,10 +1435,6 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
             (3,6) : bytearray(b'0, 0, 0, 0,(N.),(),(d\x00S\x00),(),()'),
         }
 
-        # c = SCons.Action._function_contents(func1)
-        # assert c == expected[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected \n"+"\n"+repr(expected[sys.version_info[:2]])
-
-
         meth_matches = [
             b"1, 1, 0, 0,(),(),(d\000\000S),(),()",
             b"1, 1, 0, 0,(),(),(d\x00\x00S),(),()",
@@ -1838,17 +1823,6 @@ class LazyActionTestCase(unittest.TestCase):
             (3,6) : bytearray(b'0, 0, 0, 0,(N.),(),(d\x00S\x00),(),()'),
         }
 
-        # if TestCmd.IS_PY3 and TestCmd.IS_WINDOWS:
-        #     func_matches = [
-        #         b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()',
-        #         b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'
-        #         ]
-        # else:
-        #     func_matches = [
-        #         b"0, 0, 0, 0,(),(),(d\000\000S),(),()",
-        #         b"0, 0, 0, 0,(),(),(d\x00\x00S),(),()",
-        #         ]
-
         meth_matches = [
             b"1, 1, 0, 0,(),(),(d\000\000S),(),()",
             b"1, 1, 0, 0,(),(),(d\x00\x00S),(),()",
-- 
cgit v0.12


From ca74badb50c6874e5bf438ed9f8455e95e1897e0 Mon Sep 17 00:00:00 2001
From: William Deegan <bill@baddogconsulting.com>
Date: Tue, 1 Aug 2017 13:37:15 -0700
Subject: Update CHANGES.txt

---
 src/CHANGES.txt | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index c585eaf..c3cafca 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -40,6 +40,8 @@ may cause rebuilds.  In no case should rebuilds not happen.
       tool found is used)
     - Fixed MSVSProject example code (http://scons.tigris.org/issues/show_bug.cgi?id=2979)
     - Defined MS SDK 10.0 and Changed VS 2015 to use SDK 10.0
+    - Changes to Action Function and Action Class signiture creation.  NOTE: This will cause rebuilds
+      for many builds when upgrading to SCons 3.0
 
   From Daniel Holth:
     - Add basic support for PyPy (by deleting __slots__ from Node with a
-- 
cgit v0.12


From 8093bd31e5a12c8e17b9953732020c39c1eb09c4 Mon Sep 17 00:00:00 2001
From: William Deegan <bill@baddogconsulting.com>
Date: Tue, 1 Aug 2017 18:56:27 -0700
Subject: PY2/3 fix action function sigs to skip docstrings

---
 src/engine/SCons/Action.py      |  7 ++++---
 src/engine/SCons/ActionTests.py | 42 ++++++++++++++++++++---------------------
 2 files changed, 25 insertions(+), 24 deletions(-)

diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py
index 4449bea..d6fe30b 100644
--- a/src/engine/SCons/Action.py
+++ b/src/engine/SCons/Action.py
@@ -210,7 +210,7 @@ def _object_contents(obj):
                         return bytearray(repr(obj), 'utf-8')
 
 
-def _code_contents(code):
+def _code_contents(code, docstring=None):
     """Return the signature contents of a code object.
 
     By providing direct access to the code object of the
@@ -257,7 +257,8 @@ def _code_contents(code):
     # function. Note that we have to call _object_contents on each
     # constants because the code object of nested functions can
     # show-up among the constants.
-    z = [_object_contents(cc) for cc in code.co_consts]
+
+    z = [_object_contents(cc) for cc in code.co_consts[1:]]
     contents.extend(b',(')
     contents.extend(bytearray(',', 'utf-8').join(z))
     contents.extend(b')')
@@ -295,7 +296,7 @@ def _function_contents(func):
     func.__closure__  - None or a tuple of cells that contain bindings for the function's free variables.
     """
 
-    contents = [_code_contents(func.__code__)]
+    contents = [_code_contents(func.__code__, func.__doc__)]
 
     # The function contents depends on the value of defaults arguments
     if func.__defaults__:
diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py
index cf42307..9d856c9 100644
--- a/src/engine/SCons/ActionTests.py
+++ b/src/engine/SCons/ActionTests.py
@@ -1430,9 +1430,9 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
 
         # Since the python bytecode has per version differences, we need different expected results per version
         func_matches = {
-            (2,7) : bytearray(b'0, 0, 0, 0,(N.),(),(d\x00\x00S),(),()'),
-            (3,5) : bytearray(b'0, 0, 0, 0,(N.),(),(d\x00\x00S),(),()'),
-            (3,6) : bytearray(b'0, 0, 0, 0,(N.),(),(d\x00S\x00),(),()'),
+            (2,7) : bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'),
+            (3,5) : bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'),
+            (3,6) : bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'),
         }
 
         meth_matches = [
@@ -1593,15 +1593,15 @@ class FunctionActionTestCase(unittest.TestCase):
             pass
 
         func_matches = {
-            (2,7) : bytearray(b'0, 0, 0, 0,(N.),(),(d\x00\x00S),(),()'),
-            (3,5) : bytearray(b'0, 0, 0, 0,(N.),(),(d\x00\x00S),(),()'),
-            (3,6) : bytearray(b'0, 0, 0, 0,(N.),(),(d\x00S\x00),(),()'),
+            (2,7) : bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'),
+            (3,5) : bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'),
+            (3,6) : bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'),
         }
 
         meth_matches = {
-            (2,7) : bytearray(b'1, 1, 0, 0,(N.),(),(d\x00\x00S),(),()'),
-            (3,5) : bytearray(b'1, 1, 0, 0,(N.),(),(d\x00\x00S),(),()'),
-            (3,6) : bytearray(b'1, 1, 0, 0,(N.),(),(d\x00S\x00),(),()'),
+            (2,7) : bytearray(b'1, 1, 0, 0,(),(),(d\x00\x00S),(),()'),
+            (3,5) : bytearray(b'1, 1, 0, 0,(),(),(d\x00\x00S),(),()'),
+            (3,6) : bytearray(b'1, 1, 0, 0,(),(),(d\x00S\x00),(),()'),
         }
 
         def factory(act, **kw):
@@ -1818,9 +1818,9 @@ class LazyActionTestCase(unittest.TestCase):
 
 
         func_matches = {
-            (2,7) : bytearray(b'0, 0, 0, 0,(N.),(),(d\x00\x00S),(),()'),
-            (3,5) : bytearray(b'0, 0, 0, 0,(N.),(),(d\x00\x00S),(),()'),
-            (3,6) : bytearray(b'0, 0, 0, 0,(N.),(),(d\x00S\x00),(),()'),
+            (2,7) : bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'),
+            (3,5) : bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'),
+            (3,6) : bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'),
         }
 
         meth_matches = [
@@ -2058,9 +2058,9 @@ class ObjectContentsTestCase(unittest.TestCase):
 
         # Since the python bytecode has per version differences, we need different expected results per version
         expected = {
-            (2,7) : bytearray(b'3, 3, 0, 0,(A test function),(),(|\x00\x00S),(),()'),
-            (3,5) : bytearray(b'3, 3, 0, 0,(A test function),(),(|\x00\x00S),(),()'),
-            (3,6) : bytearray(b'3, 3, 0, 0,(A test function),(),(|\x00S\x00),(),()'),
+            (2,7) : bytearray(b'3, 3, 0, 0,(),(),(|\x00\x00S),(),()'),
+            (3,5) : bytearray(b'3, 3, 0, 0,(),(),(|\x00\x00S),(),()'),
+            (3,6) : bytearray(b'3, 3, 0, 0,(),(),(|\x00S\x00),(),()'),
         }
 
         c = SCons.Action._function_contents(func1)
@@ -2078,9 +2078,9 @@ class ObjectContentsTestCase(unittest.TestCase):
 
         # Since the python bytecode has per version differences, we need different expected results per version
         expected = {
-            (2,7): bytearray(b"{TestClass:__main__}[[[(<type \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<type \'object\'>,))]]]]{{1, 1, 0, 0,(N.,a,b),(a,b),(d\x01\x00|\x00\x00_\x00\x00d\x02\x00|\x00\x00_\x01\x00d\x00\x00S),(),(),2, 2, 0, 0,(N.),(),(d\x00\x00S),(),()}}{{{a=a,b=b}}}"),
-            (3,5): bytearray(b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,(N.,a,b),(a,b),(d\x01\x00|\x00\x00_\x00\x00d\x02\x00|\x00\x00_\x01\x00d\x00\x00S),(),(),2, 2, 0, 0,(N.),(),(d\x00\x00S),(),()}}{{{a=a,b=b}}}"),
-            (3,6): bytearray(b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,(N.,a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(N.),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}"),
+            (2,7): bytearray(b"{TestClass:__main__}[[[(<type \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<type \'object\'>,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01\x00|\x00\x00_\x00\x00d\x02\x00|\x00\x00_\x01\x00d\x00\x00S),(),(),2, 2, 0, 0,(),(),(d\x00\x00S),(),()}}{{{a=a,b=b}}}"),
+            (3,5): bytearray(b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01\x00|\x00\x00_\x00\x00d\x02\x00|\x00\x00_\x01\x00d\x00\x00S),(),(),2, 2, 0, 0,(),(),(d\x00\x00S),(),()}}{{{a=a,b=b}}}"),
+            (3,6): bytearray(b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}"),
         }
 
         assert c == expected[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected \n"+"\n"+repr(expected[sys.version_info[:2]])
@@ -2093,9 +2093,9 @@ class ObjectContentsTestCase(unittest.TestCase):
 
         # Since the python bytecode has per version differences, we need different expected results per version
         expected = {
-            (2,7) : bytearray(b'0, 0, 0, 0,(Hello, World!,N.),(),(d\x00\x00GHd\x01\x00S)'),
-            (3,5) : bytearray(b'0, 0, 0, 0,(Hello, World!,N.),(print),(e\x00\x00d\x00\x00\x83\x01\x00\x01d\x01\x00S)'),
-            (3,6) : bytearray(b'0, 0, 0, 0,(Hello, World!,N.),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)'),
+            (2,7) : bytearray(b'0, 0, 0, 0,(N.),(),(d\x00\x00GHd\x01\x00S)'),
+            (3,5) : bytearray(b'0, 0, 0, 0,(N.),(print),(e\x00\x00d\x00\x00\x83\x01\x00\x01d\x01\x00S)'),
+            (3,6) : bytearray(b'0, 0, 0, 0,(N.),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)'),
         }
 
         assert c == expected[sys.version_info[:2]], "Got\n"+repr(c)+"\nExpected \n"+"\n"+expected[sys.version_info[:2]]
-- 
cgit v0.12