Package lib :: Module compat
[hide private]
[frames] | no frames]

Source Code for Module lib.compat

  1  ############################################################################### 
  2  #                                                                             # 
  3  # Copyright (C) 2012-2014,2019,2024 Edward d'Auvergne                         # 
  4  #                                                                             # 
  5  # This file is part of the program relax (http://www.nmr-relax.com).          # 
  6  #                                                                             # 
  7  # This program is free software: you can redistribute it and/or modify        # 
  8  # it under the terms of the GNU General Public License as published by        # 
  9  # the Free Software Foundation, either version 3 of the License, or           # 
 10  # (at your option) any later version.                                         # 
 11  #                                                                             # 
 12  # This program is distributed in the hope that it will be useful,             # 
 13  # but WITHOUT ANY WARRANTY; without even the implied warranty of              # 
 14  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               # 
 15  # GNU General Public License for more details.                                # 
 16  #                                                                             # 
 17  # You should have received a copy of the GNU General Public License           # 
 18  # along with this program.  If not, see <http://www.gnu.org/licenses/>.       # 
 19  #                                                                             # 
 20  ############################################################################### 
 21   
 22  # Module docstring. 
 23  """Temporary module for allowing relax to support both Python 2 and 3.""" 
 24   
 25  # The platform script message. 
 26  try: 
 27      import platform 
 28  except ImportError: 
 29      print("The platform module cannot be imported.  For Python <= 2.2, try copying the platform.py file from http://hg.python.org/cpython/file/2.3/Lib/platform.py into your lib/pythonX.X/ directory.") 
 30      raise 
 31   
 32  # Python module imports. 
 33  try: 
 34      import bz2 
 35      from bz2 import BZ2File 
 36      bz2_module = True 
 37  except ImportError: 
 38      BZ2File = object 
 39      bz2_module = False 
 40      message = sys.exc_info()[1] 
 41      bz2_module_message = message.args[0] 
 42  try: 
 43      import gzip 
 44      gzip_module = True 
 45  except ImportError: 
 46      gzip = object 
 47      gzip_module = False 
 48      message = sys.exc_info()[1] 
 49      gzip_module_message = message.args[0] 
 50  try: 
 51      import io 
 52      io_module = True 
 53      from io import IOBase 
 54  except ImportError: 
 55      io_module = False 
 56      IOBase = None 
 57  import itertools 
 58  import os 
 59  import platform 
 60  import sys 
 61  import threading 
 62   
 63   
 64  # The operating system. 
 65  SYSTEM = platform.uname()[0] 
 66  if SYSTEM == 'Microsoft': 
 67      SYSTEM == 'Windows' 
 68   
 69  # The Python version. 
 70  PY_VERSION = sys.version_info[0] 
 71   
 72   
 73  # Special Python version specific imports.  These will be imported from here from the rest of relax. 
 74  #################################################################################################### 
 75   
 76  # The built-in module. 
 77  if PY_VERSION == 2: 
 78      import __builtin__ as builtins 
 79  else: 
 80      import builtins 
 81   
 82  # The queue module. 
 83  if PY_VERSION == 2: 
 84      import Queue as queue 
 85  else: 
 86      import queue 
 87   
 88  # The queue.Queue class 
 89  if PY_VERSION == 2: 
 90      from Queue import Queue as Queue2 
 91  else: 
 92      from queue import Queue as Queue3 
 93      Queue2 = Queue3 
 94   
 95  # The StringIO class. 
 96  if PY_VERSION == 2: 
 97      from cStringIO import StringIO 
 98  elif io_module: 
 99      from io import StringIO 
100  else: 
101      StringIO = None 
102   
103  # The unit test TextTestResult class. 
104  try: 
105      from unittest import TextTestResult    # Python 2.7 and above. 
106  except ImportError: 
107      from unittest import _TextTestResult as TextTestResult    # Python 2.6 and below. 
108   
109  # The pickle package. 
110  if PY_VERSION == 2: 
111      import cPickle as pickle 
112  else: 
113      import pickle 
114   
115  # Numpy. 
116  import numpy 
117  try: 
118      numpy.linalg.norm(numpy.ones((3, 3)), axis=1) 
119      numpy_norm_axis = True 
120  except: 
121      numpy_norm_axis = False 
122   
123  # Linux distribution information. 
124  try: 
125      from platform import linux_distribution 
126  except ImportError: 
127      try: 
128          from distro import linux_distribution 
129      except ImportError: 
130 - def linux_distribution(): return ["", "", ""]
131 132
133 -def bz2_open(file, mode='r'):
134 """Abstract the numerous ways BZ2 files are handled in Python. 135 136 @param file: The file path to open. 137 @type file: str 138 @keyword mode: The mode to open the file with. Only the values of 'r' and 'w' for reading and writing respectively are supported. 139 @type mode: str 140 @return: The bzip2 file object. 141 @rtype: file object 142 """ 143 144 # Check the mode. 145 if mode not in ['r', 'w']: 146 raise RelaxError("The mode '%s' must be one or 'r' or 'w'." % mode) 147 148 # Check if the bz2 module exists. 149 if not bz2_module: 150 if mode == 'r': 151 raise RelaxError("Cannot open the file %s, try uncompressing first. %s." % (file, bz2_module_message)) 152 else: 153 raise RelaxError("Cannot create bzip2 file %s, the bz2 Python module cannot be found." % file) 154 155 # Open the file for reading. 156 if mode == 'r': 157 # Python 3.3 text mode. 158 if sys.version_info[0] == 3 and sys.version_info[1] >= 3: 159 file_obj = bz2.open(file, 't') 160 161 # Python 3.0, 3.1 and 3.2 text mode. 162 elif sys.version_info[0] == 3 and sys.version_info[1] < 3: 163 file_obj = io.TextIOWrapper(Bzip2Fixed(file, 'r')) 164 165 # Python 2 text mode. 166 else: 167 file_obj = bz2.BZ2File(file, 'r') 168 169 # Open the file for writing. 170 elif mode == 'w': 171 # Python 3.3 text mode. 172 if sys.version_info[0] == 3 and sys.version_info[1] >= 3: 173 file_obj = bz2.open(file, 'wt') 174 175 # Python 3.0, 3.1 and 3.2 text mode. 176 elif sys.version_info[0] == 3 and sys.version_info[1] < 3: 177 file_obj = io.TextIOWrapper(Bzip2Fixed(file, 'w')) 178 179 # Python 2 text mode. 180 else: 181 file_obj = bz2.BZ2File(file, 'w') 182 183 # Return the file object. 184 return file_obj
185 186
187 -def gz_open(file, mode='r'):
188 """Abstract the numerous ways gzipped files are handled in Python. 189 190 @param file: The file path to open. 191 @type file: str 192 @keyword mode: The mode to open the file with. Only the values of 'r' and 'w' for reading and writing respectively are supported. 193 @type mode: str 194 @return: The gzipped file object. 195 @rtype: file object 196 """ 197 198 # Check the mode. 199 if mode not in ['r', 'w']: 200 raise RelaxError("The mode '%s' must be one or 'r' or 'w'." % mode) 201 202 # Check if the bz2 module exists. 203 if not gzip_module: 204 if mode == 'r': 205 raise RelaxError("Cannot open the file %s, try uncompressing first. %s." % (file, gzip_module_message)) 206 else: 207 raise RelaxError("Cannot create gzipped file %s, the bz2 Python module cannot be found." % file) 208 209 # Open the file for reading. 210 if mode == 'r': 211 # Python 3.3 text mode. 212 if sys.version_info[0] == 3 and sys.version_info[1] >= 3: 213 file_obj = gzip.open(file, 'rt') 214 215 # Python 3.0, 3.1 and 3.2 text mode. 216 elif sys.version_info[0] == 3 and sys.version_info[1] < 3: 217 file_obj = io.TextIOWrapper(GzipFixed(file, 'r')) 218 219 # Python 2 text mode. 220 else: 221 file_obj = gzip.GzipFile(file, 'r') 222 223 # Open the file for writing. 224 elif mode == 'w': 225 # Python 3.3 text mode. 226 if sys.version_info[0] == 3 and sys.version_info[1] >= 3: 227 file_obj = gzip.open(file, 'wt') 228 229 # Python 3.0, 3.1 and 3.2 text mode. 230 elif sys.version_info[0] == 3 and sys.version_info[1] < 3: 231 file_obj = io.TextIOWrapper(GzipFixed(file, 'w')) 232 233 # Python 2 text mode. 234 else: 235 file_obj = gzip.GzipFile(file, 'w') 236 237 # Return the file object. 238 return file_obj
239 240
241 -def from_iterable(items):
242 """Implementation of the itertools.chain.from_iterable() function for all Python versions. 243 244 @param items: The normal argument for itertools.chain.from_iterable(). 245 @type items: list 246 @return: The items of the list. 247 @rtype: unknown 248 """ 249 250 # Default to the normal function. 251 if hasattr(itertools.chain, 'from_iterable'): 252 return itertools.chain.from_iterable(items) 253 254 # Reimplement the function for earlier Python versions. 255 return from_iterable_pre_2_6(items)
256 257
258 -def from_iterable_pre_2_6(items):
259 """Replacement itertools.chain.from_iterable() function for Python < 2.6. 260 261 @param items: The normal argument for itertools.chain.from_iterable(). 262 @type items: list 263 @return: The elements 264 @rtype: unknown 265 """ 266 267 for item in items: 268 for element in item: 269 yield element
270 271
272 -def norm(x, ord=None, axis=None):
273 """Replacement numpy.linalg.norm() function to handle the axis argument for old numpy. 274 275 @param x: Input array. If `axis` is None, `x` must be 1-D or 2-D. 276 @type x: array_like 277 @keyword ord: Order of the norm (see table under ``Notes``). inf means numpy's `inf` object. 278 @type ord: {non-zero int, inf, -inf, 'fro'}, optional 279 @keyword axis: If `axis` is an integer, it specifies the axis of `x` along which to compute the vector norms. If `axis` is a 2-tuple, it specifies the axes that hold 2-D matrices, and the matrix norms of these matrices are computed. If `axis` is None then either a vector norm (when `x` is 1-D) or a matrix norm (when `x` is 2-D) is returned. 280 @type axis: {int, 2-tuple of ints, None}, optional 281 """ 282 283 # No axis argument given. 284 if axis == None: 285 return numpy.linalg.norm(x, ord=ord) 286 287 # The axis argument exists. 288 if numpy_norm_axis: 289 return numpy.linalg.norm(x, ord=ord, axis=axis) 290 291 # Support for older version (this is much slower). 292 return numpy.apply_along_axis(numpy.linalg.norm, axis, x)
293 294 295 296 # The inspect module. 297 inspect_getfullargspec = False 298 try: 299 from inspect import getfullargspec 300 inspect_getfullargspec = True 301 except ImportError: 302 from inspect import getargspec 303 304
305 -def getfullargspec_replacement(obj):
306 """Replacement inspect.getfullargspec() function for Python versions without it.""" 307 308 return getargspec(obj) + (None, None, None)
309 310 311 if not inspect_getfullargspec: 312 getfullargspec = getfullargspec_replacement 313 314
315 -class Bzip2Fixed(BZ2File):
316 """Incredibly nasty hack for bzip2 files support in Python 3.0, 3.1 and 3.2.""" 317
318 - def flush(self):
319 pass
320
321 - def read1(self, n):
322 return self.read(n)
323
324 - def readable(self):
325 return True
326
327 - def seekable(self):
328 return True
329
330 - def writable(self):
331 return True
332 333 334
335 -class GzipFixed(gzip.GzipFile):
336 """Incredibly nasty hack for gzipped files support in Python 3.0, 3.1 and 3.2.""" 337 338 closed = False 339
340 - def read1(self, n):
341 return self.read(n)
342
343 - def readable(self):
344 return True
345
346 - def seekable(self):
347 return True
348
349 - def writable(self):
350 return True
351 352 353
354 -class TaskQueue(Queue2):
355 """Python 2.4 and earlier Queuing class replacement. 356 357 This code comes from http://code.activestate.com/recipes/475160/ and is part of the Python sources from 2.5 onwards. 358 """ 359
360 - def __init__(self):
361 Queue2.__init__(self) 362 self.all_tasks_done = threading.Condition(self.mutex) 363 self.unfinished_tasks = 0
364
365 - def _put(self, item):
366 Queue2._put(self, item) 367 self.unfinished_tasks += 1
368
369 - def task_done(self):
370 """Indicate that a formerly enqueued task is complete. 371 372 Used by Queue consumer threads. For each get() used to fetch a task, 373 a subsequent call to task_done() tells the queue that the processing 374 on the task is complete. 375 376 If a join() is currently blocking, it will resume when all items 377 have been processed (meaning that a task_done() call was received 378 for every item that had been put() into the queue). 379 380 Raises a ValueError if called more times than there were items 381 placed in the queue. 382 """ 383 self.all_tasks_done.acquire() 384 try: 385 unfinished = self.unfinished_tasks - 1 386 if unfinished <= 0: 387 if unfinished < 0: 388 raise ValueError('task_done() called too many times') 389 self.all_tasks_done.notifyAll() 390 self.unfinished_tasks = unfinished 391 finally: 392 self.all_tasks_done.release()
393
394 - def join(self):
395 """Blocks until all items in the Queue have been gotten and processed. 396 397 The count of unfinished tasks goes up whenever an item is added to the 398 queue. The count goes down whenever a consumer thread calls task_done() 399 to indicate the item was retrieved and all work on it is complete. 400 401 When the count of unfinished tasks drops to zero, join() unblocks. 402 """ 403 self.all_tasks_done.acquire() 404 try: 405 while self.unfinished_tasks: 406 self.all_tasks_done.wait() 407 finally: 408 self.all_tasks_done.release()
409 410 411 # Alias the correct Queue. 412 if PY_VERSION == 2 and sys.version_info[1] <= 4: 413 Queue = TaskQueue # Alias the TaskQueue for Python 2.4 and earlier. 414 elif PY_VERSION == 2: 415 Queue = Queue2 416 else: 417 Queue = Queue3 418 419 420 # Python 2 hacks. 421 if PY_VERSION == 2: 422 # Switch all range() calls to xrange() for increased speed and memory reduction. 423 # This should work as all range() usage for Python 3 in relax must match the old xrange() usage. 424 #builtins.range = builtins.xrange 425 426 # The os.devnull object for Python 2.3 and earlier. 427 if sys.version_info[1] <= 3: 428 if SYSTEM == 'Linux': 429 os.devnull = '/dev/null' 430 elif SYSTEM == 'Windows': 431 os.devnull = 'nul' 432 elif SYSTEM == 'Darwin': 433 os.devnull = 'Dev:Null' 434 else: 435 os.devnull = None 436 437 # The unicode conversion function - essential for the GUI in Python 2. 438 unicode = builtins.unicode 439 440 # Unicode string handling. 441 from codecs import unicode_escape_decode
442 - def u(text):
443 """Create a unicode string for Python 2. 444 445 @param text: The text to convert. 446 @type text: str 447 @return: The text converted to unicode. 448 @rtype: unicode 449 """ 450 451 return unicode_escape_decode(text)[0]
452 453 454 # Python 3 work-arounds. 455 if PY_VERSION == 3: 456 # The unicode conversion function - essential for the GUI in Python 2. 457 unicode = builtins.str 458 459 # Unicode string handling.
460 - def u(text):
461 """Create a unicode string for Python 3. 462 463 @param text: The text to convert. 464 @type text: str 465 @return: The unmodified text, as all strings in Python 3 are unicode and the unicode type does not exist. 466 @rtype: str 467 """ 468 469 return text
470