Home | History | Annotate | Download | only in coverage
      1 """Add things to old Pythons so I can pretend they are newer."""
      2 
      3 # This file does lots of tricky stuff, so disable a bunch of lintisms.
      4 # pylint: disable=F0401,W0611,W0622
      5 # F0401: Unable to import blah
      6 # W0611: Unused import blah
      7 # W0622: Redefining built-in blah
      8 
      9 import os, sys
     10 
     11 # Python 2.3 doesn't have `set`
     12 try:
     13     set = set       # new in 2.4
     14 except NameError:
     15     from sets import Set as set
     16 
     17 # Python 2.3 doesn't have `sorted`.
     18 try:
     19     sorted = sorted
     20 except NameError:
     21     def sorted(iterable):
     22         """A 2.3-compatible implementation of `sorted`."""
     23         lst = list(iterable)
     24         lst.sort()
     25         return lst
     26 
     27 # Pythons 2 and 3 differ on where to get StringIO
     28 try:
     29     from cStringIO import StringIO
     30     BytesIO = StringIO
     31 except ImportError:
     32     from io import StringIO, BytesIO
     33 
     34 # What's a string called?
     35 try:
     36     string_class = basestring
     37 except NameError:
     38     string_class = str
     39 
     40 # Where do pickles come from?
     41 try:
     42     import cPickle as pickle
     43 except ImportError:
     44     import pickle
     45 
     46 # range or xrange?
     47 try:
     48     range = xrange
     49 except NameError:
     50     range = range
     51 
     52 # Exec is a statement in Py2, a function in Py3
     53 if sys.version_info >= (3, 0):
     54     def exec_code_object(code, global_map):
     55         """A wrapper around exec()."""
     56         exec(code, global_map)
     57 else:
     58     # OK, this is pretty gross.  In Py2, exec was a statement, but that will
     59     # be a syntax error if we try to put it in a Py3 file, even if it is never
     60     # executed.  So hide it inside an evaluated string literal instead.
     61     eval(
     62         compile(
     63             "def exec_code_object(code, global_map):\n"
     64             "    exec code in global_map\n",
     65             "<exec_function>", "exec"
     66             )
     67         )
     68 
     69 # ConfigParser was renamed to the more-standard configparser
     70 try:
     71     import configparser
     72 except ImportError:
     73     import ConfigParser as configparser
     74 
     75 # Python 3.2 provides `tokenize.open`, the best way to open source files.
     76 import tokenize
     77 try:
     78     open_source = tokenize.open     # pylint: disable=E1101
     79 except AttributeError:
     80     try:
     81         detect_encoding = tokenize.detect_encoding  # pylint: disable=E1101
     82     except AttributeError:
     83         def open_source(fname):
     84             """Open a source file the best way."""
     85             return open(fname, "rU")
     86     else:
     87         from io import TextIOWrapper
     88         # Copied from the 3.2 stdlib:
     89         def open_source(fname):
     90             """Open a file in read only mode using the encoding detected by
     91             detect_encoding().
     92             """
     93             buffer = open(fname, 'rb')
     94             encoding, _ = detect_encoding(buffer.readline)
     95             buffer.seek(0)
     96             text = TextIOWrapper(buffer, encoding, line_buffering=True)
     97             text.mode = 'r'
     98             return text
     99 
    100 # Python 3.x is picky about bytes and strings, so provide methods to
    101 # get them right, and make them no-ops in 2.x
    102 if sys.version_info >= (3, 0):
    103     def to_bytes(s):
    104         """Convert string `s` to bytes."""
    105         return s.encode('utf8')
    106 
    107     def to_string(b):
    108         """Convert bytes `b` to a string."""
    109         return b.decode('utf8')
    110 
    111 else:
    112     def to_bytes(s):
    113         """Convert string `s` to bytes (no-op in 2.x)."""
    114         return s
    115 
    116     def to_string(b):
    117         """Convert bytes `b` to a string (no-op in 2.x)."""
    118         return b
    119 
    120 # A few details about writing encoded text are different in 2.x and 3.x.
    121 if sys.version_info >= (3, 0):
    122     def write_encoded(fname, text, encoding='utf8', errors='strict'):
    123         '''Write string `text` to file names `fname`, with encoding.'''
    124         # Don't use "with", so that this file is still good for old 2.x.
    125         f = open(fname, 'w', encoding=encoding, errors=errors)
    126         try:
    127             f.write(text)
    128         finally:
    129             f.close()
    130 else:
    131     # It's not clear that using utf8 strings in 2.x is the right thing to do.
    132     def write_encoded(fname, text, encoding='utf8', errors='strict'):
    133         '''Write utf8 string `text` to file names `fname`, with encoding.'''
    134         import codecs
    135         f = codecs.open(fname, 'w', encoding=encoding, errors=errors)
    136         try:
    137             f.write(text.decode('utf8'))
    138         finally:
    139             f.close()
    140 
    141 # Md5 is available in different places.
    142 try:
    143     import hashlib
    144     md5 = hashlib.md5
    145 except ImportError:
    146     import md5
    147     md5 = md5.new
    148