1"""Utilities for writing code that runs on Python 2 and 3"""
2
3# Copyright (c) 2010-2013 Benjamin Peterson
4#
5# Permission is hereby granted, free of charge, to any person obtaining a copy
6# of this software and associated documentation files (the "Software"), to deal
7# in the Software without restriction, including without limitation the rights
8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9# copies of the Software, and to permit persons to whom the Software is
10# furnished to do so, subject to the following conditions:
11#
12# The above copyright notice and this permission notice shall be included in all
13# copies or substantial portions of the Software.
14#
15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21# SOFTWARE.
22
23import operator
24import sys
25import types
26
27__author__ = "Benjamin Peterson <benjamin@python.org>"
28__version__ = "1.4.1"
29
30
31# Useful for very coarse version differentiation.
32PY2 = sys.version_info[0] == 2
33PY3 = sys.version_info[0] == 3
34
35if PY3:
36    string_types = str,
37    integer_types = int,
38    class_types = type,
39    text_type = str
40    binary_type = bytes
41
42    MAXSIZE = sys.maxsize
43else:
44    string_types = basestring,
45    integer_types = (int, long)
46    class_types = (type, types.ClassType)
47    text_type = unicode
48    binary_type = str
49
50    if sys.platform.startswith("java"):
51        # Jython always uses 32 bits.
52        MAXSIZE = int((1 << 31) - 1)
53    else:
54        # It's possible to have sizeof(long) != sizeof(Py_ssize_t).
55        class X(object):
56            def __len__(self):
57                return 1 << 31
58        try:
59            len(X())
60        except OverflowError:
61            # 32-bit
62            MAXSIZE = int((1 << 31) - 1)
63        else:
64            # 64-bit
65            MAXSIZE = int((1 << 63) - 1)
66        del X
67
68
69def _add_doc(func, doc):
70    """Add documentation to a function."""
71    func.__doc__ = doc
72
73
74def _import_module(name):
75    """Import module, returning the module after the last dot."""
76    __import__(name)
77    return sys.modules[name]
78
79
80class _LazyDescr(object):
81
82    def __init__(self, name):
83        self.name = name
84
85    def __get__(self, obj, tp):
86        result = self._resolve()
87        setattr(obj, self.name, result)
88        # This is a bit ugly, but it avoids running this again.
89        delattr(tp, self.name)
90        return result
91
92
93class MovedModule(_LazyDescr):
94
95    def __init__(self, name, old, new=None):
96        super(MovedModule, self).__init__(name)
97        if PY3:
98            if new is None:
99                new = name
100            self.mod = new
101        else:
102            self.mod = old
103
104    def _resolve(self):
105        return _import_module(self.mod)
106
107
108class MovedAttribute(_LazyDescr):
109
110    def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
111        super(MovedAttribute, self).__init__(name)
112        if PY3:
113            if new_mod is None:
114                new_mod = name
115            self.mod = new_mod
116            if new_attr is None:
117                if old_attr is None:
118                    new_attr = name
119                else:
120                    new_attr = old_attr
121            self.attr = new_attr
122        else:
123            self.mod = old_mod
124            if old_attr is None:
125                old_attr = name
126            self.attr = old_attr
127
128    def _resolve(self):
129        module = _import_module(self.mod)
130        return getattr(module, self.attr)
131
132
133
134class _MovedItems(types.ModuleType):
135    """Lazy loading of moved objects"""
136
137
138_moved_attributes = [
139    MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
140    MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
141    MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"),
142    MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
143    MovedAttribute("map", "itertools", "builtins", "imap", "map"),
144    MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"),
145    MovedAttribute("reload_module", "__builtin__", "imp", "reload"),
146    MovedAttribute("reduce", "__builtin__", "functools"),
147    MovedAttribute("StringIO", "StringIO", "io"),
148    MovedAttribute("UserString", "UserString", "collections"),
149    MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
150    MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
151    MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"),
152
153    MovedModule("builtins", "__builtin__"),
154    MovedModule("configparser", "ConfigParser"),
155    MovedModule("copyreg", "copy_reg"),
156    MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
157    MovedModule("http_cookies", "Cookie", "http.cookies"),
158    MovedModule("html_entities", "htmlentitydefs", "html.entities"),
159    MovedModule("html_parser", "HTMLParser", "html.parser"),
160    MovedModule("http_client", "httplib", "http.client"),
161    MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"),
162    MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"),
163    MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"),
164    MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
165    MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
166    MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
167    MovedModule("cPickle", "cPickle", "pickle"),
168    MovedModule("queue", "Queue"),
169    MovedModule("reprlib", "repr"),
170    MovedModule("socketserver", "SocketServer"),
171    MovedModule("tkinter", "Tkinter"),
172    MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
173    MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
174    MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
175    MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
176    MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
177    MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
178    MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
179    MovedModule("tkinter_colorchooser", "tkColorChooser",
180                "tkinter.colorchooser"),
181    MovedModule("tkinter_commondialog", "tkCommonDialog",
182                "tkinter.commondialog"),
183    MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
184    MovedModule("tkinter_font", "tkFont", "tkinter.font"),
185    MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
186    MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
187                "tkinter.simpledialog"),
188    MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"),
189    MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"),
190    MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"),
191    MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
192    MovedModule("winreg", "_winreg"),
193]
194for attr in _moved_attributes:
195    setattr(_MovedItems, attr.name, attr)
196del attr
197
198moves = sys.modules[__name__ + ".moves"] = _MovedItems(__name__ + ".moves")
199
200
201
202class Module_six_moves_urllib_parse(types.ModuleType):
203    """Lazy loading of moved objects in six.moves.urllib_parse"""
204
205
206_urllib_parse_moved_attributes = [
207    MovedAttribute("ParseResult", "urlparse", "urllib.parse"),
208    MovedAttribute("parse_qs", "urlparse", "urllib.parse"),
209    MovedAttribute("parse_qsl", "urlparse", "urllib.parse"),
210    MovedAttribute("urldefrag", "urlparse", "urllib.parse"),
211    MovedAttribute("urljoin", "urlparse", "urllib.parse"),
212    MovedAttribute("urlparse", "urlparse", "urllib.parse"),
213    MovedAttribute("urlsplit", "urlparse", "urllib.parse"),
214    MovedAttribute("urlunparse", "urlparse", "urllib.parse"),
215    MovedAttribute("urlunsplit", "urlparse", "urllib.parse"),
216    MovedAttribute("quote", "urllib", "urllib.parse"),
217    MovedAttribute("quote_plus", "urllib", "urllib.parse"),
218    MovedAttribute("unquote", "urllib", "urllib.parse"),
219    MovedAttribute("unquote_plus", "urllib", "urllib.parse"),
220    MovedAttribute("urlencode", "urllib", "urllib.parse"),
221]
222for attr in _urllib_parse_moved_attributes:
223    setattr(Module_six_moves_urllib_parse, attr.name, attr)
224del attr
225
226sys.modules[__name__ + ".moves.urllib_parse"] = Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse")
227sys.modules[__name__ + ".moves.urllib.parse"] = Module_six_moves_urllib_parse(__name__ + ".moves.urllib.parse")
228
229
230class Module_six_moves_urllib_error(types.ModuleType):
231    """Lazy loading of moved objects in six.moves.urllib_error"""
232
233
234_urllib_error_moved_attributes = [
235    MovedAttribute("URLError", "urllib2", "urllib.error"),
236    MovedAttribute("HTTPError", "urllib2", "urllib.error"),
237    MovedAttribute("ContentTooShortError", "urllib", "urllib.error"),
238]
239for attr in _urllib_error_moved_attributes:
240    setattr(Module_six_moves_urllib_error, attr.name, attr)
241del attr
242
243sys.modules[__name__ + ".moves.urllib_error"] = Module_six_moves_urllib_error(__name__ + ".moves.urllib_error")
244sys.modules[__name__ + ".moves.urllib.error"] = Module_six_moves_urllib_error(__name__ + ".moves.urllib.error")
245
246
247class Module_six_moves_urllib_request(types.ModuleType):
248    """Lazy loading of moved objects in six.moves.urllib_request"""
249
250
251_urllib_request_moved_attributes = [
252    MovedAttribute("urlopen", "urllib2", "urllib.request"),
253    MovedAttribute("install_opener", "urllib2", "urllib.request"),
254    MovedAttribute("build_opener", "urllib2", "urllib.request"),
255    MovedAttribute("pathname2url", "urllib", "urllib.request"),
256    MovedAttribute("url2pathname", "urllib", "urllib.request"),
257    MovedAttribute("getproxies", "urllib", "urllib.request"),
258    MovedAttribute("Request", "urllib2", "urllib.request"),
259    MovedAttribute("OpenerDirector", "urllib2", "urllib.request"),
260    MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"),
261    MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"),
262    MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"),
263    MovedAttribute("ProxyHandler", "urllib2", "urllib.request"),
264    MovedAttribute("BaseHandler", "urllib2", "urllib.request"),
265    MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"),
266    MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"),
267    MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"),
268    MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"),
269    MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"),
270    MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"),
271    MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"),
272    MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"),
273    MovedAttribute("HTTPHandler", "urllib2", "urllib.request"),
274    MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"),
275    MovedAttribute("FileHandler", "urllib2", "urllib.request"),
276    MovedAttribute("FTPHandler", "urllib2", "urllib.request"),
277    MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"),
278    MovedAttribute("UnknownHandler", "urllib2", "urllib.request"),
279    MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"),
280    MovedAttribute("urlretrieve", "urllib", "urllib.request"),
281    MovedAttribute("urlcleanup", "urllib", "urllib.request"),
282    MovedAttribute("URLopener", "urllib", "urllib.request"),
283    MovedAttribute("FancyURLopener", "urllib", "urllib.request"),
284]
285for attr in _urllib_request_moved_attributes:
286    setattr(Module_six_moves_urllib_request, attr.name, attr)
287del attr
288
289sys.modules[__name__ + ".moves.urllib_request"] = Module_six_moves_urllib_request(__name__ + ".moves.urllib_request")
290sys.modules[__name__ + ".moves.urllib.request"] = Module_six_moves_urllib_request(__name__ + ".moves.urllib.request")
291
292
293class Module_six_moves_urllib_response(types.ModuleType):
294    """Lazy loading of moved objects in six.moves.urllib_response"""
295
296
297_urllib_response_moved_attributes = [
298    MovedAttribute("addbase", "urllib", "urllib.response"),
299    MovedAttribute("addclosehook", "urllib", "urllib.response"),
300    MovedAttribute("addinfo", "urllib", "urllib.response"),
301    MovedAttribute("addinfourl", "urllib", "urllib.response"),
302]
303for attr in _urllib_response_moved_attributes:
304    setattr(Module_six_moves_urllib_response, attr.name, attr)
305del attr
306
307sys.modules[__name__ + ".moves.urllib_response"] = Module_six_moves_urllib_response(__name__ + ".moves.urllib_response")
308sys.modules[__name__ + ".moves.urllib.response"] = Module_six_moves_urllib_response(__name__ + ".moves.urllib.response")
309
310
311class Module_six_moves_urllib_robotparser(types.ModuleType):
312    """Lazy loading of moved objects in six.moves.urllib_robotparser"""
313
314
315_urllib_robotparser_moved_attributes = [
316    MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"),
317]
318for attr in _urllib_robotparser_moved_attributes:
319    setattr(Module_six_moves_urllib_robotparser, attr.name, attr)
320del attr
321
322sys.modules[__name__ + ".moves.urllib_robotparser"] = Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib_robotparser")
323sys.modules[__name__ + ".moves.urllib.robotparser"] = Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser")
324
325
326class Module_six_moves_urllib(types.ModuleType):
327    """Create a six.moves.urllib namespace that resembles the Python 3 namespace"""
328    parse = sys.modules[__name__ + ".moves.urllib_parse"]
329    error = sys.modules[__name__ + ".moves.urllib_error"]
330    request = sys.modules[__name__ + ".moves.urllib_request"]
331    response = sys.modules[__name__ + ".moves.urllib_response"]
332    robotparser = sys.modules[__name__ + ".moves.urllib_robotparser"]
333
334
335sys.modules[__name__ + ".moves.urllib"] = Module_six_moves_urllib(__name__ + ".moves.urllib")
336
337
338def add_move(move):
339    """Add an item to six.moves."""
340    setattr(_MovedItems, move.name, move)
341
342
343def remove_move(name):
344    """Remove item from six.moves."""
345    try:
346        delattr(_MovedItems, name)
347    except AttributeError:
348        try:
349            del moves.__dict__[name]
350        except KeyError:
351            raise AttributeError("no such move, %r" % (name,))
352
353
354if PY3:
355    _meth_func = "__func__"
356    _meth_self = "__self__"
357
358    _func_closure = "__closure__"
359    _func_code = "__code__"
360    _func_defaults = "__defaults__"
361    _func_globals = "__globals__"
362
363    _iterkeys = "keys"
364    _itervalues = "values"
365    _iteritems = "items"
366    _iterlists = "lists"
367else:
368    _meth_func = "im_func"
369    _meth_self = "im_self"
370
371    _func_closure = "func_closure"
372    _func_code = "func_code"
373    _func_defaults = "func_defaults"
374    _func_globals = "func_globals"
375
376    _iterkeys = "iterkeys"
377    _itervalues = "itervalues"
378    _iteritems = "iteritems"
379    _iterlists = "iterlists"
380
381
382try:
383    advance_iterator = next
384except NameError:
385    def advance_iterator(it):
386        return it.next()
387next = advance_iterator
388
389
390try:
391    callable = callable
392except NameError:
393    def callable(obj):
394        return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
395
396
397if PY3:
398    def get_unbound_function(unbound):
399        return unbound
400
401    create_bound_method = types.MethodType
402
403    Iterator = object
404else:
405    def get_unbound_function(unbound):
406        return unbound.im_func
407
408    def create_bound_method(func, obj):
409        return types.MethodType(func, obj, obj.__class__)
410
411    class Iterator(object):
412
413        def next(self):
414            return type(self).__next__(self)
415
416    callable = callable
417_add_doc(get_unbound_function,
418         """Get the function out of a possibly unbound function""")
419
420
421get_method_function = operator.attrgetter(_meth_func)
422get_method_self = operator.attrgetter(_meth_self)
423get_function_closure = operator.attrgetter(_func_closure)
424get_function_code = operator.attrgetter(_func_code)
425get_function_defaults = operator.attrgetter(_func_defaults)
426get_function_globals = operator.attrgetter(_func_globals)
427
428
429def iterkeys(d, **kw):
430    """Return an iterator over the keys of a dictionary."""
431    return iter(getattr(d, _iterkeys)(**kw))
432
433def itervalues(d, **kw):
434    """Return an iterator over the values of a dictionary."""
435    return iter(getattr(d, _itervalues)(**kw))
436
437def iteritems(d, **kw):
438    """Return an iterator over the (key, value) pairs of a dictionary."""
439    return iter(getattr(d, _iteritems)(**kw))
440
441def iterlists(d, **kw):
442    """Return an iterator over the (key, [values]) pairs of a dictionary."""
443    return iter(getattr(d, _iterlists)(**kw))
444
445
446if PY3:
447    def b(s):
448        return s.encode("latin-1")
449    def u(s):
450        return s
451    unichr = chr
452    if sys.version_info[1] <= 1:
453        def int2byte(i):
454            return bytes((i,))
455    else:
456        # This is about 2x faster than the implementation above on 3.2+
457        int2byte = operator.methodcaller("to_bytes", 1, "big")
458    byte2int = operator.itemgetter(0)
459    indexbytes = operator.getitem
460    iterbytes = iter
461    import io
462    StringIO = io.StringIO
463    BytesIO = io.BytesIO
464else:
465    def b(s):
466        return s
467    def u(s):
468        return unicode(s, "unicode_escape")
469    unichr = unichr
470    int2byte = chr
471    def byte2int(bs):
472        return ord(bs[0])
473    def indexbytes(buf, i):
474        return ord(buf[i])
475    def iterbytes(buf):
476        return (ord(byte) for byte in buf)
477    import StringIO
478    StringIO = BytesIO = StringIO.StringIO
479_add_doc(b, """Byte literal""")
480_add_doc(u, """Text literal""")
481
482
483if PY3:
484    import builtins
485    exec_ = getattr(builtins, "exec")
486
487
488    def reraise(tp, value, tb=None):
489        if value.__traceback__ is not tb:
490            raise value.with_traceback(tb)
491        raise value
492
493
494    print_ = getattr(builtins, "print")
495    del builtins
496
497else:
498    def exec_(_code_, _globs_=None, _locs_=None):
499        """Execute code in a namespace."""
500        if _globs_ is None:
501            frame = sys._getframe(1)
502            _globs_ = frame.f_globals
503            if _locs_ is None:
504                _locs_ = frame.f_locals
505            del frame
506        elif _locs_ is None:
507            _locs_ = _globs_
508        exec("""exec _code_ in _globs_, _locs_""")
509
510
511    exec_("""def reraise(tp, value, tb=None):
512    raise tp, value, tb
513""")
514
515
516    def print_(*args, **kwargs):
517        """The new-style print function."""
518        fp = kwargs.pop("file", sys.stdout)
519        if fp is None:
520            return
521        def write(data):
522            if not isinstance(data, basestring):
523                data = str(data)
524            fp.write(data)
525        want_unicode = False
526        sep = kwargs.pop("sep", None)
527        if sep is not None:
528            if isinstance(sep, unicode):
529                want_unicode = True
530            elif not isinstance(sep, str):
531                raise TypeError("sep must be None or a string")
532        end = kwargs.pop("end", None)
533        if end is not None:
534            if isinstance(end, unicode):
535                want_unicode = True
536            elif not isinstance(end, str):
537                raise TypeError("end must be None or a string")
538        if kwargs:
539            raise TypeError("invalid keyword arguments to print()")
540        if not want_unicode:
541            for arg in args:
542                if isinstance(arg, unicode):
543                    want_unicode = True
544                    break
545        if want_unicode:
546            newline = unicode("\n")
547            space = unicode(" ")
548        else:
549            newline = "\n"
550            space = " "
551        if sep is None:
552            sep = space
553        if end is None:
554            end = newline
555        for i, arg in enumerate(args):
556            if i:
557                write(sep)
558            write(arg)
559        write(end)
560
561_add_doc(reraise, """Reraise an exception.""")
562
563
564def with_metaclass(meta, *bases):
565    """Create a base class with a metaclass."""
566    return meta("NewBase", bases, {})
567
568def add_metaclass(metaclass):
569    """Class decorator for creating a class with a metaclass."""
570    def wrapper(cls):
571        orig_vars = cls.__dict__.copy()
572        orig_vars.pop('__dict__', None)
573        orig_vars.pop('__weakref__', None)
574        for slots_var in orig_vars.get('__slots__', ()):
575            orig_vars.pop(slots_var)
576        return metaclass(cls.__name__, cls.__bases__, orig_vars)
577    return wrapper
578