2 # Cython -- Things that don't belong
3 # anywhere else in particular
6 import os, sys, re, codecs
8 def replace_suffix(path, newsuf):
9 base, _ = os.path.splitext(path)
12 def open_new_file(path):
13 if os.path.exists(path):
14 # Make sure to create a new file here so we can
15 # safely hard link the output files.
18 # we use the ISO-8859-1 encoding here because we only write pure
19 # ASCII strings or (e.g. for file names) byte encoded strings as
20 # Unicode, so we need a direct mapping from the first 256 Unicode
21 # characters to a byte sequence, which ISO-8859-1 provides
22 return codecs.open(path, "w", encoding="ISO-8859-1")
24 def castrate_file(path, st):
25 # Remove junk contents from an output file after a
27 # Also sets access and modification times back to
28 # those specified by st (a stat struct).
30 f = open_new_file(path)
31 except EnvironmentError:
35 "#error Do not use this file, it is the result of a failed Cython compilation.\n")
38 os.utime(path, (st.st_atime, st.st_mtime-1))
40 def modification_time(path):
44 def file_newer_than(path, time):
45 ftime = modification_time(path)
48 def path_exists(path):
49 # try on the filesystem first
50 if os.path.exists(path):
52 # figure out if a PEP 302 loader is around
55 # XXX the code below assumes as 'zipimport.zipimporter' instance
56 # XXX should be easy to generalize, but too lazy right now to write it
57 if path.startswith(loader.archive):
58 nrmpath = os.path.normpath(path)
59 arcname = nrmpath[len(loader.archive)+1:]
61 loader.get_data(arcname)
69 # support for source file encoding detection
71 def encode_filename(filename):
72 if isinstance(filename, unicode):
75 filename_encoding = sys.getfilesystemencoding()
76 if filename_encoding is None:
77 filename_encoding = sys.getdefaultencoding()
78 filename = filename.decode(filename_encoding)
79 except UnicodeDecodeError:
83 _match_file_encoding = re.compile(u"coding[:=]\s*([-\w.]+)").search
85 def detect_file_encoding(source_filename):
87 f = open_source_file(source_filename, encoding="UTF-8", error_handling='ignore')
92 while c and c != u'\n':
95 encoding = _match_file_encoding(u''.join(chars))
97 return encoding.group(1)
102 normalise_newlines = re.compile(u'\r\n?|\n').sub
104 class NormalisedNewlineStream(object):
105 """The codecs module doesn't provide universal newline support.
106 This class is used as a stream wrapper that provides this
107 functionality. The new 'io' in Py2.6+/3.1+ supports this out of the
110 def __init__(self, stream):
111 # let's assume .read() doesn't change
112 self._read = stream.read
113 self.close = stream.close
114 self.encoding = getattr(stream, 'encoding', 'UTF-8')
116 def read(self, count):
117 data = self._read(count)
118 if u'\r' not in data:
120 if data.endswith(u'\r'):
121 # may be missing a '\n'
122 data += self._read(1)
123 return normalise_newlines(u'\n', data)
127 data = self._read(0x1000)
130 data = self._read(0x1000)
131 return u''.join(content).split(u'\n')
134 if sys.version_info >= (2,6):
140 def open_source_file(source_filename, mode="r",
141 encoding=None, error_handling=None,
142 require_normalised_newlines=True):
144 encoding = detect_file_encoding(source_filename)
148 if source_filename.startswith(loader.archive):
149 return open_source_from_loader(
150 loader, source_filename,
151 encoding, error_handling,
152 require_normalised_newlines)
153 except (NameError, AttributeError):
157 return io.open(source_filename, mode=mode,
158 encoding=encoding, errors=error_handling)
160 # codecs module doesn't have universal newline support
161 stream = codecs.open(source_filename, mode=mode,
162 encoding=encoding, errors=error_handling)
163 if require_normalised_newlines:
164 stream = NormalisedNewlineStream(stream)
167 def open_source_from_loader(loader,
169 encoding=None, error_handling=None,
170 require_normalised_newlines=True):
171 nrmpath = os.path.normpath(source_filename)
172 arcname = nrmpath[len(loader.archive)+1:]
173 data = loader.get_data(arcname)
175 return io.TextIOWrapper(io.BytesIO(data),
177 errors=error_handling)
180 import cStringIO as StringIO
183 reader = codecs.getreader(encoding)
184 stream = reader(StringIO.StringIO(data))
185 if require_normalised_newlines:
186 stream = NormalisedNewlineStream(stream)
189 def long_literal(value):
190 if isinstance(value, basestring):
194 value = int(value, 8)
195 elif value[1] in 'xX':
196 value = int(value[2:], 16)
199 return not -2**31 <= value < 2**31
201 def none_or_sub(s, data):