1from __future__ import absolute_import 2 3import io 4import abc 5import sys 6import email 7 8 9if sys.version_info > (3,): # pragma: nocover 10 import builtins 11 from configparser import ConfigParser 12 from contextlib import suppress 13 FileNotFoundError = builtins.FileNotFoundError 14 IsADirectoryError = builtins.IsADirectoryError 15 NotADirectoryError = builtins.NotADirectoryError 16 PermissionError = builtins.PermissionError 17 map = builtins.map 18else: # pragma: nocover 19 from backports.configparser import ConfigParser 20 from itertools import imap as map # type: ignore 21 from contextlib2 import suppress # noqa 22 FileNotFoundError = IOError, OSError 23 IsADirectoryError = IOError, OSError 24 NotADirectoryError = IOError, OSError 25 PermissionError = IOError, OSError 26 27if sys.version_info > (3, 5): # pragma: nocover 28 import pathlib 29else: # pragma: nocover 30 import pathlib2 as pathlib 31 32try: 33 ModuleNotFoundError = builtins.FileNotFoundError 34except (NameError, AttributeError): # pragma: nocover 35 ModuleNotFoundError = ImportError # type: ignore 36 37 38if sys.version_info >= (3,): # pragma: nocover 39 from importlib.abc import MetaPathFinder 40else: # pragma: nocover 41 class MetaPathFinder(object): 42 __metaclass__ = abc.ABCMeta 43 44 45__metaclass__ = type 46__all__ = [ 47 'install', 'NullFinder', 'MetaPathFinder', 'ModuleNotFoundError', 48 'pathlib', 'ConfigParser', 'map', 'suppress', 'FileNotFoundError', 49 'NotADirectoryError', 'email_message_from_string', 50 ] 51 52 53def install(cls): 54 """ 55 Class decorator for installation on sys.meta_path. 56 57 Adds the backport DistributionFinder to sys.meta_path and 58 attempts to disable the finder functionality of the stdlib 59 DistributionFinder. 60 """ 61 sys.meta_path.append(cls()) 62 disable_stdlib_finder() 63 return cls 64 65 66def disable_stdlib_finder(): 67 """ 68 Give the backport primacy for discovering path-based distributions 69 by monkey-patching the stdlib O_O. 70 71 See #91 for more background for rationale on this sketchy 72 behavior. 73 """ 74 def matches(finder): 75 return ( 76 finder.__module__ == '_frozen_importlib_external' 77 and hasattr(finder, 'find_distributions') 78 ) 79 for finder in filter(matches, sys.meta_path): # pragma: nocover 80 del finder.find_distributions 81 82 83class NullFinder: 84 """ 85 A "Finder" (aka "MetaClassFinder") that never finds any modules, 86 but may find distributions. 87 """ 88 @staticmethod 89 def find_spec(*args, **kwargs): 90 return None 91 92 # In Python 2, the import system requires finders 93 # to have a find_module() method, but this usage 94 # is deprecated in Python 3 in favor of find_spec(). 95 # For the purposes of this finder (i.e. being present 96 # on sys.meta_path but having no other import 97 # system functionality), the two methods are identical. 98 find_module = find_spec 99 100 101def py2_message_from_string(text): # nocoverpy3 102 # Work around https://bugs.python.org/issue25545 where 103 # email.message_from_string cannot handle Unicode on Python 2. 104 io_buffer = io.StringIO(text) 105 return email.message_from_file(io_buffer) 106 107 108email_message_from_string = ( 109 py2_message_from_string 110 if sys.version_info < (3,) else 111 email.message_from_string 112 ) 113 114# https://bitbucket.org/pypy/pypy/issues/3021/ioopen-directory-leaks-a-file-descriptor 115PYPY_OPEN_BUG = getattr(sys, 'pypy_version_info', (9, 9, 9))[:3] <= (7, 1, 1) 116 117 118def ensure_is_path(ob): 119 """Construct a Path from ob even if it's already one. 120 Specialized for Python 3.4. 121 """ 122 if (3,) < sys.version_info < (3, 5): 123 ob = str(ob) # pragma: nocover 124 return pathlib.Path(ob) 125 126 127class PyPy_repr: 128 """ 129 Override repr for EntryPoint objects on PyPy to avoid __iter__ access. 130 Ref #97, #102. 131 """ 132 affected = hasattr(sys, 'pypy_version_info') 133 134 def __compat_repr__(self): # pragma: nocover 135 def make_param(name): 136 value = getattr(self, name) 137 return '{name}={value!r}'.format(**locals()) 138 params = ', '.join(map(make_param, self._fields)) 139 return 'EntryPoint({params})'.format(**locals()) 140 141 if affected: # pragma: nocover 142 __repr__ = __compat_repr__ 143 del affected 144