Index: third_party/google-endpoints/future/standard_library/__init__.py
|
diff --git a/third_party/google-endpoints/future/standard_library/__init__.py b/third_party/google-endpoints/future/standard_library/__init__.py
|
new file mode 100644
|
index 0000000000000000000000000000000000000000..aca58c3e5c7389835b1270c05b6ea87a7c95cbd7
|
--- /dev/null
|
+++ b/third_party/google-endpoints/future/standard_library/__init__.py
|
@@ -0,0 +1,813 @@
|
+"""
|
+Python 3 reorganized the standard library (PEP 3108). This module exposes
|
+several standard library modules to Python 2 under their new Python 3
|
+names.
|
+
|
+It is designed to be used as follows::
|
+
|
+ from future import standard_library
|
+ standard_library.install_aliases()
|
+
|
+And then these normal Py3 imports work on both Py3 and Py2::
|
+
|
+ import builtins
|
+ import copyreg
|
+ import queue
|
+ import reprlib
|
+ import socketserver
|
+ import winreg # on Windows only
|
+ import test.support
|
+ import html, html.parser, html.entites
|
+ import http, http.client, http.server
|
+ import http.cookies, http.cookiejar
|
+ import urllib.parse, urllib.request, urllib.response, urllib.error, urllib.robotparser
|
+ import xmlrpc.client, xmlrpc.server
|
+
|
+ import _thread
|
+ import _dummy_thread
|
+ import _markupbase
|
+
|
+ from itertools import filterfalse, zip_longest
|
+ from sys import intern
|
+ from collections import UserDict, UserList, UserString
|
+ from collections import OrderedDict, Counter # even on Py2.6
|
+ from subprocess import getoutput, getstatusoutput
|
+ from subprocess import check_output # even on Py2.6
|
+
|
+(The renamed modules and functions are still available under their old
|
+names on Python 2.)
|
+
|
+This is a cleaner alternative to this idiom (see
|
+http://docs.pythonsprints.com/python3_porting/py-porting.html)::
|
+
|
+ try:
|
+ import queue
|
+ except ImportError:
|
+ import Queue as queue
|
+
|
+
|
+Limitations
|
+-----------
|
+We don't currently support these modules, but would like to::
|
+
|
+ import dbm
|
+ import dbm.dumb
|
+ import dbm.gnu
|
+ import collections.abc # on Py33
|
+ import pickle # should (optionally) bring in cPickle on Python 2
|
+
|
+"""
|
+
|
+from __future__ import absolute_import, division, print_function
|
+
|
+import sys
|
+import logging
|
+import imp
|
+import contextlib
|
+import types
|
+import copy
|
+import os
|
+
|
+# Make a dedicated logger; leave the root logger to be configured
|
+# by the application.
|
+flog = logging.getLogger('future_stdlib')
|
+_formatter = logging.Formatter(logging.BASIC_FORMAT)
|
+_handler = logging.StreamHandler()
|
+_handler.setFormatter(_formatter)
|
+flog.addHandler(_handler)
|
+flog.setLevel(logging.WARN)
|
+
|
+from future.utils import PY2, PY3
|
+
|
+# The modules that are defined under the same names on Py3 but with
|
+# different contents in a significant way (e.g. submodules) are:
|
+# pickle (fast one)
|
+# dbm
|
+# urllib
|
+# test
|
+# email
|
+
|
+REPLACED_MODULES = set(['test', 'urllib', 'pickle', 'dbm']) # add email and dbm when we support it
|
+
|
+# The following module names are not present in Python 2.x, so they cause no
|
+# potential clashes between the old and new names:
|
+# http
|
+# html
|
+# tkinter
|
+# xmlrpc
|
+# Keys: Py2 / real module names
|
+# Values: Py3 / simulated module names
|
+RENAMES = {
|
+ # 'cStringIO': 'io', # there's a new io module in Python 2.6
|
+ # that provides StringIO and BytesIO
|
+ # 'StringIO': 'io', # ditto
|
+ # 'cPickle': 'pickle',
|
+ '__builtin__': 'builtins',
|
+ 'copy_reg': 'copyreg',
|
+ 'Queue': 'queue',
|
+ 'future.moves.socketserver': 'socketserver',
|
+ 'ConfigParser': 'configparser',
|
+ 'repr': 'reprlib',
|
+ # 'FileDialog': 'tkinter.filedialog',
|
+ # 'tkFileDialog': 'tkinter.filedialog',
|
+ # 'SimpleDialog': 'tkinter.simpledialog',
|
+ # 'tkSimpleDialog': 'tkinter.simpledialog',
|
+ # 'tkColorChooser': 'tkinter.colorchooser',
|
+ # 'tkCommonDialog': 'tkinter.commondialog',
|
+ # 'Dialog': 'tkinter.dialog',
|
+ # 'Tkdnd': 'tkinter.dnd',
|
+ # 'tkFont': 'tkinter.font',
|
+ # 'tkMessageBox': 'tkinter.messagebox',
|
+ # 'ScrolledText': 'tkinter.scrolledtext',
|
+ # 'Tkconstants': 'tkinter.constants',
|
+ # 'Tix': 'tkinter.tix',
|
+ # 'ttk': 'tkinter.ttk',
|
+ # 'Tkinter': 'tkinter',
|
+ '_winreg': 'winreg',
|
+ 'thread': '_thread',
|
+ 'dummy_thread': '_dummy_thread',
|
+ # 'anydbm': 'dbm', # causes infinite import loop
|
+ # 'whichdb': 'dbm', # causes infinite import loop
|
+ # anydbm and whichdb are handled by fix_imports2
|
+ # 'dbhash': 'dbm.bsd',
|
+ # 'dumbdbm': 'dbm.dumb',
|
+ # 'dbm': 'dbm.ndbm',
|
+ # 'gdbm': 'dbm.gnu',
|
+ 'future.moves.xmlrpc': 'xmlrpc',
|
+ # 'future.backports.email': 'email', # for use by urllib
|
+ # 'DocXMLRPCServer': 'xmlrpc.server',
|
+ # 'SimpleXMLRPCServer': 'xmlrpc.server',
|
+ # 'httplib': 'http.client',
|
+ # 'htmlentitydefs' : 'html.entities',
|
+ # 'HTMLParser' : 'html.parser',
|
+ # 'Cookie': 'http.cookies',
|
+ # 'cookielib': 'http.cookiejar',
|
+ # 'BaseHTTPServer': 'http.server',
|
+ # 'SimpleHTTPServer': 'http.server',
|
+ # 'CGIHTTPServer': 'http.server',
|
+ # 'future.backports.test': 'test', # primarily for renaming test_support to support
|
+ # 'commands': 'subprocess',
|
+ # 'urlparse' : 'urllib.parse',
|
+ # 'robotparser' : 'urllib.robotparser',
|
+ # 'abc': 'collections.abc', # for Py33
|
+ # 'future.utils.six.moves.html': 'html',
|
+ # 'future.utils.six.moves.http': 'http',
|
+ 'future.moves.html': 'html',
|
+ 'future.moves.http': 'http',
|
+ # 'future.backports.urllib': 'urllib',
|
+ # 'future.utils.six.moves.urllib': 'urllib',
|
+ 'future.moves._markupbase': '_markupbase',
|
+ }
|
+
|
+
|
+# It is complicated and apparently brittle to mess around with the
|
+# ``sys.modules`` cache in order to support "import urllib" meaning two
|
+# different things (Py2.7 urllib and backported Py3.3-like urllib) in different
|
+# contexts. So we require explicit imports for these modules.
|
+assert len(set(RENAMES.values()) & set(REPLACED_MODULES)) == 0
|
+
|
+
|
+# Harmless renames that we can insert.
|
+# These modules need names from elsewhere being added to them:
|
+# subprocess: should provide getoutput and other fns from commands
|
+# module but these fns are missing: getstatus, mk2arg,
|
+# mkarg
|
+# re: needs an ASCII constant that works compatibly with Py3
|
+
|
+# etc: see lib2to3/fixes/fix_imports.py
|
+
|
+# (New module name, new object name, old module name, old object name)
|
+MOVES = [('collections', 'UserList', 'UserList', 'UserList'),
|
+ ('collections', 'UserDict', 'UserDict', 'UserDict'),
|
+ ('collections', 'UserString','UserString', 'UserString'),
|
+ ('itertools', 'filterfalse','itertools', 'ifilterfalse'),
|
+ ('itertools', 'zip_longest','itertools', 'izip_longest'),
|
+ ('sys', 'intern','__builtin__', 'intern'),
|
+ # The re module has no ASCII flag in Py2, but this is the default.
|
+ # Set re.ASCII to a zero constant. stat.ST_MODE just happens to be one
|
+ # (and it exists on Py2.6+).
|
+ ('re', 'ASCII','stat', 'ST_MODE'),
|
+ ('base64', 'encodebytes','base64', 'encodestring'),
|
+ ('base64', 'decodebytes','base64', 'decodestring'),
|
+ ('subprocess', 'getoutput', 'commands', 'getoutput'),
|
+ ('subprocess', 'getstatusoutput', 'commands', 'getstatusoutput'),
|
+ ('subprocess', 'check_output', 'future.backports.misc', 'check_output'),
|
+ ('math', 'ceil', 'future.backports.misc', 'ceil'),
|
+ ('collections', 'OrderedDict', 'future.backports.misc', 'OrderedDict'),
|
+ ('collections', 'Counter', 'future.backports.misc', 'Counter'),
|
+ ('itertools', 'count', 'future.backports.misc', 'count'),
|
+ ('reprlib', 'recursive_repr', 'future.backports.misc', 'recursive_repr'),
|
+ ('functools', 'cmp_to_key', 'future.backports.misc', 'cmp_to_key'),
|
+
|
+# This is no use, since "import urllib.request" etc. still fails:
|
+# ('urllib', 'error', 'future.moves.urllib', 'error'),
|
+# ('urllib', 'parse', 'future.moves.urllib', 'parse'),
|
+# ('urllib', 'request', 'future.moves.urllib', 'request'),
|
+# ('urllib', 'response', 'future.moves.urllib', 'response'),
|
+# ('urllib', 'robotparser', 'future.moves.urllib', 'robotparser'),
|
+ ]
|
+
|
+
|
+# A minimal example of an import hook:
|
+# class WarnOnImport(object):
|
+# def __init__(self, *args):
|
+# self.module_names = args
|
+#
|
+# def find_module(self, fullname, path=None):
|
+# if fullname in self.module_names:
|
+# self.path = path
|
+# return self
|
+# return None
|
+#
|
+# def load_module(self, name):
|
+# if name in sys.modules:
|
+# return sys.modules[name]
|
+# module_info = imp.find_module(name, self.path)
|
+# module = imp.load_module(name, *module_info)
|
+# sys.modules[name] = module
|
+# flog.warning("Imported deprecated module %s", name)
|
+# return module
|
+
|
+
|
+class RenameImport(object):
|
+ """
|
+ A class for import hooks mapping Py3 module names etc. to the Py2 equivalents.
|
+ """
|
+ # Different RenameImport classes are created when importing this module from
|
+ # different source files. This causes isinstance(hook, RenameImport) checks
|
+ # to produce inconsistent results. We add this RENAMER attribute here so
|
+ # remove_hooks() and install_hooks() can find instances of these classes
|
+ # easily:
|
+ RENAMER = True
|
+
|
+ def __init__(self, old_to_new):
|
+ '''
|
+ Pass in a dictionary-like object mapping from old names to new
|
+ names. E.g. {'ConfigParser': 'configparser', 'cPickle': 'pickle'}
|
+ '''
|
+ self.old_to_new = old_to_new
|
+ both = set(old_to_new.keys()) & set(old_to_new.values())
|
+ assert (len(both) == 0 and
|
+ len(set(old_to_new.values())) == len(old_to_new.values())), \
|
+ 'Ambiguity in renaming (handler not implemented)'
|
+ self.new_to_old = dict((new, old) for (old, new) in old_to_new.items())
|
+
|
+ def find_module(self, fullname, path=None):
|
+ # Handles hierarchical importing: package.module.module2
|
+ new_base_names = set([s.split('.')[0] for s in self.new_to_old])
|
+ # Before v0.12: Was: if fullname in set(self.old_to_new) | new_base_names:
|
+ if fullname in new_base_names:
|
+ return self
|
+ return None
|
+
|
+ def load_module(self, name):
|
+ path = None
|
+ if name in sys.modules:
|
+ return sys.modules[name]
|
+ elif name in self.new_to_old:
|
+ # New name. Look up the corresponding old (Py2) name:
|
+ oldname = self.new_to_old[name]
|
+ module = self._find_and_load_module(oldname)
|
+ # module.__future_module__ = True
|
+ else:
|
+ module = self._find_and_load_module(name)
|
+ # In any case, make it available under the requested (Py3) name
|
+ sys.modules[name] = module
|
+ return module
|
+
|
+ def _find_and_load_module(self, name, path=None):
|
+ """
|
+ Finds and loads it. But if there's a . in the name, handles it
|
+ properly.
|
+ """
|
+ bits = name.split('.')
|
+ while len(bits) > 1:
|
+ # Treat the first bit as a package
|
+ packagename = bits.pop(0)
|
+ package = self._find_and_load_module(packagename, path)
|
+ try:
|
+ path = package.__path__
|
+ except AttributeError:
|
+ # This could be e.g. moves.
|
+ flog.debug('Package {0} has no __path__.'.format(package))
|
+ if name in sys.modules:
|
+ return sys.modules[name]
|
+ flog.debug('What to do here?')
|
+
|
+ name = bits[0]
|
+ module_info = imp.find_module(name, path)
|
+ return imp.load_module(name, *module_info)
|
+
|
+
|
+class hooks(object):
|
+ """
|
+ Acts as a context manager. Saves the state of sys.modules and restores it
|
+ after the 'with' block.
|
+
|
+ Use like this:
|
+
|
+ >>> from future import standard_library
|
+ >>> with standard_library.hooks():
|
+ ... import http.client
|
+ >>> import requests
|
+
|
+ For this to work, http.client will be scrubbed from sys.modules after the
|
+ 'with' block. That way the modules imported in the 'with' block will
|
+ continue to be accessible in the current namespace but not from any
|
+ imported modules (like requests).
|
+ """
|
+ def __enter__(self):
|
+ # flog.debug('Entering hooks context manager')
|
+ self.old_sys_modules = copy.copy(sys.modules)
|
+ self.hooks_were_installed = detect_hooks()
|
+ # self.scrubbed = scrub_py2_sys_modules()
|
+ install_hooks()
|
+ return self
|
+
|
+ def __exit__(self, *args):
|
+ # flog.debug('Exiting hooks context manager')
|
+ # restore_sys_modules(self.scrubbed)
|
+ if not self.hooks_were_installed:
|
+ remove_hooks()
|
+ # scrub_future_sys_modules()
|
+
|
+# Sanity check for is_py2_stdlib_module(): We aren't replacing any
|
+# builtin modules names:
|
+if PY2:
|
+ assert len(set(RENAMES.values()) & set(sys.builtin_module_names)) == 0
|
+
|
+
|
+def is_py2_stdlib_module(m):
|
+ """
|
+ Tries to infer whether the module m is from the Python 2 standard library.
|
+ This may not be reliable on all systems.
|
+ """
|
+ if PY3:
|
+ return False
|
+ if not 'stdlib_path' in is_py2_stdlib_module.__dict__:
|
+ stdlib_files = [contextlib.__file__, os.__file__, copy.__file__]
|
+ stdlib_paths = [os.path.split(f)[0] for f in stdlib_files]
|
+ if not len(set(stdlib_paths)) == 1:
|
+ # This seems to happen on travis-ci.org. Very strange. We'll try to
|
+ # ignore it.
|
+ flog.warn('Multiple locations found for the Python standard '
|
+ 'library: %s' % stdlib_paths)
|
+ # Choose the first one arbitrarily
|
+ is_py2_stdlib_module.stdlib_path = stdlib_paths[0]
|
+
|
+ if m.__name__ in sys.builtin_module_names:
|
+ return True
|
+
|
+ if hasattr(m, '__file__'):
|
+ modpath = os.path.split(m.__file__)
|
+ if (modpath[0].startswith(is_py2_stdlib_module.stdlib_path) and
|
+ 'site-packages' not in modpath[0]):
|
+ return True
|
+
|
+ return False
|
+
|
+
|
+def scrub_py2_sys_modules():
|
+ """
|
+ Removes any Python 2 standard library modules from ``sys.modules`` that
|
+ would interfere with Py3-style imports using import hooks. Examples are
|
+ modules with the same names (like urllib or email).
|
+
|
+ (Note that currently import hooks are disabled for modules like these
|
+ with ambiguous names anyway ...)
|
+ """
|
+ if PY3:
|
+ return {}
|
+ scrubbed = {}
|
+ for modulename in REPLACED_MODULES & set(RENAMES.keys()):
|
+ if not modulename in sys.modules:
|
+ continue
|
+
|
+ module = sys.modules[modulename]
|
+
|
+ if is_py2_stdlib_module(module):
|
+ flog.debug('Deleting (Py2) {} from sys.modules'.format(modulename))
|
+ scrubbed[modulename] = sys.modules[modulename]
|
+ del sys.modules[modulename]
|
+ return scrubbed
|
+
|
+
|
+def scrub_future_sys_modules():
|
+ """
|
+ Deprecated.
|
+ """
|
+ return {}
|
+
|
+class suspend_hooks(object):
|
+ """
|
+ Acts as a context manager. Use like this:
|
+
|
+ >>> from future import standard_library
|
+ >>> standard_library.install_hooks()
|
+ >>> import http.client
|
+ >>> # ...
|
+ >>> with standard_library.suspend_hooks():
|
+ >>> import requests # incompatible with ``future``'s standard library hooks
|
+
|
+ 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()
|
+ # self.scrubbed = scrub_future_sys_modules()
|
+ return self
|
+
|
+ def __exit__(self, *args):
|
+ if self.hooks_were_installed:
|
+ install_hooks()
|
+ # restore_sys_modules(self.scrubbed)
|
+
|
+
|
+def restore_sys_modules(scrubbed):
|
+ """
|
+ Add any previously scrubbed modules back to the sys.modules cache,
|
+ but only if it's safe to do so.
|
+ """
|
+ clash = set(sys.modules) & set(scrubbed)
|
+ if len(clash) != 0:
|
+ # If several, choose one arbitrarily to raise an exception about
|
+ first = list(clash)[0]
|
+ raise ImportError('future module {} clashes with Py2 module'
|
+ .format(first))
|
+ sys.modules.update(scrubbed)
|
+
|
+
|
+def install_aliases():
|
+ """
|
+ Monkey-patches the standard library in Py2.6/7 to provide
|
+ aliases for better Py3 compatibility.
|
+ """
|
+ if PY3:
|
+ return
|
+ # if hasattr(install_aliases, 'run_already'):
|
+ # return
|
+ for (newmodname, newobjname, oldmodname, oldobjname) in MOVES:
|
+ __import__(newmodname)
|
+ # We look up the module in sys.modules because __import__ just returns the
|
+ # top-level package:
|
+ newmod = sys.modules[newmodname]
|
+ # newmod.__future_module__ = True
|
+
|
+ __import__(oldmodname)
|
+ oldmod = sys.modules[oldmodname]
|
+
|
+ obj = getattr(oldmod, oldobjname)
|
+ setattr(newmod, newobjname, obj)
|
+
|
+ # Hack for urllib so it appears to have the same structure on Py2 as on Py3
|
+ import urllib
|
+ from future.backports.urllib import request
|
+ from future.backports.urllib import response
|
+ from future.backports.urllib import parse
|
+ from future.backports.urllib import error
|
+ from future.backports.urllib import robotparser
|
+ urllib.request = request
|
+ urllib.response = response
|
+ urllib.parse = parse
|
+ urllib.error = error
|
+ urllib.robotparser = robotparser
|
+ sys.modules['urllib.request'] = request
|
+ sys.modules['urllib.response'] = response
|
+ sys.modules['urllib.parse'] = parse
|
+ sys.modules['urllib.error'] = error
|
+ sys.modules['urllib.robotparser'] = robotparser
|
+
|
+ # Patch the test module so it appears to have the same structure on Py2 as on Py3
|
+ try:
|
+ import test
|
+ except ImportError:
|
+ pass
|
+ try:
|
+ from future.moves.test import support
|
+ except ImportError:
|
+ pass
|
+ else:
|
+ test.support = support
|
+ sys.modules['test.support'] = support
|
+
|
+ # Patch the dbm module so it appears to have the same structure on Py2 as on Py3
|
+ try:
|
+ import dbm
|
+ except ImportError:
|
+ pass
|
+ else:
|
+ from future.moves.dbm import dumb
|
+ dbm.dumb = dumb
|
+ sys.modules['dbm.dumb'] = dumb
|
+ try:
|
+ from future.moves.dbm import gnu
|
+ except ImportError:
|
+ pass
|
+ else:
|
+ dbm.gnu = gnu
|
+ sys.modules['dbm.gnu'] = gnu
|
+ try:
|
+ from future.moves.dbm import ndbm
|
+ except ImportError:
|
+ pass
|
+ else:
|
+ dbm.ndbm = ndbm
|
+ sys.modules['dbm.ndbm'] = ndbm
|
+
|
+ # install_aliases.run_already = True
|
+
|
+
|
+def install_hooks():
|
+ """
|
+ This function installs the future.standard_library import hook into
|
+ sys.meta_path.
|
+ """
|
+ if PY3:
|
+ return
|
+
|
+ install_aliases()
|
+
|
+ flog.debug('sys.meta_path was: {0}'.format(sys.meta_path))
|
+ flog.debug('Installing hooks ...')
|
+
|
+ # Add it unless it's there already
|
+ newhook = RenameImport(RENAMES)
|
+ if not detect_hooks():
|
+ sys.meta_path.append(newhook)
|
+ flog.debug('sys.meta_path is now: {0}'.format(sys.meta_path))
|
+
|
+
|
+def enable_hooks():
|
+ """
|
+ Deprecated. Use install_hooks() instead. This will be removed by
|
+ ``future`` v1.0.
|
+ """
|
+ install_hooks()
|
+
|
+
|
+def remove_hooks(scrub_sys_modules=False):
|
+ """
|
+ This function removes the import hook from sys.meta_path.
|
+ """
|
+ if PY3:
|
+ return
|
+ flog.debug('Uninstalling hooks ...')
|
+ # Loop backwards, so deleting items keeps the ordering:
|
+ for i, hook in list(enumerate(sys.meta_path))[::-1]:
|
+ if hasattr(hook, 'RENAMER'):
|
+ del sys.meta_path[i]
|
+
|
+ # Explicit is better than implicit. In the future the interface should
|
+ # probably change so that scrubbing the import hooks requires a separate
|
+ # function call. Left as is for now for backward compatibility with
|
+ # v0.11.x.
|
+ if scrub_sys_modules:
|
+ scrub_future_sys_modules()
|
+
|
+
|
+def disable_hooks():
|
+ """
|
+ Deprecated. Use remove_hooks() instead. This will be removed by
|
+ ``future`` v1.0.
|
+ """
|
+ remove_hooks()
|
+
|
+
|
+def detect_hooks():
|
+ """
|
+ Returns True if the import hooks are installed, False if not.
|
+ """
|
+ flog.debug('Detecting hooks ...')
|
+ present = any([hasattr(hook, 'RENAMER') for hook in sys.meta_path])
|
+ if present:
|
+ flog.debug('Detected.')
|
+ else:
|
+ flog.debug('Not detected.')
|
+ return present
|
+
|
+
|
+# As of v0.12, this no longer happens implicitly:
|
+# if not PY3:
|
+# install_hooks()
|
+
|
+
|
+if not hasattr(sys, 'py2_modules'):
|
+ sys.py2_modules = {}
|
+
|
+def cache_py2_modules():
|
+ """
|
+ Currently this function is unneeded, as we are not attempting to provide import hooks
|
+ for modules with ambiguous names: email, urllib, pickle.
|
+ """
|
+ if len(sys.py2_modules) != 0:
|
+ return
|
+ assert not detect_hooks()
|
+ import urllib
|
+ sys.py2_modules['urllib'] = urllib
|
+
|
+ import email
|
+ sys.py2_modules['email'] = email
|
+
|
+ import pickle
|
+ sys.py2_modules['pickle'] = pickle
|
+
|
+ # Not all Python installations have test module. (Anaconda doesn't, for example.)
|
+ # try:
|
+ # import test
|
+ # except ImportError:
|
+ # sys.py2_modules['test'] = None
|
+ # sys.py2_modules['test'] = test
|
+
|
+ # import dbm
|
+ # sys.py2_modules['dbm'] = dbm
|
+
|
+
|
+def import_(module_name, backport=False):
|
+ """
|
+ Pass a (potentially dotted) module name of a Python 3 standard library
|
+ module. This function imports the module compatibly on Py2 and Py3 and
|
+ returns the top-level module.
|
+
|
+ Example use:
|
+ >>> http = import_('http.client')
|
+ >>> http = import_('http.server')
|
+ >>> urllib = import_('urllib.request')
|
+
|
+ Then:
|
+ >>> conn = http.client.HTTPConnection(...)
|
+ >>> response = urllib.request.urlopen('http://mywebsite.com')
|
+ >>> # etc.
|
+
|
+ Use as follows:
|
+ >>> package_name = import_(module_name)
|
+
|
+ On Py3, equivalent to this:
|
+
|
+ >>> import module_name
|
+
|
+ On Py2, equivalent to this if backport=False:
|
+
|
+ >>> from future.moves import module_name
|
+
|
+ or to this if backport=True:
|
+
|
+ >>> from future.backports import module_name
|
+
|
+ except that it also handles dotted module names such as ``http.client``
|
+ The effect then is like this:
|
+
|
+ >>> from future.backports import module
|
+ >>> from future.backports.module import submodule
|
+ >>> module.submodule = submodule
|
+
|
+ Note that this would be a SyntaxError in Python:
|
+
|
+ >>> from future.backports import http.client
|
+
|
+ """
|
+ # Python 2.6 doesn't have importlib in the stdlib, so it requires
|
+ # the backported ``importlib`` package from PyPI as a dependency to use
|
+ # this function:
|
+ import importlib
|
+
|
+ if PY3:
|
+ return __import__(module_name)
|
+ else:
|
+ # client.blah = blah
|
+ # Then http.client = client
|
+ # etc.
|
+ if backport:
|
+ prefix = 'future.backports'
|
+ else:
|
+ prefix = 'future.moves'
|
+ parts = prefix.split('.') + module_name.split('.')
|
+
|
+ modules = []
|
+ for i, part in enumerate(parts):
|
+ sofar = '.'.join(parts[:i+1])
|
+ modules.append(importlib.import_module(sofar))
|
+ for i, part in reversed(list(enumerate(parts))):
|
+ if i == 0:
|
+ break
|
+ setattr(modules[i-1], part, modules[i])
|
+
|
+ # Return the next-most top-level module after future.backports / future.moves:
|
+ return modules[2]
|
+
|
+
|
+def from_import(module_name, *symbol_names, **kwargs):
|
+ """
|
+ Example use:
|
+ >>> HTTPConnection = from_import('http.client', 'HTTPConnection')
|
+ >>> HTTPServer = from_import('http.server', 'HTTPServer')
|
+ >>> urlopen, urlparse = from_import('urllib.request', 'urlopen', 'urlparse')
|
+
|
+ Equivalent to this on Py3:
|
+
|
+ >>> from module_name import symbol_names[0], symbol_names[1], ...
|
+
|
+ and this on Py2:
|
+
|
+ >>> from future.moves.module_name import symbol_names[0], ...
|
+
|
+ or:
|
+
|
+ >>> from future.backports.module_name import symbol_names[0], ...
|
+
|
+ except that it also handles dotted module names such as ``http.client``.
|
+ """
|
+
|
+ if PY3:
|
+ return __import__(module_name)
|
+ else:
|
+ if 'backport' in kwargs and bool(kwargs['backport']):
|
+ prefix = 'future.backports'
|
+ else:
|
+ prefix = 'future.moves'
|
+ parts = prefix.split('.') + module_name.split('.')
|
+ module = importlib.import_module(prefix + '.' + module_name)
|
+ output = [getattr(module, name) for name in symbol_names]
|
+ if len(output) == 1:
|
+ return output[0]
|
+ else:
|
+ return output
|
+
|
+
|
+class exclude_local_folder_imports(object):
|
+ """
|
+ A context-manager that prevents standard library modules like configparser
|
+ from being imported from the local python-future source folder on Py3.
|
+
|
+ (This was need prior to v0.16.0 because the presence of a configparser
|
+ folder would otherwise have prevented setuptools from running on Py3. Maybe
|
+ it's not needed any more?)
|
+ """
|
+ def __init__(self, *args):
|
+ assert len(args) > 0
|
+ self.module_names = args
|
+ # Disallow dotted module names like http.client:
|
+ if any(['.' in m for m in self.module_names]):
|
+ raise NotImplementedError('Dotted module names are not supported')
|
+
|
+ def __enter__(self):
|
+ self.old_sys_path = copy.copy(sys.path)
|
+ self.old_sys_modules = copy.copy(sys.modules)
|
+ if sys.version_info[0] < 3:
|
+ return
|
+ # The presence of all these indicates we've found our source folder,
|
+ # because `builtins` won't have been installed in site-packages by setup.py:
|
+ FUTURE_SOURCE_SUBFOLDERS = ['future', 'past', 'libfuturize', 'libpasteurize', 'builtins']
|
+
|
+ # Look for the future source folder:
|
+ for folder in self.old_sys_path:
|
+ if all([os.path.exists(os.path.join(folder, subfolder))
|
+ for subfolder in FUTURE_SOURCE_SUBFOLDERS]):
|
+ # Found it. Remove it.
|
+ sys.path.remove(folder)
|
+
|
+ # Ensure we import the system module:
|
+ for m in self.module_names:
|
+ # Delete the module and any submodules from sys.modules:
|
+ # for key in list(sys.modules):
|
+ # if key == m or key.startswith(m + '.'):
|
+ # try:
|
+ # del sys.modules[key]
|
+ # except KeyError:
|
+ # pass
|
+ try:
|
+ module = __import__(m, level=0)
|
+ except ImportError:
|
+ # There's a problem importing the system module. E.g. the
|
+ # winreg module is not available except on Windows.
|
+ pass
|
+
|
+ def __exit__(self, *args):
|
+ # Restore sys.path and sys.modules:
|
+ sys.path = self.old_sys_path
|
+ for m in set(self.old_sys_modules.keys()) - set(sys.modules.keys()):
|
+ sys.modules[m] = self.old_sys_modules[m]
|
+
|
+TOP_LEVEL_MODULES = ['builtins',
|
+ 'copyreg',
|
+ 'html',
|
+ 'http',
|
+ 'queue',
|
+ 'reprlib',
|
+ 'socketserver',
|
+ 'test',
|
+ 'tkinter',
|
+ 'winreg',
|
+ 'xmlrpc',
|
+ '_dummy_thread',
|
+ '_markupbase',
|
+ '_thread',
|
+ ]
|
+
|
+def import_top_level_modules():
|
+ with exclude_local_folder_imports(*TOP_LEVEL_MODULES):
|
+ for m in TOP_LEVEL_MODULES:
|
+ try:
|
+ __import__(m)
|
+ except ImportError: # e.g. winreg
|
+ pass
|
|