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 if not isinstance(f, (io.FileIO, io.BufferedReader, io.BufferedWriter)):
51 return False
52 try:
53 # BufferedReader/Writer may raise OSError when
54 # fetching `fileno()` (e.g. when wrapping BytesIO).
55 f.fileno()
56 return True
57 except OSError:
58 return False
59
60def open_latin1(filename, mode='r'):
61 return open(filename, mode=mode, encoding='iso-8859-1')
62
63def sixu(s):
64 return s
65
66strchar = 'U'
67
68def getexception():
69 return sys.exc_info()[1]
70
71def asbytes_nested(x):
72 if hasattr(x, '__iter__') and not isinstance(x, (bytes, unicode)):
73 return [asbytes_nested(y) for y in x]
74 else:
75 return asbytes(x)
76
77def asunicode_nested(x):
78 if hasattr(x, '__iter__') and not isinstance(x, (bytes, unicode)):
79 return [asunicode_nested(y) for y in x]
80 else:
81 return asunicode(x)
82
83def is_pathlib_path(obj):
84 """
85 Check whether obj is a `pathlib.Path` object.
86
87 Prefer using ``isinstance(obj, os.PathLike)`` instead of this function.
88 """
89 return isinstance(obj, Path)
90
91# from Python 3.7
92class contextlib_nullcontext:
93 """Context manager that does no additional processing.
94
95 Used as a stand-in for a normal context manager, when a particular
96 block of code is only sometimes used with a normal context manager:
97
98 cm = optional_cm if condition else nullcontext()
99 with cm:
100 # Perform operation, using optional_cm if condition is True
101
102 .. note::
103 Prefer using `contextlib.nullcontext` instead of this context manager.
104 """
105
106 def __init__(self, enter_result=None):
107 self.enter_result = enter_result
108
109 def __enter__(self):
110 return self.enter_result
111
112 def __exit__(self, *excinfo):
113 pass
114
115
116def npy_load_module(name, fn, info=None):
117 """
118 Load a module. Uses ``load_module`` which will be deprecated in python
119 3.12. An alternative that uses ``exec_module`` is in
120 numpy.distutils.misc_util.exec_mod_from_location
121
122 .. versionadded:: 1.11.2
123
124 Parameters
125 ----------
126 name : str
127 Full module name.
128 fn : str
129 Path to module file.
130 info : tuple, optional
131 Only here for backward compatibility with Python 2.*.
132
133 Returns
134 -------
135 mod : module
136
137 """
138 # Explicitly lazy import this to avoid paying the cost
139 # of importing importlib at startup
140 from importlib.machinery import SourceFileLoader
141 return SourceFileLoader(name, fn).load_module()
142
143
144os_fspath = os.fspath
145os_PathLike = os.PathLike