1# Copyright 2015 Google Inc. All Rights Reserved. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14"""Utilities for Python2 / Python3 compatibility.""" 15 16import codecs 17import io 18import os 19import sys 20 21PY3 = sys.version_info[0] >= 3 22PY36 = sys.version_info[0] >= 3 and sys.version_info[1] >= 6 23PY37 = sys.version_info[0] >= 3 and sys.version_info[1] >= 7 24PY38 = sys.version_info[0] >= 3 and sys.version_info[1] >= 8 25 26if PY3: 27 StringIO = io.StringIO 28 BytesIO = io.BytesIO 29 30 import codecs # noqa: F811 31 32 def open_with_encoding(filename, mode, encoding, newline=''): # pylint: disable=unused-argument # noqa 33 return codecs.open(filename, mode=mode, encoding=encoding) 34 35 import functools 36 lru_cache = functools.lru_cache 37 38 range = range 39 ifilter = filter 40 41 def raw_input(): 42 wrapper = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8') 43 return wrapper.buffer.raw.readall().decode('utf-8') 44 45 import configparser 46 47 # Mappings from strings to booleans (such as '1' to True, 'false' to False, 48 # etc.) 49 CONFIGPARSER_BOOLEAN_STATES = configparser.ConfigParser.BOOLEAN_STATES 50else: 51 import __builtin__ 52 import cStringIO 53 StringIO = BytesIO = cStringIO.StringIO 54 55 open_with_encoding = io.open 56 57 # Python 2.7 doesn't have a native LRU cache, so do nothing. 58 def lru_cache(maxsize=128, typed=False): 59 60 def fake_wrapper(user_function): 61 return user_function 62 63 return fake_wrapper 64 65 range = xrange # noqa: F821 66 67 from itertools import ifilter 68 raw_input = raw_input 69 70 import ConfigParser as configparser 71 CONFIGPARSER_BOOLEAN_STATES = configparser.ConfigParser._boolean_states # pylint: disable=protected-access # noqa 72 73 74def EncodeAndWriteToStdout(s, encoding='utf-8'): 75 """Encode the given string and emit to stdout. 76 77 The string may contain non-ascii characters. This is a problem when stdout is 78 redirected, because then Python doesn't know the encoding and we may get a 79 UnicodeEncodeError. 80 81 Arguments: 82 s: (string) The string to encode. 83 encoding: (string) The encoding of the string. 84 """ 85 if PY3: 86 sys.stdout.buffer.write(s.encode(encoding)) 87 elif sys.platform == 'win32': 88 # On python 2 and Windows universal newline transformation will be in 89 # effect on stdout. Python 2 will not let us avoid the easily because 90 # it happens based on whether the file handle is opened in O_BINARY or 91 # O_TEXT state. However we can tell Windows itself to change the current 92 # mode, and python 2 will follow suit. However we must take care to change 93 # the mode on the actual external stdout not just the current sys.stdout 94 # which may have been monkey-patched inside the python environment. 95 import msvcrt # pylint: disable=g-import-not-at-top 96 if sys.__stdout__ is sys.stdout: 97 msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) 98 sys.stdout.write(s.encode(encoding)) 99 else: 100 sys.stdout.write(s.encode(encoding)) 101 102 103if PY3: 104 basestring = str 105 unicode = str # pylint: disable=redefined-builtin,invalid-name 106else: 107 basestring = basestring 108 109 def unicode(s): # pylint: disable=invalid-name 110 """Force conversion of s to unicode.""" 111 return __builtin__.unicode(s, 'utf-8') 112 113 114# In Python 3.2+, readfp is deprecated in favor of read_file, which doesn't 115# exist in Python 2 yet. To avoid deprecation warnings, subclass ConfigParser to 116# fix this - now read_file works across all Python versions we care about. 117class ConfigParser(configparser.ConfigParser): 118 if not PY3: 119 120 def read_file(self, fp, source=None): 121 self.readfp(fp, filename=source) 122 123 124def removeBOM(source): 125 """Remove any Byte-order-Mark bytes from the beginning of a file.""" 126 bom = codecs.BOM_UTF8 127 if PY3: 128 bom = bom.decode('utf-8') 129 if source.startswith(bom): 130 return source[len(bom):] 131 return source 132