1""" 2Backported functions to implement the PEP 519 (Adding a file system path protocol) API. 3""" 4 5import abc 6import sys 7import io 8import pathlib 9 10try: 11 from os import PathLike, fspath, fsencode, fsdecode 12except ImportError: 13 14 class PathLike(abc.ABC): 15 """Abstract base class for implementing the file system path protocol.""" 16 17 @abc.abstractmethod 18 def __fspath__(self): 19 """Return the file system path representation of the object.""" 20 raise NotImplementedError 21 22 PathLike.register(pathlib.Path) 23 24 def fspath(path): 25 """Return the string representation of the path. 26 27 If str or bytes is passed in, it is returned unchanged. If __fspath__() 28 returns something other than str or bytes then TypeError is raised. If 29 this function is given something that is not str, bytes, or os.PathLike 30 then TypeError is raised. 31 """ 32 if isinstance(path, (str, bytes)): 33 return path 34 35 if isinstance(path, pathlib.Path): 36 return str(path) 37 38 # Work from the object's type to match method resolution of other magic 39 # methods. 40 path_type = type(path) 41 try: 42 path = path_type.__fspath__(path) 43 except AttributeError: 44 if hasattr(path_type, "__fspath__"): 45 raise 46 else: 47 if isinstance(path, (str, bytes)): 48 return path 49 else: 50 raise TypeError( 51 "expected __fspath__() to return str or bytes, " 52 "not " + type(path).__name__ 53 ) 54 55 raise TypeError( 56 "expected str, bytes or os.PathLike object, not " + path_type.__name__ 57 ) 58 59 def _fscodec(): 60 encoding = sys.getfilesystemencoding() 61 if encoding == "mbcs": 62 errors = "strict" 63 else: 64 errors = "surrogateescape" 65 66 def fsencode(filename): 67 """Encode filename (an os.PathLike, bytes, or str) to the filesystem 68 encoding with 'surrogateescape' error handler, return bytes unchanged. 69 On Windows, use 'strict' error handler if the file system encoding is 70 'mbcs' (which is the default encoding). 71 """ 72 filename = fspath(filename) # Does type-checking of `filename`. 73 if isinstance(filename, str): 74 return filename.encode(encoding, errors) 75 else: 76 return filename 77 78 def fsdecode(filename): 79 """Decode filename (an os.PathLike, bytes, or str) from the filesystem 80 encoding with 'surrogateescape' error handler, return str unchanged. On 81 Windows, use 'strict' error handler if the file system encoding is 82 'mbcs' (which is the default encoding). 83 """ 84 filename = fspath(filename) # Does type-checking of `filename`. 85 if isinstance(filename, bytes): 86 return filename.decode(encoding, errors) 87 else: 88 return filename 89 90 return fsencode, fsdecode 91 92 fsencode, fsdecode = _fscodec() 93 del _fscodec 94 95 def open(file, *pargs, **kwargs): 96 if isinstance(file, PathLike): 97 file = fspath(file) 98 return io.open(file, *pargs, **kwargs) 99