Index: third_party/google-endpoints/past/translation/__init__.py |
diff --git a/third_party/google-endpoints/past/translation/__init__.py b/third_party/google-endpoints/past/translation/__init__.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..7b21d9f5f10b26c87320d1ecd5cb79bbc111638b |
--- /dev/null |
+++ b/third_party/google-endpoints/past/translation/__init__.py |
@@ -0,0 +1,498 @@ |
+# -*- coding: utf-8 -*- |
+""" |
+past.translation |
+================== |
+ |
+The ``past.translation`` package provides an import hook for Python 3 which |
+transparently runs ``futurize`` fixers over Python 2 code on import to convert |
+print statements into functions, etc. |
+ |
+It is intended to assist users in migrating to Python 3.x even if some |
+dependencies still only support Python 2.x. |
+ |
+Usage |
+----- |
+ |
+Once your Py2 package is installed in the usual module search path, the import |
+hook is invoked as follows: |
+ |
+ >>> from past import autotranslate |
+ >>> autotranslate('mypackagename') |
+ |
+Or: |
+ |
+ >>> autotranslate(['mypackage1', 'mypackage2']) |
+ |
+You can unregister the hook using:: |
+ |
+ >>> from past.translation import remove_hooks |
+ >>> remove_hooks() |
+ |
+Author: Ed Schofield. |
+Inspired by and based on ``uprefix`` by Vinay M. Sajip. |
+""" |
+ |
+import imp |
+import logging |
+import marshal |
+import os |
+import sys |
+import copy |
+from lib2to3.pgen2.parse import ParseError |
+from lib2to3.refactor import RefactoringTool |
+ |
+from libfuturize import fixes |
+ |
+ |
+logger = logging.getLogger(__name__) |
+logger.setLevel(logging.DEBUG) |
+ |
+myfixes = (list(fixes.libfuturize_fix_names_stage1) + |
+ list(fixes.lib2to3_fix_names_stage1) + |
+ list(fixes.libfuturize_fix_names_stage2) + |
+ list(fixes.lib2to3_fix_names_stage2)) |
+ |
+ |
+# We detect whether the code is Py2 or Py3 by applying certain lib2to3 fixers |
+# to it. If the diff is empty, it's Python 3 code. |
+ |
+py2_detect_fixers = [ |
+# From stage 1: |
+ 'lib2to3.fixes.fix_apply', |
+ # 'lib2to3.fixes.fix_dict', # TODO: add support for utils.viewitems() etc. and move to stage2 |
+ 'lib2to3.fixes.fix_except', |
+ 'lib2to3.fixes.fix_execfile', |
+ 'lib2to3.fixes.fix_exitfunc', |
+ 'lib2to3.fixes.fix_funcattrs', |
+ 'lib2to3.fixes.fix_filter', |
+ 'lib2to3.fixes.fix_has_key', |
+ 'lib2to3.fixes.fix_idioms', |
+ 'lib2to3.fixes.fix_import', # makes any implicit relative imports explicit. (Use with ``from __future__ import absolute_import) |
+ 'lib2to3.fixes.fix_intern', |
+ 'lib2to3.fixes.fix_isinstance', |
+ 'lib2to3.fixes.fix_methodattrs', |
+ 'lib2to3.fixes.fix_ne', |
+ 'lib2to3.fixes.fix_numliterals', # turns 1L into 1, 0755 into 0o755 |
+ 'lib2to3.fixes.fix_paren', |
+ 'lib2to3.fixes.fix_print', |
+ 'lib2to3.fixes.fix_raise', # uses incompatible with_traceback() method on exceptions |
+ 'lib2to3.fixes.fix_renames', |
+ 'lib2to3.fixes.fix_reduce', |
+ # 'lib2to3.fixes.fix_set_literal', # this is unnecessary and breaks Py2.6 support |
+ 'lib2to3.fixes.fix_repr', |
+ 'lib2to3.fixes.fix_standarderror', |
+ 'lib2to3.fixes.fix_sys_exc', |
+ 'lib2to3.fixes.fix_throw', |
+ 'lib2to3.fixes.fix_tuple_params', |
+ 'lib2to3.fixes.fix_types', |
+ 'lib2to3.fixes.fix_ws_comma', |
+ 'lib2to3.fixes.fix_xreadlines', |
+ |
+# From stage 2: |
+ 'lib2to3.fixes.fix_basestring', |
+ # 'lib2to3.fixes.fix_buffer', # perhaps not safe. Test this. |
+ # 'lib2to3.fixes.fix_callable', # not needed in Py3.2+ |
+ # 'lib2to3.fixes.fix_dict', # TODO: add support for utils.viewitems() etc. |
+ 'lib2to3.fixes.fix_exec', |
+ # 'lib2to3.fixes.fix_future', # we don't want to remove __future__ imports |
+ 'lib2to3.fixes.fix_getcwdu', |
+ # 'lib2to3.fixes.fix_imports', # called by libfuturize.fixes.fix_future_standard_library |
+ # 'lib2to3.fixes.fix_imports2', # we don't handle this yet (dbm) |
+ # 'lib2to3.fixes.fix_input', |
+ # 'lib2to3.fixes.fix_itertools', |
+ # 'lib2to3.fixes.fix_itertools_imports', |
+ 'lib2to3.fixes.fix_long', |
+ # 'lib2to3.fixes.fix_map', |
+ # 'lib2to3.fixes.fix_metaclass', # causes SyntaxError in Py2! Use the one from ``six`` instead |
+ 'lib2to3.fixes.fix_next', |
+ 'lib2to3.fixes.fix_nonzero', # TODO: add a decorator for mapping __bool__ to __nonzero__ |
+ # 'lib2to3.fixes.fix_operator', # we will need support for this by e.g. extending the Py2 operator module to provide those functions in Py3 |
+ 'lib2to3.fixes.fix_raw_input', |
+ # 'lib2to3.fixes.fix_unicode', # strips off the u'' prefix, which removes a potentially helpful source of information for disambiguating unicode/byte strings |
+ # 'lib2to3.fixes.fix_urllib', |
+ 'lib2to3.fixes.fix_xrange', |
+ # 'lib2to3.fixes.fix_zip', |
+] |
+ |
+ |
+class RTs: |
+ """ |
+ A namespace for the refactoring tools. This avoids creating these at |
+ the module level, which slows down the module import. (See issue #117). |
+ |
+ There are two possible grammars: with or without the print statement. |
+ Hence we have two possible refactoring tool implementations. |
+ """ |
+ _rt = None |
+ _rtp = None |
+ _rt_py2_detect = None |
+ _rtp_py2_detect = None |
+ |
+ @staticmethod |
+ def setup(): |
+ """ |
+ Call this before using the refactoring tools to create them on demand |
+ if needed. |
+ """ |
+ if None in [RTs._rt, RTs._rtp]: |
+ RTs._rt = RefactoringTool(myfixes) |
+ RTs._rtp = RefactoringTool(myfixes, {'print_function': True}) |
+ |
+ |
+ @staticmethod |
+ def setup_detect_python2(): |
+ """ |
+ Call this before using the refactoring tools to create them on demand |
+ if needed. |
+ """ |
+ if None in [RTs._rt_py2_detect, RTs._rtp_py2_detect]: |
+ RTs._rt_py2_detect = RefactoringTool(py2_detect_fixers) |
+ RTs._rtp_py2_detect = RefactoringTool(py2_detect_fixers, |
+ {'print_function': True}) |
+ |
+ |
+# We need to find a prefix for the standard library, as we don't want to |
+# process any files there (they will already be Python 3). |
+# |
+# The following method is used by Sanjay Vinip in uprefix. This fails for |
+# ``conda`` environments: |
+# # In a non-pythonv virtualenv, sys.real_prefix points to the installed Python. |
+# # In a pythonv venv, sys.base_prefix points to the installed Python. |
+# # Outside a virtual environment, sys.prefix points to the installed Python. |
+ |
+# if hasattr(sys, 'real_prefix'): |
+# _syslibprefix = sys.real_prefix |
+# else: |
+# _syslibprefix = getattr(sys, 'base_prefix', sys.prefix) |
+ |
+# Instead, we use the portion of the path common to both the stdlib modules |
+# ``math`` and ``urllib``. |
+ |
+def splitall(path): |
+ """ |
+ Split a path into all components. From Python Cookbook. |
+ """ |
+ allparts = [] |
+ while True: |
+ parts = os.path.split(path) |
+ if parts[0] == path: # sentinel for absolute paths |
+ allparts.insert(0, parts[0]) |
+ break |
+ elif parts[1] == path: # sentinel for relative paths |
+ allparts.insert(0, parts[1]) |
+ break |
+ else: |
+ path = parts[0] |
+ allparts.insert(0, parts[1]) |
+ return allparts |
+ |
+ |
+def common_substring(s1, s2): |
+ """ |
+ Returns the longest common substring to the two strings, starting from the |
+ left. |
+ """ |
+ chunks = [] |
+ path1 = splitall(s1) |
+ path2 = splitall(s2) |
+ for (dir1, dir2) in zip(path1, path2): |
+ if dir1 != dir2: |
+ break |
+ chunks.append(dir1) |
+ return os.path.join(*chunks) |
+ |
+# _stdlibprefix = common_substring(math.__file__, urllib.__file__) |
+ |
+ |
+def detect_python2(source, pathname): |
+ """ |
+ Returns a bool indicating whether we think the code is Py2 |
+ """ |
+ RTs.setup_detect_python2() |
+ try: |
+ tree = RTs._rt_py2_detect.refactor_string(source, pathname) |
+ except ParseError as e: |
+ if e.msg != 'bad input' or e.value != '=': |
+ raise |
+ tree = RTs._rtp.refactor_string(source, pathname) |
+ |
+ if source != str(tree)[:-1]: # remove added newline |
+ # The above fixers made changes, so we conclude it's Python 2 code |
+ logger.debug('Detected Python 2 code: {0}'.format(pathname)) |
+ with open('/tmp/original_code.py', 'w') as f: |
+ f.write('### Original code (detected as py2): %s\n%s' % |
+ (pathname, source)) |
+ with open('/tmp/py2_detection_code.py', 'w') as f: |
+ f.write('### Code after running py3 detection (from %s)\n%s' % |
+ (pathname, str(tree)[:-1])) |
+ return True |
+ else: |
+ logger.debug('Detected Python 3 code: {0}'.format(pathname)) |
+ with open('/tmp/original_code.py', 'w') as f: |
+ f.write('### Original code (detected as py3): %s\n%s' % |
+ (pathname, source)) |
+ try: |
+ os.remove('/tmp/futurize_code.py') |
+ except OSError: |
+ pass |
+ return False |
+ |
+ |
+class Py2Fixer(object): |
+ """ |
+ An import hook class that uses lib2to3 for source-to-source translation of |
+ Py2 code to Py3. |
+ """ |
+ |
+ # See the comments on :class:future.standard_library.RenameImport. |
+ # We add this attribute here so remove_hooks() and install_hooks() can |
+ # unambiguously detect whether the import hook is installed: |
+ PY2FIXER = True |
+ |
+ def __init__(self): |
+ self.found = None |
+ self.base_exclude_paths = ['future', 'past'] |
+ self.exclude_paths = copy.copy(self.base_exclude_paths) |
+ self.include_paths = [] |
+ |
+ def include(self, paths): |
+ """ |
+ Pass in a sequence of module names such as 'plotrique.plotting' that, |
+ if present at the leftmost side of the full package name, would |
+ specify the module to be transformed from Py2 to Py3. |
+ """ |
+ self.include_paths += paths |
+ |
+ def exclude(self, paths): |
+ """ |
+ Pass in a sequence of strings such as 'mymodule' that, if |
+ present at the leftmost side of the full package name, would cause |
+ the module not to undergo any source transformation. |
+ """ |
+ self.exclude_paths += paths |
+ |
+ def find_module(self, fullname, path=None): |
+ logger.debug('Running find_module: {0}...'.format(fullname)) |
+ if '.' in fullname: |
+ parent, child = fullname.rsplit('.', 1) |
+ if path is None: |
+ loader = self.find_module(parent, path) |
+ mod = loader.load_module(parent) |
+ path = mod.__path__ |
+ fullname = child |
+ |
+ # Perhaps we should try using the new importlib functionality in Python |
+ # 3.3: something like this? |
+ # thing = importlib.machinery.PathFinder.find_module(fullname, path) |
+ try: |
+ self.found = imp.find_module(fullname, path) |
+ except Exception as e: |
+ logger.debug('Py2Fixer could not find {0}') |
+ logger.debug('Exception was: {0})'.format(fullname, e)) |
+ return None |
+ self.kind = self.found[-1][-1] |
+ if self.kind == imp.PKG_DIRECTORY: |
+ self.pathname = os.path.join(self.found[1], '__init__.py') |
+ elif self.kind == imp.PY_SOURCE: |
+ self.pathname = self.found[1] |
+ return self |
+ |
+ def transform(self, source): |
+ # This implementation uses lib2to3, |
+ # you can override and use something else |
+ # if that's better for you |
+ |
+ # lib2to3 likes a newline at the end |
+ RTs.setup() |
+ source += '\n' |
+ try: |
+ tree = RTs._rt.refactor_string(source, self.pathname) |
+ except ParseError as e: |
+ if e.msg != 'bad input' or e.value != '=': |
+ raise |
+ tree = RTs._rtp.refactor_string(source, self.pathname) |
+ # could optimise a bit for only doing str(tree) if |
+ # getattr(tree, 'was_changed', False) returns True |
+ return str(tree)[:-1] # remove added newline |
+ |
+ def load_module(self, fullname): |
+ logger.debug('Running load_module for {0}...'.format(fullname)) |
+ if fullname in sys.modules: |
+ mod = sys.modules[fullname] |
+ else: |
+ if self.kind in (imp.PY_COMPILED, imp.C_EXTENSION, imp.C_BUILTIN, |
+ imp.PY_FROZEN): |
+ convert = False |
+ # elif (self.pathname.startswith(_stdlibprefix) |
+ # and 'site-packages' not in self.pathname): |
+ # # We assume it's a stdlib package in this case. Is this too brittle? |
+ # # Please file a bug report at https://github.com/PythonCharmers/python-future |
+ # # if so. |
+ # convert = False |
+ # in theory, other paths could be configured to be excluded here too |
+ elif any([fullname.startswith(path) for path in self.exclude_paths]): |
+ convert = False |
+ elif any([fullname.startswith(path) for path in self.include_paths]): |
+ convert = True |
+ else: |
+ convert = False |
+ if not convert: |
+ logger.debug('Excluded {0} from translation'.format(fullname)) |
+ mod = imp.load_module(fullname, *self.found) |
+ else: |
+ logger.debug('Autoconverting {0} ...'.format(fullname)) |
+ mod = imp.new_module(fullname) |
+ sys.modules[fullname] = mod |
+ |
+ # required by PEP 302 |
+ mod.__file__ = self.pathname |
+ mod.__name__ = fullname |
+ mod.__loader__ = self |
+ |
+ # This: |
+ # mod.__package__ = '.'.join(fullname.split('.')[:-1]) |
+ # seems to result in "SystemError: Parent module '' not loaded, |
+ # cannot perform relative import" for a package's __init__.py |
+ # file. We use the approach below. Another option to try is the |
+ # minimal load_module pattern from the PEP 302 text instead. |
+ |
+ # Is the test in the next line more or less robust than the |
+ # following one? Presumably less ... |
+ # ispkg = self.pathname.endswith('__init__.py') |
+ |
+ if self.kind == imp.PKG_DIRECTORY: |
+ mod.__path__ = [ os.path.dirname(self.pathname) ] |
+ mod.__package__ = fullname |
+ else: |
+ #else, regular module |
+ mod.__path__ = [] |
+ mod.__package__ = fullname.rpartition('.')[0] |
+ |
+ try: |
+ cachename = imp.cache_from_source(self.pathname) |
+ if not os.path.exists(cachename): |
+ update_cache = True |
+ else: |
+ sourcetime = os.stat(self.pathname).st_mtime |
+ cachetime = os.stat(cachename).st_mtime |
+ update_cache = cachetime < sourcetime |
+ # # Force update_cache to work around a problem with it being treated as Py3 code??? |
+ # update_cache = True |
+ if not update_cache: |
+ with open(cachename, 'rb') as f: |
+ data = f.read() |
+ try: |
+ code = marshal.loads(data) |
+ except Exception: |
+ # pyc could be corrupt. Regenerate it |
+ update_cache = True |
+ if update_cache: |
+ if self.found[0]: |
+ source = self.found[0].read() |
+ elif self.kind == imp.PKG_DIRECTORY: |
+ with open(self.pathname) as f: |
+ source = f.read() |
+ |
+ if detect_python2(source, self.pathname): |
+ source = self.transform(source) |
+ with open('/tmp/futurized_code.py', 'w') as f: |
+ f.write('### Futurized code (from %s)\n%s' % |
+ (self.pathname, source)) |
+ |
+ code = compile(source, self.pathname, 'exec') |
+ |
+ dirname = os.path.dirname(cachename) |
+ if not os.path.exists(dirname): |
+ os.makedirs(dirname) |
+ try: |
+ with open(cachename, 'wb') as f: |
+ data = marshal.dumps(code) |
+ f.write(data) |
+ except Exception: # could be write-protected |
+ pass |
+ exec(code, mod.__dict__) |
+ except Exception as e: |
+ # must remove module from sys.modules |
+ del sys.modules[fullname] |
+ raise # keep it simple |
+ |
+ if self.found[0]: |
+ self.found[0].close() |
+ return mod |
+ |
+_hook = Py2Fixer() |
+ |
+ |
+def install_hooks(include_paths=(), exclude_paths=()): |
+ if isinstance(include_paths, str): |
+ include_paths = (include_paths,) |
+ if isinstance(exclude_paths, str): |
+ exclude_paths = (exclude_paths,) |
+ assert len(include_paths) + len(exclude_paths) > 0, 'Pass at least one argument' |
+ _hook.include(include_paths) |
+ _hook.exclude(exclude_paths) |
+ # _hook.debug = debug |
+ enable = sys.version_info[0] >= 3 # enabled for all 3.x |
+ if enable and _hook not in sys.meta_path: |
+ sys.meta_path.insert(0, _hook) # insert at beginning. This could be made a parameter |
+ |
+ # We could return the hook when there are ways of configuring it |
+ #return _hook |
+ |
+ |
+def remove_hooks(): |
+ if _hook in sys.meta_path: |
+ sys.meta_path.remove(_hook) |
+ |
+ |
+def detect_hooks(): |
+ """ |
+ Returns True if the import hooks are installed, False if not. |
+ """ |
+ return _hook in sys.meta_path |
+ # present = any([hasattr(hook, 'PY2FIXER') for hook in sys.meta_path]) |
+ # return present |
+ |
+ |
+class hooks(object): |
+ """ |
+ Acts as a context manager. Use like this: |
+ |
+ >>> from past import translation |
+ >>> with translation.hooks(): |
+ ... import mypy2module |
+ >>> import requests # py2/3 compatible anyway |
+ >>> # etc. |
+ """ |
+ def __enter__(self): |
+ self.hooks_were_installed = detect_hooks() |
+ install_hooks() |
+ return self |
+ |
+ def __exit__(self, *args): |
+ if not self.hooks_were_installed: |
+ remove_hooks() |
+ |
+ |
+class suspend_hooks(object): |
+ """ |
+ Acts as a context manager. Use like this: |
+ |
+ >>> from past import translation |
+ >>> translation.install_hooks() |
+ >>> import http.client |
+ >>> # ... |
+ >>> with translation.suspend_hooks(): |
+ >>> import requests # or others that support Py2/3 |
+ |
+ If the hooks were disabled before the context, they are not installed when |
+ the context is left. |
+ """ |
+ def __enter__(self): |
+ self.hooks_were_installed = detect_hooks() |
+ remove_hooks() |
+ return self |
+ def __exit__(self, *args): |
+ if self.hooks_were_installed: |
+ install_hooks() |
+ |