1# -*- coding: utf-8 -*- 2import inspect 3import os 4import re 5import sys 6from contextlib import contextmanager 7 8IS_WIN32 = sys.platform == "win32" 9 10 11def _setdoc(super): # @ReservedAssignment 12 """This inherits the docs on the current class. Not really needed for Python 3.5, 13 due to new behavoir of inspect.getdoc, but still doesn't hurt.""" 14 15 def deco(func): 16 func.__doc__ = getattr(getattr(super, func.__name__, None), "__doc__", None) 17 return func 18 19 return deco 20 21 22class ProcInfo(object): 23 def __init__(self, pid, uid, stat, args): 24 self.pid = pid 25 self.uid = uid 26 self.stat = stat 27 self.args = args 28 29 def __repr__(self): 30 return "ProcInfo({!r}, {!r}, {!r}, {!r})".format( 31 self.pid, self.uid, self.stat, self.args 32 ) 33 34 35class six(object): 36 """ 37 A light-weight version of six (which works on IronPython) 38 """ 39 40 PY3 = sys.version_info[0] >= 3 41 if sys.version_info >= (3, 4): 42 from abc import ABC 43 else: 44 from abc import ABCMeta 45 46 ABC = ABCMeta("ABC", (object,), {"__module__": __name__, "__slots__": ()}) 47 48 # Be sure to use named-tuple access, so that usage is not affected 49 try: 50 getfullargspec = staticmethod(inspect.getfullargspec) 51 except AttributeError: 52 getfullargspec = staticmethod( 53 inspect.getargspec 54 ) # extra fields will not be available 55 56 if PY3: 57 integer_types = (int,) 58 string_types = (str,) 59 MAXSIZE = sys.maxsize 60 ascii = ascii # @UndefinedVariable 61 bytes = bytes # @ReservedAssignment 62 unicode_type = str 63 64 @staticmethod 65 def b(s): 66 return s.encode("latin-1", "replace") 67 68 @staticmethod 69 def u(s): 70 return s 71 72 @staticmethod 73 def get_method_function(m): 74 return m.__func__ 75 76 else: 77 integer_types = (int, long) 78 string_types = (str, unicode) 79 MAXSIZE = getattr(sys, "maxsize", sys.maxint) 80 ascii = repr # @ReservedAssignment 81 bytes = str # @ReservedAssignment 82 unicode_type = unicode 83 84 @staticmethod 85 def b(st): 86 return st 87 88 @staticmethod 89 def u(s): 90 return s.decode("unicode-escape") 91 92 @staticmethod 93 def get_method_function(m): 94 return m.im_func 95 96 str = unicode_type 97 98 99# Try/except fails because io has the wrong StringIO in Python2 100# You'll get str/unicode errors 101if sys.version_info >= (3, 0): 102 from io import StringIO 103else: 104 from StringIO import StringIO 105 106if sys.version_info >= (3,): 107 from glob import escape as glob_escape 108else: 109 _magic_check = re.compile(u"([*?[])") 110 _magic_check_bytes = re.compile(b"([*?[])") 111 112 def glob_escape(pathname): 113 drive, pathname = os.path.splitdrive(pathname) 114 if isinstance(pathname, str): 115 pathname = _magic_check_bytes.sub(r"[\1]", pathname) 116 else: 117 pathname = _magic_check.sub(u"[\\1]", pathname) 118 return drive + pathname 119 120 121@contextmanager 122def captured_stdout(stdin=""): 123 """ 124 Captures stdout (similar to the redirect_stdout in Python 3.4+, but with slightly different arguments) 125 """ 126 prevstdin = sys.stdin 127 prevstdout = sys.stdout 128 sys.stdin = StringIO(six.u(stdin)) 129 sys.stdout = StringIO() 130 try: 131 yield sys.stdout 132 finally: 133 sys.stdin = prevstdin 134 sys.stdout = prevstdout 135 136 137class StaticProperty(object): 138 """This acts like a static property, allowing access via class or object. 139 This is a non-data descriptor.""" 140 141 def __init__(self, function): 142 self._function = function 143 self.__doc__ = function.__doc__ 144 145 def __get__(self, obj, klass=None): 146 return self._function() 147 148 149def getdoc(object): 150 """ 151 This gets a docstring if available, and cleans it, but does not look up docs in 152 inheritance tree (Pre 3.5 behavior of ``inspect.getdoc``). 153 """ 154 try: 155 doc = object.__doc__ 156 except AttributeError: 157 return None 158 if not isinstance(doc, str): 159 return None 160 return inspect.cleandoc(doc) 161 162 163def read_fd_decode_safely(fd, size=4096): 164 """ 165 This reads a utf-8 file descriptor and returns a chunck, growing up to 166 three bytes if needed to decode the character at the end. 167 168 Returns the data and the decoded text. 169 """ 170 data = os.read(fd.fileno(), size) 171 for i in range(4): 172 try: 173 return data, data.decode("utf-8") 174 except UnicodeDecodeError as e: 175 if e.reason != "unexpected end of data": 176 raise 177 if i == 3: 178 raise 179 data += os.read(fd.fileno(), 1) 180