diff options
Diffstat (limited to 'Lib/importlib')
-rw-r--r-- | Lib/importlib/__init__.py | 3 | ||||
-rw-r--r-- | Lib/importlib/_bootstrap.py | 62 | ||||
-rw-r--r-- | Lib/importlib/test/__main__.py | 7 | ||||
-rw-r--r-- | Lib/importlib/test/regrtest.py | 7 |
4 files changed, 41 insertions, 38 deletions
diff --git a/Lib/importlib/__init__.py b/Lib/importlib/__init__.py index 2baaf93732..9b20367f35 100644 --- a/Lib/importlib/__init__.py +++ b/Lib/importlib/__init__.py @@ -81,11 +81,10 @@ except ImportError: except ImportError: raise ImportError('posix, nt, or os2 module required for importlib') _bootstrap._os = _os -import imp, sys, marshal, errno, _io +import imp, sys, marshal, _io _bootstrap.imp = imp _bootstrap.sys = sys _bootstrap.marshal = marshal -_bootstrap.errno = errno _bootstrap._io = _io import _warnings _bootstrap._warnings = _warnings diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index 425b8bf8c7..209e041b76 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -7,7 +7,7 @@ work. One should use importlib as the public-facing version of this module. """ -# Injected modules are '_warnings', 'imp', 'sys', 'marshal', 'errno', '_io', +# Injected modules are '_warnings', 'imp', 'sys', 'marshal', '_io', # and '_os' (a.k.a. 'posix', 'nt' or 'os2'). # Injected attribute is path_sep. # @@ -80,6 +80,30 @@ def _path_absolute(path): return _path_join(_os.getcwd(), path) +def _write_atomic(path, data): + """Best-effort function to write data to a path atomically. + Be prepared to handle a FileExistsError if concurrent writing of the + temporary file is attempted.""" + if not sys.platform.startswith('win'): + # On POSIX-like platforms, renaming is atomic. id() is used to generate + # a pseudo-random filename. + path_tmp = '{}.{}'.format(path, id(path)) + fd = _os.open(path_tmp, _os.O_EXCL | _os.O_CREAT | _os.O_WRONLY, 0o666) + try: + with _io.FileIO(fd, 'wb') as file: + file.write(data) + _os.rename(path_tmp, path) + except OSError: + try: + _os.unlink(path_tmp) + except OSError: + pass + raise + else: + with _io.FileIO(path, 'wb') as file: + file.write(data) + + def _wrap(new, old): """Simple substitute for functools.wraps.""" for replace in ['__module__', '__name__', '__doc__']: @@ -240,7 +264,7 @@ class BuiltinImporter: @classmethod @_requires_builtin def is_package(cls, fullname): - """Return None as built-in module are never packages.""" + """Return None as built-in modules are never packages.""" return False @@ -404,6 +428,7 @@ class SourceLoader(_LoaderBasics): else: found = marshal.loads(bytes_data) if isinstance(found, code_type): + imp._fix_co_filename(found, source_path) return found else: msg = "Non-code object in {}" @@ -479,28 +504,19 @@ class _SourceFileLoader(_FileLoader, SourceLoader): parent = _path_join(parent, part) try: _os.mkdir(parent) - except OSError as exc: + except FileExistsError: # Probably another Python process already created the dir. - if exc.errno == errno.EEXIST: - continue - else: - raise - except IOError as exc: + continue + except PermissionError: # If can't get proper access, then just forget about writing # the data. - if exc.errno == errno.EACCES: - return - else: - raise - try: - with _io.FileIO(path, 'wb') as file: - file.write(data) - except IOError as exc: - # Don't worry if you can't write bytecode. - if exc.errno == errno.EACCES: return - else: - raise + try: + _write_atomic(path, data) + except (PermissionError, FileExistsError): + # Don't worry if you can't write bytecode or someone is writing + # it at the same time. + pass class _SourcelessFileLoader(_FileLoader, _LoaderBasics): @@ -758,14 +774,14 @@ class _ImportLockContext: _IMPLICIT_META_PATH = [BuiltinImporter, FrozenImporter, _DefaultPathFinder] -_ERR_MSG = 'No module named {}' +_ERR_MSG = 'No module named {!r}' def _gcd_import(name, package=None, level=0): """Import and return the module based on its name, the package the call is being made from, and the level adjustment. This function represents the greatest common denominator of functionality - between import_module and __import__. This includes settting __package__ if + between import_module and __import__. This includes setting __package__ if the loader did not. """ @@ -855,7 +871,7 @@ def __import__(name, globals={}, locals={}, fromlist=[], level=0): module = _gcd_import(name) else: # __package__ is not guaranteed to be defined or could be set to None - # to represent that it's proper value is unknown + # to represent that its proper value is unknown package = globals.get('__package__') if package is None: package = globals['__name__'] diff --git a/Lib/importlib/test/__main__.py b/Lib/importlib/test/__main__.py index decc53d8c5..a1990b1f01 100644 --- a/Lib/importlib/test/__main__.py +++ b/Lib/importlib/test/__main__.py @@ -4,7 +4,6 @@ Specifying the ``--builtin`` flag will run tests, where applicable, with builtins.__import__ instead of importlib.__import__. """ -import importlib from importlib.test.import_ import util import os.path from test.support import run_unittest @@ -13,11 +12,7 @@ import unittest def test_main(): - if '__pycache__' in __file__: - parts = __file__.split(os.path.sep) - start_dir = sep.join(parts[:-2]) - else: - start_dir = os.path.dirname(__file__) + start_dir = os.path.dirname(__file__) top_dir = os.path.dirname(os.path.dirname(start_dir)) test_loader = unittest.TestLoader() if '--builtin' in sys.argv: diff --git a/Lib/importlib/test/regrtest.py b/Lib/importlib/test/regrtest.py index b103ae7d0e..dc0eb97022 100644 --- a/Lib/importlib/test/regrtest.py +++ b/Lib/importlib/test/regrtest.py @@ -5,13 +5,6 @@ invalidates are automatically skipped if the entire test suite is run. Otherwise all command-line options valid for test.regrtest are also valid for this script. -XXX FAILING - * test_import - - test_incorrect_code_name - file name differing between __file__ and co_filename (r68360 on trunk) - - test_import_by_filename - exception for trying to import by file name does not match - """ import importlib import sys |