about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/past/translation
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/past/translation')
-rw-r--r--.venv/lib/python3.12/site-packages/past/translation/__init__.py453
1 files changed, 453 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/past/translation/__init__.py b/.venv/lib/python3.12/site-packages/past/translation/__init__.py
new file mode 100644
index 00000000..ae6c0d90
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/past/translation/__init__.py
@@ -0,0 +1,453 @@
+# -*- 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 sys
+# imp was deprecated in python 3.6
+if sys.version_info >= (3, 6):
+    import importlib as imp
+else:
+    import imp
+import logging
+import os
+import copy
+from lib2to3.pgen2.parse import ParseError
+from lib2to3.refactor import RefactoringTool
+
+from libfuturize import fixes
+
+try:
+    from importlib.machinery import (
+        PathFinder,
+        SourceFileLoader,
+    )
+except ImportError:
+    PathFinder = None
+    SourceFileLoader = object
+
+if sys.version_info[:2] < (3, 4):
+    import imp
+
+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
+
+
+def transform(source, pathname):
+    # 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, pathname)
+    except ParseError as e:
+        if e.msg != 'bad input' or e.value != '=':
+            raise
+        tree = RTs._rtp.refactor_string(source, 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
+
+
+class PastSourceFileLoader(SourceFileLoader):
+    exclude_paths = []
+    include_paths = []
+
+    def _convert_needed(self):
+        fullname = self.name
+        if 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
+        return convert
+
+    def _exec_transformed_module(self, module):
+        source = self.get_source(self.name)
+        pathname = self.path
+        if detect_python2(source, pathname):
+            source = transform(source, pathname)
+        code = compile(source, pathname, "exec")
+        exec(code, module.__dict__)
+
+    # For Python 3.3
+    def load_module(self, fullname):
+        logger.debug("Running load_module for %s", fullname)
+        if fullname in sys.modules:
+            mod = sys.modules[fullname]
+        else:
+            if self._convert_needed():
+                logger.debug("Autoconverting %s", fullname)
+                mod = imp.new_module(fullname)
+                sys.modules[fullname] = mod
+
+                # required by PEP 302
+                mod.__file__ = self.path
+                mod.__loader__ = self
+                if self.is_package(fullname):
+                    mod.__path__ = []
+                    mod.__package__ = fullname
+                else:
+                    mod.__package__ = fullname.rpartition('.')[0]
+                self._exec_transformed_module(mod)
+            else:
+                mod = super().load_module(fullname)
+        return mod
+
+    # For Python >=3.4
+    def exec_module(self, module):
+        logger.debug("Running exec_module for %s", module)
+        if self._convert_needed():
+            logger.debug("Autoconverting %s", self.name)
+            self._exec_transformed_module(module)
+        else:
+            super().exec_module(module)
+
+
+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
+
+    # For Python 3.3
+    def find_module(self, fullname, path=None):
+        logger.debug("Running find_module: (%s, %s)", fullname, path)
+        loader = PathFinder.find_module(fullname, path)
+        if not loader:
+            logger.debug("Py2Fixer could not find %s", fullname)
+            return None
+        loader.__class__ = PastSourceFileLoader
+        loader.exclude_paths = self.exclude_paths
+        loader.include_paths = self.include_paths
+        return loader
+
+    # For Python >=3.4
+    def find_spec(self, fullname, path=None, target=None):
+        logger.debug("Running find_spec: (%s, %s, %s)", fullname, path, target)
+        spec = PathFinder.find_spec(fullname, path, target)
+        if not spec:
+            logger.debug("Py2Fixer could not find %s", fullname)
+            return None
+        spec.loader.__class__ = PastSourceFileLoader
+        spec.loader.exclude_paths = self.exclude_paths
+        spec.loader.include_paths = self.include_paths
+        return spec
+
+
+_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