• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""Python 2/3 compat layer leftovers."""
2
3import decimal as _decimal
4import math as _math
5import warnings
6from contextlib import redirect_stderr, redirect_stdout
7from io import BytesIO
8from io import StringIO as UnicodeIO
9from types import SimpleNamespace
10
11warnings.warn(
12    "The py23 module has been deprecated and will be removed in a future release. "
13    "Please update your code.",
14    DeprecationWarning,
15)
16
17__all__ = [
18    "basestring",
19    "bytechr",
20    "byteord",
21    "BytesIO",
22    "bytesjoin",
23    "open",
24    "Py23Error",
25    "range",
26    "RecursionError",
27    "round",
28    "SimpleNamespace",
29    "StringIO",
30    "strjoin",
31    "Tag",
32    "tobytes",
33    "tostr",
34    "tounicode",
35    "unichr",
36    "unicode",
37    "UnicodeIO",
38    "xrange",
39    "zip",
40]
41
42
43class Py23Error(NotImplementedError):
44    pass
45
46
47RecursionError = RecursionError
48StringIO = UnicodeIO
49
50basestring = str
51isclose = _math.isclose
52isfinite = _math.isfinite
53open = open
54range = range
55round = round3 = round
56unichr = chr
57unicode = str
58zip = zip
59
60
61def bytechr(n):
62    return bytes([n])
63
64
65def byteord(c):
66    return c if isinstance(c, int) else ord(c)
67
68
69def strjoin(iterable, joiner=""):
70    return tostr(joiner).join(iterable)
71
72
73def tobytes(s, encoding="ascii", errors="strict"):
74    if not isinstance(s, bytes):
75        return s.encode(encoding, errors)
76    else:
77        return s
78
79
80def tounicode(s, encoding="ascii", errors="strict"):
81    if not isinstance(s, unicode):
82        return s.decode(encoding, errors)
83    else:
84        return s
85
86
87tostr = tounicode
88
89
90class Tag(str):
91    @staticmethod
92    def transcode(blob):
93        if isinstance(blob, bytes):
94            blob = blob.decode("latin-1")
95        return blob
96
97    def __new__(self, content):
98        return str.__new__(self, self.transcode(content))
99
100    def __ne__(self, other):
101        return not self.__eq__(other)
102
103    def __eq__(self, other):
104        return str.__eq__(self, self.transcode(other))
105
106    def __hash__(self):
107        return str.__hash__(self)
108
109    def tobytes(self):
110        return self.encode("latin-1")
111
112
113def bytesjoin(iterable, joiner=b""):
114    return tobytes(joiner).join(tobytes(item) for item in iterable)
115
116
117def xrange(*args, **kwargs):
118    raise Py23Error("'xrange' is not defined. Use 'range' instead.")
119
120
121def round2(number, ndigits=None):
122    """
123    Implementation of Python 2 built-in round() function.
124    Rounds a number to a given precision in decimal digits (default
125    0 digits). The result is a floating point number. Values are rounded
126    to the closest multiple of 10 to the power minus ndigits; if two
127    multiples are equally close, rounding is done away from 0.
128    ndigits may be negative.
129    See Python 2 documentation:
130    https://docs.python.org/2/library/functions.html?highlight=round#round
131    """
132    if ndigits is None:
133        ndigits = 0
134
135    if ndigits < 0:
136        exponent = 10 ** (-ndigits)
137        quotient, remainder = divmod(number, exponent)
138        if remainder >= exponent // 2 and number >= 0:
139            quotient += 1
140        return float(quotient * exponent)
141    else:
142        exponent = _decimal.Decimal("10") ** (-ndigits)
143
144        d = _decimal.Decimal.from_float(number).quantize(
145            exponent, rounding=_decimal.ROUND_HALF_UP
146        )
147
148        return float(d)
149