1"""

2 Python 3.X compatibility tools.

3

4 While this file was originally intended for Python 2 -> 3 transition,

5 it is now used to create a compatibility layer between different

6 minor versions of Python 3.

7

8 While the active version of numpy may not support a given version of python, we

9 allow downstream libraries to continue to use these shims for forward

10 compatibility with numpy while they transition their code to newer versions of

11 Python.

12"""

13__all__ = ['bytes', 'asbytes', 'isfileobj', 'getexception', 'strchar',

14 'unicode', 'asunicode', 'asbytes_nested', 'asunicode_nested',

15 'asstr', 'open_latin1', 'long', 'basestring', 'sixu',

16 'integer_types', 'is_pathlib_path', 'npy_load_module', 'Path',

17 'pickle', 'contextlib_nullcontext', 'os_fspath', 'os_PathLike']

18

19 import sys

20 import os

21 from pathlib import Path

22 import io

23 try:

24 import pickle5 as pickle

25 except ImportError:

26 import pickle

27

28 long = int

29 integer_types = (int,)

30 basestring = str

31 unicode = str

32 bytes = bytes

33

34 def asunicode(s):

35 if isinstance(s, bytes):

36 return s.decode('latin1')

37 return str(s)

38

39 def asbytes(s):

40 if isinstance(s, bytes):

41 return s

42 return str(s).encode('latin1')

43

44 def asstr(s):

45 if isinstance(s, bytes):

46 return s.decode('latin1')

47 return str(s)

48

49 def isfileobj(f):

50 return isinstance(f, (io.FileIO, io.BufferedReader, io.BufferedWriter))

51

52 def open_latin1(filename, mode='r'):

53 return open(filename, mode=mode, encoding='iso-8859-1')

54

55 def sixu(s):

56 return s

57

58 strchar = 'U'

59

60 def getexception():

61 return sys.exc_info()[1]

62

63 def asbytes_nested(x):

64 if hasattr(x, '__iter__') and not isinstance(x, (bytes, unicode)):

65 return [asbytes_nested(y) for y in x]

66 else:

67 return asbytes(x)

68

69 def asunicode_nested(x):

70 if hasattr(x, '__iter__') and not isinstance(x, (bytes, unicode)):

71 return [asunicode_nested(y) for y in x]

72 else:

73 return asunicode(x)

74

75 def is_pathlib_path(obj):

76 """

77 Check whether obj is a `pathlib.Path` object.

78

79 Prefer using ``isinstance(obj, os.PathLike)`` instead of this function.

80 """

81 return isinstance(obj, Path)

82

83# from Python 3.7

84 class contextlib_nullcontext:

85 """Context manager that does no additional processing.

86

87 Used as a stand-in for a normal context manager, when a particular

88 block of code is only sometimes used with a normal context manager:

89

90 cm = optional_cm if condition else nullcontext()

91 with cm:

92 # Perform operation, using optional_cm if condition is True

93

94 .. note::

95 Prefer using `contextlib.nullcontext` instead of this context manager.

96 """

97

98 def __init__(self, enter_result=None):

99 self.enter_result = enter_result

100

101 def __enter__(self):

102 return self.enter_result

103

104 def __exit__(self, *excinfo):

105 pass

106

107

108 def npy_load_module(name, fn, info=None):

109 """

110 Load a module. Uses ``load_module`` which will be deprecated in python

111 3.12. An alternative that uses ``exec_module`` is in

112 numpy.distutils.misc_util.exec_mod_from_location

113

114 .. versionadded:: 1.11.2

115

116 Parameters

117 ----------

118 name : str

119 Full module name.

120 fn : str

121 Path to module file.

122 info : tuple, optional

123 Only here for backward compatibility with Python 2.*.

124

125 Returns

126 -------

127 mod : module

128

129 """

130 # Explicitly lazy import this to avoid paying the cost

131 # of importing importlib at startup

132 from importlib.machinery import SourceFileLoader

133 return SourceFileLoader(name, fn).load_module()

134

135

136 os_fspath = os.fspath

137 os_PathLike = os.PathLike