diff planemo/lib/python3.7/site-packages/past/translation/__init__.py @ 1:56ad4e20f292 draft

"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author guerler
date Fri, 31 Jul 2020 00:32:28 -0400
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/planemo/lib/python3.7/site-packages/past/translation/__init__.py	Fri Jul 31 00:32:28 2020 -0400
@@ -0,0 +1,485 @@
+# -*- 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.translation 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))
+        return True
+    else:
+        logger.debug('Detected Python 3 code: {0}'.format(pathname))
+        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)
+
+                        code = compile(source, self.pathname, 'exec')
+
+                        dirname = os.path.dirname(cachename)
+                        try:
+                            if not os.path.exists(dirname):
+                                os.makedirs(dirname)
+                            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()
+
+
+# alias
+autotranslate = install_hooks