1"""Utilities for writing code that runs on Python 2 and 3"""
2
3# Copyright (c) 2010-2014 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.5.2"
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) # Invokes __set__.
88        # This is a bit ugly, but it avoids running this again.
89        delattr(obj.__class__, 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    def __getattr__(self, attr):
108        # Hack around the Django autoreloader. The reloader tries to get
109        # __file__ or __name__ of every module in sys.modules. This doesn't work
110        # well if this MovedModule is for an module that is unavailable on this
111        # machine (like winreg on Unix systems). Thus, we pretend __file__ and
112        # __name__ don't exist if the module hasn't been loaded yet. See issues
113        # #51 and #53.
114        if attr in ("__file__", "__name__") and self.mod not in sys.modules:
115            raise AttributeError
116        _module = self._resolve()
117        value = getattr(_module, attr)
118        setattr(self, attr, value)
119        return value
120
121
122class _LazyModule(types.ModuleType):
123
124    def __init__(self, name):
125        super(_LazyModule, self).__init__(name)
126        self.__doc__ = self.__class__.__doc__
127
128    def __dir__(self):
129        attrs = ["__doc__", "__name__"]
130        attrs += [attr.name for attr in self._moved_attributes]
131        return attrs
132
133    # Subclasses should override this
134    _moved_attributes = []
135
136
137class MovedAttribute(_LazyDescr):
138
139    def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
140        super(MovedAttribute, self).__init__(name)
141        if PY3:
142            if new_mod is None:
143                new_mod = name
144            self.mod = new_mod
145            if new_attr is None:
146                if old_attr is None:
147                    new_attr = name
148                else:
149                    new_attr = old_attr
150            self.attr = new_attr
151        else:
152            self.mod = old_mod
153            if old_attr is None:
154                old_attr = name
155            self.attr = old_attr
156
157    def _resolve(self):
158        module = _import_module(self.mod)
159        return getattr(module, self.attr)
160
161
162
163class _MovedItems(_LazyModule):
164    """Lazy loading of moved objects"""
165
166
167_moved_attributes = [
168    MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
169    MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
170    MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"),
171    MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
172    MovedAttribute("map", "itertools", "builtins", "imap", "map"),
173    MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"),
174    MovedAttribute("reload_module", "__builtin__", "imp", "reload"),
175    MovedAttribute("reduce", "__builtin__", "functools"),
176    MovedAttribute("StringIO", "StringIO", "io"),
177    MovedAttribute("UserString", "UserString", "collections"),
178    MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
179    MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
180    MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"),
181
182    MovedModule("builtins", "__builtin__"),
183    MovedModule("configparser", "ConfigParser"),
184    MovedModule("copyreg", "copy_reg"),
185    MovedModule("dbm_gnu", "gdbm", "dbm.gnu"),
186    MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
187    MovedModule("http_cookies", "Cookie", "http.cookies"),
188    MovedModule("html_entities", "htmlentitydefs", "html.entities"),
189    MovedModule("html_parser", "HTMLParser", "html.parser"),
190    MovedModule("http_client", "httplib", "http.client"),
191    MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"),
192    MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"),
193    MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"),
194    MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
195    MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
196    MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
197    MovedModule("cPickle", "cPickle", "pickle"),
198    MovedModule("queue", "Queue"),
199    MovedModule("reprlib", "repr"),
200    MovedModule("socketserver", "SocketServer"),
201    MovedModule("_thread", "thread", "_thread"),
202    MovedModule("tkinter", "Tkinter"),
203    MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
204    MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
205    MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
206    MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
207    MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
208    MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"),
209    MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
210    MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
211    MovedModule("tkinter_colorchooser", "tkColorChooser",
212                "tkinter.colorchooser"),
213    MovedModule("tkinter_commondialog", "tkCommonDialog",
214                "tkinter.commondialog"),
215    MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
216    MovedModule("tkinter_font", "tkFont", "tkinter.font"),
217    MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
218    MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
219                "tkinter.simpledialog"),
220    MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"),
221    MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"),
222    MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"),
223    MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
224    MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"),
225    MovedModule("winreg", "_winreg"),
226]
227for attr in _moved_attributes:
228    setattr(_MovedItems, attr.name, attr)
229    if isinstance(attr, MovedModule):
230        sys.modules[__name__ + ".moves." + attr.name] = attr
231del attr
232
233_MovedItems._moved_attributes = _moved_attributes
234
235moves = sys.modules[__name__ + ".moves"] = _MovedItems(__name__ + ".moves")
236
237
238class Module_six_moves_urllib_parse(_LazyModule):
239    """Lazy loading of moved objects in six.moves.urllib_parse"""
240
241
242_urllib_parse_moved_attributes = [
243    MovedAttribute("ParseResult", "urlparse", "urllib.parse"),
244    MovedAttribute("parse_qs", "urlparse", "urllib.parse"),
245    MovedAttribute("parse_qsl", "urlparse", "urllib.parse"),
246    MovedAttribute("urldefrag", "urlparse", "urllib.parse"),
247    MovedAttribute("urljoin", "urlparse", "urllib.parse"),
248    MovedAttribute("urlparse", "urlparse", "urllib.parse"),
249    MovedAttribute("urlsplit", "urlparse", "urllib.parse"),
250    MovedAttribute("urlunparse", "urlparse", "urllib.parse"),
251    MovedAttribute("urlunsplit", "urlparse", "urllib.parse"),
252    MovedAttribute("quote", "urllib", "urllib.parse"),
253    MovedAttribute("quote_plus", "urllib", "urllib.parse"),
254    MovedAttribute("unquote", "urllib", "urllib.parse"),
255    MovedAttribute("unquote_plus", "urllib", "urllib.parse"),
256    MovedAttribute("urlencode", "urllib", "urllib.parse"),
257]
258for attr in _urllib_parse_moved_attributes:
259    setattr(Module_six_moves_urllib_parse, attr.name, attr)
260del attr
261
262Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes
263
264sys.modules[__name__ + ".moves.urllib_parse"] = sys.modules[__name__ + ".moves.urllib.parse"] = Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse")
265
266
267class Module_six_moves_urllib_error(_LazyModule):
268    """Lazy loading of moved objects in six.moves.urllib_error"""
269
270
271_urllib_error_moved_attributes = [
272    MovedAttribute("URLError", "urllib2", "urllib.error"),
273    MovedAttribute("HTTPError", "urllib2", "urllib.error"),
274    MovedAttribute("ContentTooShortError", "urllib", "urllib.error"),
275]
276for attr in _urllib_error_moved_attributes:
277    setattr(Module_six_moves_urllib_error, attr.name, attr)
278del attr
279
280Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes
281
282sys.modules[__name__ + ".moves.urllib_error"] = sys.modules[__name__ + ".moves.urllib.error"] = Module_six_moves_urllib_error(__name__ + ".moves.urllib.error")
283
284
285class Module_six_moves_urllib_request(_LazyModule):
286    """Lazy loading of moved objects in six.moves.urllib_request"""
287
288
289_urllib_request_moved_attributes = [
290    MovedAttribute("urlopen", "urllib2", "urllib.request"),
291    MovedAttribute("install_opener", "urllib2", "urllib.request"),
292    MovedAttribute("build_opener", "urllib2", "urllib.request"),
293    MovedAttribute("pathname2url", "urllib", "urllib.request"),
294    MovedAttribute("url2pathname", "urllib", "urllib.request"),
295    MovedAttribute("getproxies", "urllib", "urllib.request"),
296    MovedAttribute("Request", "urllib2", "urllib.request"),
297    MovedAttribute("OpenerDirector", "urllib2", "urllib.request"),
298    MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"),
299    MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"),
300    MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"),
301    MovedAttribute("ProxyHandler", "urllib2", "urllib.request"),
302    MovedAttribute("BaseHandler", "urllib2", "urllib.request"),
303    MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"),
304    MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"),
305    MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"),
306    MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"),
307    MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"),
308    MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"),
309    MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"),
310    MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"),
311    MovedAttribute("HTTPHandler", "urllib2", "urllib.request"),
312    MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"),
313    MovedAttribute("FileHandler", "urllib2", "urllib.request"),
314    MovedAttribute("FTPHandler", "urllib2", "urllib.request"),
315    MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"),
316    MovedAttribute("UnknownHandler", "urllib2", "urllib.request"),
317    MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"),
318    MovedAttribute("urlretrieve", "urllib", "urllib.request"),
319    MovedAttribute("urlcleanup", "urllib", "urllib.request"),
320    MovedAttribute("URLopener", "urllib", "urllib.request"),
321    MovedAttribute("FancyURLopener", "urllib", "urllib.request"),
322    MovedAttribute("proxy_bypass", "urllib", "urllib.request"),
323]
324for attr in _urllib_request_moved_attributes:
325    setattr(Module_six_moves_urllib_request, attr.name, attr)
326del attr
327
328Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes
329
330sys.modules[__name__ + ".moves.urllib_request"] = sys.modules[__name__ + ".moves.urllib.request"] = Module_six_moves_urllib_request(__name__ + ".moves.urllib.request")
331
332
333class Module_six_moves_urllib_response(_LazyModule):
334    """Lazy loading of moved objects in six.moves.urllib_response"""
335
336
337_urllib_response_moved_attributes = [
338    MovedAttribute("addbase", "urllib", "urllib.response"),
339    MovedAttribute("addclosehook", "urllib", "urllib.response"),
340    MovedAttribute("addinfo", "urllib", "urllib.response"),
341    MovedAttribute("addinfourl", "urllib", "urllib.response"),
342]
343for attr in _urllib_response_moved_attributes:
344    setattr(Module_six_moves_urllib_response, attr.name, attr)
345del attr
346
347Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes
348
349sys.modules[__name__ + ".moves.urllib_response"] = sys.modules[__name__ + ".moves.urllib.response"] = Module_six_moves_urllib_response(__name__ + ".moves.urllib.response")
350
351
352class Module_six_moves_urllib_robotparser(_LazyModule):
353    """Lazy loading of moved objects in six.moves.urllib_robotparser"""
354
355
356_urllib_robotparser_moved_attributes = [
357    MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"),
358]
359for attr in _urllib_robotparser_moved_attributes:
360    setattr(Module_six_moves_urllib_robotparser, attr.name, attr)
361del attr
362
363Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes
364
365sys.modules[__name__ + ".moves.urllib_robotparser"] = sys.modules[__name__ + ".moves.urllib.robotparser"] = Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser")
366
367
368class Module_six_moves_urllib(types.ModuleType):
369    """Create a six.moves.urllib namespace that resembles the Python 3 namespace"""
370    parse = sys.modules[__name__ + ".moves.urllib_parse"]
371    error = sys.modules[__name__ + ".moves.urllib_error"]
372    request = sys.modules[__name__ + ".moves.urllib_request"]
373    response = sys.modules[__name__ + ".moves.urllib_response"]
374    robotparser = sys.modules[__name__ + ".moves.urllib_robotparser"]
375
376    def __dir__(self):
377        return ['parse', 'error', 'request', 'response', 'robotparser']
378
379
380sys.modules[__name__ + ".moves.urllib"] = Module_six_moves_urllib(__name__ + ".moves.urllib")
381
382
383def add_move(move):
384    """Add an item to six.moves."""
385    setattr(_MovedItems, move.name, move)
386
387
388def remove_move(name):
389    """Remove item from six.moves."""
390    try:
391        delattr(_MovedItems, name)
392    except AttributeError:
393        try:
394            del moves.__dict__[name]
395        except KeyError:
396            raise AttributeError("no such move, %r" % (name,))
397
398
399if PY3:
400    _meth_func = "__func__"
401    _meth_self = "__self__"
402
403    _func_closure = "__closure__"
404    _func_code = "__code__"
405    _func_defaults = "__defaults__"
406    _func_globals = "__globals__"
407
408    _iterkeys = "keys"
409    _itervalues = "values"
410    _iteritems = "items"
411    _iterlists = "lists"
412else:
413    _meth_func = "im_func"
414    _meth_self = "im_self"
415
416    _func_closure = "func_closure"
417    _func_code = "func_code"
418    _func_defaults = "func_defaults"
419    _func_globals = "func_globals"
420
421    _iterkeys = "iterkeys"
422    _itervalues = "itervalues"
423    _iteritems = "iteritems"
424    _iterlists = "iterlists"
425
426
427try:
428    advance_iterator = next
429except NameError:
430    def advance_iterator(it):
431        return it.next()
432next = advance_iterator
433
434
435try:
436    callable = callable
437except NameError:
438    def callable(obj):
439        return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
440
441
442if PY3:
443    def get_unbound_function(unbound):
444        return unbound
445
446    create_bound_method = types.MethodType
447
448    Iterator = object
449else:
450    def get_unbound_function(unbound):
451        return unbound.im_func
452
453    def create_bound_method(func, obj):
454        return types.MethodType(func, obj, obj.__class__)
455
456    class Iterator(object):
457
458        def next(self):
459            return type(self).__next__(self)
460
461    callable = callable
462_add_doc(get_unbound_function,
463         """Get the function out of a possibly unbound function""")
464
465
466get_method_function = operator.attrgetter(_meth_func)
467get_method_self = operator.attrgetter(_meth_self)
468get_function_closure = operator.attrgetter(_func_closure)
469get_function_code = operator.attrgetter(_func_code)
470get_function_defaults = operator.attrgetter(_func_defaults)
471get_function_globals = operator.attrgetter(_func_globals)
472
473
474def iterkeys(d, **kw):
475    """Return an iterator over the keys of a dictionary."""
476    return iter(getattr(d, _iterkeys)(**kw))
477
478def itervalues(d, **kw):
479    """Return an iterator over the values of a dictionary."""
480    return iter(getattr(d, _itervalues)(**kw))
481
482def iteritems(d, **kw):
483    """Return an iterator over the (key, value) pairs of a dictionary."""
484    return iter(getattr(d, _iteritems)(**kw))
485
486def iterlists(d, **kw):
487    """Return an iterator over the (key, [values]) pairs of a dictionary."""
488    return iter(getattr(d, _iterlists)(**kw))
489
490
491if PY3:
492    def b(s):
493        return s.encode("latin-1")
494    def u(s):
495        return s
496    unichr = chr
497    if sys.version_info[1] <= 1:
498        def int2byte(i):
499            return bytes((i,))
500    else:
501        # This is about 2x faster than the implementation above on 3.2+
502        int2byte = operator.methodcaller("to_bytes", 1, "big")
503    byte2int = operator.itemgetter(0)
504    indexbytes = operator.getitem
505    iterbytes = iter
506    import io
507    StringIO = io.StringIO
508    BytesIO = io.BytesIO
509else:
510    def b(s):
511        return s
512    # Workaround for standalone backslash
513    def u(s):
514        return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape")
515    unichr = unichr
516    int2byte = chr
517    def byte2int(bs):
518        return ord(bs[0])
519    def indexbytes(buf, i):
520        return ord(buf[i])
521    def iterbytes(buf):
522        return (ord(byte) for byte in buf)
523    import StringIO
524    StringIO = BytesIO = StringIO.StringIO
525_add_doc(b, """Byte literal""")
526_add_doc(u, """Text literal""")
527
528
529if PY3:
530    exec_ = getattr(moves.builtins, "exec")
531
532
533    def reraise(tp, value, tb=None):
534        if value.__traceback__ is not tb:
535            raise value.with_traceback(tb)
536        raise value
537
538else:
539    def exec_(_code_, _globs_=None, _locs_=None):
540        """Execute code in a namespace."""
541        if _globs_ is None:
542            frame = sys._getframe(1)
543            _globs_ = frame.f_globals
544            if _locs_ is None:
545                _locs_ = frame.f_locals
546            del frame
547        elif _locs_ is None:
548            _locs_ = _globs_
549        exec("""exec _code_ in _globs_, _locs_""")
550
551
552    exec_("""def reraise(tp, value, tb=None):
553    raise tp, value, tb
554""")
555
556
557print_ = getattr(moves.builtins, "print", None)
558if print_ is None:
559    def print_(*args, **kwargs):
560        """The new-style print function for Python 2.4 and 2.5."""
561        fp = kwargs.pop("file", sys.stdout)
562        if fp is None:
563            return
564        def write(data):
565            if not isinstance(data, basestring):
566                data = str(data)
567            # If the file has an encoding, encode unicode with it.
568            if (isinstance(fp, file) and
569                isinstance(data, unicode) and
570                fp.encoding is not None):
571                errors = getattr(fp, "errors", None)
572                if errors is None:
573                    errors = "strict"
574                data = data.encode(fp.encoding, errors)
575            fp.write(data)
576        want_unicode = False
577        sep = kwargs.pop("sep", None)
578        if sep is not None:
579            if isinstance(sep, unicode):
580                want_unicode = True
581            elif not isinstance(sep, str):
582                raise TypeError("sep must be None or a string")
583        end = kwargs.pop("end", None)
584        if end is not None:
585            if isinstance(end, unicode):
586                want_unicode = True
587            elif not isinstance(end, str):
588                raise TypeError("end must be None or a string")
589        if kwargs:
590            raise TypeError("invalid keyword arguments to print()")
591        if not want_unicode:
592            for arg in args:
593                if isinstance(arg, unicode):
594                    want_unicode = True
595                    break
596        if want_unicode:
597            newline = unicode("\n")
598            space = unicode(" ")
599        else:
600            newline = "\n"
601            space = " "
602        if sep is None:
603            sep = space
604        if end is None:
605            end = newline
606        for i, arg in enumerate(args):
607            if i:
608                write(sep)
609            write(arg)
610        write(end)
611
612_add_doc(reraise, """Reraise an exception.""")
613
614
615def with_metaclass(meta, *bases):
616    """Create a base class with a metaclass."""
617    return meta("NewBase", bases, {})
618
619def add_metaclass(metaclass):
620    """Class decorator for creating a class with a metaclass."""
621    def wrapper(cls):
622        orig_vars = cls.__dict__.copy()
623        orig_vars.pop('__dict__', None)
624        orig_vars.pop('__weakref__', None)
625        slots = orig_vars.get('__slots__')
626        if slots is not None:
627            if isinstance(slots, str):
628                slots = [slots]
629            for slots_var in slots:
630                orig_vars.pop(slots_var)
631        return metaclass(cls.__name__, cls.__bases__, orig_vars)
632    return wrapper
633