I recently gave running SCons under PyPy a shot. It actually worked, except for running out of file descriptors due to the GC not closing things immediately.

I went through and added some explicit close calls (actually, mostly 'with's) and then it worked fine. It looks like you actually picked up most of them (probably all the important ones) in 8716da8, but in case you wanted, I thought I'd send a patch with a few more. (We're using 2.5.0, and I'm not sure if the commit cited above landed in time for .1 or after that.)

I don't know if the remainder matter though, or in the case of the MD5 change, if it's in code that's even called. (Though if it is called, it may well matter.) I just did a search for 'open' and made replacements for ones where it looked easy to do and I didn't have to think too hard. :-)

Evan

P.S. On scons.org, Development -> Development, the "Developer's Guidelines" link is broken.

P.P.S. The FAQ still has the question "why is SCons written for Python 2.4"

Index: Debug.py
===================================================================
--- Debug.py	(revision 137834)
+++ Debug.py	(working copy)
@@ -72,57 +72,58 @@
         file.write("%s: %d\n" % (classname, len(tracked_classes[classname])))
 
 def listLoggedInstances(classes, file=sys.stdout):
     for classname in string_to_classes(classes):
         file.write('\n%s:\n' % classname)
         for ref in tracked_classes[classname]:
             if inspect.isclass(ref):
                 obj = ref()
             else:
                 obj = ref
             if obj is not None:
                 file.write('    %s\n' % repr(obj))
 
 def dumpLoggedInstances(classes, file=sys.stdout):
     for classname in string_to_classes(classes):
         file.write('\n%s:\n' % classname)
         for ref in tracked_classes[classname]:
             obj = ref()
             if obj is not None:
                 file.write('    %s:\n' % obj)
                 for key, value in obj.__dict__.items():
                     file.write('        %20s : %s\n' % (key, value))
 
 
 
 if sys.platform[:5] == "linux":
     # Linux doesn't actually support memory usage stats from getrusage().
     def memory():
-        mstr = open('/proc/self/stat').read()
+        with open('/proc/self/stat') as f:
+            mstr = f.read()
         mstr = mstr.split()[22]
         return int(mstr)
 elif sys.platform[:6] == 'darwin':
     #TODO really get memory stats for OS X
     def memory():
         return 0
 else:
     try:
         import resource
     except ImportError:
         try:
             import win32process
             import win32api
         except ImportError:
             def memory():
                 return 0
         else:
             def memory():
                 process_handle = win32api.GetCurrentProcess()
                 memory_info = win32process.GetProcessMemoryInfo( process_handle )
                 return memory_info['PeakWorkingSetSize']
     else:
         def memory():
             res = resource.getrusage(resource.RUSAGE_SELF)
             return res[4]
 
 # returns caller's stack
 def caller_stack():
@@ -208,34 +209,35 @@
 def Trace(msg, file=None, mode='w', tstamp=None):
     """Write a trace message to a file.  Whenever a file is specified,
     it becomes the default for the next call to Trace()."""
     global TraceDefault
     global TimeStampDefault
     global PreviousTime
     if file is None:
         file = TraceDefault
     else:
         TraceDefault = file
     if tstamp is None:
         tstamp = TimeStampDefault
     else:
         TimeStampDefault = tstamp
     try:
         fp = TraceFP[file]
     except KeyError:
         try:
             fp = TraceFP[file] = open(file, mode)
         except TypeError:
             # Assume we were passed an open file pointer.
             fp = file
     if tstamp:
         now = time.time()
         fp.write('%8.4f %8.4f:  ' % (now - StartTime, now - PreviousTime))
         PreviousTime = now
     fp.write(msg)
     fp.flush()
+    fp.close()
 
 # Local Variables:
 # tab-width:4
 # indent-tabs-mode:nil
 # End:
 # vim: set expandtab tabstop=4 shiftwidth=4:
Index: Tool/linkloc.py
===================================================================
--- Tool/linkloc.py	(revision 137834)
+++ Tool/linkloc.py	(working copy)
@@ -27,58 +27,58 @@
 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 # 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/Tool/linkloc.py rel_2.5.0:3543:937e55cd78f7 2016/04/09 11:29:54 bdbaddog"
 
 import os.path
 import re
 
 import SCons.Action
 import SCons.Defaults
 import SCons.Errors
 import SCons.Tool
 import SCons.Util
 
 from SCons.Tool.MSCommon import msvs_exists, merge_default_version
 from SCons.Tool.PharLapCommon import addPharLapPaths
 
 _re_linker_command = re.compile(r'(\s)@\s*([^\s]+)')
 
 def repl_linker_command(m):
     # Replaces any linker command file directives (e.g. "@foo.lnk") with
     # the actual contents of the file.
     try:
-        f=open(m.group(2), "r")
-        return m.group(1) + f.read()
+        with open(m.group(2), "r") as f:
+            return m.group(1) + f.read()
     except IOError:
         # the linker should return an error if it can't
         # find the linker command file so we will remain quiet.
         # However, we will replace the @ with a # so we will not continue
         # to find it with recursive substitution
         return m.group(1) + '#' + m.group(2)
 
 class LinklocGenerator(object):
     def __init__(self, cmdline):
         self.cmdline = cmdline
 
     def __call__(self, env, target, source, for_signature):
         if for_signature:
             # Expand the contents of any linker command files recursively
             subs = 1
             strsub = env.subst(self.cmdline, target=target, source=source)
             while subs:
                 strsub, subs = _re_linker_command.subn(repl_linker_command, strsub)
             return strsub
         else:
             return "${TEMPFILE('" + self.cmdline + "')}"
 
 def generate(env):
     """Add Builders and construction variables for ar to an Environment."""
     SCons.Tool.createSharedLibBuilder(env)
     SCons.Tool.createProgBuilder(env)
 
     env['SUBST_CMD_FILE'] = LinklocGenerator
Index: Util.py
===================================================================
--- Util.py	(revision 137834)
+++ Util.py	(working copy)
@@ -1404,60 +1404,58 @@
     if name is None:
         name = function.func_name
     else:
         function = RenameFunction(function, name)
 
     if hasattr(obj, '__class__') and obj.__class__ is not type:
         # "obj" is an instance, so it gets a bound method.
         setattr(obj, name, MethodType(function, obj, obj.__class__))
     else:
         # "obj" is a class, so it gets an unbound method.
         setattr(obj, name, MethodType(function, None, obj))
 
 def RenameFunction(function, name):
     """
     Returns a function identical to the specified function, but with
     the specified name.
     """
     return FunctionType(function.func_code,
                         function.func_globals,
                         name,
                         function.func_defaults)
 
 
 md5 = False
 def MD5signature(s):
     return str(s)
 
 def MD5filesignature(fname, chunksize=65536):
-    f = open(fname, "rb")
-    result = f.read()
-    f.close()
-    return result
+    with open(fname, "rb") as f:
+        return f.read()
 
 try:
     import hashlib
 except ImportError:
     pass
 else:
     if hasattr(hashlib, 'md5'):
         md5 = True
         def MD5signature(s):
             m = hashlib.md5()
             m.update(str(s))
             return m.hexdigest()
 
         def MD5filesignature(fname, chunksize=65536):
             m = hashlib.md5()
             f = open(fname, "rb")
             while True:
                 blck = f.read(chunksize)
                 if not blck:
                     break
                 m.update(str(blck))
             f.close()
             return m.hexdigest()
             
 def MD5collect(signatures):
     """
     Collects a list of signatures into an aggregate signature.
 
Index: Variables/__init__.py
===================================================================
--- Variables/__init__.py	(revision 137834)
+++ Variables/__init__.py	(working copy)
@@ -150,57 +150,59 @@
             )
         """
         for o in optlist:
             self._do_add(*o)
 
 
     def Update(self, env, args=None):
         """
         Update an environment with the option variables.
 
         env - the environment to update.
         """
 
         values = {}
 
         # first set the defaults:
         for option in self.options:
             if not option.default is None:
                 values[option.key] = option.default
 
         # next set the value specified in the options file
         for filename in self.files:
             if os.path.exists(filename):
                 dir = os.path.split(os.path.abspath(filename))[0]
                 if dir:
                     sys.path.insert(0, dir)
                 try:
                     values['__name__'] = filename
-                    exec open(filename, 'rU').read() in {}, values
+                    with open(filename, 'rU') as f:
+                        contents = f.read()
+                    exec contents in {}, values
                 finally:
                     if dir:
                         del sys.path[0]
                     del values['__name__']
 
         # set the values specified on the command line
         if args is None:
             args = self.args
 
         for arg, value in args.items():
             added = False
             for option in self.options:
                 if arg in list(option.aliases) + [ option.key ]:
                     values[option.key] = value
                     added = True
             if not added:
                 self.unknown[arg] = value
 
         # put the variables in the environment:
         # (don't copy over variables that are not declared as options)
         for option in self.options:
             try:
                 env[option.key] = values[option.key]
             except KeyError:
                 pass
 
         # Call the convert functions:
         for option in self.options:
Index: cpp.py
===================================================================
--- cpp.py	(revision 137834)
+++ cpp.py	(working copy)
@@ -354,57 +354,58 @@
         eval()ing it in the C preprocessor namespace we use to
         track #define values.
         """
         t = CPP_to_Python(' '.join(t[1:]))
         try: return eval(t, self.cpp_namespace)
         except (NameError, TypeError): return 0
 
     def initialize_result(self, fname):
         self.result = [fname]
 
     def finalize_result(self, fname):
         return self.result[1:]
 
     def find_include_file(self, t):
         """
         Finds the #include file for a given preprocessor tuple.
         """
         fname = t[2]
         for d in self.searchpath[t[1]]:
             if d == os.curdir:
                 f = fname
             else:
                 f = os.path.join(d, fname)
             if os.path.isfile(f):
                 return f
         return None
 
     def read_file(self, file):
-        return open(file).read()
+        with open(file) as f:
+            return f.read()
 
     # Start and stop processing include lines.
 
     def start_handling_includes(self, t=None):
         """
         Causes the PreProcessor object to start processing #import,
         #include and #include_next lines.
 
         This method will be called when a #if, #ifdef, #ifndef or #elif
         evaluates True, or when we reach the #else in a #if, #ifdef,
         #ifndef or #elif block where a condition already evaluated
         False.
 
         """
         d = self.dispatch_table
         p = self.stack[-1] if self.stack else self.default_table
 
         for k in ('import', 'include', 'include_next'):
             d[k] = p[k]
 
     def stop_handling_includes(self, t=None):
         """
         Causes the PreProcessor object to stop processing #import,
         #include and #include_next lines.
 
         This method will be called when a #if, #ifdef, #ifndef or #elif
         evaluates False, or when we reach the #else in a #if, #ifdef,
         #ifndef or #elif block where a condition already evaluated True.
_______________________________________________
Scons-dev mailing list
[email protected]
https://pairlist2.pair.net/mailman/listinfo/scons-dev

Reply via email to