Index: third_party/cython/src/Cython/Utils.py |
diff --git a/third_party/cython/src/Cython/Utils.py b/third_party/cython/src/Cython/Utils.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..8594118f3294c309f47f4355ea3d9b76f594eda9 |
--- /dev/null |
+++ b/third_party/cython/src/Cython/Utils.py |
@@ -0,0 +1,415 @@ |
+# |
+# Cython -- Things that don't belong |
+# anywhere else in particular |
+# |
+ |
+import os, sys, re, codecs |
+ |
+modification_time = os.path.getmtime |
+ |
+def cached_function(f): |
+ cache = {} |
+ uncomputed = object() |
+ def wrapper(*args): |
+ res = cache.get(args, uncomputed) |
+ if res is uncomputed: |
+ res = cache[args] = f(*args) |
+ return res |
+ return wrapper |
+ |
+def cached_method(f): |
+ cache_name = '__%s_cache' % f.__name__ |
+ def wrapper(self, *args): |
+ cache = getattr(self, cache_name, None) |
+ if cache is None: |
+ cache = {} |
+ setattr(self, cache_name, cache) |
+ if args in cache: |
+ return cache[args] |
+ res = cache[args] = f(self, *args) |
+ return res |
+ return wrapper |
+ |
+def replace_suffix(path, newsuf): |
+ base, _ = os.path.splitext(path) |
+ return base + newsuf |
+ |
+def open_new_file(path): |
+ if os.path.exists(path): |
+ # Make sure to create a new file here so we can |
+ # safely hard link the output files. |
+ os.unlink(path) |
+ |
+ # we use the ISO-8859-1 encoding here because we only write pure |
+ # ASCII strings or (e.g. for file names) byte encoded strings as |
+ # Unicode, so we need a direct mapping from the first 256 Unicode |
+ # characters to a byte sequence, which ISO-8859-1 provides |
+ return codecs.open(path, "w", encoding="ISO-8859-1") |
+ |
+def castrate_file(path, st): |
+ # Remove junk contents from an output file after a |
+ # failed compilation. |
+ # Also sets access and modification times back to |
+ # those specified by st (a stat struct). |
+ try: |
+ f = open_new_file(path) |
+ except EnvironmentError: |
+ pass |
+ else: |
+ f.write( |
+ "#error Do not use this file, it is the result of a failed Cython compilation.\n") |
+ f.close() |
+ if st: |
+ os.utime(path, (st.st_atime, st.st_mtime-1)) |
+ |
+def file_newer_than(path, time): |
+ ftime = modification_time(path) |
+ return ftime > time |
+ |
+@cached_function |
+def search_include_directories(dirs, qualified_name, suffix, pos, |
+ include=False, sys_path=False): |
+ # Search the list of include directories for the given |
+ # file name. If a source file position is given, first |
+ # searches the directory containing that file. Returns |
+ # None if not found, but does not report an error. |
+ # The 'include' option will disable package dereferencing. |
+ # If 'sys_path' is True, also search sys.path. |
+ if sys_path: |
+ dirs = dirs + tuple(sys.path) |
+ if pos: |
+ file_desc = pos[0] |
+ from Cython.Compiler.Scanning import FileSourceDescriptor |
+ if not isinstance(file_desc, FileSourceDescriptor): |
+ raise RuntimeError("Only file sources for code supported") |
+ if include: |
+ dirs = (os.path.dirname(file_desc.filename),) + dirs |
+ else: |
+ dirs = (find_root_package_dir(file_desc.filename),) + dirs |
+ |
+ dotted_filename = qualified_name |
+ if suffix: |
+ dotted_filename += suffix |
+ if not include: |
+ names = qualified_name.split('.') |
+ package_names = tuple(names[:-1]) |
+ module_name = names[-1] |
+ module_filename = module_name + suffix |
+ package_filename = "__init__" + suffix |
+ |
+ for dir in dirs: |
+ path = os.path.join(dir, dotted_filename) |
+ if path_exists(path): |
+ return path |
+ if not include: |
+ package_dir = check_package_dir(dir, package_names) |
+ if package_dir is not None: |
+ path = os.path.join(package_dir, module_filename) |
+ if path_exists(path): |
+ return path |
+ path = os.path.join(dir, package_dir, module_name, |
+ package_filename) |
+ if path_exists(path): |
+ return path |
+ return None |
+ |
+ |
+@cached_function |
+def find_root_package_dir(file_path): |
+ dir = os.path.dirname(file_path) |
+ if file_path == dir: |
+ return dir |
+ elif is_package_dir(dir): |
+ return find_root_package_dir(dir) |
+ else: |
+ return dir |
+ |
+@cached_function |
+def check_package_dir(dir, package_names): |
+ for dirname in package_names: |
+ dir = os.path.join(dir, dirname) |
+ if not is_package_dir(dir): |
+ return None |
+ return dir |
+ |
+@cached_function |
+def is_package_dir(dir_path): |
+ for filename in ("__init__.py", |
+ "__init__.pyx", |
+ "__init__.pxd"): |
+ path = os.path.join(dir_path, filename) |
+ if path_exists(path): |
+ return 1 |
+ |
+@cached_function |
+def path_exists(path): |
+ # try on the filesystem first |
+ if os.path.exists(path): |
+ return True |
+ # figure out if a PEP 302 loader is around |
+ try: |
+ loader = __loader__ |
+ # XXX the code below assumes a 'zipimport.zipimporter' instance |
+ # XXX should be easy to generalize, but too lazy right now to write it |
+ archive_path = getattr(loader, 'archive', None) |
+ if archive_path: |
+ normpath = os.path.normpath(path) |
+ if normpath.startswith(archive_path): |
+ arcname = normpath[len(archive_path)+1:] |
+ try: |
+ loader.get_data(arcname) |
+ return True |
+ except IOError: |
+ return False |
+ except NameError: |
+ pass |
+ return False |
+ |
+# file name encodings |
+ |
+def decode_filename(filename): |
+ if isinstance(filename, unicode): |
+ return filename |
+ try: |
+ filename_encoding = sys.getfilesystemencoding() |
+ if filename_encoding is None: |
+ filename_encoding = sys.getdefaultencoding() |
+ filename = filename.decode(filename_encoding) |
+ except UnicodeDecodeError: |
+ pass |
+ return filename |
+ |
+# support for source file encoding detection |
+ |
+_match_file_encoding = re.compile(u"coding[:=]\s*([-\w.]+)").search |
+ |
+def detect_file_encoding(source_filename): |
+ f = open_source_file(source_filename, encoding="UTF-8", error_handling='ignore') |
+ try: |
+ return detect_opened_file_encoding(f) |
+ finally: |
+ f.close() |
+ |
+def detect_opened_file_encoding(f): |
+ # PEPs 263 and 3120 |
+ # Most of the time the first two lines fall in the first 250 chars, |
+ # and this bulk read/split is much faster. |
+ lines = f.read(250).split("\n") |
+ if len(lines) > 2: |
+ m = _match_file_encoding(lines[0]) or _match_file_encoding(lines[1]) |
+ if m: |
+ return m.group(1) |
+ else: |
+ return "UTF-8" |
+ else: |
+ # Fallback to one-char-at-a-time detection. |
+ f.seek(0) |
+ chars = [] |
+ for i in range(2): |
+ c = f.read(1) |
+ while c and c != u'\n': |
+ chars.append(c) |
+ c = f.read(1) |
+ encoding = _match_file_encoding(u''.join(chars)) |
+ if encoding: |
+ return encoding.group(1) |
+ return "UTF-8" |
+ |
+ |
+def skip_bom(f): |
+ """ |
+ Read past a BOM at the beginning of a source file. |
+ This could be added to the scanner, but it's *substantially* easier |
+ to keep it at this level. |
+ """ |
+ if f.read(1) != u'\uFEFF': |
+ f.seek(0) |
+ |
+ |
+normalise_newlines = re.compile(u'\r\n?|\n').sub |
+ |
+ |
+class NormalisedNewlineStream(object): |
+ """The codecs module doesn't provide universal newline support. |
+ This class is used as a stream wrapper that provides this |
+ functionality. The new 'io' in Py2.6+/3.x supports this out of the |
+ box. |
+ """ |
+ |
+ def __init__(self, stream): |
+ # let's assume .read() doesn't change |
+ self.stream = stream |
+ self._read = stream.read |
+ self.close = stream.close |
+ self.encoding = getattr(stream, 'encoding', 'UTF-8') |
+ |
+ def read(self, count=-1): |
+ data = self._read(count) |
+ if u'\r' not in data: |
+ return data |
+ if data.endswith(u'\r'): |
+ # may be missing a '\n' |
+ data += self._read(1) |
+ return normalise_newlines(u'\n', data) |
+ |
+ def readlines(self): |
+ content = [] |
+ data = self.read(0x1000) |
+ while data: |
+ content.append(data) |
+ data = self.read(0x1000) |
+ |
+ return u''.join(content).splitlines(True) |
+ |
+ def seek(self, pos): |
+ if pos == 0: |
+ self.stream.seek(0) |
+ else: |
+ raise NotImplementedError |
+ |
+ |
+io = None |
+if sys.version_info >= (2,6): |
+ try: |
+ import io |
+ except ImportError: |
+ pass |
+ |
+ |
+def open_source_file(source_filename, mode="r", |
+ encoding=None, error_handling=None, |
+ require_normalised_newlines=True): |
+ if encoding is None: |
+ # Most of the time the coding is unspecified, so be optimistic that |
+ # it's UTF-8. |
+ f = open_source_file(source_filename, encoding="UTF-8", mode=mode, error_handling='ignore') |
+ encoding = detect_opened_file_encoding(f) |
+ if (encoding == "UTF-8" |
+ and error_handling == 'ignore' |
+ and require_normalised_newlines): |
+ f.seek(0) |
+ skip_bom(f) |
+ return f |
+ else: |
+ f.close() |
+ # |
+ if not os.path.exists(source_filename): |
+ try: |
+ loader = __loader__ |
+ if source_filename.startswith(loader.archive): |
+ return open_source_from_loader( |
+ loader, source_filename, |
+ encoding, error_handling, |
+ require_normalised_newlines) |
+ except (NameError, AttributeError): |
+ pass |
+ # |
+ if io is not None: |
+ stream = io.open(source_filename, mode=mode, |
+ encoding=encoding, errors=error_handling) |
+ else: |
+ # codecs module doesn't have universal newline support |
+ stream = codecs.open(source_filename, mode=mode, |
+ encoding=encoding, errors=error_handling) |
+ if require_normalised_newlines: |
+ stream = NormalisedNewlineStream(stream) |
+ skip_bom(stream) |
+ return stream |
+ |
+ |
+def open_source_from_loader(loader, |
+ source_filename, |
+ encoding=None, error_handling=None, |
+ require_normalised_newlines=True): |
+ nrmpath = os.path.normpath(source_filename) |
+ arcname = nrmpath[len(loader.archive)+1:] |
+ data = loader.get_data(arcname) |
+ if io is not None: |
+ return io.TextIOWrapper(io.BytesIO(data), |
+ encoding=encoding, |
+ errors=error_handling) |
+ else: |
+ try: |
+ import cStringIO as StringIO |
+ except ImportError: |
+ import StringIO |
+ reader = codecs.getreader(encoding) |
+ stream = reader(StringIO.StringIO(data)) |
+ if require_normalised_newlines: |
+ stream = NormalisedNewlineStream(stream) |
+ return stream |
+ |
+def str_to_number(value): |
+ # note: this expects a string as input that was accepted by the |
+ # parser already |
+ if len(value) < 2: |
+ value = int(value, 0) |
+ elif value[0] == '0': |
+ if value[1] in 'xX': |
+ # hex notation ('0x1AF') |
+ value = int(value[2:], 16) |
+ elif value[1] in 'oO': |
+ # Py3 octal notation ('0o136') |
+ value = int(value[2:], 8) |
+ elif value[1] in 'bB': |
+ # Py3 binary notation ('0b101') |
+ value = int(value[2:], 2) |
+ else: |
+ # Py2 octal notation ('0136') |
+ value = int(value, 8) |
+ else: |
+ value = int(value, 0) |
+ return value |
+ |
+def long_literal(value): |
+ if isinstance(value, basestring): |
+ value = str_to_number(value) |
+ return not -2**31 <= value < 2**31 |
+ |
+# all() and any() are new in 2.5 |
+try: |
+ # Make sure to bind them on the module, as they will be accessed as |
+ # attributes |
+ all = all |
+ any = any |
+except NameError: |
+ def all(items): |
+ for item in items: |
+ if not item: |
+ return False |
+ return True |
+ |
+ def any(items): |
+ for item in items: |
+ if item: |
+ return True |
+ return False |
+ |
+@cached_function |
+def get_cython_cache_dir(): |
+ """get the cython cache dir |
+ |
+ Priority: |
+ |
+ 1. CYTHON_CACHE_DIR |
+ 2. (OS X): ~/Library/Caches/Cython |
+ (posix not OS X): XDG_CACHE_HOME/cython if XDG_CACHE_HOME defined |
+ 3. ~/.cython |
+ |
+ """ |
+ if 'CYTHON_CACHE_DIR' in os.environ: |
+ return os.environ['CYTHON_CACHE_DIR'] |
+ |
+ parent = None |
+ if os.name == 'posix': |
+ if sys.platform == 'darwin': |
+ parent = os.path.expanduser('~/Library/Caches') |
+ else: |
+ # this could fallback on ~/.cache |
+ parent = os.environ.get('XDG_CACHE_HOME') |
+ |
+ if parent and os.path.isdir(parent): |
+ return os.path.join(parent, 'cython') |
+ |
+ # last fallback: ~/.cython |
+ return os.path.expanduser(os.path.join('~', '.cython')) |