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