7e93a71efa73355a7d493a5082a4876fabb1aef1
[scons.git] / src / engine / SCons / compat / __init__.py
1 #
2 # __COPYRIGHT__
3 #
4 # Permission is hereby granted, free of charge, to any person obtaining
5 # a copy of this software and associated documentation files (the
6 # "Software"), to deal in the Software without restriction, including
7 # without limitation the rights to use, copy, modify, merge, publish,
8 # distribute, sublicense, and/or sell copies of the Software, and to
9 # permit persons to whom the Software is furnished to do so, subject to
10 # the following conditions:
11 #
12 # The above copyright notice and this permission notice shall be included
13 # in all copies or substantial portions of the Software.
14 #
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
16 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 #
23
24 __doc__ = """
25 SCons compatibility package for old Python versions
26
27 This subpackage holds modules that provide backwards-compatible
28 implementations of various things that we'd like to use in SCons but which
29 only show up in later versions of Python than the early, old version(s)
30 we still support.
31
32 Other code will not generally reference things in this package through
33 the SCons.compat namespace.  The modules included here add things to
34 the __builtin__ namespace or the global module list so that the rest
35 of our code can use the objects and names imported here regardless of
36 Python version.
37
38 Simply enough, things that go in the __builtin__ name space come from
39 our builtins module.
40
41 The rest of the things here will be in individual compatibility modules
42 that are either: 1) suitably modified copies of the future modules that
43 we want to use; or 2) backwards compatible re-implementations of the
44 specific portions of a future module's API that we want to use.
45
46 GENERAL WARNINGS:  Implementations of functions in the SCons.compat
47 modules are *NOT* guaranteed to be fully compliant with these functions in
48 later versions of Python.  We are only concerned with adding functionality
49 that we actually use in SCons, so be wary if you lift this code for
50 other uses.  (That said, making these more nearly the same as later,
51 official versions is still a desirable goal, we just don't need to be
52 obsessive about it.)
53
54 We name the compatibility modules with an initial '_scons_' (for example,
55 _scons_subprocess.py is our compatibility module for subprocess) so
56 that we can still try to import the real module name and fall back to
57 our compatibility module if we get an ImportError.  The import_as()
58 function defined below loads the module as the "real" name (without the
59 '_scons'), after which all of the "import {module}" statements in the
60 rest of our code will find our pre-loaded compatibility module.
61 """
62
63 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
64
65 def import_as(module, name):
66     """
67     Imports the specified module (from our local directory) as the
68     specified name.
69     """
70     import imp
71     import os.path
72     dir = os.path.split(__file__)[0]
73     file, filename, suffix_mode_type = imp.find_module(module, [dir])
74     imp.load_module(name, file, filename, suffix_mode_type)
75
76 import builtins
77
78 try:
79     import hashlib
80 except ImportError:
81     # Pre-2.5 Python has no hashlib module.
82     try:
83         import_as('_scons_hashlib', 'hashlib')
84     except ImportError:
85         # If we failed importing our compatibility module, it probably
86         # means this version of Python has no md5 module.  Don't do
87         # anything and let the higher layer discover this fact, so it
88         # can fall back to using timestamp.
89         pass
90
91 try:
92     set
93 except NameError:
94     # Pre-2.4 Python has no native set type
95     import_as('_scons_sets', 'sets')
96     import __builtin__, sets
97     __builtin__.set = sets.Set
98
99
100 import collections
101 try:
102     collections.UserDict
103 except AttributeError:
104     import UserDict
105     collections.UserDict = UserDict.UserDict
106     del UserDict
107 try:
108     collections.UserList
109 except AttributeError:
110     import UserList
111     collections.UserList = UserList.UserList
112     del UserList
113 try:
114     collections.UserString
115 except AttributeError:
116     import UserString
117     collections.UserString = UserString.UserString
118     del UserString
119
120
121 import fnmatch
122 try:
123     fnmatch.filter
124 except AttributeError:
125     # Pre-2.2 Python has no fnmatch.filter() function.
126     def filter(names, pat):
127         """Return the subset of the list NAMES that match PAT"""
128         import os,posixpath
129         result=[]
130         pat = os.path.normcase(pat)
131         if pat not in fnmatch._cache:
132             import re
133             res = fnmatch.translate(pat)
134             fnmatch._cache[pat] = re.compile(res)
135         match = fnmatch._cache[pat].match
136         if os.path is posixpath:
137             # normcase on posix is NOP. Optimize it away from the loop.
138             for name in names:
139                 if match(name):
140                     result.append(name)
141         else:
142             for name in names:
143                 if match(os.path.normcase(name)):
144                     result.append(name)
145         return result
146     fnmatch.filter = filter
147     del filter
148
149 try:
150     import io
151 except ImportError:
152     # Pre-2.6 Python has no io module.
153     import_as('_scons_io', 'io')
154
155 try:
156     import itertools
157 except ImportError:
158     # Pre-2.3 Python has no itertools module.
159     import_as('_scons_itertools', 'itertools')
160
161 # If we need the compatibility version of textwrap, it  must be imported
162 # before optparse, which uses it.
163 try:
164     import textwrap
165 except ImportError:
166     # Pre-2.3 Python has no textwrap module.
167     import_as('_scons_textwrap', 'textwrap')
168
169 try:
170     import optparse
171 except ImportError:
172     # Pre-2.3 Python has no optparse module.
173     import_as('_scons_optparse', 'optparse')
174
175 import os
176 try:
177     os.devnull
178 except AttributeError:
179     # Pre-2.4 Python has no os.devnull attribute
180     import sys
181     _names = sys.builtin_module_names
182     if 'posix' in _names:
183         os.devnull = '/dev/null'
184     elif 'nt' in _names:
185         os.devnull = 'nul'
186     os.path.devnull = os.devnull
187 try:
188     os.path.lexists
189 except AttributeError:
190     # Pre-2.4 Python has no os.path.lexists function
191     def lexists(path):
192         return os.path.exists(path) or os.path.islink(path)
193     os.path.lexists = lexists
194
195
196 try:
197     import platform
198 except ImportError:
199     # Pre-2.3 Python has no platform module.
200     import_as('_scons_platform', 'platform')
201
202
203 try:
204     import queue
205 except ImportError:
206     # Before Python 3.0, the 'queue' module was named 'Queue'.
207     import imp
208     file, filename, suffix_mode_type = imp.find_module('Queue')
209     imp.load_module('queue', file, filename, suffix_mode_type)
210
211
212 import shlex
213 try:
214     shlex.split
215 except AttributeError:
216     # Pre-2.3 Python has no shlex.split() function.
217     #
218     # The full white-space splitting semantics of shlex.split() are
219     # complicated to reproduce by hand, so just use a compatibility
220     # version of the shlex module cribbed from Python 2.5 with some
221     # minor modifications for older Python versions.
222     del shlex
223     import_as('_scons_shlex', 'shlex')
224
225
226 import shutil
227 try:
228     shutil.move
229 except AttributeError:
230     # Pre-2.3 Python has no shutil.move() function.
231     #
232     # Cribbed from Python 2.5.
233     import os
234
235     def move(src, dst):
236         """Recursively move a file or directory to another location.
237
238         If the destination is on our current filesystem, then simply use
239         rename.  Otherwise, copy src to the dst and then remove src.
240         A lot more could be done here...  A look at a mv.c shows a lot of
241         the issues this implementation glosses over.
242
243         """
244         try:
245             os.rename(src, dst)
246         except OSError:
247             if os.path.isdir(src):
248                 if shutil.destinsrc(src, dst):
249                     raise Error, "Cannot move a directory '%s' into itself '%s'." % (src, dst)
250                 shutil.copytree(src, dst, symlinks=True)
251                 shutil.rmtree(src)
252             else:
253                 shutil.copy2(src,dst)
254                 os.unlink(src)
255     shutil.move = move
256     del move
257
258     def destinsrc(src, dst):
259         src = os.path.abspath(src)
260         return os.path.abspath(dst)[:len(src)] == src
261     shutil.destinsrc = destinsrc
262     del destinsrc
263
264
265 try:
266     import subprocess
267 except ImportError:
268     # Pre-2.4 Python has no subprocess module.
269     import_as('_scons_subprocess', 'subprocess')
270
271 import sys
272 try:
273     sys.intern
274 except AttributeError:
275     # Pre-2.6 Python has no sys.intern() function.
276     import __builtin__
277     try:
278         sys.intern = __builtin__.intern
279     except AttributeError:
280         # Pre-2.x Python has no builtin intern() function.
281         def intern(x):
282            return x
283         sys.intern = intern
284         del intern
285 try:
286     sys.maxsize
287 except AttributeError:
288     # Pre-2.6 Python has no sys.maxsize attribute
289     # Wrapping sys in () is silly, but protects it from 2to3 renames fixer
290     sys.maxsize = (sys).maxint
291
292
293 import tempfile
294 try:
295     tempfile.mkstemp
296 except AttributeError:
297     # Pre-2.3 Python has no tempfile.mkstemp function, so try to simulate it.
298     # adapted from the mkstemp implementation in python 3.
299     import os
300     import errno
301     def mkstemp(*args, **kw):
302         text = False
303         # TODO (1.5)
304         #if 'text' in kw :
305         if 'text' in kw.keys() :
306             text = kw['text']
307             del kw['text']
308         elif len( args ) == 4 :
309             text = args[3]
310             args = args[:3]
311         flags = os.O_RDWR | os.O_CREAT | os.O_EXCL
312         if not text and hasattr( os, 'O_BINARY' ) :
313             flags = flags | os.O_BINARY
314         while True:
315             try :
316                 name = tempfile.mktemp(*args, **kw)
317                 fd = os.open( name, flags, 0600 )
318                 return (fd, os.path.abspath(name))
319             except OSError, e:
320                 if e.errno == errno.EEXIST:
321                     continue
322                 raise
323
324     tempfile.mkstemp = mkstemp
325     del mkstemp
326
327 try:
328     # pre-2.7 doesn't have the memoryview() built-in
329     memoryview
330 except NameError:
331     class memoryview:
332         from types import SliceType
333         def __init__(self, obj):
334             # wrapping buffer in () keeps the fixer from changing it
335             self.obj = (buffer)(obj)
336         def __getitem__(self, indx):
337             if isinstance(indx, self.SliceType):
338                 return self.obj[indx.start:indx.stop]
339             else:
340                 return self.obj[indx]
341     import __builtin__
342     __builtin__.memoryview = memoryview
343
344
345 # Local Variables:
346 # tab-width:4
347 # indent-tabs-mode:nil
348 # End:
349 # vim: set expandtab tabstop=4 shiftwidth=4: