1"""
2Python 3.X compatibility tools.
3
4While this file was originally intended for Python 2 -> 3 transition,
5it is now used to create a compatibility layer between different
6minor versions of Python 3.
7
8While the active version of numpy may not support a given version of python, we
9allow downstream libraries to continue to use these shims for forward
10compatibility with numpy while they transition their code to newer versions of
11Python.
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
19import sys
20import os
21from pathlib import Path
22import io
23try:
24 import pickle5 as pickle
25except ImportError:
26 import pickle
27
28long = int
29integer_types = (int,)
30basestring = str
31unicode = str
32bytes = bytes
33
34def asunicode(s):
35 if isinstance(s, bytes):
36 return s.decode('latin1')
37 return str(s)
38
39def asbytes(s):
40 if isinstance(s, bytes):
41 return s
42 return str(s).encode('latin1')
43
44def asstr(s):
45 if isinstance(s, bytes):
46 return s.decode('latin1')
47 return str(s)
48
49def isfileobj(f):
50 return isinstance(f, (io.FileIO, io.BufferedReader, io.BufferedWriter))
51
52def _isfileobj(f):
53 if not isinstance(f, (io.FileIO, io.BufferedReader, io.BufferedWriter)):
54 return False
55 try:
56 # BufferedReader/Writer may raise OSError when
57 # fetching `fileno()` (e.g. when wrapping BytesIO).
58 f.fileno()
59 return True
60 except OSError:
61 return False
62
63def open_latin1(filename, mode='r'):
64 return open(filename, mode=mode, encoding='iso-8859-1')
65
66def sixu(s):
67 return s
68
69strchar = 'U'
70
71def getexception():
72 return sys.exc_info()[1]
73
74def asbytes_nested(x):
75 if hasattr(x, '__iter__') and not isinstance(x, (bytes, unicode)):
76 return [asbytes_nested(y) for y in x]
77 else:
78 return asbytes(x)
79
80def asunicode_nested(x):
81 if hasattr(x, '__iter__') and not isinstance(x, (bytes, unicode)):
82 return [asunicode_nested(y) for y in x]
83 else:
84 return asunicode(x)
85
86def is_pathlib_path(obj):
87 """
88 Check whether obj is a `pathlib.Path` object.
89
90 Prefer using ``isinstance(obj, os.PathLike)`` instead of this function.
91 """
92 return isinstance(obj, Path)
93
94# from Python 3.7
95class contextlib_nullcontext:
96 """Context manager that does no additional processing.
97
98 Used as a stand-in for a normal context manager, when a particular
99 block of code is only sometimes used with a normal context manager:
100
101 cm = optional_cm if condition else nullcontext()
102 with cm:
103 # Perform operation, using optional_cm if condition is True
104
105 .. note::
106 Prefer using `contextlib.nullcontext` instead of this context manager.
107 """
108
109 def __init__(self, enter_result=None):
110 self.enter_result = enter_result
111
112 def __enter__(self):
113 return self.enter_result
114
115 def __exit__(self, *excinfo):
116 pass
117
118
119def npy_load_module(name, fn, info=None):
120 """
121 Load a module. Uses ``load_module`` which will be deprecated in python
122 3.12. An alternative that uses ``exec_module`` is in
123 numpy.distutils.misc_util.exec_mod_from_location
124
125 .. versionadded:: 1.11.2
126
127 Parameters
128 ----------
129 name : str
130 Full module name.
131 fn : str
132 Path to module file.
133 info : tuple, optional
134 Only here for backward compatibility with Python 2.*.
135
136 Returns
137 -------
138 mod : module
139
140 """
141 # Explicitly lazy import this to avoid paying the cost
142 # of importing importlib at startup
143 from importlib.machinery import SourceFileLoader
144 return SourceFileLoader(name, fn).load_module()
145
146
147os_fspath = os.fspath
148os_PathLike = os.PathLike