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)
71 def decode_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 # support for source file encoding detection
85 _match_file_encoding = re.compile(u"coding[:=]\s*([-\w.]+)").search
87 def detect_file_encoding(source_filename):
89 f = open_source_file(source_filename, encoding="UTF-8", error_handling='ignore')
94 while c and c != u'\n':
97 encoding = _match_file_encoding(u''.join(chars))
99 return encoding.group(1)
104 normalise_newlines = re.compile(u'\r\n?|\n').sub
106 class NormalisedNewlineStream(object):
107 """The codecs module doesn't provide universal newline support.
108 This class is used as a stream wrapper that provides this
109 functionality. The new 'io' in Py2.6+/3.x supports this out of the
112 def __init__(self, stream):
113 # let's assume .read() doesn't change
114 self._read = stream.read
115 self.close = stream.close
116 self.encoding = getattr(stream, 'encoding', 'UTF-8')
118 def read(self, count=-1):
119 data = self._read(count)
120 if u'\r' not in data:
122 if data.endswith(u'\r'):
123 # may be missing a '\n'
124 data += self._read(1)
125 return normalise_newlines(u'\n', data)
129 data = self.read(0x1000)
132 data = self.read(0x1000)
133 return u''.join(content).split(u'\n')
136 if sys.version_info >= (2,6):
142 def open_source_file(source_filename, mode="r",
143 encoding=None, error_handling=None,
144 require_normalised_newlines=True):
146 encoding = detect_file_encoding(source_filename)
150 if source_filename.startswith(loader.archive):
151 return open_source_from_loader(
152 loader, source_filename,
153 encoding, error_handling,
154 require_normalised_newlines)
155 except (NameError, AttributeError):
159 return io.open(source_filename, mode=mode,
160 encoding=encoding, errors=error_handling)
162 # codecs module doesn't have universal newline support
163 stream = codecs.open(source_filename, mode=mode,
164 encoding=encoding, errors=error_handling)
165 if require_normalised_newlines:
166 stream = NormalisedNewlineStream(stream)
169 def open_source_from_loader(loader,
171 encoding=None, error_handling=None,
172 require_normalised_newlines=True):
173 nrmpath = os.path.normpath(source_filename)
174 arcname = nrmpath[len(loader.archive)+1:]
175 data = loader.get_data(arcname)
177 return io.TextIOWrapper(io.BytesIO(data),
179 errors=error_handling)
182 import cStringIO as StringIO
185 reader = codecs.getreader(encoding)
186 stream = reader(StringIO.StringIO(data))
187 if require_normalised_newlines:
188 stream = NormalisedNewlineStream(stream)
191 def str_to_number(value):
192 # note: this expects a string as input that was accepted by the
195 value = int(value, 0)
196 elif value[0] == '0':
198 # hex notation ('0x1AF')
199 value = int(value[2:], 16)
200 elif value[1] in 'oO':
201 # Py3 octal notation ('0o136')
202 value = int(value[2:], 8)
203 elif value[1] in 'bB':
204 # Py3 binary notation ('0b101')
205 value = int(value[2:], 2)
207 # Py2 octal notation ('0136')
208 value = int(value, 8)
210 value = int(value, 0)
213 def long_literal(value):
214 if isinstance(value, basestring):
215 value = str_to_number(value)
216 return not -2**31 <= value < 2**31
218 def none_or_sub(s, data):